dispatch_once过度杀伤了+ ?

如果我在+[NSObject initialize]里面创build一个单例,我需要把我的代码放在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 

编辑

我很担心这一点,因为我想确保所有的线程都会在调用+[Foo initialize]之后看到我已经设置了Bar 。 文档说+[NSObject initialize]是线程安全的,但这是否意味着它是内存安全的?

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

然而,你并没有描述所需的访问模式,所以根据你想要的,你可能希望做相反的事情 – 如果你希望子类也可以访问Bar ,那么这将是脆弱的; 如果子类在Foo本身之前被初始化,那么类检查将阻止创buildBar 。 如果你打算这样做,那么使用dispatch_once但删除类检查 – 通常会允许在第一次Foo或其任何子类被初始化时创buildBar 。 (注意:除非子类也覆盖+initialize ,当然 。)

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

关于+initialize线程和内存安全性,感谢这个tweet ,我find了相关的运行时间源 。 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. 

另外,类初始化状态variables由monitor_t来保护, 它实际上定义为 :

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

由于它是一个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 

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

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

编辑

正如@Vincent Gable在注释中提到的那样,如果Foo的子类本身没有实现initialize方法,则可能会多次调用initialize方法。 然而,这样的调用将不会是一个问题,因为有一个self == [Foo class]检查。