正确的单例模式Objective C(iOS)?

我在网上发现了一些使用GCD创build单例类的信息。 这很酷,因为它是线程安全的,开销很低。 可悲的是我找不到完整的解决scheme,但只有片段的sharedInstance方法。 所以我用自己的试错法做了我自己的课 – 等等,下面出来:

@implementation MySingleton // MARK: - // MARK: Singleton Pattern using GCD + (id)allocWithZone:(NSZone *)zone { return [[self sharedInstance] retain]; } - (id)copyWithZone:(NSZone *)zone { return self; } - (id)autorelease { return self; } - (oneway void)release { /* Singletons can't be released */ } - (void)dealloc { [super dealloc]; /* should never be called */ } - (id)retain { return self; } - (NSUInteger)retainCount { return NSUIntegerMax; /* That's soooo non-zero */ } + (MySingleton *)sharedInstance { static MySingleton * instance = nil; static dispatch_once_t predicate; dispatch_once(&predicate, ^{ // --- call to super avoids a deadlock with the above allocWithZone instance = [[super allocWithZone:nil] init]; }); return instance; } // MARK: - // MARK: Initialization - (id)init { self = [super init]; if (self) { // Initialization code here. } return self; } @end 

请随意评论,并告诉我,如果我错过了什么或完全错误的东西;)

干杯斯特凡

把事情简单化:

 +(instancetype)sharedInstance { static dispatch_once_t pred; static id sharedInstance = nil; dispatch_once(&pred, ^{ sharedInstance = [[self alloc] init]; }); return sharedInstance; } - (void)dealloc { // implement -dealloc & remove abort() when refactoring for // non-singleton use. abort(); } 

这就对了。 覆盖retainreleaseretainCount和其余的只是隐藏错误,并添加一堆不必要的代码行。 每行代码都是一个等待发生的错误。 实际上,如果你在你的共享实例上调用dealloc ,你的应用程序中会出现一个非常严重的错误 。 该错误应该修复,而不是隐藏。

这种方法也适用于重构,以支持非单身使用模式。 几乎每个存活的单身人士几乎都会被重构成非单身人士forms。 有些(如NSFileManager )继续支持单例模式,同时也支持任意实例化。

请注意,以上也是ARC中的“正常工作”。

 // See Mike Ash "Care and Feeding of Singletons" // See Cocoa Samurai "Singletons: You're doing them wrong" +(MySingleton *)singleton { static dispatch_once_t pred; static MySingleton *shared = nil; dispatch_once(&pred, ^{ shared = [[MySingleton alloc] init]; shared.someIvar = @"blah"; }); return shared; } 

请注意, dispatch_once不是可重入的 ,所以从dispatch_once块中调用自己会使程序死锁。

不要试图对自己进行防守编码。 如果你没有编写框架,像正常一样对待你的类,然后坚持上面的单例成语。 把单身习语当成一种方便的方法,而不是你们class的定义特征。 你想在unit testing期间把你的类看作一个普通的类,所以可以留下一个可访问的构造函数。

不要打扰使用allocWithZone:

  • 它忽略了它的论点,并且performance得和alloc一样。 内存区域不再在Objective-C中使用,所以allocWithZone:只保留与旧代码的兼容性。
  • 它不起作用。 你不能在Objective-C中强制单例行为,因为总是可以使用NSAllocateObject()class_createInstance()来创build更多的实例。

单例工厂方法总是返回以下三种types之一:

  • id来表明返回types不完全知道(你正在build立一个类集群的情况下)。
  • instancetype指示返回的types是封闭类的一个实例。
  • 类名本身(例如MySingleton )保持简单。

既然你标记了这个iOS,一个单身人士的替代scheme是将伊娃保存到应用程序委托,然后使用一个方便的macros,你可以重新定义,如果你改变主意:

 #define coreDataManager() \ ((AppDelegate*)[[UIApplication sharedApplication] delegate]).coreDataManager 

如果你想unit testing你的单身人士,你也必须做到这一点,以便你可以用一个模拟的单身人士replace它和/或重置为正常的:

 @implementation ArticleManager static ArticleManager *_sharedInstance = nil; static dispatch_once_t once_token = 0; +(ArticleManager *)sharedInstance { dispatch_once(&once_token, ^{ if (_sharedInstance == nil) { _sharedInstance = [[ArticleManager alloc] init]; } }); return _sharedInstance; } +(void)setSharedInstance:(ArticleManager *)instance { once_token = 0; // resets the once_token so dispatch_once will run again _sharedInstance = instance; } @end