Eyal
Eyal

Reputation: 10818

Objective c - Canceling an operation in dealloc

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

Answers (1)

msk
msk

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

Related Questions