为了使用全局变量和方法,我将 Singleton 实现为一种健康的编码实践。我关注了Apple documents , john wordsworth blog在实现之前。首先,我没有让我的单例线程安全,我实现了这个方法以及博客和 Apple 文档中提到的所有其他方法。
+ (SingletonClass *)sharedManager
{
static SingletonClass *sharedManager = nil;
if (sharedManager == nil) {
sharedManager = [[super allocWithZone:NULL] init];
}
return sharedManager;
}
之后为了使 Singleton 线程安全,我像这样更改了 + (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;
}
如果我删除这个线程安全的代码片段并恢复到以前的代码,它可以正常工作并执行代码。
请注意,我还查看了 bbum's answer here他在提问之前提到了可能出现的僵局情况,但我无法弄清楚这个问题。任何解释或解决方案都会对我有所帮助。谢谢。
Edit 1:
如果有人想查看完整代码,我创建了 gist为了那个原因。请跟着那里。谢谢。
最佳答案
让我们考虑一下如果两个线程几乎同时调用 sharedManager
的第二个版本会发生什么。
线程 1 首先调用。它检查 sharedManager !=nil
,这是错误的,所以它继续到 dispatch_once
。在 dispatch_once
block 中,它执行 [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
block 中调用 +[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;
}
关于iphone - 代码执行在使用线程安全单例初始化代码时停止,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14192140/