我在网上发现了一些使用GCD创建单例类的信息。这很酷,因为它的线程安全,开销很低。遗憾的是,我找不到完整的解决方案,只能找到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
请随时发表评论并告诉我,如果我遗失了某些内容或做了一些完全错误的事情;)
干杯 斯蒂芬
答案 0 :(得分:82)
保持简单:
+(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();
}
就是这样。覆盖retain
,release
,retainCount
,其余的只是隐藏错误并添加一堆不必要的代码。每行代码都是等待发生的错误。实际上,如果您导致在共享实例上调用dealloc
,则您的应用中存在非常严重的错误。该错误应该修复,而不是隐藏。
这种方法也适用于重构以支持非单例使用模式。几乎所有存在于单个版本之后的单例最终都会被重构为非单例形式。一些(如NSFileManager
)继续支持单例模式,同时也支持任意实例化。
请注意,上述内容在ARC中也“正常”。
答案 1 :(得分:19)
// 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 is not reentrant,因此从dispatch_once块中调用自身会使程序死锁。
不要试图对自己进行防御性编码。如果你没有编写框架代码,请将你的类视为正常,然后坚持上面的单例习语。把单身成语想象成一种方便的方法,而不是你班级的定义特征。您希望在单元测试期间将您的类视为普通类,因此可以保留可访问的构造函数。
不要使用allocWithZone:
alloc
完全相同。 Objective-C中不再使用内存区域,因此仅保留allocWithZone:
以与旧代码兼容。 NSAllocateObject()
和class_createInstance()
创建更多实例。 单件工厂方法始终返回以下三种类型之一:
id
表示返回类型未完全知晓(建立类集群的情况)。instancetype
表示返回的类型是封闭类的实例。 MySingleton
)以保持简单。由于您标记了此iOS,因此单例的替代方法是将ivar保存到应用委托,然后使用便捷宏,如果您改变主意,可以重新定义:
#define coreDataManager() \
((AppDelegate*)[[UIApplication sharedApplication] delegate]).coreDataManager
答案 2 :(得分:1)
如果你想对你的单身人士进行单元测试,你也必须这样做,以便你可以用模拟单身人士替换它和/或将其重置为正常的单身人士:
@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