1

I am using NSOperation subclass in my app which will do following 4 tasks in a single operation, i wanted all these 4 tasks to run on background thread so I wrapped up into single NSOperation class, so that I can easily either pause or cancel it

Tasks

  1. long time running calculation
  2. fetching data from core data
  3. Updating to server
  4. Updating coredata

here each has to execute synchronously which means each one is depend on another one except long time running calculation.

Code

// MyOperation.h
@interface MyOperation : NSOperation {
}
@property (nonatomic, getter = isCancelled) BOOL cancelled;
@end

// MyOperation.m
@implementation MyOperation

- (void)cancel
{
   @synchronized (self)
   {
     if (!self.cancelled)
     {
        [self willChangeValueForKey:@"isCancelled"];
        self.cancelled = YES;
        [webServiceOperation cancel]
        [self didChangeValueForKey:@"isCancelled"];
     }
   }
}

- (void)main {
   if ([self isCancelled]) {
    NSLog(@"** operation cancelled **");
    return;
   }    
   @autoreleasepool{
    [self performTasks];
   }
}
- (void)performTasks {

    [self calculate:^{

          if (self.isCancelled)
              return;

          [self fetchDataFromCoredata:^{

                if (self.isCancelled)
                    return;

                //I am using AFNetWorking for updateWebService task so it shall run on separate NSOperation so it would be like nested NSOPeration

                webServiceOperation = [self updateWebService:^{

                                            if (self.isCancelled)
                                            return;

                                            [self updateCoreData:^{

                                                  if (self.isCancelled)
                                                        return;
                                            }];
             }];
        }];


    }];

}
@end

I am assuming that I am not following proper approach because when I tested this code using KVO the NSOperationQueuegets complete notification before it reaches calculate's completion block.

Questions

  1. Is my approach right?
  2. If not, can you some please guide me the right approach?
  3. If it is right approach then why does NSOPerationQueue get complete notification before completion block execution?

Thanks in advance! looking forward your response

Paulw11
  • 108,386
  • 14
  • 159
  • 186
thavasidurai
  • 1,972
  • 1
  • 26
  • 51
  • 2
    You're handling cancellation as if this was asynchronous operation, but havent implemented `isAsychronous`, nor doing any of the `isExecuting` or `isFinished` KVO. – Rob Jan 26 '15 at 08:06
  • @Rob The question is: why do you use operations? *AFNeworking* already provides them under the hood. – Lorenzo B Jan 26 '15 at 08:27
  • @flexaddicted Why operations? To nicely encapsulate complex logic within stand-alone object. To control degree of concurrency. To manage dependencies. Etc. There are lots of reasons to use `NSOperation` objects and I'm not going to begrudge anyone who wants to understand this incredibly useful pattern. Re AFNetworking, while it is not without its own flaws, you're right that it is it is strong, and should be considered. FYI, it only provides operation pattern within the `NSURLConnection`-based `AFHTTPRequestOperationManager`, but not within the `NSURLSession`-based `AFHTTPSessionManager`. – Rob Jan 26 '15 at 11:54
  • @Rob My question was concerning about abstraction. In other words, *AFNetworking* provides a nice level of abstraction. So, IMO, there is no need to wrap it within `NSOperation`s. Anyway, yes. If the OP wants to learn about pros about using operations, I agree your comment. – Lorenzo B Jan 26 '15 at 12:34
  • @flexaddicted Agreed. Sorry, I missed your point. Yes, of course, one shouldn't reinvent the wheel. AFNetworking is a good place to start. And the OP can also look at the source for `AFURLConnectionOperation` for an example of what a proper `NSOperation` subclass implementation looks like (though, admittedly, a complex one). – Rob Jan 29 '15 at 13:22

3 Answers3

1

Only actually the web call needs to be explicitly asynchronous as your operation will already run on a background thread. So you can simplify the block structure in your code so there isn't so much nesting.

Then you need to properly construct your operation subclass to handle asynchronous content, as described here.

Wain
  • 118,658
  • 15
  • 128
  • 151
  • 3
    FYI, that link is a little dated nowadays you set `isAsynchronous`, not `isConcurrent`. He also doesn't handle the cancellation of a request in progress. But it's a decent, if dated, primer on the topic. I'd also refer OP to subclassing notes in NSOperation Reference as well as the operation queues chapter of the concurrency programming guide. – Rob Jan 26 '15 at 08:01
0

If you want to use NSOperation just follow "Wain's" answer you need process task sequentially.

You can still go with your nested block structure using GCD and avoiding use NSOperation.

NSOperation vs Grand Central Dispatch

Community
  • 1
  • 1
Girish Kolari
  • 2,515
  • 2
  • 24
  • 34
  • 1
    "Block with NSOperation is not a good option." Not true. It's a perfectly acceptable pattern. Sure, sometimes GCD approach is adequate, but I other cases, operation queue approach is much better. It depends upon considerations beyond the scope of this question. – Rob Jan 26 '15 at 12:39
0

The performTasks method doesn't actually perform any tasks. It dispatches tasks to queues or calls methods that dispatch tasks to queues. When the dispatching is done, performTasks is finished and your NSOperationQueue will treat it as finished, even though the task is still executing.

I don't think there's much reason to use NSOperation here. Just use GCD.

gnasher729
  • 51,477
  • 5
  • 75
  • 98