I've created a singleton class using this code:
static MyClass *sharedMyClass = nil;
+ (id)getInstance {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyClass = [[self alloc] init];
});
return sharedMyClass;
}
my concern is that the users of my class can call the alloc method an create other instantiations of the class. Therefore this would no longer be a singleton Do I need to override the alloc method? If so I suggest overriding it this way:
+ (id)alloc
{
id instance = sharedMyClass;
if (instance == nil) {
instance = [super alloc];
}
return instance;
}
Implement it this way?
+(id)alloc
{ #synchronized([MyClass class])
{
NSAssert(_sharedMyClass == nil, #"Attempted to allocate a second instance of a singleton.");
_sharedMyClass = [super alloc];
return _sharedMyClass;
}
return nil;
}
Yes, you need to overwrite the alloc methode.
You can archive this by implementing
+ (instancetype)sharedInstance {
static MyClass *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[super allocWithZone:NULL] init];
});
return sharedInstance;
}
+ (id)allocWithZone:(NSZone *)zone {
return [self sharedInstance];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
Apple provides a detailed description for this case which you can find here (non ARC description) https://developer.apple.com/legacy/library/documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/CocoaObjects.html#//apple_ref/doc/uid/TP40002974-CH4-SW32
You can forbid alloc at compile time:
+ (instancetype)new NS_UNAVAILABLE;
+ (instancetype)alloc NS_UNAVAILABLE;
Using super alloc in your own implementation:
+ (instancetype)getInstance {
static id sharedMyClass
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyClass = [[super alloc] init];
});
return sharedMyClass;
}
It doesn't protect you from some runtime code, but it's clear enough.
Related
I have a Singleton class in Objective-C. I want to know if the shared object is already initialized or not, before accessing sharedInstance. How can I do so?
+ (id)sharedInstance {
static MyObject *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (id)init {
if (self = [super init]) {}
return self;
}
I can think of 2 possible ways:
First approach: Move static MyObject *sharedInstance = nil; outside of the method. It will look like:
#implementation MyObject
static MyObject *sharedInstance = nil;
+ (id)sharedInstance {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
+ (BOOL)isSharedInstanceInitialized {
return sharedInatance != nil;
}
- (id)init {
if (self = [super init]) {}
return self;
}
#end
Second approach: As suggested in the comments above to use additional BOOL static value
#implementation MyObject
static BOOL isInitialized = NO;
+ (id)sharedInstance {
static MyObject *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
isInitialized = YES;
});
return sharedInstance;
}
+ (BOOL)isSharedInstanceInitialized {
return isInitialized;
}
- (id)init {
if (self = [super init]) {}
return self;
}
#end
This question already has answers here:
Singleton in iOS Objective C doesn't prevent more than one instance
(9 answers)
Closed 7 years ago.
As we know that a singleton object can be instantiated only once and we use singletons in objective C to have a global access to shared resources. We also know that singletons are instantiated using the following method.
+ (instancetype)sharedManager
{
static PhotoManager *sharedPhotoManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedPhotoManager = [[[self class] alloc] init];
sharedPhotoManager->_photosArray = [NSMutableArray array];
});
return sharedPhotoManager;
}
But I can also do this -
PhotoManager *sharedManager = [[PhotoManager alloc] init];
Now this way I can also create another instance of the same singleton class then how come the class is singleton if it is having two instances.
Please clarify.
You can forbid calls of init with such trick:
add - (instancetype)init NS_UNAVAILABLE; definition to your singleton interface.
Instead of [[PhotoManager alloc] init]; use [[[self class] alloc] init];
PhotoManager *sharedManager = [[PhotoManager alloc] init]; won't compile.
There is my example:
#interface SomeSingleton : NSObject
+ (instancetype)sharedInstance;
- (instancetype)init NS_UNAVAILABLE;
#end
#implementation SomeSingleton
+ (instancetype)sharedInstance {
static dispatch_once_t onceToken;
static SomeSingleton *instance;
dispatch_once(&onceToken, ^{
instance = [[[self class] alloc] init];
});
return instance;
}
- (instancetype)init {
self = [super init];
return self;
}
#end
As result SomeSingleton *s1 = [SomeSingleton sharedInstance]; works, but SomeSingleton *s2 = [[SomeSingleton alloc] init]; leads to the compile error.
You have singleton behaviour for object only when you use sharedManager: class method. All other ways you instantiate object are not guaranteed to produce singleton object.
Objective-C allows you to do lots of things that may not be intended. Like calling a private method if you know the method name.
If you are obsessed to make sure your class is only used as singleton, maybe something like this would be of use:
static PhotoManager *sharedPhotoManager = nil;
+ (instancetype)sharedManager
{
if (!sharedPhotoManager) {
sharedPhotoManager = [[PhotoManager alloc] init];
}
return sharedPhotoManager;
}
- (instancetype)init {
if (sharedPhotoManager) {
// init method is allowed to return different object
return sharedPhotoManager;
}
self = [super init];
if (self) {
}
return self;
}
When you write
PhotoManager *sharedManager = [[PhotoManager alloc] init];
you are not getting your sharedInstance, you are creating a new one. The way you should use it is
[PhotoManager sharedInstance];
instead of
PhotoManager *sharedManager = [[PhotoManager alloc] init];
When you create a singleton class you has never use alloc init, you always should use sharedInstance method to keep the same instance in your app
By the way... as T_77 suggest you, you should use dispatch_once instead of your implementation to create an instance.
+ (instancetype)sharedInstance
{
static dispatch_once_t once;
static id sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
I have created a singleton:
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
NSAssert(FALSE, #"Please use getSharedInstance class method of MotionManager to avoid singleton abuse. =)");
return nil;
}
+ (id) getSharedInstance
{
if (!instance)
{
instance = [[super allocWithZone:NULL] init];
}
return instance;
}
Why does the above works fine, but the below one throws exception?
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
NSAssert(FALSE, #"Please use getSharedInstance class method of MotionManager to avoid singleton abuse. =)");
return nil;
}
+ (id) getSharedInstance
{
if (!instance)
{
instance = [[super alloc] init];
}
return instance;
}
This is the proper way to create a singleton:
+ (id)sharedManager {
static Singleton *sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedManager;
}
This is because alloc is also calling allocWithZone: internally, see NSObject doc
That's the reason why your code instance = [[super allocWithZone:NULL] init]; works while instance = [[super alloc] init]; doesn't.
I know that we can implement the Singleton pattern in Objective-C like this:
+ (instancetype)sharedInstance
{
static PPSettings *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
However, how do I make sure users would never be able to create more objects like this:
PPSettings *settingsObj1 = [[PPSettings alloc] init];
PPSettings *settingsObj2 = [[PPSettings alloc] init];
?
In other words, how we can achieve the same idea as Private Constructors in Java or C#?
Thanks in advance!
P.S.
Also, is it correct like:
instance = [[self alloc] init];
or better like this:
instance = [[[self class] alloc] init];
You can disallow init in your header by using the __attribute__ compiler directive:
- (instancetype) init __attribute__((unavailable("init not available, all access should be handled through the [YourClass sharedInstance] singleton")));
You can use + (id) allocWithZone:(struct _NSZone *)zone to make sure alloc init would never be able to create more objects.
static SingletonClass *sharedInstance = nil;
+ (SingletonClass *) sharedInstance {
if(!sharedInstance) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[super allocWithZone:NULL] init];
});
}
return sharedInstance;
}
+ (id) allocWithZone:(struct _NSZone *)zone {
return [self sharedInstance];
}
There have been many true singleton patterns (rather than the shared instance that is common used and mis-called a singleton), with ARC the following will work:
- (instancetype) init
{
static Singleton *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken,
^{
self = [super init];
if (self)
{
// any one-time init code
}
// save for next time
instance = self;
});
return instance;
}
Now you can:
[Singleton new]
or
[[Singleton alloc] init]
and you will only ever get the same instance. There will be a redundant alloc for each instance after the first which ARC will immediately clean up.
HTH
Addendum
Re: #HotLicks's first comment: If the cost of the alloc calls are too high you can just combine this with a singletonInstance method following the question:
+ (instancetype)singletonInstance
{
static Singleton *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
Of course programmers who choose to do [[Singleton alloc] init] will pay the cost, and that could be avoid by implementing allocWithZone:, but whether that is worth it will depend on the application. What the above init does is ensure that only one instance is ever created, regardless of whether singletonInstance or [[Singleton alloc] init] is used, and does so simply.
Re: #HotLick's second comment: Fair enough, that was assumed to be part of the one-time init code but it was probably a bit too brief. Edited code to make it clear.
HTH
what is the difference between these singleton implementations:
declaring static instance outside the sharedManager functions
#implementation MyManager
static MyManager * manager = nil;
+(instancetype)sharedManager
{
#synchronized(self) {
if(manager==nil){
manager = [[MyManager alloc]init];
}
return manager;
}
}
2.
declaring static instance inside the sharedManager function
#implementation MyManager
+(instancetype)sharedManager
{
static MyManager * manager = nil;
#synchronized(self) {
if(manager==nil){
manager = [[MyManager alloc]init];
}
return manager;
}
}
#end
declaring MyManager as extern in the interface
4.
+ (instancetype)sharedManager {
static MyManager *singleton=nil;
static dispatch_once_t once;
dispatch_once(&once, ^{
singleton = [[self alloc] init];
});
return singleton;
}
You should make it a static variable inside the accessor method. This prevents you from accidentally accessing it before it's been properly initialized.
The modern way to initialize a singleton safely is like this:
+ (instancetype)sharedManager {
static MyManager *singleton;
static dispatch_once_t once;
dispatch_once(&once, ^{
singleton = [[self alloc] init];
});
return singleton;
}
dispatch_once is significantly faster than #synchronized if the once-block has already been performed.