Mac*_*uch 33 singleton multithreading memory-management objective-c thread-safety
使用哪种同步方法来确保单例仍然是单例?
+(Foo*)sharedInstance
{
   @synchronized(self)
   {
      if (nil == _sharedInstance)
      {
         _sharedInstance = [[Foo alloc] init];
         ...
      }
   }
   return _sharedInstance;
}
还是使用互斥?
#import <pthread.h>
static pthread_mutex_t _mutex = PTHREAD_MUTEX_INITIALIZER;
+(Foo*)sharedInstance
{
   pthread_mutex_lock(&_mutex);
   if (nil == _sharedInstance)
   {
      _sharedInstance = [[Foo alloc] init];
      ...
   }
   pthread_mutex_unlock(&_mutex);
   return _sharedInstance;
}
嗯..对此有何评论?
bbu*_*bum 56
请务必阅读有关此问题/答案的讨论. 我们为什么要分开alloc和init调用以避免Objective-C中的死锁?
扩大竞争条件问题; 在真正的解决办法是你的应用程序中没有不确定初始化. 不确定或懒惰的初始化会导致行为很容易因看似无害的变化而改变 - 配置,"不相关"的代码更改等等......
最好在程序的生命周期中明确初始化已知良好点上的子系统.即拖放[MyClass sharedInstance];到你的应用程序委托的applicationDidFinishLaunching:方法,如果你真的需要一个子系统,在项目早期进行初始化(甚至更早移动它,如果你想成为额外的防御).
最好还是完全从该方法中移出初始化.即[MyClass initializeSharedInstance];,+sharedInstance如果不首先调用该方法,则asserts().
尽管我是一个方便的粉丝,25年的ObjC编程告诉我,懒惰的初始化是更多维护和重构头痛的源泉,而不是它的价值.
虽然存在下面描述的竞争条件,但是该代码不能解决下面描述的内容.几十年来我们不担心共享实例初始化器中的并发性.为繁荣留下错误的代码.
请记住,对于科林和哈拉德的正确答案,有一种非常微妙的竞争条件可能会让你陷入悲痛的世界.
也就是说,如果-init正在分配的类恰好调用该sharedInstance方法,它将在设置变量之前执行此操作.在这两种情况下都会导致僵局.
这是您想要分离alloc和init的一次.克服Colin的代码因为它是最好的解决方案(假设是Mac OS X):
+(MyClass *)sharedInstance
{   
    static MyClass *sharedInstance = nil;
    static dispatch_once_t pred;
    // partial fix for the "new" concurrency issue
    if (sharedInstance) return sharedInstance;
    // partial because it means that +sharedInstance *may* return an un-initialized instance
    // this is from https://stackoverflow.com/questions/20895214/why-should-we-separate-alloc-and-init-calls-to-avoid-deadlocks-in-objective-c/20895427#20895427
    dispatch_once(&pred, ^{
        sharedInstance = [MyClass alloc];
        sharedInstance = [sharedInstance init];
    });
    return sharedInstance;
}
请注意,这仅适用于Mac OS X; 特别是X 10.6+和iOS 4.0+.在没有块的旧操作系统上,使用锁定或一种不基于块的操作的方法之一.
上述模式实际上并不能防止文本中描述的问题,并且在遇到问题时会导致死锁.问题是,这dispatch_once()不是可重入的,因此,如果init呼叫sharedInstance,楔入城市.
Col*_*ler 38
最快的线程安全方法是使用Grand Central Dispatch(libdispatch)和dispatch_once()
+(MyClass *)sharedInstance
{   
    static MyClass *sharedInstance = nil;
    static dispatch_once_t pred;
    dispatch_once(&pred, ^{
        sharedInstance = [[MyClass alloc] init];
    });
    return sharedInstance;
}
Arv*_*vin 12
如果有人关心,这里有一个宏相同的东西:
   /*!
    * @function Singleton GCD Macro
    */
    #ifndef SINGLETON_GCD
    #define SINGLETON_GCD(classname)                            \
                                                                \
    + (classname *)shared##classname {                          \
                                                                \
        static dispatch_once_t pred;                            \
        static classname * shared##classname = nil;             \
        dispatch_once( &pred, ^{                                \
            shared##classname = [[self alloc] init];            \
        });                                                     \
        return shared##classname;                               \
    }                                                           
    #endif