Search code examples
iphoneasynchronousqueuenotificationsnsoperation

Get notification when NSOperationQueue finishes all tasks


NSOperationQueue has waitUntilAllOperationsAreFinished, but I don't want to wait synchronously for it. I just want to hide progress indicator in UI when queue finishes.

What's the best way to accomplish this?

I can't send notifications from my NSOperations, because I don't know which one is going to be last, and [queue operations] might not be empty yet (or worse - repopulated) when notification is received.


Solution

  • Use KVO to observe the operations property of your queue, then you can tell if your queue has completed by checking for [queue.operations count] == 0.

    Somewhere in the file you're doing the KVO in, declare a context for KVO like this (more info):

    static NSString *kQueueOperationsChanged = @"kQueueOperationsChanged";
    

    When you setup your queue, do this:

    [self.queue addObserver:self forKeyPath:@"operations" options:0 context:&kQueueOperationsChanged];
    

    Then do this in your observeValueForKeyPath:

    - (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object 
                             change:(NSDictionary *)change context:(void *)context
    {
        if (object == self.queue && [keyPath isEqualToString:@"operations"] && context == &kQueueOperationsChanged) {
            if ([self.queue.operations count] == 0) {
                // Do something here when your queue has completed
                NSLog(@"queue has completed");
            }
        }
        else {
            [super observeValueForKeyPath:keyPath ofObject:object 
                                   change:change context:context];
        }
    }
    

    (This is assuming that your NSOperationQueue is in a property named queue)

    At some point before your object fully deallocs (or when it stops caring about the queue state), you'll need to unregister from KVO like this:

    [self.queue removeObserver:self forKeyPath:@"operations" context:&kQueueOperationsChanged];
    


    Addendum: iOS 4.0 has an NSOperationQueue.operationCount property, which according to the docs is KVO compliant. This answer will still work in iOS 4.0 however, so it's still useful for backwards compatibility.