我的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; }
好的,让我解释这是如何工作的:
-
快速的情况下:在正常的执行
sharedInstance
已经被设置,所以while
循环从来没有执行和函数返回后,简单地testingvariables的存在; -
慢速案例:如果
sharedInstance
不存在,则使用比较和交换('CAS')分配实例并复制到实例中; -
有争议的情况:如果两个线程同时尝试调用
sharedInstance
且sharedInstance
不同时存在,那么它们都将初始化单例的新实例,并尝试将其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是:
- 子分类(到第n级)
- ARC兼容
- 用
alloc
和init
安全- 懒洋洋地装着
- 线程安全
- 无锁(使用+初始化,而不是@synchronize)
- macros观免费
- 调酒,免费
- 简单
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; }