Reputation: 10818
I'm trying to understand if it's a good idea to let an Object cancel a network operation in it's dealloc method, when the Object is referenced in the operation's completion block.
I'll try to explain with an example:
I have a User
object with property picture
, User
has a [self fetchPictureFromServer]
:
- (void)fetchPictureFromServer
{
NSDictionary *cmdParameters = ...; // set parameters
__block User *weakSelf = self;
[[AppClient sharedClient] sendCommand:@"getpicture" parameters:cmdParameters success:^(AFHTTPRequestOperation *operation, id response)
{
// success
UIImage *downloadedImage = ...; // get image from response
weakSelf.picture = downloadedImage; // set image to user's picture property
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// failed
}];
The sendCommand
method will create (and enqueue to NSOperationQueue
) a NSOperation
that in time will initiate a NSURLConnection
to download the image. When the operation is successfully finish the success block will be called.
I'm using weakSelf
because I don't want the block to retain my User object (and prevent it from being dealloc), I want that the user will be responsible to cancel an existing operation (if exists) when he gets dealloc.
So the User dealloc method look like this:
- (void)dealloc
{
[[AppClient sharedClient] cancelAllCommandsForSender:self];
[super dealloc];
}
But it seems that although I'm canceling the operation in the dealloc method, sometimes the success block is called after the user gets dealloc, and I get EXC_BAD_ACCESS
in line weakSelf.picture = downloadedImage
.
Why is it happening? Could it be that the operation try to finish the connection even if it was canceled?
I know that if I'll use just self and not weakSelf in the block, this will not happen because the block will retain the User. But I do not want this behavior, I want that the user will get dealloc immediately when it's released, even if a fetching operation exists.
UPDATE: I noticed that the operation state isFinished = YES, it seems that with the library I use, when an operation is finished, it doesn't execute the success block immediately, but it dispatch_async the success block with the main queue.
So what properly happen is:
1. user fetch image -> an operation is created
2. the operation finish -> dispatch_async the success block (still not executed)
3. before the success block is executed the user dealloc -> cancel all his operations (but not the operation from step1, because it is in isFinished state)
4. now the success block is executed -> I get EXC_BAD_ACCESS when try to reference user.
Still no idea how to solve this..
Upvotes: 2
Views: 815
Reputation: 8905
[[AppClient sharedClient] sendCommand:@"getpicture" parameters:cmdParameters success:^(AFHTTPRequestOperation *operation, id response)
{
if(!operation.isCancelled)
{
// success
UIImage *downloadedImage = ...; // get image from response
weakSelf.picture = downloadedImage; // set image to user's picture property
}
}
Upvotes: 2