Singleton pattern is used when you need to ensure that only one instance of a class can be instantiated and you need a global access to it. The most widely used implementation of this pattern in .NET is the following (from MSDN):
public sealed class Singleton
{
private static readonly Singleton instance = new Singleton();
private Singleton(){}
public static Singleton Instance
{
get { return instance; }
}
}
This implementation is the most trivial one, but works according to a definition of singleton and can be also used in multi-threaded scenarios. What are the options for Objective-C?
According to Apple you should implement your singleton classes in the following way:
static MyGizmoClass *sharedGizmoManager = nil;
+ (MyGizmoClass*)sharedManager
{
if (sharedGizmoManager == nil) {
sharedGizmoManager = [[super allocWithZone:NULL] init];
}
return sharedGizmoManager;
}
+ (id)allocWithZone:(NSZone *)zone
{
return [[self sharedManager] retain];
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
- (id)retain
{
return self;
}
- (NSUInteger)retainCount
{
return NSUIntegerMax; //denotes an object that cannot be released
}
- (void)release
{
//do nothing
}
- (id)autorelease
{
return self;
}
In most cases, if you are not building a library and you will not be exposing singleton class to external parties, you could even simply omit some of the methods (leaving only sharedManager class method and a variable) and make this implementation a bit shorter.
This implementation however might suffer in a multi-threaded environment when different threads will try to get an access to a singleton instance when it's not yet initialized. In multi-threaded environments we could just surround initialization code with @synchronized, like this:
+ (MyGizmoClass*)sharedManager
{
@synchronized(self) {
if (sharedGizmoManager == nil) {
sharedGizmoManager = [[super allocWithZone:NULL] init];
}
}
return sharedGizmoManager;
}
Knowing the cost of synchronization every time instance is accessed we could optimize this code a bit using a known technique called double checked locking:
+ (MyGizmoClass*)sharedManager
{
if (sharedGizmoManager == nil) {
@synchronized(self) {
if (sharedGizmoManager == nil) {
sharedGizmoManager = [[super allocWithZone:NULL] init];
}
}
}
return sharedGizmoManager;
}
We are only synchronizing when instance is not yet initialized to ensure that only one thread will access initialization block. However some studies show that this might not work as expected in Objective-C and it looks like even making sharedGizmodoManager variable a volatile won't help.
After some research I have suddenly stumbled upon an interesting article on stackoverflow and a nice idea brought by Kendall Helmstetter Gelner of leveraging Obj-C runtime library functions to replace a thread-safe version of an accessor method with a simple one after singleton instance is initialized. The idea is that we are still using fully synchronized version of initialization as shown in a second example, but after sharedGizmoManager is initialized, we just need to replace an implementation with a simple one, that will just simply return our sharedGizmodoManager instance without doing any locks.
//... after sharedGizmoManager is initialized
Method origMethod = class_getClassMethod(self, @selector(sharedManager));
Method newMethod = class_getClassMethod(self, @selector(simpleSharedManager));
method_exchangeImplementations(origMethod, newMethod);
// simpleSharedManager method that will be used instead of a synchronized one
+ (MyGizmodoClass*)simpleSharedManager {
return sharedGizmodoManager;
}
I like this idea a lot and it really encourages me to carefully read Objective-C Runtime Reference. I know this singleton implementation is much more complicated but if you are looking for an efficient singleton implementation that will work correctly in multi-threaded environments this one might be really interesting one. In most of the cases though I am pretty sure that non-thread safe one will work correctly and is the easiest to remember and implement.
What are your thoughts, which implementation do you use in your iPhone / iPad / iOS applications / games?