EDIT
I am including this at top, below you can see my historical original questions and implementation. However I think I found the optimal way to provide a sharedInstance method with no locking overhead, I would love to hear potential concerns about this:
// Volatile to make sure we are not foiled by CPU caches
static volatile ALBackendRequestManager *sharedInstance;
// There's no need to call this directly, as method swizzling in sharedInstance
// means this will get called after the singleton is initialized.
+ (MySingleton *)simpleSharedInstance
{
return (MySingleton *)sharedInstance;
}
+ (MySingleton*)sharedInstance
{
@synchronized(self)
{
if (sharedInstance == nil)
{
sharedInstance = [[MySingleton alloc] init];
// Replace expensive thread-safe method
// with the simpler one that just returns the allocated instance.
SEL orig = @selector(sharedInstance);
SEL new = @selector(simpleSharedInstance);
Method origMethod = class_getClassMethod(self, orig);
Method newMethod = class_getClassMethod(self, new);
method_exchangeImplementations(origMethod, newMethod);
}
}
return (MySingleton *)sharedInstance;
}
And the historical discussion around initialize:
I see now the original code was actually rather like mine (below), except for having the check for the instance outside the lock.
Although the new + (void) initialize method is interesting, I am not sure I like this better. It seems like now to get a singleton instance you must now always call:
MySingleton instance = [[MySingleton alloc] init];
Is that not correct? That feels odd, and is it more efficient if the call to initialize is already locked for you? The double-lock method seems to work OK for this use case, while also avoiding locking (at the potential cost of a double allocation I think since more than one thread could fall through the if).
The other thing that seems odd if, if the initialize method were really preferred why do we not see it elsewhere? Objective-C has been around a LONG time and I am wary of fundamental mechanisms that differ from just about all published examples.
My code I currently use (which mirrors that I have seen elsewhere, including this answer):
+ (MySingleton *)sharedInstance
{
@synchronized(self)
{
if (sharedInstance == nil)
sharedInstance = [[MySingleton alloc] init];
}
return sharedInstance;
}
+ (id)allocWithZone:(NSZone *)zone
{
@synchronized(self)
{
if (sharedInstance == nil)
{
sharedInstance = [super allocWithZone:zone];
return sharedInstance; // assignment and return on first allocation
}
}
return nil; // on subsequent allocation attempts return nil
}