How do I implement an Objective-C singleton that is compatible with ARC?
在xcode 4.2中使用自动引用计数(arc)时,如何转换(或创建)一个编译并正确运行的单例类?
和你本该做的完全一样:
1 2 3 4 5 6 7 8 9 10 | + (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; } |
如果要根据需要创建其他实例,请执行以下操作:
1 2 3 4 5 6 7 8 9 10 | + (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; } |
否则,您应该这样做:
1 2 3 4 5 6 7 8 9 | + (id)allocWithZone:(NSZone *)zone { static MyClass *sharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedInstance = [super allocWithZone:zone]; }); return sharedInstance; } |
这是ARC和非ARC的版本
如何使用:
mysingletonclass.h系列
1 2 3 4 5 | @interface MySingletonClass : NSObject +(MySingletonClass *)sharedInstance; @end |
mysingletonclass.m系列
1 2 3 4 5 | #import"MySingletonClass.h" #import"SynthesizeSingleton.h" @implementation MySingletonClass SYNTHESIZE_SINGLETON_FOR_CLASS(MySingletonClass) @end |
读这个答案,然后去读另一个答案。
首先,你必须知道单例是什么意思,它的要求是什么,如果你不理解它,那么你就不会理解这个解决方案——一点也不理解!
要成功创建singleton,您必须能够执行以下3项操作:
- 如果存在竞争条件,那么我们不能允许同时创建多个sharedInstance实例!
- 记住并保持多个调用之间的值。
- 只创建一次。通过控制入口点。
EDOCX1[1]帮助您"记住"它在任何数量的调用。它是怎么记得的?它不允许再次创建具有sharedInstance的确切名称的任何新实例。它只与最初创建的实例一起使用。
不使用调用
可可本身带来的一些最常见的单子系统是:
[UIApplication sharedApplication] [NSUserDefaults standardUserDefaults] [NSFileManager defaultManager] [NSBundle mainBundle] [NSOperations mainQueue] [NSNotificationCenter defaultCenter]
基本上,任何需要有集中效果的东西都需要遵循某种单例设计模式。
这是我在弧线下的图案。使用GCD满足新模式,也满足苹果的旧实例化预防模式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | @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 |
或者,Objective-C为nsObject及其所有子类提供了+(void)初始化方法。它总是在类的任何方法之前调用。
我在iOS6中设置了一次断点,在堆栈帧中出现了一次分派。
接受的答案有两个问题,可能与您的目的有关,也可能与您的目的无关。
以下代码处理这两个问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | + (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; } |
singleton类:在任何情况下或通过任何方式,都不能创建类的多个对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | + (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]; } |
如果你需要用swift创建singleton,
1 2 3 4 5 6 | class var sharedInstance: MyClass { struct Singleton { static let instance = MyClass() } return Singleton.instance } |
或
1 2 3 4 5 6 7 | struct Singleton { static let sharedInstance = MyClass() } class var sharedInstance: MyClass { return Singleton.sharedInstance } |
你可以用这种方式
1 | let sharedClass = LibraryAPI.sharedInstance |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | #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 |
希望上面的代码能帮上忙。