使用线程安全的Singleton初始化代码停止代码执行

roh*_*tel 2 iphone singleton objective-c ios

为了利用全局变量和方法,我将Singleton实现为一种健康的编码实践.在实施之前我跟着Apple文档,john wordsworth博客.首先,我没有使我的单例线程安全,我实现了这个方法以及博客和Apple文档中提到的所有其他方法.

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

之后,为了使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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

编辑1:

如果有人想要查看完整的代码,我已经为此创建了要点.请跟着那里.谢谢.

rob*_*off 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;
}
Run Code Online (Sandbox Code Playgroud)

现在,我看了你的要点,你的问题就在这里:

+ (id)allocWithZone:(NSZone*)zone {
    return [[self sharedManager] retain];
}
Run Code Online (Sandbox Code Playgroud)

您的+[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;
}
Run Code Online (Sandbox Code Playgroud)