我如何实现与ARC兼容的Objective-C单例?

如何转换(或创build)在Xcode 4.2中使用自动引用计数(ARC)时编译和行为正确的单例类?

正如你(应该)已经这样做了:

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

如果你想根据需要创build其他实例。

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

否则,你应该这样做:

 + (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

 @interface MySingletonClass : NSObject +(MySingletonClass *)sharedInstance; @end 

MySingletonClass.m

 #import "MySingletonClass.h" #import "SynthesizeSingleton.h" @implementation MySingletonClass SYNTHESIZE_SINGLETON_FOR_CLASS(MySingletonClass) @end 

这是我在ARC下的模式。 使用GCD满足新的模式,也满足苹果旧的预防模式。

 @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 

阅读这个答案,然后去阅读另一个答案。

你必须首先知道Singleton是什么意思,如果你不明白它的要求是什么,那么你根本不会理解这个解决scheme!

要成功创build一个Singleton,您必须能够执行以下3个操作:

  • 如果有竞争条件 ,那么我们不能同时创buildSharedInstance的多个实例!
  • 记住并保持多个调用中的值。
  • 只创build一次。 通过控制入口点。

dispatch_once_t可以帮助你解决一个竞争条件 ,只允许一次调度它的块。

Static可以帮助您在任意数量的调用中“记住”它的值。 它如何记住? 它不允许再次创build具有您的sharedInstance的确切名称的任何新实例,它只与最初创build的实例一起工作。

我们的sharedInstance类没有使用调用alloc init (即我们仍然有alloc init方法,因为我们是一个NSObject子类,尽pipe我们不应该使用它们),我们通过使用+(instancetype)sharedInstance来实现这+(instancetype)sharedInstance一次 ,不pipe来自不同线程的多次尝试同时记住它的价值。

Cocoa自带的一些最常见的系统单身人士是:

  • [UIApplication sharedApplication]
  • [NSUserDefaults standardUserDefaults]
  • [NSFileManager defaultManager]
  • [NSBundle mainBundle]
  • [NSOperations mainQueue]
  • [NSNotificationCenter defaultCenter]

基本上任何需要集中化效果的东西都需要遵循某种单一devise模式。

或者,Objective-C为NSObject及其所有子类提供了+(void)初始化方法。 它总是在课程的任何方法之前被调用。

我在iOS 6中设置了一个断点,dispatch_once出现在堆栈帧中。

单身人士课堂:无论如何也不能以任何方式创造出一个以上的课堂对象。

 + (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]; } 

接受的答案有两个问题,可能与您的目的相关,也可能不相关。

  1. 如果从init方法中,再次调用sharedInstance方法(例如,因为其他对象是从那里使用单例构造的),它会导致堆栈溢出。
  2. 对于类层次结构,只有一个单例(即:调用sharedInstance方法的层次结构中的第一个类),而不是层次结构中每个具体类的一个单例。

以下代码处理这两个问题:

 + (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 

希望上面的代码将帮助它。

如果你需要快速创build单身人士,

 class var sharedInstance: MyClass { struct Singleton { static let instance = MyClass() } return Singleton.instance } 

要么

 struct Singleton { static let sharedInstance = MyClass() } class var sharedInstance: MyClass { return Singleton.sharedInstance } 

你可以用这种方式

 let sharedClass = LibraryAPI.sharedInstance