A note on Objective-C singletons

30 June 2011 A note on Objective-C singletons

I'm not a big fan of singletons, especially when they are used as they often are - as glorified globals. That being said, it occasionally makes sense to have some kind of shared instance of an object; not a true singleton that strictly prevents more than one allocation, but a convenient default instance.

Apple uses this pattern frequently throughout their frameworks: [NSUserDefaults standardUserDefaults], [UIScreen mainScreen] and many, many more.

Conceptually, I like to think of these methods as convenient accessors to instances representing a shared or common resource rather than singletons and I still try and avoid using them as globals where possible.

Singletons: the wrong way

If you were trying to find out a way of implementing a singleton or shared instance in Objective-C, you might well find yourself implementing Apple's own example. You might even find yourself using Matt Gallagher's SynthesizeSingleton macro.

No disrespect to Matt, but please don't do this. You almost certainly don't need such a strict implementation. Furthermore, much of the code isn't even relevant in our shiny ARC future.

Singletons: the right way

Up until recently, if anybody asked me what the best way of going about creating a singleton in Objective-C is, I'd point them towards Chris Hanson's great article on the subject. In fact, I still recommend you read it.

But there is an even easier way using Grand Central Dispatch; I've seen this mentioned in several Apple WWDC videos and Colin Wheeler mentions it in this blog post but it doesn't seem to have gained much traction, possibly due to people still supporting iOS 3. But with iOS 5 round the corner, I think it's time to move on and embrace blocks, GCD and eventually, ARC.

Here's what the GCD (and ARC) version looks like:

+ (id)sharedInstance
{
  static dispatch_once_t pred = 0;
  __strong static id _sharedObject = nil;
  dispatch_once(&pred, ^{
    _sharedObject = [[self alloc] init]; // or some other init method
  });
  return _sharedObject;
}

The block given to dispatch_once will, as the name implies, only ever be called once. This will ensure only one instance is created. It's also fast and thread safe: no synchronized or nil checks required.

We can go one step further and wrap this up in a convenient macro. Now all you need to do is this:

+ (id)sharedInstance
{
  DEFINE_SHARED_INSTANCE_USING_BLOCK(^{
    return [[self alloc] init];
  });
}

A bit simpler than the Apple version, don't you think?