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.
June 17, 2009
Another way to make a singleton, that is still lazily loaded, thread-safe and only incurs the cost of synchronization once (i.e. it’s like a double-checked lock, except that it actually works):
June 17, 2009
(Of course, that doesn’t address the subclassing issue that you mentioned)
June 17, 2009
Yeah; using
@synchronized (self)
in a class method that could be invoked on a subclass is risky.I reformatted your code into Markdown, since it didn’t survive the HTML editor widget thingy.
June 29, 2009
Then finally which one is gonna be the best one regarding to performance? and how are we supposed to use them form outside? is there any sample code available for that?
July 6, 2009
You really shouldn’t need to be worried about performance of singleton objects.
September 27, 2009
correct me if I’m wrong, but there seems to be a typo in the very first example as the static declaration is inside the class method, which means it will always be set to nil, and thus not really work as a “somewhat” singleton. Is that correct?
October 6, 2009
GCD now offers the possibility of doing this with dispatch_once(). This is almost certainly the most performant technique that’s both thread-safe and concise:
{
static id sharedInstance = nil;
static dispatch_once_t once = 0;
dispatch_once(&once, ^ {
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
January 23, 2010
There’s actually no way for +initialize to be called with a self that isn’t the class +initialize belongs to, by the runtime. The runtime calls initialize in reverse order for the entire superclass tree (assuming that class has not already been initialized) and does not call “[super initialize].” +initialize is called once and only once for every class, as noted in the Apple documentation.
The if(self == [SomeManager class]) guard is therefore unnecessary.
January 23, 2010
Amending my last comment: “Except for the case in which a subclass of yours does not provide +initialize.”
February 1, 2010
But your singleton isn’t a singleton at all – you can [[SomeManager alloc] init] as many instances as you like. The point of the Apple way which overrides allocWithZone: is to enforce singleton behaviour – if that’s not important to a singleton class, it maybe doesn’t need to be a singleton anyway.
@synchronized([SomeManager class]) {
}
Is an improvement on Apple’s example locking, I think..?
May 21, 2010
I’ve written up an in-depth discussion of the singleton pattern here:
http://www.duckrowing.com/2010/05/21/using-the-singleton-pattern-in-objective-c/
September 28, 2010
@scott: When a static variable is declared inside a function, it’s only initialized the first time the function is executed.
August 19, 2011
Is this thread-safe?
October 21, 2011
I’ve run into a situation that demonstrates that the example published here is not actually thread safe.
Two threads call +sharedManager at (more or less) the same time. The earlier call to +sharedManager triggers +initialize on its thread, but before +initialize completes the second thread executes +sharedManager.
When +sharedManager is called on the second thread it can return nil if +initialize hasn’t finished yet on the first thread.
Using some sort of locking is necessary for thread safety.
I use a lock in +sharedManager.around
October 21, 2011
addendum to my previous comment – I meant to say,
I use the first method described in the article and put a lock around:
December 15, 2011
This is very very interesting. I’m in the situation that I want to create a global “manager” to manage Core Data (all the methods to access Core Data in the delegate make me really uncomfortable).
Now I’m wondering: what is the right thing to do if singleton is bad?