views:

23

answers:

0

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?