5

If I create a singleton inside of +[NSObject initialize], do I need to put my code inside a dispatch_once block like so?

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

EDIT

I'm concerned about this because I want to make sure that all threads will see that I've set Bar after +[Foo initialize] is called. The documentation says +[NSObject initialize] is thread-safe, but does that imply it is memory-safe?

Heath Borders
  • 30,998
  • 16
  • 147
  • 256
  • I'd suggest keeping your logic to **only** needing the `dispatch_once()` and just use that. It is more straightforward and copy/paste during a refactoring of the code won't break it. – bbum Mar 17 '14 at 15:09
  • I agree. That's what I do now. – Heath Borders Mar 17 '14 at 15:12

3 Answers3

7

The answer to your direct question is that you don't need the dispatch_once, but you do need the class check that you have in there, because +initialize will be called once per "non-implementing subclass" as well. It will only be called once for the specific class you care about (Foo), so the dispatch_once is extraneous. Re: thread safety, the +initialize method will complete before any other methods are dispatched to the class (or its instances).

However, you don't describe the desired access pattern, so depending on what you want, you may wish to do the opposite-- if you expect subclasses to have access to Bar, too, then this would be fragile; if the subclass gets initialized before Foo itself is, then the class check will prevent Bar from being created. If you intend this behavior, then use the dispatch_once but remove the class check-- that will generally allow Bar to be created the first time Foo or any of its subclasses are initialized. (Caveat: unless a subclass also overrides +initialize, of course.)

Ben Zotto
  • 70,108
  • 23
  • 141
  • 204
  • I would actually prefer checking `if (Bar == nil)` rather than `self == [Foo class]`, but I think that boils down to style. – Ben S Sep 16 '13 at 20:42
  • 2
    `self == [Foo class]` is a correct pattern for all uses of `+initialize`. Checking something else, like `Bar==nil` is a special case that applies to this code, but is not a general pattern. I would recommend the standard class check. – Rob Napier Sep 16 '13 at 21:15
  • But what about memory safety? Are all threads guaranteed to see the change to Bar that +[Foo initialize] made? – Heath Borders Sep 17 '13 at 03:12
  • @HeathBorders: Yes, but it sounds like there's another nuanced behavior here, especially around subclassing. I updated the answer, hope that helps. – Ben Zotto Sep 17 '13 at 16:51
4

Bill Bumgarner says that dispatch_once is Apple's recommended practice now.

Relating to thread and memory-safety of +initialize, thanks to this tweet, I found the relevant runtime sources to check. objc-initialize.mm says:

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

Classes may be initialized on different threads, and objc-initialize.mm has a strategy to avoid them deadlocking:

*  +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.

Additionally, class initialization state variables, are guarded by a monitor_t, which is actually defined as:

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

Since it is a p_thread_mutex, and p_thread calls implement memory barriers, it is equally safe to use:

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

and

static NSObject * Bar;
@implementation Foo
+ (void)initialize {
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    Bar = [NSObject new];
  });
}
@end
Community
  • 1
  • 1
Heath Borders
  • 30,998
  • 16
  • 147
  • 256
  • Because of everything you listed, it is undoubtedly fastest just to say `+ (void)initialize { static BOOL initialized = NO; if (!initialized) { /* initialize here */ initialized = YES; } }` – Lily Ballard Mar 17 '14 at 20:21
  • Also, micro-benchmarking performed on armv7s and arm64 indicates that `if (self == [Foo class])` outperforms `dispatch_once()`, but the difference is <100ns. Also, trying to micro-optimize here is rather pointless. So my overall suggestion is use whichever of the 3 approaches you find most readable. – Lily Ballard Mar 17 '14 at 20:22
1

The docs mention the initialize method is only called once per class in a thread-safe manner. Therefore dispatch_once is not necessary.

The runtime sends initialize to each class in a program exactly one time just before the class, or any class that inherits from it, is sent its first message from within the program. (Thus the method may never be invoked if the class is not used.) The runtime sends the initialize message to classes in a thread-safe manner. Superclasses receive this message before their subclasses.

EDIT

As @Vincent Gable mentions in the comments, the initialize method may be called more than once if a subclass of Foo does not implement initialize method itself. However, such calls won't be a problem because there is a self == [Foo class] check.

murat
  • 4,893
  • 4
  • 31
  • 29
  • 3
    Not quite, +initialize can happen more than once sometimes "…if a subclass does not implement +initialize but its superclass does, then that superclass’s +initialize will be invoked once per non-implementing subclass and once for itself." http://www.friday.com/bbum/2009/09/06/iniailize-can-be-executed-multiple-times-load-not-so-much/ – Vincent Gable Sep 16 '13 at 19:50
  • But what about memory safety? Are all threads guaranteed to see the change to `Bar` that `+[Foo initialize]` made? – Heath Borders Sep 17 '13 at 03:11
  • Per the docs: "The runtime sends `initialize` to each class in a program exactly one time just before the class, or any class that inherits from it, is sent its first message from within the program." Taking the most paranoid interpretation, I could see how all threads might *not* see the change to the global `Bar` before reading it. An easy solution would be to move `Bar` from being a global to being a singleton accessed via a method -- that way a message would have to be sent to access it, guaranteeing that it will have been initialized before anyone can read it. – ipmcc Sep 24 '13 at 11:39