How do I convert (or create) a singleton class that compiles and behaves correctly when using automatic reference counting (ARC) in Xcode 4.2?
In exactly the same way that you (should) have been doing it already:
+ (instancetype)sharedInstance
{
static MyClass *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[MyClass alloc] init];
// Do any other initialisation stuff here
});
return sharedInstance;
}
dispatch_once_t
is defined in dispatch/once.h
, and it's typedefed as a long
. It's probably used as an index on an array or some other global data structure which stores the state of every Dispatch Once token. You can call it whatever you want, and it will still work. –
Gloom static MyClass *sharedInstance = nil;
be placed outside of the method? –
Mont sharedInstance
is safely encapsulated within a method that ensures it will be created properly. Nothing outside of that method should have access to it directly. If you needed to explicitly clear or nil-out the sharedInstance
or something, then you would need to place it outside of the method. –
Gloom static
variables declared within a method/function are the same as a static
variable declared outside a method/function, they are just only valid within the scope of that method/function. Every separate run through the +sharedInstance
method (even on different threads) will 'see' the same sharedInstance
variable. –
Gloom static
variables are 0
(or nil
) by default, the = nil
is redundant. –
Optics +alloc~
method to prevent external instantiation completely. I posted an example as an answer. –
Micamicaela if you want to create other instance as needed.do this:
+ (MyClass *)sharedInstance
{
static MyClass *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[MyClass alloc] init];
// Do any other initialisation stuff here
});
return sharedInstance;
}
else,you should do this:
+ (id)allocWithZone:(NSZone *)zone
{
static MyClass *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [super allocWithZone:zone];
});
return sharedInstance;
}
dispatch_once()
bit means that you won't get additional instances, even in the first example...? –
Roguish [[MyClass alloc] init]
and bypass the sharedInstance
access. DongXu, you should look at Peter Hosey's Singleton article. If you're going to override allocWithZone:
to prevent more instances from being created, you also should override init
to prevent the shared instance from being re-initialized. –
Merrileemerrili allocWithZone:
version. Thx. –
Roguish allocWithZone:
, you should keep a dictionary of MyClass
objects, where the keys are the NSZone
s. –
Sombrous This is a version for ARC and non-ARC
How To use:
MySingletonClass.h
@interface MySingletonClass : NSObject
+(MySingletonClass *)sharedInstance;
@end
MySingletonClass.m
#import "MySingletonClass.h"
#import "SynthesizeSingleton.h"
@implementation MySingletonClass
SYNTHESIZE_SINGLETON_FOR_CLASS(MySingletonClass)
@end
This is my pattern under ARC. Satisfies new pattern using GCD and also satisfies Apple's old instantiation prevention pattern.
@implementation AAA
+ (id)alloc
{
return [self allocWithZone:nil];
}
+ (id)allocWithZone:(NSZone *)zone
{
[self doesNotRecognizeSelector:_cmd];
abort();
}
+ (instancetype)theController
{
static AAA* c1 = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
c1 = [[super allocWithZone:nil] init];
// For confirm...
NSLog(@"%@", NSStringFromClass([c1 class])); // Prints AAA
NSLog(@"%@", @([c1 class] == self)); // Prints 1
Class real_superclass_obj = class_getSuperclass(self);
NSLog(@"%@", @(real_superclass_obj == self)); // Prints 0
});
return c1;
}
@end
c1
being an instance of AAA
's superclass? You need to call +alloc
on self
, not on super
. –
Gloom super
doesn't mean the super-class object. You cannot get super-class object It just means routing messages to the super-class version of method. super
still points self
class. If you want to get super-class object, you need to get runtime reflection functions. –
Micamicaela -allocWithZone:
method is just a simple chain to runtime's allocation function to offer overriding point. So ultimately, self
pointer == current class object will be passed to allocator, and finally AAA
instance will be allocated. –
Micamicaela super
works in class methods. –
Gloom Read this answer and then go and read the other answer.
You must first know what does a Singleton mean and what are its requirements, if you don't understand it, than you won't understand the solution--at all!
To create a Singleton successfully you must be able to do the following 3:
- If there was a race condition, then we must not allow multiple instances of your SharedInstance to be created at the same time!
- Remember and keep the value among multiple invocations.
- Create it only once. By controlling the entry point.
dispatch_once_t
helps you to solve a race condition by only allowing its block to be dispatched once.
Static
helps you to “remember” its value across any number of
invocations. How does it remember? It doesn't allow any new instance with that exact name of your sharedInstance to be created again it just works with the one that was created originally.
Not using calling alloc
init
(i.e. we still have alloc
init
methods since we are an NSObject subclass, though we should NOT use them) on our sharedInstance class, we achieve this by using +(instancetype)sharedInstance
, which is bounded to only be initiated once, regardless of multiple attempts from different threads at the same time and remember its value.
Some of the most common system Singletons that come with Cocoa itself are:
[UIApplication sharedApplication]
[NSUserDefaults standardUserDefaults]
[NSFileManager defaultManager]
[NSBundle mainBundle]
[NSOperations mainQueue]
[NSNotificationCenter defaultCenter]
Basically anything that would need to have centralized effect would need to follow some sort of a Singleton design pattern.
Alternatively, Objective-C provides the +(void)initialize method for NSObject and all its sub-classes. It is always called before any methods of the class.
I set a breakpoint in one once in iOS 6 and dispatch_once appeared in the stack frames.
Singleton Class : No one can create more than one object of class in any case or through any way.
+ (instancetype)sharedInstance
{
static ClassName *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[ClassName alloc] init];
// Perform other initialisation...
});
return sharedInstance;
}
// You need need to override init method as well, because developer can call [[MyClass alloc]init] method also. that time also we have to return sharedInstance only.
-(MyClass)init
{
return [ClassName sharedInstance];
}
There are two issues with the accepted answer, which may or may not be relevant for your purpose.
- If from the init method, somehow the sharedInstance method is called again (e.g. because other objects are constructed from there which use the singleton) it will cause a stack overflow.
- For class hierarchies there is only one singleton (namely: the first class in the hierarchy on which the sharedInstance method was called), instead of one singleton per concrete class in the hierarchy.
The following code takes care of both of these problems:
+ (instancetype)sharedInstance {
static id mutex = nil;
static NSMutableDictionary *instances = nil;
//Initialize the mutex and instances dictionary in a thread safe manner
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
mutex = [NSObject new];
instances = [NSMutableDictionary new];
});
id instance = nil;
//Now synchronize on the mutex
//Note: do not synchronize on self, since self may differ depending on which class this method is called on
@synchronized(mutex) {
id <NSCopying> key = (id <NSCopying>)self;
instance = instances[key];
if (instance == nil) {
//Break allocation and initialization into two statements to prevent a stack overflow, if init somehow calls the sharedInstance method
id allocatedInstance = [self alloc];
//Store the instance into the dictionary, one per concrete class (class acts as key for the dictionary)
//Do this right after allocation to avoid the stackoverflow problem
if (allocatedInstance != nil) {
instances[key] = allocatedInstance;
}
instance = [allocatedInstance init];
//Following code may be overly cautious
if (instance != allocatedInstance) {
//Somehow the init method did not return the same instance as the alloc method
if (instance == nil) {
//If init returns nil: immediately remove the instance again
[instances removeObjectForKey:key];
} else {
//Else: put the instance in the dictionary instead of the allocatedInstance
instances[key] = instance;
}
}
}
}
return instance;
}
#import <Foundation/Foundation.h>
@interface SingleTon : NSObject
@property (nonatomic,strong) NSString *name;
+(SingleTon *) theSingleTon;
@end
#import "SingleTon.h"
@implementation SingleTon
+(SingleTon *) theSingleTon{
static SingleTon *theSingleTon = nil;
if (!theSingleTon) {
theSingleTon = [[super allocWithZone:nil] init
];
}
return theSingleTon;
}
+(id)allocWithZone:(struct _NSZone *)zone{
return [self theSingleTon];
}
-(id)init{
self = [super init];
if (self) {
// Set Variables
_name = @"Kiran";
}
return self;
}
@end
Hope above code will help it out.
if you need to create singleton in swift,
class var sharedInstance: MyClass {
struct Singleton {
static let instance = MyClass()
}
return Singleton.instance
}
or
struct Singleton {
static let sharedInstance = MyClass()
}
class var sharedInstance: MyClass {
return Singleton.sharedInstance
}
you can use this way
let sharedClass = LibraryAPI.sharedInstance
© 2022 - 2024 — McMap. All rights reserved.