代码执行在使用线程安全的单例初始化代码时停止



为了利用全局变量和方法,我将Singleton实现为一种健康的编码实践。在实现它之前,我遵循了苹果的文档和john wordsworth的博客。在第一种情况下,我没有使我的单例线程安全,我实现了这个方法以及博客和苹果文档中提到的所有其他方法。

+ (SingletonClass *)sharedManager 
{
  static SingletonClass *sharedManager = nil;
  if (sharedManager == nil) {
    sharedManager = [[super allocWithZone:NULL] init];
}
  return sharedManager;
}

之后,使单例线程安全,我改变了+ (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;
}

如果我删除这个线程安全的代码片段,并恢复到以前的代码,它工作正常,代码得到执行。

请注意,我也看了相册的答案,他在提问之前提到了可能的死锁情况,但我无法弄清楚这个问题。任何解释或解决方法都会对我有帮助。谢谢。

编辑1:

如果有人想看完整的代码,我已经创建了gist。请跟着那里走。谢谢。

让我们考虑一下如果两个线程几乎同时调用第二个版本的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]方法调用dispatch_once块中的+[SingletonClass alloc]。因为你没有覆盖alloc,所以+[SingletonClass alloc]调用+[SingletonClass allocWithZone:NULL]+[SingletonClass allocWithZone:]方法调用+[SingletonClass sharedManager]。在第二次调用sharedManager时,你的程序挂起在dispatch_once中,因为你仍然在第一次调用dispatch_once的内部。

最简单的修复是删除allocWithZone:的实现。只要证明sharedManager是获得SingletonClass实例的唯一受支持的方法,然后继续。

如果您想要钝化并使[[SingletonClass alloc] init]返回单例,即使您重复执行,它也很复杂。不要试图覆盖allocallocWithZone:。这样做:

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;
}

您不需要顶部的检查,去掉if语句。dispatch_once保证该块在应用程序的生命周期中只执行一次,因此第一次检查是多余的。

更多信息:http://cocoasamurai.blogspot.jp/2011/04/singletons-your-doing-them-wrong.html

最新更新