What should my Objective-C singleton look like?
我的singleton访问方法通常是以下的一些变体:
1 2 3 4 5 6 7 8 9 10 11 12 | static MyClass *gInstance = NULL; + (MyClass *)instance { @synchronized(self) { if (gInstance == NULL) gInstance = [[self alloc] init]; } return(gInstance); } |
我能做些什么来改进这个?
另一个选项是使用"
The runtime sends
initialize to each class in a program exactly one time just before the class, or any class that inherits from it, is sent its first message from within the program. (Thus the method may never be invoked if the class is not used.) The runtime sends theinitialize message to classes in a thread-safe manner. Superclasses receive this message before their subclasses.
所以你可以做一些类似的两本:
1 2 3 4 5 6 7 8 9 10 11 | static MySingleton *sharedSingleton; + (void)initialize { static BOOL initialized = NO; if(!initialized) { initialized = YES; sharedSingleton = [[MySingleton alloc] init]; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | @interface MySingleton : NSObject { } + (MySingleton *)sharedSingleton; @end @implementation MySingleton + (MySingleton *)sharedSingleton { static MySingleton *sharedSingleton; @synchronized(self) { if (!sharedSingleton) sharedSingleton = [[MySingleton alloc] init]; return sharedSingleton; } } @end |
[源码]
其他回答下面每我,我认为你应该做的:
1 2 3 4 5 6 7 | + (id)sharedFoo { static dispatch_once_t once; static MyFoo *sharedFoo; dispatch_once(&once, ^ { sharedFoo = [[self alloc] init]; }); return sharedFoo; } |
从一个单独的线程安全的肯德尔发布这两个attempts避免锁定成本的思想,会把一个IP:]。
1 2 3 4 5 6 7 8 9 10 11 12 13 | #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; } |
好的,让我解释这是如何工作的:
案例:固定在正常执行
案例:
contended案例:如果两个线程都
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 45 46 47 48 49 50 51 | static MyClass *sharedInst = nil; + (id)sharedInstance { @synchronize( self ) { if ( sharedInst == nil ) { /* sharedInst set up in init */ [[self alloc] init]; } } return sharedInst; } - (id)init { if ( sharedInst != nil ) { [NSException raise:NSInternalInconsistencyException format:@"[%@ %@] cannot be called; use +[%@ %@] instead"], NSStringFromClass([self class]), NSStringFromSelector(_cmd), NSStringFromClass([self class]), NSStringFromSelector(@selector(sharedInstance)"]; } else if ( self = [super init] ) { sharedInst = self; /* Whatever class specific here */ } return sharedInst; } /* These probably do nothing in a GC app. Keeps singleton as an actual singleton in a non CG app */ - (NSUInteger)retainCount { return NSUIntegerMax; } - (oneway void)release { } - (id)retain { return sharedInst; } - (id)autorelease { return sharedInst; } |
编辑:此实现与ARC一起废弃。请看一下如何实现与ARC兼容的Objective-C单例?以便正确实施。
我在其他答案中读到的初始化的所有实现都有一个共同的错误。
1 2 3 4 5 6 7 8 9 | + (void) initialize { _instance = [[MySingletonClass alloc] init] // <----- Wrong! } + (void) initialize { if (self == [MySingletonClass class]){ // <----- Correct! _instance = [[MySingletonClass alloc] init] } } |
Apple文档建议您检查初始化块中的类类型。因为子类在默认情况下调用初始化。存在一种不明显的情况,子类可以通过kvo间接创建。如果在另一个类中添加以下行:
1 | [[MySingletonClass getInstance] addObserver:self forKeyPath:@"foo" options:0 context:nil] |
objective-c将隐式地创建mysingletonclass的一个子类,导致第二次触发
您可能认为应该隐式检查init块中的重复初始化,例如:
1 2 3 4 5 6 7 8 9 | - (id) init { <----- Wrong! if (_instance != nil) { // Some hack } else { // Do stuff } return self; } |
但是你会射自己的脚;或者更糟的是,给另一个开发者机会射自己的脚。
1 2 3 4 5 6 7 8 | - (id) init { <----- Correct! NSAssert(_instance == nil, @"Duplication initialization of singleton"); self = [super init]; if (self){ // Do stuff } return self; } |
tl;dr,这是我的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | @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 |
(用我们自己的断言宏替换zassert;或者只替换nsassert。)
一thorough解释of the Singleton宏代码在博客冰可可与法律
cocoawithlove.com http:/ / / / / 2008年11 singletons-appdelegates-and-top-level.html。
在有一个有趣的变化对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 | // 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; } |
简短的回答:太棒了。
长话短说:比如……
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 | 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标题以了解发生了什么。在这种情况下,标题注释比文档或手册页更适用。
在VC的单卷到其他类的类,所以可以inherit单身置业。
singleton.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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:
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 45 46 47 48 49 50 51 | #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 |
这里的岩石和一些类的实例,你想成为单身。
1 2 3 4 5 6 7 8 9 10 11 12 13 | #import"Singleton.h" @interface SomeClass : Singleton { } @end @implementation SomeClass DEFINE_SHARED_INSTANCE; @end |
唯一的限制是单级的,是的,它是nsobject subclass。但在使用时必须singletons在我的代码,他们都是事实nsobject subclasses这类,所以我的生命真的放松和做清洁的代码。
在我们深入讨论of the Singleton模式在Objective-C,看这里:
使用Singleton模式在《Objective-C
本厂在非垃圾收集环境也。
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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | @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 |
这里的宏观调控,在放在一起:
http:/ / / / github.com cjhanson Objective - C的改进模式
它是基于工作在这里,马特尔市 但改变两个使用方法的实施,我市swizzling AA described maclachlan(谷歌的戴夫。
欢迎在评论/等等。
不应该是线程安全和避免自锁的扩张后的第一呼叫吗?
1 2 3 4 5 6 7 8 9 10 11 | + (MySingleton*)sharedInstance { if (sharedInstance == nil) { @synchronized(self) { if (sharedInstance == nil) { sharedInstance = [[MySingleton alloc] init]; } } } return (MySingleton *)sharedInstance; } |
怎么样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | static MyClass *gInstance = NULL; + (MyClass *)instance { if (gInstance == NULL) { @synchronized(self) { if (gInstance == NULL) gInstance = [[self alloc] init]; } } return(gInstance); } |
所以您可以避免初始化后的同步开销?
KLSingleton is:
Subclassible (to the n-th degree) ARC compatible Safe with alloc andinit Loaded lazily Thread-safe Lock-free (uses +initialize, not @synchronize) Macro-free Swizzle-free Simple
克朗斯顿
我没有通读所有的解决方案,所以如果这段代码是多余的,请原谅。
在我看来,这是最安全的线程实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | +(SingletonObject *) sharedManager { static SingletonObject * sharedResourcesObj = nil; @synchronized(self) { if (!sharedResourcesObj) { sharedResourcesObj = [[SingletonObject alloc] init]; } } return sharedResourcesObj; } |
只是想离开这个在这里,所以不要失去它。"这是一个两个优势,这usable interfacebuilder的细胞,这是一个巨大的优势。这是由从另一个问题,我问:
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 | 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; } |
我知道关于这个"问题"有很多评论,但是我没有看到很多人建议使用宏来定义单例。这是一种常见的模式,宏大大简化了单例。
下面是我根据我看到的几个objc实现编写的宏。
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 | /** @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; \ } |
使用实例:
1 2 3 4 5 6 7 | @interface MyManager SingletonInterface(MyManager, sharedManager); // ... @end |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | @implementation MyManager - (id)init { self = [super init]; if (self) { // Initialization code here. } return self; } SingletonImplementation(MyManager, sharedManager); // ... @end |
为什么界面宏几乎为空?头文件和代码文件之间的代码一致性;可维护性,以防您想添加更多的自动方法或更改它。
我正在使用初始化方法创建singleton,这是这里最流行的答案(在编写时)中使用的方法。
你不想synchronize在线自…由于对象的自我,不存在!你比IP锁定在一个临时的ID值。你想确保你没有一个可以运行其他类的方法(sharedinstance alloc allocwithzone:,,,等等),所以你需要synchronize的对象,而不是类:
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 | @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 |
使用目标C类方法,我们可以避免使用通常的单例模式,从:
1 | [[Librarian sharedInstance] openLibrary] |
到:
1 | [Librarian openLibrary] |
通过将类包装在另一个只具有类方法的类中,这样就不会意外地创建重复实例,因为我们不会创建任何实例!
我在这里写了一个更详细的博客:)
要扩展@robbie hanson的示例…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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; } |
我的方法很简单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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; } |
如果singleton已经初始化,则不会输入锁块。第二个检查如果(!初始化)是为了确保在当前线程获取锁时它还没有初始化。
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 45 46 47 48 49 50 | 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 |
我经常使用的代码roughly相似,在两个hoffstein Ben的回答(我也有了维基百科)。在使用它的原因(除非市汉森在他的评论。
然而,有时在一个需要的地方有一独生子到笔尖,在案例中,使用下面的:
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 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 |
在离开的执行
接受的答案,尽管它是编译的,是不正确的。
1 2 3 4 5 6 7 8 9 | + (MySingleton*)sharedInstance { @synchronized(self) <-------- self does not exist at class scope { if (sharedInstance == nil) sharedInstance = [[MySingleton alloc] init]; } return sharedInstance; } |
根据苹果文件:
…您可以采用类似的方法,使用类对象而不是自身来同步关联类的类方法。
即使使用"自我工作",它也不应该,这对我来说就像是一个复制粘贴错误。类工厂方法的正确实现是:
1 2 3 4 5 6 7 8 9 | + (MySingleton*)getInstance { @synchronized([MySingleton class]) { if (sharedInstance == nil) sharedInstance = [[MySingleton alloc] init]; } return sharedInstance; } |