Dec 2010

Some notes on threading

My current, barely-organized thoughts on threading and Cocoa apps:

Main Thread Gravity

Communication between systems always happens on the main thread. (A system is one or more objects that work together.) In most cases, communication between objects in the same system happens on the main thread too.

If an object does something in the background, that is that object’s business and nobody else’s.

This extends to notifications: from background threads, I always make sure notifications get posted on the main thread. This way every notification observer can always assume that it’s getting called on the main thread.

I think of threads as like hang-gliding — dangerous and fun, aloft for a little while, but you always come to earth. The key is to land, not crash.

I’m very deliberate about my threading. I like Grand Central Dispatch, but for my purposes NSOperationQueue works best. (I’m doing web services stuff mostly: downloading, parsing files, storing stuff in databases. The formal, easily-monitored approach of NSOperationQueue works best here.) I definitely recommend against anything but GCD and NSOperationQueue — anything else (NSThread’s detach method, etc.) is too random. (Yes, we used it for years, but we have better stuff now.) (In fact, I almost never even use NSInvocationOperation — I much prefer the structure and defined stop/start of NSOperation.)

I use an NSOperation subclass that takes a target and selector in its init method. When it’s finished, it calls the target and selector on the main thread. This way the caller only ever sees the main thread — it creates the operation on the main thread, then gets called back on the main thread. The fact that the operation happens in the background is unknown to the caller: the caller just knows that it’s async, and doesn’t know anything else.

KVO is a trickier thing. Here’s how I handle that: anything happening in a background thread is not observable (by convention). Whatever is going on is private. When it’s time to set public stuff, stuff that could be observed, those things are set on the main thread.

Here’s an example: I do XML parsing in an operation on a background thread. You could imagine another object wanting to observe changes — for instance, whatever object is charged with saving data to a database might want to observe the XML parser.

But I just don’t do it that way. Instead, the XML parser defines a protocol and can take a delegate. It calls the delegate when certain defined things happen. In this case, yes, the communication all does happen on the background thread, but only because the XML parser and the data-saver delegate are intimates, working together in the same system. But the link between them is still thin and well-defined (just two methods in a protocol).

Make sense? I’ll sum up:

  1. Stuff happening in threads is private and self-contained. Black boxes, train cars.

  2. Communication between two things should be on the main thread, except in carefully controlled circumstances. Communication includes notifications and KVO, not just direct calls.

  3. Using GCD or NSOperationQueue is way better than the old ways of doing threading.

Reviewability

This is probably my favorite reason for using NSOperation for all threading: I can see everything that happens in background threads just by looking at my NSOperation subclasses.

This is so much easier than the old way, where I might look at any method in any class and not know, at a glance, whether or not it was running in the main thread or other threads.

Sure, I could have organized things better. I would have invented something like NSOperation. Since it exists now, I just use it.

When it comes to threading, you want to look, and know — you never want to guess.

Locking

Locking sucks. If you have to do it, you have to do it. It doesn’t mean you’ve failed — failure would be not-locking when you really do need a lock.

Here’s a thing to think about: can you define an expensive, self-contained operation that doesn’t need locking? If so, then that’s a great candidate for something that could go on a background thread. XML parsing, again, is a great example.

There are a couple other things you can do which make sense.

  1. Use performSelectorOnMainThread to set some data that would otherwise require locking. (This is not a trick to use willy-nilly, though — typically it comes at the very end of an operation, not sprinkled throughout.)

  2. Use NSCache — it’s thread-safe.

As a developer, I always find it interesting to read about lock-less techniques and memory barriers and various cool stuff. But I never, ever code with that stuff. (I assume these things are used behind the scenes, in the frameworks and runtime, which is totally cool. I’m happy to let the super-smart engineers at Apple make stuff faster.)

Here’s the thing about code: the better it is, the more it looks and reads like a children’s book. Bad code looks like James Joyce wrote it. (When it comes to app development, that is. Compilers, runtimes, kernel extensions — maybe they’re different beasts. I don’t know.)

My odd video habit

On my laptop I have a small handful of Doctor Who episodes — and just about all the WWDC videos from 2009 and 2010. (Plus a few from 2008.)

I play them in the background while I’m writing software. I’m sure I’ve played each one a dozen times at least.

I don’t discriminate — I play the videos on things like Core Data which are very relevant to me, but I also play the videos on things like game design which, going by title alone, don’t seem to apply to me.

(I even play the videos on Opens CL and GL. If you know me, you may be in shock at hearing this news.)

Here’s why I play all the videos, over and over, even so long past WWDC:

  • The tone of them keeps me in the right world.

  • Things catch my ear, and I end up doing more research and learning about them, even if I don’t have immediate plans to use them.

  • Sometimes things catch my ear which I can use pretty much right away. This usually comes as a surprise — a happy, sunny surprise.

  • Having broad awareness, if not deep knowledge, of the entire platform is a very good thing. Sticking to a purely pragmatic approach to learning — learning only what you think you need to know — is an error.

  • Some of the videos are inspiring. This morning’s highlight was WWDC 2009 Session 125: Matt Drance’s talk on “Effective iPhone App Architecture.” Another inspiring video is WWDC 2010 Session 103: “iPad and iPhone User Interface Design.” (There are plenty more, and your list will be different from mine.)

Reminder: the videos are free to Apple developers. If you’re a Mac or iOS developer, you should get them. All of them. And play them.

PS I also listen to three podcasts: Core Intuition, Build and Analyze, and The Talk Show.

Select vs. choose

You know how some people think “I” or “myself” is the polite form of “me?”

They say things like “John and myself went to the store.” Or “The clerks at the store were rude to John and I.”

Oh, it burns. It stings. It makes me crazy on the inside, in the inner part of my brain, where the fires and voices fight for attention.

There’s a similar thing in user interface text. People think “select” is the polite form of “choose.”

It’s not. They have different meanings. In general, you select a noun (subject) and choose a verb — but you also choose when there’s a list of options (direct and indirect objects, often).

You can select some text, one or more items in a table, one or more icons in the Finder, part of a photo (or one or more photos) in iPhoto.

You choose a menu command or a toolbar command, an item in a popup menu, an item in a segmented control.

One way to think about it: if you could apply a menu command to the item, then it’s a selection. Otherwise it’s (usually) a choice.

Another way to think about it: selections are often free-form — some part of a photo, one or more not-necessarily-contiguous items in a table, a range (possibly even vertical) of text. Choices tend to be between discrete items: a list of languages, a list of web browsers.

Select your words wisely.

Archive