问题描述
为了利用全局变量和方法,我将Singleton实施为健康的编码实践.我遵循了 Apple文档,约翰·华兹华斯博客.首先,我没有使我的单例线程安全,而是将这种方法与博客和Apple文档中提到的所有其他方法一起实现.
To make use of global variables and methods I implemented Singleton as a healthy coding practice. I followed Apple documents, john wordsworth blog before implementing it. In the first, I did not make my singleton thread safe and I implemented this method along with all other mentioned in the blog and Apple document.
+ (SingletonClass *)sharedManager
{
static SingletonClass *sharedManager = nil;
if (sharedManager == nil) {
sharedManager = [[super allocWithZone:NULL] init];
}
return sharedManager;
}
在那之后,为了使Singleton线程安全,我像这样对 +(SingletonClass *)sharedManager
类进行了更改,我的应用程序停止启动.我放置了断点,并观察到 dispatch_once
被调用了两次,然后代码停止了进一步执行.
After that to make Singleton thread safe I made changes to + (SingletonClass *)sharedManager
class like this and my app stops launching. I put break points and observed dispatch_once
gets called twice and then code stops executing further.
+(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;
}
如果我删除了该线程安全代码段,并恢复为先前的代码,它将正常工作并执行代码.
If i remove this thread safe code snippet and revert back to previous code it works fine and code gets executed.
请注意,我还查看了 bbum在这里的答案,其中他在提出问题之前已经提到了可能出现的僵局,但是我无法弄清楚问题所在.任何解释或解决方案将对我有所帮助.谢谢.
Please note that I also looked at the bbum's answer here in which he has mentioned possible deadlock situation before asking question but I am not able to figure out the issue. Any explanation or solution will be helpful for me. Thanks.
万一有人想看完整的代码,我创建了 要领 .请跟随那里.谢谢.
In case someone wants to look at the complete code, I have created gist for that. Please follow there. Thanks.
推荐答案
让我们考虑一下,如果两个线程几乎同时调用 sharedManager
的第二版,会发生什么情况.
Let's consider what happens if two threads call second version of sharedManager
almost simultaneously.
线程1首先调用.它检查 sharedManager!= nil
,这是错误的,因此继续进行 dispatch_once
.在 dispatch_once
块中,它执行 [SingletonClass alloc]
,并将结果存储在 sharedManager
中.
Thread 1 calls first. It checks sharedManager !=nil
, which is false, so it goes on to the dispatch_once
. In the dispatch_once
block, it executes [SingletonClass alloc]
and stores the result in sharedManager
.
现在,在线程1继续到下一行之前,线程2出现并调用 sharedManager
.线程2检查 sharedManager!= nil
,现在是正确的.因此它返回 sharedManager
,然后调用方尝试使用 sharedManager
.但是目前, sharedManager
尚未完全初始化.不好.
Now, before thread 1 continues on to the next line, thread 2 comes along and calls sharedManager
. Thread 2 checks sharedManager !=nil
, which is now true. So it returns sharedManager
, and the caller then tries to use sharedManager
. But at this time, sharedManager
hasn't been fully initialized yet. That's bad.
在设置完全初始化对象之前,您无法设置 sharedManager
.另外(如borrrden所指出的),您不需要在顶部进行 sharedManager!= nil
检查,因为 dispatch_once
仍然非常有效.
You cannot set sharedManager
until you have a fully initialized object to set it to. Also (as borrrden pointed out), you don't need the sharedManager !=nil
check at the top, because dispatch_once
is very efficient anyway.
+ (SingletonClass *)sharedManager {
static dispatch_once_t pred;
static SingletonClass *sharedManager;
dispatch_once(&pred, ^{
sharedManager = [[SingletonClass alloc] init];
});
return sharedManager;
}
现在,我已经查看了您的要点,而您的问题在这里:
Now, I've looked at your gist and your problem is here:
+ (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
之内.
Your +[SingletonClass sharedManager]
method calls +[SingletonClass alloc]
in the dispatch_once
block. Since you don't override alloc
, +[SingletonClass alloc]
calls +[SingletonClass allocWithZone:NULL]
. And +[SingletonClass allocWithZone:]
method calls +[SingletonClass sharedManager]
. On this second call to sharedManager
, your program hangs in dispatch_once
, because you're still inside the first call to dispatch_once
.
最简单的解决方法是删除 allocWithZone:
的实现.只需证明 sharedManager
是获取 SingletonClass
实例并继续前进的唯一受支持的方法.
The simplest fix is to remove your implementation of allocWithZone:
. Just document that sharedManager
is the only supported way to get an instance of SingletonClass
and move on.
如果您想变钝并使 [[[SingletonClass alloc] init]
返回单例,即使您反复执行此操作,也很复杂.不要尝试覆盖 alloc
或 allocWithZone:
.这样做:
If you want to be obtuse and make [[SingletonClass alloc] init]
return the singleton, even if you do it repeatedly, it's complicated. Don't try to override alloc
or allocWithZone:
. Do this:
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;
}
这篇关于使用线程安全的Singleton初始化代码停止执行代码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!