September 2009 Archives
Thanks, Wolf, for putting on another great C4 and giving me the chance to speak about unit testing. At the end of my talk, I put up a list of books for further reading, and wanted to throw up quick and dirty links to those:
I previously wrote a post about concurrent operations and how to use them for asynchronous APIs like NSURLConnection. Unfortunately, the code in that post originally contained a serious error that broke when running on 10.6 (I’ve since updated it). The API documentation for NSOperation mentions new behavior for concurrent operations:
Note: In Mac OS X v10.6, operation queues ignore the value returned by
isConcurrentand always call thestartmethod of your operation from a separate thread. In Mac OS X v10.5, however, operation queues create a thread only ifisConcurrentreturnsNO. In general, if you are always using operations with an operation queue, there is no reason to make them concurrent.
This new behavior raises a couple of issues. First, if you’re using a main-thread only API, it obviously won’t work well or at all when called from a background thread. Also, our start method is designed to start an asynchronous operation and return as quickly as possible. The thread that where start is called will generally die after the start method returns (most likely due to operation queues being implemented on top of Grand Central Dispatch). Thus, even if the operation is safe to run on a background thread, if it requires a run loop, it won’t work. This is because run loops are tied to a thread, and if the thread dies, then any run loop activity dies with it.
The simple fix I’ve found is to ensure that start is called on the main thread. This makes it safe for main-thread only APIs as well as asynchronous APIs that rely on the run loop:
- (void)start
{
if (![NSThread isMainThread])
{
[self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
return;
}
[self willChangeValueForKey:@"isExecuting"];
_isExecuting = YES;
[self didChangeValueForKey:@"isExecuting"];
// Start asynchronous API
}

This fix works well on both 10.5 and 10.6.
I’ve known about this problem for some time, and I apologize for not updating the previous post as soon as Snow Leopard was released.
