I know there are several threads on this, but none answer my questions.
I've implemented my singleton class like this (being aware of the controversy about singletons):
+ (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 instantiating a different object and compared it to the one returned by sharedInstance with '==' and they were indeed different.
Questions:
Your observation is correct, many of the "singleton" patterns you see in Objective-C are not singletons at all but rather a "shared instance" model where other instances can be created.
In the old MRC days Apple used to have sample code showing how to implement a true singleton.
The code you have is the recommended pattern for ARC and thread-safe singletons, you just need to place 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 will ensure that there is only ever one instance of MyClass
regardless of how many [MyClass new]
or [[MyClass alloc] init]
calls are made.
That is all you need to do, but you can go further. First if you wish to have a class method to return the singleton it is simply:
+ (instancetype) singletonInstance
{
return [self new];
}
This method ends up calling init
which returns the singleton, creating it if needed.
If MyClass
implements NSCopying
then you also need to implement copyWithZone:
- which is the method which copy
calls. As you've a singleton this is really simple:
- (instancetype) copyWithZone:(NSZone *)zone
{
return self;
}
Finally in Objective-C the operations of allocating a new object instance and initialising it are distinct. The above scheme ensures only one instance of MyClass
is initialised and used, however for every call to new
or alloc
another instance is allocated and then promptly discarded by init
and cleaned up by ARC. This is somewhat wasteful!
This is easily addressed by implementing allocWithZone:
(like copy
above this is the method alloc
actually ends up calling) 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 allocate it and then init
will initialise it, all subsequent calls will return the already existing object. No discarded unneeded allocations.
That's it, a true singleton, and no harder than the faux-singletons that are so common.
HTH
You can't make the init
method private, like you would do in Java with the constructor. So nothing stops you from calling [[MyClass alloc] init]
which indeed creates a different object. As long as you don't do that, but stick to the sharedInstance
method, your implementation is fine.
What you could do: have the init
method raise an exception (e.g. with [self doesNotRecognizeSelector:@_cmd]
) and perform the initialization in a different method (e.g. privateInit
) which is not exposed in the header file.
With objective-c, you can prevent your singleton class to create more than one object. You can prevent alloc and init call 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 will try to call init or new methods on SingletonClass, then these methods would not be available to call.
2# If you comment out mentioned below methods in header file and try to call the init on SingletonClass method then app will be crashed with reason "You can't override the init call in class SingletonClass".
- (instancetype) init __attribute__((unavailable("Use +[SingletonClass sharedInstance] instead")));
+ (instancetype) new __attribute__ ((unavailable("Use +[SingletonClass sharedInstance] instead")));
Just use this code to create single object to Singleton Pattern and prevent alloc init call for singleton pattern from other classes. I had tested this code with xCode 7.0+ and its working fine.
To prevent creating multiple objects of single class, you need to do following things. you just fine to created singleton object. but while calling init, copy, mutable copy, you need to handle such way.
- (instancetype)init{
if (!_sharedInstance) {
_sharedInstance = [MyClass sharedInstance];
}
return _sharedInstance;
}
- (id)copy{
if (!_sharedInstance) {
_sharedInstance = [MyClass sharedInstance];
}
return _sharedInstance;
}
the same things for mutable copy as well. so this implementation make sure that once one instance is available throughout..
May this help you.
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