为了利用全局变量和方法,我将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中他提到可能出现的死锁情况,但我无法弄清楚问题。任何解释或解决方案对我都有帮助。感谢。
编辑1:
如果有人想查看完整的代码,我已为此创建了gist。请跟着那里。感谢。
答案 0 :(得分:7)
让我们考虑如果两个线程几乎同时调用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]
方法会在+[SingletonClass alloc]
块中调用dispatch_once
。由于您未覆盖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;
}