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

标签 iphone objective-c ios singleton

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

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

之后为了使 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;
}

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

请注意,我还查看了 bbum's answer here他在提问之前提到了可能出现的僵局情况,但我无法弄清楚这个问题。任何解释或解决方案都会对我有所帮助。谢谢。

Edit 1:

如果有人想查看完整代码,我创建了 gist为了那个原因。请跟着那里。谢谢。

最佳答案

让我们考虑一下如果两个线程几乎同时调用 sharedManager 的第二个版本会发生什么。

线程 1 首先调用。它检查 sharedManager !=nil,这是错误的,所以它继续到 dispatch_once。在 dispatch_once block 中,它执行 [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 block 中调用 +[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;
}

关于iphone - 代码执行在使用线程安全单例初始化代码时停止,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14192140/

相关文章:

iphone - 如何在UILabel中绘制竖排文本

mysql - 我正在制作一个 IOS 应用程序,如何让该应用程序向我发送其所在设备的 UDID?

jquery - iOS 上的 Safari 无法 Ajax 上传某些图像文件 : "Cannot connect to server" (but the website works ok)

objective-c - 段错误 11 Objective-C,但 C 中没有

ios - iOS Spritekit 游戏中的肌酐水平

iphone - 如何将简单的字符串写入 iPhone 上的文件?

ios - 在 xcode 项目中使用 Matlab 功能

iphone - objective-c:如何访问 Tableview 第二部分第一行的文本字段?

ios - UIView animateWithDuration 导致应用程序崩溃

javascript - iOS 上的缩放控件不再可见