0

I have a model class called DataFetcher that fetches data via a web service, then persists the data to a Core Data datastore, then updates a ViewController via delegate methods. Here is the normal sequence (which works properly) without using NSOperation:

NSArray *serviceQueryResult = [self queryServiceFor:@"car"];
[self setData:serviceQueryResult];
[self persistData:_data];
[_loadDelegate updateCount:_data.count]; 
[_loadDelegate receivedData:_data];

I need to place the web service call and the database update call on a background thread. My thoughts are to create an NSBlockOperation to make the web service call, then another NSBlockOperation to do the database update. There will be a dependency that the web service operation completes before the database update operation begins. Here is the code I am trying to implement:

__weak DataFetcher *weakSelf = self;
__block NSArray *serviceQUeryResult;
NSBlockOperation *webServiceOperation = [NSBlockOperation blockOperationWithBlock:^{
    serviceQUeryResult = [weakSelf queryServiceFor:@"mini"];
    [weakSelf setData:serviceQUeryResult];
}];
NSBlockOperation *dbInsertOperation = [NSBlockOperation blockOperationWithBlock:^{
    [weakSelf persistData:serviceQUeryResult];
}];

[webServiceOperation addDependency:dbInsertOperation];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:webServiceOperation];

When set up this way my queryServiceFor: method never gets called. I am also unsure of where to place the two delegate method calls since they update the UI and should be on the main thread. I have used GCD several times in the past, but now will be needing some of the extra functionality of NSOperations. Can anyone help? Thanks!

Pheepster
  • 6,045
  • 6
  • 41
  • 75

1 Answers1

2

The fundamental issue is that you've declared webServiceOperation to be dependent upon dbInsertOperation (i.e. it won't start webServiceOperation until dbInsertOperation finishes), but you never start dbInsertOperation, so webServiceOperation will never run.

  1. If you want to make dbInsertOperation dependent upon webServiceOperation, you don't want:

    [webServiceOperation addDependency:dbInsertOperation];
    

    You instead want:

    [dbInsertOperation addDependency:webServiceOperation];
    
  2. Once you've created this dependency, make sure to add both of these operations to your queue.

    [queue addOperation:webServiceOperation];
    [queue addOperation:dbInsertOperation];
    

    The dependency will ensure that dbInsertOperation won't start until webServiceOperation finishes. Note, this assumes that webServiceOperation performs its block synchronously. If the network request runs asynchronously, you might want to wrap it in its own concurrent/asynchronous NSOperation subclass.


If you want to update the UI from these background operations, you can either:

[[NSOperationQueue mainQueue] addOperationWithBlock:^{
    // update the UI here
}];

or use GCD, if you want:

dispatch_async(dispatch_get_main_queue(), ^{
    // update the UI here
});
Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • Thanks, I incorrectly assumed that the dependency implicitly added the other. Thanks for clearing up that part of the equation. – Pheepster Sep 26 '14 at 17:04
  • I'm still unclear, however, on where to make the delegate calls. Since they ultimately update the UI, they can't be placed in the queue, correct? So, with GCD it's pretty straight forward to call the main thread, but here, I'm not sure where or how to call updates to the UI. – Pheepster Sep 26 '14 at 17:18
  • @Pheepster Re UI updates, you can use GCD to dispatch the code to the main queue, or you can use the operation queue equivalent, which I show in my revised answer. If I'm in an operation, I'll generally use the operation queue rendition, but both patterns work fine. – Rob Sep 26 '14 at 17:40
  • I have two delegate methods that update the UI. They are shown at the top of my post in the original "working code" – Pheepster Sep 26 '14 at 17:42
  • Just call them inside the appropriate operation (if they're dependent upon the insert operation, put them after that; if they're dependent upon the web service call only, put them in that). It's not entirely clear from the method names precisely what they're doing and what the logical dependencies are, so it's hard for me to be more specific. If they're updating your model objects and/or UI, though, make sure they're dispatched to the main queue. – Rob Sep 26 '14 at 17:47