为了利用全局变量和方法,我将Singleton实现为一种健康的编码实践。在实现它之前,我遵循了苹果的文档和john wordsworth的博客。在第一种情况下,我没有使我的单例线程安全,我实现了这个方法以及博客和苹果文档中提到的所有其他方法。
+ (SingletonClass *)sharedManager
{
static SingletonClass *sharedManager = nil;
if (sharedManager == nil) {
sharedManager = [[super allocWithZone:NULL] init];
}
return sharedManager;
}
之后,使单例线程安全,我改变了+ (SingletonClass *)sharedManager
类像这样,我的应用程序停止启动。我放置断点并观察到dispatch_once
被调用两次,然后代码停止进一步执行。
+(SingletonClass *)sharedManager
{
static SingletonClass *sharedManager = nil;
if (sharedManager !=nil)
{
return sharedManager;
}
static dispatch_once_t pred;
dispatch_once(&pred, ^{
sharedManager = [SingletonClass alloc];
sharedManager=[sharedManager init];
});
return sharedManager;
}
如果我删除这个线程安全的代码片段,并恢复到以前的代码,它工作正常,代码得到执行。
请注意,我也看了相册的答案,他在提问之前提到了可能的死锁情况,但我无法弄清楚这个问题。任何解释或解决方法都会对我有帮助。谢谢。
编辑1:
如果有人想看完整的代码,我已经创建了gist。请跟着那里走。谢谢。
让我们考虑一下如果两个线程几乎同时调用第二个版本的sharedManager
会发生什么。
线程1先调用。它检查sharedManager !=nil
,它是假的,所以它继续检查dispatch_once
。在dispatch_once
块中,它执行[SingletonClass alloc]
并将结果存储在sharedManager
中。
现在,在线程1继续下一行之前,线程2出现并调用sharedManager
。线程2检查sharedManager !=nil
,现在为真。所以它返回sharedManager
,然后调用者尝试使用sharedManager
。但是此时,sharedManager
还没有完全初始化。这是不好的。
在完全初始化对象之前不能设置sharedManager
。此外(正如borrrden指出的),你不需要在顶部检查sharedManager !=nil
,因为dispatch_once
无论如何都非常有效。
+ (SingletonClass *)sharedManager {
static dispatch_once_t pred;
static SingletonClass *sharedManager;
dispatch_once(&pred, ^{
sharedManager = [[SingletonClass alloc] init];
});
return sharedManager;
}
现在,我看了你的要点,你的问题在这里:
+ (id)allocWithZone:(NSZone*)zone {
return [[self sharedManager] retain];
}
你的+[SingletonClass sharedManager]
方法调用dispatch_once
块中的+[SingletonClass alloc]
。因为你没有覆盖alloc
,所以+[SingletonClass alloc]
调用+[SingletonClass allocWithZone:NULL]
。+[SingletonClass allocWithZone:]
方法调用+[SingletonClass sharedManager]
。在第二次调用sharedManager
时,你的程序挂起在dispatch_once
中,因为你仍然在第一次调用dispatch_once
的内部。
最简单的修复是删除allocWithZone:
的实现。只要证明sharedManager
是获得SingletonClass
实例的唯一受支持的方法,然后继续。
如果您想要钝化并使[[SingletonClass alloc] init]
返回单例,即使您重复执行,它也很复杂。不要试图覆盖alloc
或allocWithZone:
。这样做:
static SingletonClass *sharedManager; // outside of any method
+ (SingletonClass *)sharedManager {
return sharedManager ? sharedManager : [[SingletonClass alloc] init];
}
- (id)init {
static dispatch_once_t once;
dispatch_once(&once, ^{
if (self = [super init]) {
// initialization here...
sharedManager = self;
}
});
self = sharedManager;
return self;
}
您不需要顶部的检查,去掉if
语句。dispatch_once
保证该块在应用程序的生命周期中只执行一次,因此第一次检查是多余的。
更多信息:http://cocoasamurai.blogspot.jp/2011/04/singletons-your-doing-them-wrong.html