在阅读有关线程安全的单身人士时,我发现了 线程安全实例化单例 在这里,以及在接受的答案中这段代码:
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
声明没有线程安全。