Search code examples
objective-cmultithreadingnsoperationnsoperationqueue

How to check if NSOperationQueue is finished and if any operation failed?


I'm trying to parse some XML files in the background so that the UI doesn't freeze. I have to check two things:

  • NSOperationQueue is finished?
  • NSOperation - parsing did fail?

I have a class that subclasses NSOperation and a delegate is called if the parsing failed. Operations in the queue are limited to one simultaneously.

My problem is that I can't rely on the fact that the failed message is executed before I get the queue did finish message. Sometimes I don't get a failed message before I get the finished message. Then, for example, I have this order:

Operation 1 Successful Operation 2 Successful OperationQueue finished Operation 3 Failed

All messages are sent to the main thread. After I get the finished message I want to proceed in my code, but only if all operations were successful. How can I handle the problem that the delegate message is called after my queue is finished.

This are some parts of my code:

//XMLOperation.m
- (void)main {    
    NSLog(@"Operation started");

    if ([self parseXML]) {
            [self performSelectorOnMainThread:@selector(finishedXMLParsing) withObject:nil waitUntilDone:NO];
        } else {
            [self performSelectorOnMainThread:@selector(failedXMLParsing) withObject:nil waitUntilDone:NO];
        }
    }
    NSLog(@"Operation finished");
}


//StartController.m
[self.xmlParseQueue addObserver:self forKeyPath:@"operations" options:0 context:NULL];

...

- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object 
                         change:(NSDictionary *)change context:(void *)context
{
    if (object == self.xmlParseQueue && [keyPath isEqualToString:@"operations"]) {
        if ([self.xmlParseQueue.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];
    }
}

Solution

  • This probably happens because your KVO notification for the operations property of the queue is not necessarily delivered on the main thread while your finished/failed notifications are.

    You should ensure that the completion notification is performed on the main thread as well, so that the order of your notifications is defined.