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:
MyLongRunningOp 0x1
The user navigates to the map. The queue now looks like this:
MyLongRunningOp 0x1
MyMapOp 0x2
They move the map, which cancels MyMapOp 0x2 and adds MyMapOp 0x3:
MyLongRunningOp 0x1
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?