5

如果我在里面创建一个单例+[NSObject initialize],我需要把我的代码放在dispatch_once这样的块中吗?

static NSObject * Bar;
@implementation Foo
+ (void)initialize {
  if (self == [Foo class]) {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
      Bar = [NSObject new];
    });
  }
}
@end

编辑

我对此感到担忧,因为我想确保所有线程都会看到我在调用Bar后设置+[Foo initialize]的内容。文档说+[NSObject initialize]是线程安全的,但这是否意味着它是内存安全的?

4

3 回答 3

7

您的直接问题的答案是您不需要dispatch_once,但您确实需要那里的类检查,因为每个“非实现子类”+initialize也会调用一次。它只会为您关心的特定类调用一次 ( ),因此是无关的。回复:线程安全,该方法将在任何其他方法被分派到类(或其实例)之前完成。Foodispatch_once+initialize

但是,您没有描述所需的访问模式,因此根据您的需要,您可能希望做相反的事情——如果您希望子类也可以访问Bar,那么这将是脆弱的;如果子类在自身之前被初始化Foo,则将阻止Bar创建类检查。如果您打算这样做,请使用dispatch_once但删除类检查 - 通常允许Bar第一次创建Foo或初始化其任何子类。(警告:除非子类也覆盖+initialize,当然。)

于 2013-09-16T20:10:39.800 回答
4

Bill Bumgarner说这dispatch_once是苹果现在推荐的做法

关于线程和内存安全+initialize,感谢这条推文,我找到了相关的运行时源来检查。objc-initialize.mm说:

 * Only one thread is allowed to actually initialize a class and send 
 * +initialize. Enforced by allowing only one thread to set CLS_INITIALIZING.

类可以在不同的线程上初始化,并且objc-initialize.mm有避免死锁的策略:

*  +initialize deadlock case when a class is marked initializing while 
 *  its superclass is initialized. Solved by completely initializing 
 *  superclasses before beginning to initialize a class.
 *
 *  OmniWeb class hierarchy:
 *                 OBObject 
 *                     |    ` OBPostLoader
 *                 OFObject
 *                 /     \
 *      OWAddressEntry  OWController
 *                        | 
 *                      OWConsoleController
 *
 *  Thread 1 (evil testing thread):
 *    initialize OWAddressEntry
 *    super init OFObject
 *    super init OBObject            
 *    [OBObject initialize] runs OBPostLoader, which inits lots of classes...
 *    initialize OWConsoleController
 *    super init OWController - wait for Thread 2 to finish OWController init
 *
 *  Thread 2 (normal OmniWeb thread):
 *    initialize OWController
 *    super init OFObject - wait for Thread 1 to finish OFObject init
 *
 *  deadlock!
 *
 *  Solution: fully initialize super classes before beginning to initialize 
 *  a subclass. Then the initializing+initialized part of the class hierarchy
 *  will be a contiguous subtree starting at the root, so other threads 
 *  can't jump into the middle between two initializing classes, and we won't 
 *  get stuck while a superclass waits for its subclass which waits for the 
 *  superclass.

此外,类初始化状态变量由 a 保护monitor_t它实际上定义为

typedef struct {
    pthread_mutex_t mutex;
    pthread_cond_t cond;
} monitor_t;

由于它是 a p_thread_mutex,并且p_thread 调用实现了内存屏障,因此使用起来同样安全:

static NSObject * Bar;
@implementation Foo
+ (void)initialize {
  if (self == [Foo class]) {
    Bar = [NSObject new];
  }
}
@end

static NSObject * Bar;
@implementation Foo
+ (void)initialize {
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    Bar = [NSObject new];
  });
}
@end
于 2014-03-17T14:32:53.360 回答
1

文档提到该initialize方法仅以线程安全的方式在每个类中调用一次。因此dispatch_once没有必要。

运行时恰好在类或从它继承的任何类从程序中发送其第一条消息之前向程序中的每个类发送一次初始化。(因此,如果不使用该类,则可能永远不会调用该方法。)运行时以线程安全的方式将初始化消息发送到类。超类在其子类之前收到此消息。

编辑

正如@Vincent Gable 在评论中提到的那样,如果子类本身没有实现方法,则该initialize方法可能会被多次调用。但是,这样的调用不会有问题,因为有检查。Fooinitializeself == [Foo class]

于 2013-09-16T19:49:02.647 回答