4

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?

Razvan
  • 4,122
  • 2
  • 26
  • 44
  • just checking , did you call `[myCustomOperation start]` on your custom NSOperation? – lead_the_zeppelin Jun 17 '15 at 20:09
  • No because they're not concurrent and I don't want to start them manually. I want to allow the NSOperationQueue to start them. I also did not override the `start` method of the subclass. – Razvan Jun 17 '15 at 20:12
  • 1
    And your `-main` is synchronous? It doesn't return until the operation is completely finished? – Ken Thomases Jun 17 '15 at 20:29
  • 1
    so your operation usually doesn't return until completely finished? Can you clarify? Your code says `[myNSOperationQueueInstance addOperations:@[myCustomOperation_1,myCustomOperation_2] waitUntilFinished:NO];` – lead_the_zeppelin Jun 17 '15 at 20:49
  • 1
    Can you post code of your `[MyCustomOperationClass customInit]`? – lead_the_zeppelin Jun 17 '15 at 20:50
  • @lead_the_zeppelin I was playing with `waitUntilFinished` to see if it matters and I copied the last change here :). It seems it doesn't matter because the `isFinished` boolean is `YES` whatsoever. – Razvan Jun 17 '15 at 20:55
  • @KenThomases You were right about your assumption: since I use the operations to import to Core Data, the method called from `-main` was inside the `performBlock` of the `ManagedObjectContext` I was using to import so whatever code was running inside the operation was running on another thread :). Thanks to you and `lead_the_zeppelin` I was able to pinpoint the flaw (lead_the_zeppelin made me look again at my init method and since it was just above `-main` I could see what's in there :D. Thank you guys! I managed to get the expected results after modifying my code. – Razvan Jun 17 '15 at 21:02

1 Answers1

2

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.

Razvan
  • 4,122
  • 2
  • 26
  • 44