A common problem in software is to do something exactly once – e.g. create a singleton. A corollary of that action is to do something exactly once in the life of an instance. I have found myself using the corollary much more frequently as I move away from using singletons, which I’ve done to improve testability.
With singletons, a common solution is to use locking or, to avoid the performance penalty of locking, double-checked locking. Of course, double-checked locking has issues with most modern languages caused by CPU and compiler instruction reordering. Objective-C has provided an elegant solution to those problems, but the solution comes up a little bit short.
Creating a Singleton – Single-Threaded
In a single -threaded application, creating a singleton can be done without additional protection, like this:
+ (MyClass *) sharedInstance { static MyClass * s_sharedInstance = nil; if (s_sharedInstance == nil) { s_sharedInstance = [MyClass new]; } return s_sharedInstance; }
Creating a Singleton – Multi-Threaded
In a multi-threaded application, it’s not so simple as you will want to protect yourself against multiple callers of this method. A simple solution simply adds a lock:
+ (MyClass *) sharedInstance { static MyClass * s_sharedInstance = nil; @synchronized(self) { if (s_sharedInstance == nil) { s_sharedInstance = [MyClass new]; } } return s_sharedInstance; }
The problem with this solution is that you pay the price of locking even though you only need it the first time the method is called.
Double-Checked Locking
A common solution to avoid the penalty of locking is to use double-checked locking. With double-checked locking you check the value of the static instance both outside and inside the lock, like this:
+ (MyClass *) sharedInstance { static MyClass * s_sharedInstance = nil; if (s_sharedInstance == nil) { @synchronized(self) { if (s_sharedInstance == nil) { s_sharedInstance = [MyClass new]; } } } return s_sharedInstance; }
At first blush, it would seem that this would solve the problem of assigning the static variable AND avoiding the penalty of the lock. The problem is that the CPU and/or the compiler could reorder the memory access instructions. A solution suggested by Mike Ash inserts memory barriers before accessing the variable and uses the volatile keyword. The memory barrier, available in libkern/OSAtomic.h, solves the CPU problem and the volatile keyword solves the compiler problem.
+ (MyClass *) sharedInstance { static MyClass * volatile s_sharedInstance = nil; if (s_sharedInstance == nil) { @synchronized(self) { if (s_sharedInstance == nil) { OSMemoryBarrier(); s_sharedInstance = [MyClass new]; } } } OSMemoryBarrier(); return s_sharedInstance; }
The problem with this solution is that it is still quite expensive in the most common case. Apple has provided another solution: dispatch_once
.
dispatch_once
dispatch_once is provided with Grand Central Dispatch to execute statements exactly once, like this:
+ (MyClass *) sharedInstance { static MyClass * s_sharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ s_sharedInstance = [MyClass new]; }); return s_sharedInstance }
This is a terrific solution for use with singletons, but what if you want to use it with an instance? It actually happens to work, but Apple has a warning against using it in this way in the documentation for dispatch_once:
The predicate must point to a variable stored in global or static scope. The result of using a predicate with automatic or dynamic storage (including Objective-C instance variables) is undefined.
Hmm, that’s unfortunate. Here’s hoping Apple will change their implementation. In the meantime, it would appear that the only solutions for the non-static and non-global case are:
- Use a synchronized block for every call.
- Use memory barriers.