iosmultithreadingfacebooknsoperation

Adding NSOperation with Dependencies after a For Loop


So I'm trying to execute lastOperation after all the block operations, but for some reason it is being executed first. Why is this? Is adding dependencies the wrong way to do it?

[self facebookAccount:^(NSError *error, ACAccount *facebookAccount) {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue setMaxConcurrentOperationCount:1];
    NSBlockOperation *lastOperation = [NSBlockOperation blockOperationWithBlock:completionAll];
    for (NSString *postID in postIDs) {
        NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
            NSString *postIDString = [NSString stringWithFormat:@"https://graph.facebook.com/v2.0/%@", postID];
            NSURL *postIDURL = [NSURL URLWithString:postIDString];
            SLRequest *postIDRequest = [SLRequest requestForServiceType:SLServiceTypeFacebook requestMethod:SLRequestMethodGET URL:postIDURL parameters:nil];
            postIDRequest.account = facebookAccount;

            [postIDRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
                NSError *parseError;
                NSDictionary *response = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingAllowFragments error:&parseError];

                completion(response);
            }];

        }];
        [queue addOperation:operation];
        [lastOperation addDependency:operation];
    }

    [queue addOperation:lastOperation];

}];

If I add the following code inside the [self facebookAccount:^(NSError *error, ACAccount *facebookAccount)]:

[RACObserve(queue, operationCount) subscribeNext:^(id x) {
        NSLog(@"Operation count for queue: %@", x);
    }];

then the output to console is:

https://i.sstatic.net/w5akr.png (sorry I can't post pictures, but I'll get my reputation up soon)

And the position of the "all done" varies around between 5 and 10 usually. So I'm pretty sure that it depends on which NSOperations finish processing the GET request before the queue gets to the lastOperation.


Solution

  • I am guessing performRequestWithHandler: returns immediately, since it takes a callback block as its argument. As soon as the block for an NSBlockOperation returns, the operation is considered finished, so all your operations finish immediately and only later are their completion blocks called.

    You might need to subclass NSOperation and implement the concurrent methods (minimum: start, isConcurrent, isExecuting, and isFinished) instead of the non-concurrent methods (minimum: main). The callback from performRequestWithHandler: should trigger a key-value notification for state-related properties to indicate the NSOperation is finished.