Avoiding singletonton abuse in objective C - ios

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.

Related

How to check if a Singleton is already initialized?

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

Singleton Object in iOS [duplicate]

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;
}

How to implement the Singleton in Objective-C and make sure the user never creates more objects?

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

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.

Trying to temporarily store objects in an NSMutableArray in a Singleton class for later use in iOS

I have a Singleton class that has two methods:
- (void)saveString:(NSString *)stringObject {
[[[Singleton sharedInstance] stringArray] addObject:stringObject];
}
- (NSArray *)getArrayContents {
return [[Singelton sharedInstance] stringArray];
}
Here is the implementation code of my Singleton class:
static Singleton *sharedSingleton = nil;
+ (Singleton *) sharedInstance {
if (sharedSingleton == nil) {
sharedSingleton = [[super alloc] init];
}
return sharedSingleton;
}
I have two View Controllers (vcA, and vcB) in my application. What I am trying to do is temporarily store the data from vcA, so that the data inside stringArray will be accessible later to vcB.
Here is the code that vcA uses to store the data:
[[Singleton sharedInstance] saveString:stringName];
Later in the lifecycle of the application, vcB calls the Singleton class to retrieve the values from the NSMutableArray:
NSArray *newArray = [[Singleton sharedInstance] getArrayContents];
for (NSString *test in newArray) {
NSLog(#"Here are the contents of the array %#", test);
}
Unfortunately, when I make the call in vcB to print the contents of the Array, there is no output because the array is empty, despite the fact that values are added to the array. What is it I'm doing wrong?
Try this,
to create Singleton
+(Singleton *)sharedSingleton {
static dispatch_once_t once;
static Singleton *sharedSingleton;
dispatch_once(&once, ^{
sharedSingleton = [[self alloc] init];
});
return sharedSingleton;
}
and the init method of singleton class
- (id)init
{
self = [super init];
if (self) {
//#property stringArray
self.stringArray = [[NSMutableArray alloc] init];
}
return self;
}
Other methods of Singleton
- (void)saveString:(NSString *)stringObject {
[self.stringArray addObject:stringObject];
}
- (NSArray *)getArrayContents {
return self.stringArray;
}
I had this problem. My code in the singleton looked like this:
+ (ReportDataList*)sharedDataArray
{
static dispatch_once_t pred;
static ReportDataList *shared = nil;
dispatch_once(&pred, ^{
shared = [[ReportDataList alloc] init];
self.rDetailsArray = [[NSMutableArray alloc] init];
});
return shared;
}
I had incorrectly initialised the array, so it was emptying it when I created a reference to the singleton later in my code. I removed the array initialisation, which is done in the -(id)init method and it worked fine. So, my code then looked like this:
+ (ReportDataList*)sharedDataArray
{
static dispatch_once_t pred;
static ReportDataList *shared = nil;
dispatch_once(&pred, ^{
shared = [[ReportDataList alloc] init];
});
return shared;
}
- (id)init
{
self = [super init];
if (self) {
self.rDetailsArray = [[NSMutableArray alloc] init];
[self initWithDummyValues];
}else{
NSLog(#"problem initialising array list");
}
return self;
}
First off, these two methods should probably use self, not sharedInstance:
- (void)saveString:(NSString *)stringObject {
[[self stringArray] addObject:stringObject];
}
- (NSArray *)getArrayContents {
return [self stringArray];
}
Second, there’s no point in having a getArrayContents method when you already have stringArray, and get as a prefix is usually reserved for methods that take a parameter to be copied into, anyhow.
Third, I don’t see you initializing stringArray anywhere, so unless there’s code missing, it’s nil and it’s staying nil. Maybe try:
+ (Singleton *) sharedInstance {
if (!sharedSingleton) {
sharedSingleton = [[self alloc] init];
sharedSingleton.stringArray = [NSMutableArray new];
}
return sharedSingleton;
}
Assuming stringArray is declared something like:
#property (readwrite, strong) NSMutableArray *stringArray;

Resources