I found some information in the net to create a singleton class using GCD. Thats cool because it's thread-safe with very low overhead. Sadly I could not find complete solutions but only snippets of the sharedInstance method. So I made my own class using the trial and error method - and et voila - the following came out:
@implementation MySingleton
// MARK: -
// MARK: Singleton Pattern using GCD
+ (id)allocWithZone:(NSZone *)zone { return [[self sharedInstance] retain]; }
- (id)copyWithZone:(NSZone *)zone { return self; }
- (id)autorelease { return self; }
- (oneway void)release { /* Singletons can't be released */ }
- (void)dealloc { [super dealloc]; /* should never be called */ }
- (id)retain { return self; }
- (NSUInteger)retainCount { return NSUIntegerMax; /* That's soooo non-zero */ }
+ (MySingleton *)sharedInstance
{
static MySingleton * instance = nil;
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
// --- call to super avoids a deadlock with the above allocWithZone
instance = [[super allocWithZone:nil] init];
});
return instance;
}
// MARK: -
// MARK: Initialization
- (id)init
{
self = [super init];
if (self)
{
// Initialization code here.
}
return self;
}
@end
Please feel free to comment and tell me if I've missing something or doing something completely wrong ;)
Cheers Stefan
Keep it simple:
That is it. Overriding
retain
,release
,retainCount
and the rest is just hiding bugs and adding a bunch of lines of unnecessary code. Every line of code is a bug waiting to happen. In reality, if you are causingdealloc
to be called on your shared instance, you have a very serious bug in your app. That bug should be fixed, not hidden.This approach also lends itself to refactoring to support non-singleton usage modes. Pretty much every singleton that survives beyond a few releases will eventually be refactored into a non-singleton form. Some (like
NSFileManager
) continue to support a singleton mode while also supporting arbitrary instantiation.Note that the above also "just works" in ARC.
If you want to unit test your singleton you also have to make it so that you can replace it with a mock singleton and/or reset it to the normal one:
Be aware that dispatch_once is not reentrant, so calling itself from inside the dispatch_once block will deadlock the program.
Don't try to code defensively against yourself. If you are not coding a framework, treat your class as normal then stick the singleton idiom above. Think of the singleton idiom as a convenience method, not as a defining trait of your class. You want to treat your class as a normal class during unit testing, so it's OK to leave an accessible constructor.
Don't bother using
allocWithZone:
alloc
. Memory zones are no longer used in Objective-C soallocWithZone:
is only kept for compatibility with old code.NSAllocateObject()
andclass_createInstance()
.A singleton factory method always returns one of these three types:
id
to indicate the return type is not fully known (case where you are building a class cluster).instancetype
to indicate that the type returned is an instance of the enclosing class.MySingleton
in the example) to keep it simple.Since you tagged this iOS, an alternative to a singleton is saving the ivar to the app delegate and then using a convenience macro that you can redefine if you change your mind: