5

Matt Galloway suggests this as the correct way to initialize a singleton:

+ (id)sharedManager {
    static MyManager *sharedMyManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedMyManager = [[self alloc] init];
    });
    return sharedMyManager;
}

I have tested and it works correctly but I don't understand why. When the singleton is first created the variable sharedMyManager is set with the contents of the init line but when the singleton is already created and I access it using [MySingleton sharedManager]; the first line of the code runs, setting sharedMyManager to nil and then the last line runs returning what is, in theory, nil. In fact, it is returning the correct singleton, but why?

How can sharedMyManager return the correct object if it is being set as nil?

Attention because I am talking to the subsequent calls to sharedManager, after the singleton was created.

I suppose the static keyword is doing the magic by now allowing the value to be assigned more then once, but if this is true, the init part should not work, because the static variable is being assigned to nil in the first place.

Please explain me as I was five years old. Thanks.

Duck
  • 34,902
  • 47
  • 248
  • 470
  • How do you know that sharedMyManager gets set to nil (after the first time)? Maybe it just goes to the static declaration, realizes that sharedMyManager exists and keeps going... right before "return sharedMyManager" is run, what is the value of it? – tofutim Jan 14 '14 at 05:04
  • Hint: *when* does `dispatch_once` run the block supplied to it? The block causes an immediate side-effect to the static variable when it is run (and this is guaranteed to run once [via `dispatch_once` and the static `onceToken` sentinel] and only once before the first return from the method). – user2864740 Jan 14 '14 at 05:04
  • 1
    As far as the "magic" `static` modifer: it effectively makes the variable a *persisted global variable* - that can only be accessed from that method context. The initialization of such `static` variables happens *before* the method is ever called. If you were to write `static MyManager *sharedMyManager; sharedMyManager = nil;` then the assignment - not initialization! - would occur every time. – user2864740 Jan 14 '14 at 05:09
  • @user2864740 - I am talking about subsequent calls to the method, not the first time. – Duck Jan 14 '14 at 05:09
  • http://stackoverflow.com/a/3474644/2864740 – user2864740 Jan 14 '14 at 05:13
  • That's what user2864740 tries to explain: It's a 30 year old feature of the C language. Unlike usual local variables in the method or function that are created every time you call the method, static variables are there forever. The initialisation happens _once_ on the very first call. So on the first call, sharedManager is nil. On the second call, it is whatever was stored in the first call. – gnasher729 Apr 14 '14 at 17:37

2 Answers2

5

sharedMyManager is a static variable, so by definition its initializer will only run once. The first time (only) the block is entered it is set to nil. Then the dispatch_once block runs and assigns it, then sharedMyManager is returned.

For all subsequent calls, the initialization to nil does not happen, nor does the dispatch_once() content get reexecuted, so effectively all that happens is the return of the initialized variable.

par
  • 17,361
  • 4
  • 65
  • 80
  • So setting it to nil does not count as an initialization. I was suspecting that. Just needed confirmation. THANKS! – Duck Jan 14 '14 at 05:14
  • No problem. Here's a SO thread you might find useful as well: http://stackoverflow.com/questions/5567529/what-makes-a-static-variable-initialize-only-once – par Jan 14 '14 at 05:15
  • And yes, setting it to nil *does* count as initialization. Since it's static it would be initialized to zero anyway, but the point is the initialization code (i.e. where it is assigned the value of nil) will only happen once. The compiler behind the scenes makes sure the first assignment only happens once. – par Jan 14 '14 at 05:19
0

dispatch_once() instantiates the class once.

So after calling sharedManager() once, the singleton is already there, and re-used.

Geremy
  • 2,415
  • 1
  • 23
  • 27
  • ok but I am talking about subsequent calls to the method, not the first time. If you look at the method, every time sharedManager is called it sets sharedMyManager to nil then returns sharedMyManager, that should be nil, but it isn't. – Duck Jan 14 '14 at 05:11