Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does NSOperationQueue on iPhone OS 3.1 hold on to long-cancelled (and released) operations?

I have an app that uses NSOperations to manage service calls to a web API (the calls are based on CURLOperation in Jon Wight's touchcode).

There is a certain call that downloads map locations when the center of a map view changes significantly; since these can stack up so quickly, if you move the map around, I try to aggressively cancel stale operations. It works great on 4.0.

However, on 3.1, it seems that in certain cases the operation queue will hold on to cancelled (and released) operations, causing a crash when it reaches the place they should be in the queue.

Here is an illustration.

I begin with a relatively heavyweight service call in the queue:

  1. MyLongRunningOp 0x1

The user navigates to the map. The queue now looks like this:

  1. MyLongRunningOp 0x1
  2. MyMapOp 0x2

They move the map, which cancels MyMapOp 0x2 and adds MyMapOp 0x3:

  1. MyLongRunningOp 0x1
  2. MyMapOp 0x3

MyMapOp 0x2 is now released, as it has been removed from the queue. Now MyLongRunningOp 0x1 finishes. In the KVO callbacks for setting the isFinished key on MyLongRunningOp, I see the operation queue handle the notification and try to add the MyMapOp 0x2 to some NSArray. Naturally, with NSZombies enabled,

[MyMapOp retain]: message sent to deallocated instance 0x2

It appears that the NSOperationQueue is somehow hanging on to a pointer to the canceled/released operation, and attempting to activate it after the prior operation finishes.

I haven't been able to reproduce this behavior on 4.0, so I believe it's a 3.1 bug.

I'm having a lot of trouble working around it—as far as I can tell, the only workaround is to never cancel my operations, which makes for a suboptimal experience when the network gets iffy.

Has anyone else experienced this? Any ideas?

like image 929
qwzybug Avatar asked Sep 08 '10 17:09

qwzybug


1 Answers

I had (what I believe to be) a similar problem using KVO on NSOperations.

Just reading your description, my first instinct would be to check the rules for operation queues. Is it possible that once you hand it to the queue, the queue should assume ownership and therefore you shouldn't manually release it? (Not sure if you are or not).

From my personal experience, and maybe it will help or not:

1) When you cancel an op, remove the KVO observers from it. I've had it happen KVO's don't get unlinked when either side gets deleted.

2) Be advised that the KVO callbacks run in the same thread as the NSOperation. So it's possible for an object to go out of scope between the time you start the operation and a KVO callback.

I might be able to give you some more help if you post code. Hopefully the above has been useful to you!

like image 123
pzearfoss Avatar answered Oct 24 '22 06:10

pzearfoss