Search code examples
objective-camazon-s3objective-c-blocksnsoperationnsoperationqueue

At which moment exactly is completionBlock executed on NSOperation?


I am just wondering at what exact moment a completionBlock is executed on a NSOperation owned by a NSOperationQueue.

On my newest project, a client for Amazon S3 (https://github.com/StudioIstanbul/SIAFAWSClient), I use a NSOperation with a completionBlock for requests to Amazon REST API. The client is able to monitor the status of all scheduled requests via a property on the main class called isBusy. In my operation's completion block I set the value for this property to NO if there are no other operations scheduled in my NSOperationQueue. I now figured out that in some rare cases my current NSOperation is still included in my NSOperationQueue when the completionBlock gets called. This seems a little strange to me. I ended up checking for existence of the current NSOperation in my queue to fix this, but this seems unnecessary from a design point of view.

__weak AWSOperation* thisOperation = operation;
[operation setCompletionBlock:^{
    if (self.operationQueue.operationCount <= 0
      || (self.operationQueue.operationCount == 1
      && [self.operationQueue.operations objectAtIndex:0] == thisOperation)) {
        [self willChangeValueForKey:@"isBusy"];
        _isBusy = NO;
        [self didChangeValueForKey:@"isBusy"];
    }
}];

Does anybody have more information on this behavior?


Solution

  • From the docs:

    The exact execution context for your completion block is not guaranteed but is typically a secondary thread. Therefore, you should not use this block to do any work that requires a very specific execution context.

    So, you can't guarantee that it will be called before or after the operation is removed from the queue. It's sent after the trigger to remove the operation from the queue (because the operation is finished) but there is effectively a race between those 2 things.

    Your best option is to push your consideration to the next iteration of the main runloop if you're interrogating the operation queue itself to determine the state.