我的Objective-C单例应该是什么样子?

我的singleton访问器方法通常是以下的一些变体:

static MyClass *gInstance = NULL; + (MyClass *)instance { @synchronized(self) { if (gInstance == NULL) gInstance = [[self alloc] init]; } return(gInstance); } 

我能做些什么来改善呢?

另一种select是使用+(void)initialize方法。 从文档:

运行时发送initialize给程序中的每个类,恰好在类之前,或者从它inheritance的任何类中,从程序中发送它的第一条消息。 (因此,如果不使用该类,则永远不会调用该方法。)运行时以线程安全的方式将initialize消息发送给类。 超类在它们的子类之前收到这个消息。

所以你可以做类似这样的事情:

 static MySingleton *sharedSingleton; + (void)initialize { static BOOL initialized = NO; if(!initialized) { initialized = YES; sharedSingleton = [[MySingleton alloc] init]; } } 
 @interface MySingleton : NSObject { } + (MySingleton *)sharedSingleton; @end @implementation MySingleton + (MySingleton *)sharedSingleton { static MySingleton *sharedSingleton; @synchronized(self) { if (!sharedSingleton) sharedSingleton = [[MySingleton alloc] init]; return sharedSingleton; } } @end 

[资源]

根据我下面的其他答案,我认为你应该做的:

 + (id)sharedFoo { static dispatch_once_t once; static MyFoo *sharedFoo; dispatch_once(&once, ^ { sharedFoo = [[self alloc] init]; }); return sharedFoo; } 

由于肯德尔发布了一个试图避免locking成本的线程安全单例,我还以为也会抛出一个:

 #import <libkern/OSAtomic.h> static void * volatile sharedInstance = nil; + (className *) sharedInstance { while (!sharedInstance) { className *temp = [[self alloc] init]; if(!OSAtomicCompareAndSwapPtrBarrier(0x0, temp, &sharedInstance)) { [temp release]; } } return sharedInstance; } 

好的,让我解释这是如何工作的:

  1. 快速的情况下:在正常的执行sharedInstance已经被设置,所以while循环从来没有执行和函数返回后,简单地testingvariables的存在;

  2. 慢速案例:如果sharedInstance不存在,则使用比较和交换('CAS')分配实例并复制到实例中;

  3. 有争议的情况:如果两个线程同时尝试调用sharedInstancesharedInstance不同时存在,那么它们都将初始化单例的新实例,并尝试将其CAS放入到位。 无论哪一个获得CAS,立即返回,无论哪一个丢失释放它刚刚分配的实例,并返回(现在设置) sharedInstance 。 单个OSAtomicCompareAndSwapPtrBarrier既是设置线程的写屏障,也是testing线程的读屏障。

 static MyClass * sharedInst = nil;

 +(id)sharedInstance
 {
     @synchronize(self){
         if(sharedInst == nil){
             / * sharedInst在init中设置* /
             [[self alloc] init];
         }
     }
     return sharedInst;
 }

 - (id)init
 {
     if(sharedInst!= nil){
         [NSException raise:NSInternalInconsistencyException
            格式:@“[%@%@]不能被调用;用+ [%@%@]替代”],
             NSStringFromClass([self class]),NSStringFromSelector(_cmd), 
             NSStringFromClass([self class]),
             NSStringFromSelector(@selector(sharedInstance)“];
     } else if(self = [super init]){
         sharedInst = self;
         / *这里的具体类别* /
     }
     return sharedInst;
 }

 / *这些可能在什么都没有
   一个GC应用程序。 保持单身
   作为一个实际的单身人士
   非CG应用程序
 * /
 - (NSUInteger)retainCount
 {
    返回NSUIntegerMax;
 }

 - (单向无效)释放
 {
 }

 - (id)保留
 {
     return sharedInst;
 }

 - (id)autorelease
 {
     return sharedInst;
 }

编辑:这个实现过时了ARC。 请看看如何实现与ARC兼容的Objective-C单例? 正确实施。

所有的初始化我读过其他答案的实现共享一个共同的错误。

 + (void) initialize { _instance = [[MySingletonClass alloc] init] // <----- Wrong! } + (void) initialize { if (self == [MySingletonClass class]){ // <----- Correct! _instance = [[MySingletonClass alloc] init] } } 

Apple文档build议您在初始化块中检查类的types。 因为子类默认调用初始化。 存在非显而易见的情况,其中子类可以通过KVO间接创build。 对于如果您在另一个类中添加以下行:

 [[MySingletonClass getInstance] addObserver:self forKeyPath:@"foo" options:0 context:nil] 

Objective-C将隐含地创buildMySingletonClass的子类,导致第二次触发+initialize

你可能会认为你应该在init块中隐式地检查重复的初始化,如下所示:

 - (id) init { <----- Wrong! if (_instance != nil) { // Some hack } else { // Do stuff } return self; } 

但是你会在脚下开枪。 或者更糟的是让另一个开发者有机会在脚下自杀。

 - (id) init { <----- Correct! NSAssert(_instance == nil, @"Duplication initialization of singleton"); self = [super init]; if (self){ // Do stuff } return self; } 

TL; DR,这是我的实现

 @implementation MySingletonClass static MySingletonClass * _instance; + (void) initialize { if (self == [MySingletonClass class]){ _instance = [[MySingletonClass alloc] init]; } } - (id) init { ZAssert (_instance == nil, @"Duplication initialization of singleton"); self = [super init]; if (self) { // Initialization } return self; } + (id) getInstance { return _instance; } @end 

(用我们自己的断言macrosreplaceZAssert;或者只是NSAssert。)

Singletonmacros代码的详细解释在Cocoa With Love博客上

http://cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html

对于线程安全的sharedInstance,我有一个有趣的变化,但在初始化后不locking。 我还不确定是否按照要求修改了最重要的答案,但我提出了进一步的讨论:

 // Volatile to make sure we are not foiled by CPU caches static volatile ALBackendRequestManager *sharedInstance; // There's no need to call this directly, as method swizzling in sharedInstance // means this will get called after the singleton is initialized. + (MySingleton *)simpleSharedInstance { return (MySingleton *)sharedInstance; } + (MySingleton*)sharedInstance { @synchronized(self) { if (sharedInstance == nil) { sharedInstance = [[MySingleton alloc] init]; // Replace expensive thread-safe method // with the simpler one that just returns the allocated instance. SEL origSel = @selector(sharedInstance); SEL newSel = @selector(simpleSharedInstance); Method origMethod = class_getClassMethod(self, origSel); Method newMethod = class_getClassMethod(self, newSel); method_exchangeImplementations(origMethod, newMethod); } } return (MySingleton *)sharedInstance; } 

简短的回答:很棒。

长答案:像….

 static SomeSingleton *instance = NULL; @implementation SomeSingleton + (id) instance { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ if (instance == NULL){ instance = [[super allocWithZone:NULL] init]; } }); return instance; } + (id) allocWithZone:(NSZone *)paramZone { return [[self instance] retain]; } - (id) copyWithZone:(NSZone *)paramZone { return self; } - (id) autorelease { return self; } - (NSUInteger) retainCount { return NSUIntegerMax; } - (id) retain { return self; } @end 

请务必阅读dispatch / once.h头文件以了解正在发生的事情。 在这种情况下,标题注释比文档或手册页更适用。

我已经把singleton转换成了一个类,所以其他类可以inheritancesingleton属性。

Singleton.h:

 static id sharedInstance = nil; #define DEFINE_SHARED_INSTANCE + (id) sharedInstance { return [self sharedInstance:&sharedInstance]; } \ + (id) allocWithZone:(NSZone *)zone { return [self allocWithZone:zone forInstance:&sharedInstance]; } @interface Singleton : NSObject { } + (id) sharedInstance; + (id) sharedInstance:(id*)inst; + (id) allocWithZone:(NSZone *)zone forInstance:(id*)inst; @end 

Singleton.m:

 #import "Singleton.h" @implementation Singleton + (id) sharedInstance { return [self sharedInstance:&sharedInstance]; } + (id) sharedInstance:(id*)inst { @synchronized(self) { if (*inst == nil) *inst = [[self alloc] init]; } return *inst; } + (id) allocWithZone:(NSZone *)zone forInstance:(id*)inst { @synchronized(self) { if (*inst == nil) { *inst = [super allocWithZone:zone]; return *inst; // assignment and return on first allocation } } return nil; // on subsequent allocation attempts return nil } - (id)copyWithZone:(NSZone *)zone { return self; } - (id)retain { return self; } - (unsigned)retainCount { return UINT_MAX; // denotes an object that cannot be released } - (void)release { //do nothing } - (id)autorelease { return self; } @end 

这里有一个例子,你想成为单身。

 #import "Singleton.h" @interface SomeClass : Singleton { } @end @implementation SomeClass DEFINE_SHARED_INSTANCE; @end 

关于Singleton类的唯一限制是它是NSObject的子类。 但是大多数时候我在我的代码中使用单例,实际上它们是NSObject的子类,所以这个类真正减轻了我的生活,使代码更加清晰。

这也适用于非垃圾收集环境。

 @interface MySingleton : NSObject { } +(MySingleton *)sharedManager; @end @implementation MySingleton static MySingleton *sharedMySingleton = nil; +(MySingleton*)sharedManager { @synchronized(self) { if (sharedMySingleton == nil) { [[self alloc] init]; // assignment not done here } } return sharedMySingleton; } +(id)allocWithZone:(NSZone *)zone { @synchronized(self) { if (sharedMySingleton == nil) { sharedMySingleton = [super allocWithZone:zone]; return sharedMySingleton; // assignment and return on first allocation } } return nil; //on subsequent allocation attempts return nil } -(void)dealloc { [super dealloc]; } -(id)copyWithZone:(NSZone *)zone { return self; } -(id)retain { return self; } -(unsigned)retainCount { return UINT_MAX; //denotes an object that cannot be release } -(void)release { //do nothing } -(id)autorelease { return self; } -(id)init { self = [super init]; sharedMySingleton = self; //initialize here return self; } @end 

这是不是线程安全,并避免第一次通话后的昂贵的locking?

 + (MySingleton*)sharedInstance { if (sharedInstance == nil) { @synchronized(self) { if (sharedInstance == nil) { sharedInstance = [[MySingleton alloc] init]; } } } return (MySingleton *)sharedInstance; } 

这是一个我放在一起的macros :

http://github.com/cjhanson/Objective-C-Optimized-Singleton

它基于Matt Gallagher在这里的工作,但是正如谷歌的Dave MacLachlan所描述的那样,改变了使用方法debugging的方法 。

我欢迎评论/贡献。

怎么样

 static MyClass *gInstance = NULL; + (MyClass *)instance { if (gInstance == NULL) { @synchronized(self) { if (gInstance == NULL) gInstance = [[self alloc] init]; } } return(gInstance); } 

那么你在初始化之后避免了同步成本?

有关Objective-C中单例模式的深入讨论,请看这里:

在Objective-C中使用单例模式

KLSingleton是:

  1. 子分类(到第n级)
  2. ARC兼容
  3. allocinit安全
  4. 懒洋洋地装着
  5. 线程安全
  6. 无锁(使用+初始化,而不是@synchronize)
  7. macros观免费
  8. 调酒,免费
  9. 简单

KLSingleton

你不想同步自我…因为自我对象还不存在! 你最终locking一个临时的id值。 你想确保没有其他人可以运行类方法(sharedInstance,alloc,allocWithZone:等),所以你需要在类对象上进行同步:

 @implementation MYSingleton static MYSingleton * sharedInstance = nil; +( id )sharedInstance { @synchronized( [ MYSingleton class ] ) { if( sharedInstance == nil ) sharedInstance = [ [ MYSingleton alloc ] init ]; } return sharedInstance; } +( id )allocWithZone:( NSZone * )zone { @synchronized( [ MYSingleton class ] ) { if( sharedInstance == nil ) sharedInstance = [ super allocWithZone:zone ]; } return sharedInstance; } -( id )init { @synchronized( [ MYSingleton class ] ) { self = [ super init ]; if( self != nil ) { // Insert initialization code here } return self; } } @end 

只是想离开这里,所以我不会失去它。 这个优点是可以在InterfaceBuilder中使用,这是一个巨大的优势。 这是从我问到的另一个问题 :

 static Server *instance; + (Server *)instance { return instance; } + (id)hiddenAlloc { return [super alloc]; } + (id)alloc { return [[self instance] retain]; } + (void)initialize { static BOOL initialized = NO; if(!initialized) { initialized = YES; instance = [[Server hiddenAlloc] init]; } } - (id) init { if (instance) return self; self = [super init]; if (self != nil) { // whatever } return self; } 
 static mySingleton *obj=nil; @implementation mySingleton -(id) init { if(obj != nil){ [self release]; return obj; } else if(self = [super init]) { obj = self; } return obj; } +(mySingleton*) getSharedInstance { @synchronized(self){ if(obj == nil) { obj = [[mySingleton alloc] init]; } } return obj; } - (id)retain { return self; } - (id)copy { return self; } - (unsigned)retainCount { return UINT_MAX; // denotes an object that cannot be released } - (void)release { if(obj != self){ [super release]; } //do nothing } - (id)autorelease { return self; } -(void) dealloc { [super dealloc]; } @end 

我知道这个“问题”有很多评论,但是我没有看到许多人build议使用macros来定义单例。 这是一个常见的模式,macros观大大简化了单身人士。

这里是我基于我见过的几个Objc实现写的macros。

Singeton.h

 /** @abstract Helps define the interface of a singleton. @param TYPE The type of this singleton. @param NAME The name of the singleton accessor. Must match the name used in the implementation. @discussion Typcially the NAME is something like 'sharedThing' where 'Thing' is the prefix-removed type name of the class. */ #define SingletonInterface(TYPE, NAME) \ + (TYPE *)NAME; /** @abstract Helps define the implementation of a singleton. @param TYPE The type of this singleton. @param NAME The name of the singleton accessor. Must match the name used in the interface. @discussion Typcially the NAME is something like 'sharedThing' where 'Thing' is the prefix-removed type name of the class. */ #define SingletonImplementation(TYPE, NAME) \ static TYPE *__ ## NAME; \ \ \ + (void)initialize \ { \ static BOOL initialized = NO; \ if(!initialized) \ { \ initialized = YES; \ __ ## NAME = [[TYPE alloc] init]; \ } \ } \ \ \ + (TYPE *)NAME \ { \ return __ ## NAME; \ } 

使用示例:

MyManager.h

 @interface MyManager SingletonInterface(MyManager, sharedManager); // ... @end 

MyManager.m

 @implementation MyManager - (id)init { self = [super init]; if (self) { // Initialization code here. } return self; } SingletonImplementation(MyManager, sharedManager); // ... @end 

为什么界面macros几乎是空的? 代docker和代码文件之间的代码一致性; 如果你想添加更多的自动方法或改变它的可维护性。

我正在使用初始化方法来创build单身人士,这是在这里最stream行的答案(在写作时)。

使用Objective C类的方法,我们可以避免使用单例模式的通常方式,从:

 [[Librarian sharedInstance] openLibrary] 

至:

 [Librarian openLibrary] 

通过将类包装到另一个只有Class方法的类中 ,这样就不会有意外创build重复实例的机会,因为我们没有创build任何实例!

我在这里写了一个更详细的博客:)

从@ robbie-hanson扩展例子…

 static MySingleton* sharedSingleton = nil; + (void)initialize { static BOOL initialized = NO; if (!initialized) { initialized = YES; sharedSingleton = [[self alloc] init]; } } - (id)init { self = [super init]; if (self) { // Member initialization here. } return self; } 

我的方式很简单,就是这样:

 static id instanceOfXXX = nil; + (id) sharedXXX { static volatile BOOL initialized = NO; if (!initialized) { @synchronized([XXX class]) { if (!initialized) { instanceOfXXX = [[XXX alloc] init]; initialized = YES; } } } return instanceOfXXX; } 

如果单身已经被初始化,LOCK块将不会被input。 第二个检查(!初始化)是否确保在当前线程获取LOCK时尚未初始化。

我没有读通过所有的解决scheme,所以原谅如果这个代码是多余的。

在我看来,这是最安全的线程。

 +(SingletonObject *) sharedManager { static SingletonObject * sharedResourcesObj = nil; @synchronized(self) { if (!sharedResourcesObj) { sharedResourcesObj = [[SingletonObject alloc] init]; } } return sharedResourcesObj; } 

我通常使用的代码大致类似于本霍夫斯坦的答案(我也出于维基百科)。 我使用它是由克里斯·汉森在他的评论中陈述的原因。

但是,有时候我需要将一个单例放入一个NIB中,在这种情况下,我使用下面的代码:

 @implementation Singleton static Singleton *singleton = nil; - (id)init { static BOOL initialized = NO; if (!initialized) { self = [super init]; singleton = self; initialized = YES; } return self; } + (id)allocWithZone:(NSZone*)zone { @synchronized (self) { if (!singleton) singleton = [super allocWithZone:zone]; } return singleton; } + (Singleton*)sharedSingleton { if (!singleton) [[Singleton alloc] init]; return singleton; } @end 

尽pipe上面的代码是您在垃圾收集环境中需要的所有东西,但我仍然将读写器的实现(等)留给读者。

被接受的答案,虽然编译,是不正确的。

 + (MySingleton*)sharedInstance { @synchronized(self) <-------- self does not exist at class scope { if (sharedInstance == nil) sharedInstance = [[MySingleton alloc] init]; } return sharedInstance; } 

每个Apple文档:

… You can take a similar approach to synchronize the class methods of the associated class, using the Class object instead of self.

Even if using self works, it shouldn't and this looks like a copy and paste mistake to me. The correct implementation for a class factory method would be:

 + (MySingleton*)getInstance { @synchronized([MySingleton class]) { if (sharedInstance == nil) sharedInstance = [[MySingleton alloc] init]; } return sharedInstance; }