How to check if a Singleton is already initialized? - ios

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

Related

Avoiding singletonton abuse in objective C

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.

NSCache is returning null after restarting project

I'm working with NSCache in Objective-C and Cocoa for iOS. Every time I restart the project, the getCacheRecommend call returns null and I expect it to return a value.
#import <Foundation/Foundation.h>
#class ASJsonDiscoverModel;
#interface ASUserCache : NSObject
+ (ASUserCache *)sharedInstance;
- (void)clear;
- (void)setCacheRecommend:(ASJsonDiscoverModel *)discover;
- (ASJsonDiscoverModel *)getCacheRecommend;
ASJsonDiscoverModel is my custom object class.
#import "ASUserCache.h"
#interface ASUserCache ()
#property (nonatomic,strong) NSCache *cache;
#end
#implementation ASUserCache
+ (ASUserCache *)sharedInstance
{
__strong static ASUserCache *cache = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
cache = [[ASUserCache alloc] init];
});
return cache;
}
- (instancetype)init
{
if (self = [super init]) {
_cache = [[NSCache alloc] init];
}
return self;
}
- (void)setCacheRecommend:(ASJsonDiscoverModel *)discover
{
NSString *key = #"channelRecommend";
[_cache removeObjectForKey:key];
[_cache setObject:discover forKey:key];
}
- (ASJsonDiscoverModel *)getCacheRecommend
{
NSString *key = #"channelRecommend";
return [_cache objectForKey:key];
}
- (void)clear
{
if (_cache) {
[_cache removeAllObjects];
}
}
- (NSString *)keyforUserID:(NSString *)userID
{
return [NSString stringWithFormat:#"**%#",userID];
}

where should I declare the static instance?

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.

Creating a singleton and overriding the alloc class method

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.

Issues with singleton

I have created a single ton like this for ARC,
+ (MyClass *)sharedInstance {
static MyClass *sharedSpeaker = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedSpeaker = [[self alloc] init];
});
return sharedSpeaker;
}
- (id)init {
if (self = [super init]) {
}
return self;
}
But here I am creating instances like this:
id speaker3 = [[MyClass alloc] init];
id speaker = [MyClass sharedInstance];
id speaker2 = [[MyClass alloc] init];
NSLog(#"Speaker 1= %# \n speaker 2 = %#\n Speaker3 = %#",speaker,speaker2,speaker3);`
I got output as:
Speaker 1= <MyClass : 0xa45f5e0>
speaker 2 = <MyClass : 0xa461740>
Speaker3 = <MyClass : 0xa4529e0>
This is looking like a desired behaviour. How to stop this when I am giving singleton in library to user. I need to block him from creating new instance. Do I need to make static global if I make it global he cant create the global variable of the same name there will be conflict right. So any memebers can give solution on this?
For example using an assert in the init method.
- (id)init {
static int maxInstances = 1;
assert(maxInstances > 0);
maxInstances--;
...
}
Because you are creating new instances of your singleton class using alloc, init.
You will get the singleton instance using the sharedInstance class method. Like:
id speaker = [MyClass sharedInstance];
If you don't want to create the instances with alloc or init. Override those methods.
You can write static MyClass *sharedSpeaker = nil; as a static global variable and remove it from the sharedInstance method.
in .h file
#import <Foundation/Foundation.h>
#interface Singleton : NSObject
{
///......
}
+ (Singleton *)sharedSingleton;
in .m file
#import "Singleton.h"
#implementation Singleton
static Singleton *singletonObj = NULL;
+ (Singleton *)sharedSingleton
{
#synchronized(self)
{
if (singletonObj == NULL)
singletonObj = [[self alloc] init];
}
return(singletonObj);
}
and use this in another file
#import "Singleton.h"
//.....
Singleton *sinObj = [Singleton sharedSingleton]; // not alloc and init. use direct
create instance like this it will always return you singlton
static testSinglton *myinstance = nil;
+ (testSinglton *) sharedInstance {
if (!myinstance) {
myinstance = [[testSinglton alloc] init];
}
return myinstance;
}
- (id) init {
if (myinstance) {
return myinstance;
}
if (self = [super init]) {
//new object now will be created...
myinstance = self;
}
return self;
}
NSLog(#"%#",[[testSinglton alloc] init]);
NSLog(#"%#",[testSinglton sharedInstance]);
NSLog(#"%#",[[testSinglton alloc] init]);

Resources