NSOperation & NSOperationQueue Cancellation

1k views Asked by At

I'm encountering some weird issues with some custom NSOperation instances queued in an NSOperationQueue instance:

When I call either [myOperation cancel] or [myOperationQueue cancelAllOperations] the isCancelled getter for the cancel boolean remains unchanged for my custom NSOperation subclass instances.

I'm trying to cancel the operations when the app delegate applicationWillResignActive method is called.

My implementation for NSOperation subclass is like this:

  • custom init method
  • implemented main

I have a singleton with strong references to the instances of my NSOperation subclass and NSOperationQueue instance.

In a method of that singleton I do the following:

myNSOperationQueueInstance = [[NSOperationQueue alloc] init];
myCustomOperation_1 = [MyCustomOperationClass alloc] customInit];
myCustomOperation_2 = [MyCustomOperationClass alloc] customInit];

[myNSOperationQueueInstance addOperations:@[myCustomOperation_1,myCustomOperation_2] waitUntilFinished:NO];

Until now, everything works as expected, the custom operations start executing.

However, when I call another method of my singleton from applicationWillResignActive to cancel my custom operations using either [myCustomOperation cancel] or [myNSOperationQueueInstance cancelAllOperations] the isCancelled boolean remains unchanged (I check for isCancelled periodically in the execution loop of my custom operations).

Another interesting thing is that in the same method which should cancel the operations, if I check with NSLog the isCancelled, isExecuting, isAsynchronous the booleans are set to NO but the isFinished boolean is set to YESeven though the operations are executing at that time.

Also, if I try to see what operations are queued in my NSOperationQueue using the operations property, the array is empty.

If I override the cancel property and its setter method of NSOperation subclass, it works as expected.

I also requested a long running background task from the system which works just fine so the problem isn't that the app becomes inactive before the operations could cancel.

What am I missing? Why the isCancelled getter doesn't change, why the operations array property of NSOperationQueue is empty despite the fact I added the operations and they're executing while interrogating the array and why the isFinished boolean is set to YES while the operations are executing?

1

There are 1 answers

0
Razvan On BEST ANSWER

Thanks to @KenThomases and @lead_the_zeppelin (see the comments in the OP) I was able to pinpoint the flaw in my code:

Since I created the custom NSOperation subclass to import data to a Core Data model, my flaw was that the code which performs the import called from the -main method of the subclass, was embedded inside the scope of the block callback of the asynchronous performBlock method of the ManagedObjectContext that I am using to manage the import execution.

So what happened was that the code inside -main was running asynchronously in another thread than my operation instance which, not surprisingly, was reporting that it finished its execution. In my case, to fix the issue, I changed performBlock: with performBlockAndWait:.

Conclusion: if you ever encounter the same issues like me, check if the code running inside your custom NSOperation subclass -main instance method doesn't run asynchronously.