Calling Handlers on the Main Thread

Brent’s post on API Design and the Main Thread kind of rankles a bit because I see this differently.

The main thread is special, yes, but to me it’s special in that you should stay off it unless you have a good reason to be there.

The main thread is where responsiveness happens.

When your finger is scrolling a table view, the main thread is busy making that fluid. A long enough interruption could cause the frame rate to drop, and you don’t know when this responsiveness is required. There’s no “I’m busy animating right now so hold off on the notifications” flag. If you dispatch to the main thread, then the next turn of the run loop (the same run loop that’s busy animating), it’s going to do whatever you asked it to do.

In Brent’s example:

- (void)notesWithUniqueIDs:(NSArray *)uniqueIDs 
fetchResultsBlock:(QSFetchResultsBlock)fetchResultsBlock;

He’s dispatching back to the main thread to call the completion handler, even though the method may be called from a background thread.  He’s doing this so that the chance that somebody will mess up and use the result on a thread that isn’t the main thread is reduced. This can help a programmer who isn’t careful about what thread he’s on, but you’re playing russian roulette with your framerate.

The problem isn’t just the one dispatch back to the main thread, it’s that you don’t know how many are happening or what else is going on. Let’s say Brent designs his library with this pattern in mind, and now someone else is using the library. They want to perform a background operation that requires updating a number of notes, so they fire some work onto a background queue that fetches a number of notes and does work on them. This operation could be completed entirely in the background without involving the main thread at all, but instead will now require bouncing off the main thread at least once per object. If every library adopted this pattern, then maybe many times per object. It would add up to a drop in performance that would be impossible to resolve without changes to the API of one or more libraries.

In my opinion, a better pattern is this:

When you receive a notification or a callback, dispatch onto the thread required to handle it.

Unless the API specifically documents its behaviour here, don’t depend on the caller having bounced you to the main thread.

You often see the thread bounce happening on both sides of the call:  Someone in Brent’s camp dispatches to the main thread, and then the handler also dispatches to the main thread. The second dispatch (the one that’s dispatching to the same thread you’re already on) seems to be handled specially by Cocoa – you can see on the call stack that it just calls through to the handler instead of queueing it, which is a nice optimization and makes the second context switch essentially free.