在阅读有关线程安全的单身人士时,我发现了 线程安全实例化单例 在这里,以及在接受的答案中这段代码:
sharedInstance = [MyClass alloc];
sharedInstance = [sharedInstance init];
我们为什么要分离alloc和init方法?答案的作者写道:
即,如果 init 被分配的类碰巧调用了 sharedInstance 方法,它将在设置变量之前执行此操作。在这两种情况下都会导致僵局。这是你想要分开的一次 alloc 和 init。
有人可以详细向我解释这种分离的好处是什么?我无法理解作者的意思。我真的需要分开吗? alloc 和 init 我创建一个单例时调用方法,即使我这样做 dispatch_once() 这是线程安全??
@bbum的帖子 已更新,以提及 该解决方案不能解决所描述的问题。无论你是否分开 +alloc 和 -init 或不,这个问题仍然存在。
理由是在编辑他的帖子,但要扩展, dispatch_once() 不是 折返。在这种情况下,这意味着调用 dispatch_once() 内 一个 dispatch_once() 阻塞(即递归)将导致死锁。
例如,如果您有以下代码 +sharedInstance:
+ (MyClass *)sharedInstance
{
static MyClass *sharedInstance = nil;
static dispatch_once_t pred;
dispatch_once(&pred, ^{
sharedInstance = [[MyClass alloc] init]
});
return sharedInstance;
}
..和 MyClass的 -init 方法直接或间接也调用自己的方法 +sharedInstance 类方法(例如可能是其他一些对象) MyClass -init 通过分配呼叫 MyClass的 +sharedInstance),这意味着你试图打电话 dispatch_once 从内部来看。
以来 dispatch_once 是线程安全的,同步的和设计的,以便它执行 一次,你不能调用 dispatch_once 在块内部完成执行一次之前再次执行。这样做会导致死锁,因为第二次调用 dispatch_once 将等待第一次调用(已经在执行中)完成,而第一次调用正在等待第二次(递归)调用 dispatch_once 通过。他们互相等待,因此陷入僵局。
如果你想要一个提供重入的解决方案,你需要使用类似的东西 NSRecursiveLock 这比相当贵 dispatch_once,它不使用锁定机制。
编辑:推理分裂 +alloc/-init 在@ bbum的原始答案中:
在编辑之前发布的原始代码@bbum看起来像这样:
+ (MyClass *)sharedInstance
{
static MyClass *sharedInstance = nil;
static dispatch_once_t pred;
if (sharedInstance) return sharedInstance;
dispatch_once(&pred, ^{
sharedInstance = [MyClass alloc];
sharedInstance = [sharedInstance init];
});
return sharedInstance;
}
注意这一行: if (sharedInstance) return sharedInstance;
这里的想法是指定一个非零值 sharedInstance 在打电话之前 -init 会导致现有的价值 sharedInstance (从...返回 +alloc)被退回 之前 击中了 dispatch_once() 在这种情况下调用(并避免死锁) -init 调用导致递归调用 +sharedInstance 正如我在回答中所讨论的那样。
但是,这是一个脆弱的解决方案,因为 if 声明没有线程安全。