Singleton in iOS Objective-C does not prevent more than one instance

I know that there are several threads on this, but no one answers my questions.

I implemented my singleton class like this (realizing the debate about singles):

+ (MyClass*) sharedInstance { static MyClass *_sharedInstance = nil; static dispatch_once_t oncePredicate; dispatch_once(&oncePredicate, ^{ _sharedInstance = [[MyClass alloc] init]; }); return _sharedInstance; } - (instancetype)init{ self = [super init]; if (self) { //setup code } return self; } 

I tried to instantiate another object and compared it to the one that was returned by sharedInstance with '==', and they were really different.

Questions:

  • Is it possible to create more than one object of a singleton class? Is that not so? Implementing Singleton in Java prevents this.
  • And if so, how? Should I create a setup method and call it instead of initializing and executing it?
  • Is this the correct implementation?
+6
source share
8 answers

Your observation is correct, many of the "singleton" patterns that you see in Objective-C are not singletones at all, but rather a "shared instance" model where other instances can be created.

In the old days of MRC, Apple used a sample code showing how to implement a real singleton.

The code you have is the recommended template for ARC and thread safe singleton, you just need to put it in the init method:

 - (instancetype) init { static MyClass *initedObject; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ initedObject = [super init]; }); return initedObject; } 

This code ensures that there is only one instance of MyClass , regardless of the number of calls to [MyClass new] or [[MyClass alloc] init] .

This is all you need to do, but you can move on. First, if you want the class method to return a singleton, it is simple:

 + (instancetype) singletonInstance { return [self new]; } 

This method ends with an init call that returns singleton, creating it if necessary.

If MyClass implements NSCopying , you also need to implement copyWithZone: - a method that calls copy . Since you have a singleton, it's really simple:

 - (instancetype) copyWithZone:(NSZone *)zone { return self; } 

Finally, in Objective-C, the operations of allocating an instance of a new object and initializing it are different. The above diagram ensures that only one instance of MyClass initialized and used, however, for each call to new or alloc , another instance is allocated, which is then discarded by init and cleared by ARC. This is somewhat wasteful!

This is easily solved by implementing allocWithZone: (e.g. copy above, this alloc method actually ends the call), following the same pattern as for init :

 + (instancetype) allocWithZone:(NSZone *)zone { static MyClass *allocatedObject; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ allocatedObject = [super allocWithZone:zone]; }); return allocatedObject; } 

The first time an instance is created, then allocWithZone: will allocWithZone: it, and then init initialize it, all subsequent calls will return an existing object. No discarded unnecessary distributions.

That it is a true singleton, and no more complicated than the faux-singletons that are so common.

NTN

+12
source

You cannot make the init method private, as in Java with a constructor. Therefore, nothing prevents you from calling [[MyClass alloc] init] , which actually creates another object. Until you do this, but stick with the sharedInstance method, your implementation is fine.

What you could do: the init method init an exception (for example, with [self doesNotRecognizeSelector:@_cmd] ) and initializes with another method (for example, privateInit ) that does not appear in the header file.

+5
source

With objective-c, you can prevent your single class from creating multiple objects. You can forbid calling alloc and init with your singleton class.

 #import <Foundation/Foundation.h> @interface SingletonClass : NSObject + (id) sharedInstance; - (void) someMethodCall; - (instancetype) init __attribute__((unavailable("Use +[SingletonClass sharedInstance] instead"))); + (instancetype) new __attribute__ ((unavailable("Use +[SingletonClass sharedInstance] instead"))); @end #import "SingletonClass.h" @implementation SingletonClass + (id) sharedInstance{ static SingletonClass * sharedObject = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedObject = [[self alloc] initPrivate]; }); return sharedObject; } - (instancetype)init { @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"You can't override the init call in class %@", NSStringFromClass([self class])] userInfo:nil]; } - (instancetype)initPrivate { if (self = [super init]) { } return self; } - (void) someMethodCall{ NSLog(@"Method Call"); } @end 

1 # If you try to call init or new methods on SingletonClass, these methods will not be available to call.

2 # If you comment out the methods mentioned below in the header file and try to call init in the SingletonClass method, then the application will be broken with the reason "You cannot override the init call in the SingletonClass class".

  - (instancetype) init __attribute__((unavailable("Use +[SingletonClass sharedInstance] instead"))); + (instancetype) new __attribute__ ((unavailable("Use +[SingletonClass sharedInstance] instead"))); 

Just use this code to create a single object in the Singleton Pattern and prevent init init from calling a singleton pattern from other classes. I tested this code with xCode 7.0+ and worked fine.

+3
source

To prevent the creation of multiple objects of the same class, you must do the following: you just perfectly created a single object. but when invoking init, copy, mutable copy you need to be handled this way.

 - (instancetype)init{ if (!_sharedInstance) { _sharedInstance = [MyClass sharedInstance]; } return _sharedInstance; } - (id)copy{ if (!_sharedInstance) { _sharedInstance = [MyClass sharedInstance]; } return _sharedInstance; } 

same for volatile copy. therefore, this implementation ensures that when one instance is available worldwide.

Help you.

+2
source

@VeryPoliteNerd just marks the init and new methods as inaccessible in .h :

 - (instancetype)init __attribute__((unavailable("Use +[MyClass sharedInstance] instead"))); + (instancetype)new __attribute__((unavailable("Use +[MyClass sharedInstance] instead"))); 

This will cause the compiler to complain if the caller tries to manually create these objects

+1
source

Calling alloc / init to get a second instance of the Singleton class is considered a blatant programming error. To avoid such a programming error, you do not write complex code, to prevent it, you do code reviews and tell everyone who is trying to do this, as with any programming error.

0
source

This works for me:

 static AudioRecordingGraph * __strong sharedInstance; +(instancetype)sharedInstance { @synchronized(self) { if(!sharedInstance) { sharedInstance = [AudioRecordingGraph new]; } return sharedInstance; } } 
0
source

This works for me:

 static DataModel *singleInstance; + (DataModel*)getInstance{ if (singleInstance == nil) { singleInstance = [[super alloc] init]; } return singleInstance; } 

You can call it with

 _model = [DataModel getInstance]; 
-2
source

Source: https://habr.com/ru/post/988985/


All Articles