If you can target iOS 4.0 or above
Using GCD, is it the best way to create singleton in Objective-C (thread safe)?
+ (instancetype)sharedInstance
{
instancetype
is just one of the many language extensions to Objective-C
, with more being added with each new release.
Know it, love it.
And take it as an example of how paying attention to the low-level details can give you insights into powerful new ways to transform Objective-C.
Refer here: instancetype
+ (instancetype)sharedInstance
{
static dispatch_once_t once;
static id sharedInstance;
dispatch_once(&once, ^
{
sharedInstance = [self new];
});
return sharedInstance;
}
+ (Class*)sharedInstance
{
static dispatch_once_t once;
static Class *sharedInstance;
dispatch_once(&once, ^
{
sharedInstance = [self new];
});
return sharedInstance;
}
If you want to make sure that [[MyClass alloc] init] returns the same object as sharedInstance (not necessary in my opinion, but some folks want it), that can be done very easily and safely using a second dispatch_once:
- (instancetype)init
{
static dispatch_once_t once;
static Class *sharedInstance;
dispatch_once(&once, ^
{
// Your normal init code goes here.
sharedInstance = self;
});
return sharedInstance;
}
This allows any combination of [[MyClass alloc] init] and [MyClass sharedInstance] to return the same object; [MyClass sharedInstance] would just be a bit more efficient. How it works: [MyClass sharedInstance] will call [[MyClass alloc] init] once. Other code could call it as well, any number of times. The first caller to init will do the "normal" initialisation and store the singleton object away in the init method. Any later calls to init will completely ignore what alloc returned and return the same sharedInstance; the result of alloc will be deallocated.
The +sharedInstance method will work as it always did. If it isn't the first caller to call [[MyClass alloc] init], then the result of init is not the result of the alloc call, but that is OK.
You ask whether this is the "best way to create singleton".
A few thoughts:
First, yes, this is a thread-safe solution. This dispatch_once
pattern is the modern, thread-safe way to generate singletons in Objective-C. No worries there.
You asked, though, whether this is the "best" way to do it. One should acknowledge, though, that the instancetype
and [[self alloc] init]
is potentially misleading when used in conjunction with singletons.
The benefit of instancetype
is that it's an unambiguous way of declaring that the class can be subclassed without resorting to a type of id
, like we had to do in yesteryear.
But the static
in this method presents subclassing challenges. What if ImageCache
and BlobCache
singletons were both subclasses from a Cache
superclass without implementing their own sharedCache
method?
ImageCache *imageCache = [ImageCache sharedCache]; // fine
BlobCache *blobCache = [BlobCache sharedCache]; // error; this will return the aforementioned ImageCache!!!
For this to work, you'd have to make sure subclasses implement their own sharedInstance
(or whatever you call it for your particular class) method.
Bottom line, your original sharedInstance
looks like it will support subclasses, but it won't. If you intend to support subclassing, at the very least include documentation that warns future developers that they must override this method.
For best interoperability with Swift, you probably want to define this to be a property, not a class method, e.g.:
@interface Foo : NSObject
@property (class, readonly, strong) Foo *sharedFoo;
@end
Then you can go ahead and write a getter for this property (the implementation would use the dispatch_once
pattern you suggested):
+ (Foo *)sharedFoo { ... }
The benefit of this is that if a Swift user goes to use it, they'd do something like:
let foo = Foo.shared
Note, there is no ()
, because we implemented it as a property. Starting Swift 3, this is how singletons are generally accessed. So defining it as a property helps facilitate that interoperability.
As an aside, if you look at how Apple is defining their singletons, this is the pattern that they've adopted, e.g. their NSURLSession
singleton is defined as follows:
@property (class, readonly, strong) NSURLSession *sharedSession;
Another, very minor Swift interoperability consideration was the name of the singleton. It's best if you can incorporate the name of the type, rather than sharedInstance
. For example, if the class was Foo
, you might define the singleton property as sharedFoo
. Or if the class was DatabaseManager
, you might call the property sharedManager
. Then Swift users could do:
let foo = Foo.shared
let manager = DatabaseManager.shared
Clearly, if you really want to use sharedInstance
, you could always declare the Swift name should you want to:
@property (class, readonly, strong) Foo* sharedInstance NS_SWIFT_NAME(shared);
Clearly, when writing Objective-C code, we shouldn't let Swift interoperability outweigh other design considerations, but still, if we can write code that gracefully supports both languages, that's preferable.
I agree with others who point out that if you want this to be a true singleton where developers can’t/shouldn’t (accidentally) instantiate their own instances, the unavailable
qualifier on init
and new
is prudent.
//Create Singleton
+( instancetype )defaultDBManager
{
static dispatch_once_t onceToken = 0;
__strong static id _sharedObject = nil;
dispatch_once(&onceToken, ^{
_sharedObject = [[self alloc] init];
});
return _sharedObject;
}
//In it method
-(instancetype)init
{
self = [super init];
if(self)
{
//Do your custom initialization
}
return self;
}