I read a couple of amazing resources on singletons in Obj-C:
but none of these resources addressed init
method concept explicitly and while still being a novice to Obj-C I'm confused how should I implement it.
So far I know that having init
private is not possible in Obj-C as it does not offer true private methods... so it's possible that user can call [[MyClass alloc] init]
instead of using my [MyClass sharedInstance]
.
What are my other options? I believe I should also handle subclassing scenarios of my singleton.
Well, an easy way around the init
is to just not write one to have it call the default NSObject implementation (which only returns self
). Then, for your sharedInstance
function, define and call a private function that performs init-like work when you instantiate your singleton. (This avoids user accidentally re-initializing your singleton.)
However!!! The major problem is with alloc
being called by a user of your code! For this, I personally recommend Apple's route of overriding allocWithZone:
...
+ (id)allocWithZone:(NSZone *)zone
{
return [[self sharedInstance] retain];
}
This means the user will still get your singleton instance, and they can mistakenly use as if they allocated it, and safely release it once since this custom alloc performs a retain on the singleton. (Note: alloc
calls allocWithZone:
and does not need to be separately overridden.)
Hope that helps! Let me know if you want more info~
EDIT: Expanding answer to provide example and more details --
Taking Catfish_Man's answer into consideration, it's often not important to create a bulletproof singleton, and instead just write some sensible comments in your headers/documentation and put in an assert
.
However, in my case, I wanted a thread-safe lazy-load singleton--that is, it does not get allocated until it needs to be used, instead of being automatically allocated on app launch. After learning how to do that safely, I figured I may as well go all the way with it.
EDIT#2: I now use GCD's dispatch_once(...)
for a thread-safe approach of allocating a singleton object only once for lifetime of an application. See Apple Docs: GCD dispatch_once. I also still add allocWithZone:
override bit from Apple's old singleton example, and added a private init named singletonInit
to prevent it from accidentally being called multiple times:
//Hidden/Private initialization
-(void)singletonInit
{
//your init code goes here
}
static HSCloudManager * sharedInstance = nil;
+ (HSCloudManager *) sharedManager {
static dispatch_once_t dispatchOncePredicate = 0;
dispatch_once(&dispatchOncePredicate, ^{
sharedInstance = [[super allocWithZone:NULL] init];
[sharedInstance singletonInit];//Only place you should call singletonInit
});
return sharedInstance;
}
+ (id) allocWithZone:(NSZone *)zone {
//If coder misunderstands this is a singleton, behave properly with
// ref count +1 on alloc anyway, and still return singleton!
return [[HSCloudManager sharedManager] retain];
}
HSCloudManager
subclasses NSObject
, and does not override init
leaving only the default implementation in NSObject
, which as per Apple's documentation only returns self. This means [[HSCloudManager alloc] init]
is the same as [[[HSCloud Manager sharedManager] retain] self]
, making it safe for both confused users and multi-threaded applications as a lazy-loading singleton.
As for your concern about user's subclassing your singleton, I'd say just comment/document it clearly. Anyone blindly subclassing without reading up on the class is asking for pain!
EDIT#3: For ARC compatibility, just remove the retain portion from the allocWithZone:
override, but keep the override.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With