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 isConcurrent and always call the start method of your operation from a separate thread. In Mac OS X v10.5, however, operation queues create a thread only if isConcurrent returns NO. 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];

    [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.