Skip to content

Singletons in Cocoa/Objective-C

I’ll preface this post with the standard advice: *Don’t create singletons if you don’t absolutely have to.* In general, if you’re creating a global “manager” object of some sort, you’re doing something wrong.

That said, there’s still occasionally a reason to have such a global singleton, such as a “default something.” The sample code in the Cocoa Fundamentals Guide goes to a lot more trouble than it needs to in order to *ensure* that a class is a singleton.

This is almost **never** what you want.

First off, you probably want your class to be testable in a variety of configurations. In your unit tests, instead of getting your shared singleton instance in your `-setUp` method and “resetting” its state in `-tearDown`, you’d be better off just instantiating a *new* instance in `-setUp` and *releasing* it in `-tearDown`.

Also, the example in the Cocoa Fundamentals Guide does a lot of work that it simply doesn’t need to. This is all you *really* need to do to create a singleton in Cocoa:

@interface SomeManager : NSObject
+ (id)sharedManager;
@end

@implementation SomeManager

+ (id)sharedManager {
static id sharedManager = nil;

if (sharedManager == nil) {
sharedManager = [[self alloc] init];
}

return sharedManager;
}

@end

That’s it! The astute reader will notice, of course, that this isn’t thread-safe. I got rid of the `@synchronized (self)` because it won’t do the right thing; depending on what actual class is sent `+sharedManager`, the value of `self` will be different!

For the sake of argument, though, let’s say that you do want a singleton with which you can interact from multiple threads at once. One way to do this would be to create your singleton instance in `+initialize` since it will always be run, on a single thread, before any other methods in your class:

@implementation SomeManager

static id sharedManager = nil;

+ (void)initialize {
if (self == [SomeManager class]) {
sharedManager = [[self alloc] init];
}
}

+ (id)sharedManager {
return sharedManager;
}

@end

By doing this, you avoid the performance bottleneck of `@synchronized` taking a recursive lock every time `+sharedManager` is invoked.

If you want to get fancier, and it’s OK to temporarily have more than one instance of your singleton created, you could even use `objc_atomicCompareAndSwapGlobalBarrier` to assign the value to return from `+sharedManager`, though this is probably also more work than it’s worth; after all, `+initialize` will only be invoked once for your class. (Though it can be re-invoked as a side-effect of initializing subclasses, hence the `if (self == [SomeManager class]) { }` idiom.)

In all of the above cases, you’ve done a whole lot less work than the example in the Cocoa Fundamentals Guide, and your code is a lot more likely to be correct as a result.

{ 17 } Comments