2

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

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

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

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

编辑1:

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

4

2 回答 2

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

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

+ (id)allocWithZone:(NSZone*)zone {
    return [[self sharedManager] retain];
}

您的方法在块中+[SingletonClass sharedManager]调用。由于您不覆盖,调用. 和方法调用。在第二次调用 时,您的程序挂起,因为您仍在第一次调用.+[SingletonClass alloc]dispatch_oncealloc+[SingletonClass alloc]+[SingletonClass allocWithZone:NULL]+[SingletonClass allocWithZone:]+[SingletonClass sharedManager]sharedManagerdispatch_oncedispatch_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;
}
于 2013-01-07T08:40:52.087 回答
1

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

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

于 2013-01-07T08:31:44.887 回答