33

Which one synchronization method to use to ensure a singleton remains a singleton?

+(Foo*)sharedInstance
{
   @synchronized(self)
   {
      if (nil == _sharedInstance)
      {
         _sharedInstance = [[Foo alloc] init];
         ...
      }
   }
   return _sharedInstance;
}

or using a mutex?

#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;
}

Hmmm.. any comments on this?

bbum
  • 162,346
  • 23
  • 271
  • 359
MacTouch
  • 709
  • 1
  • 9
  • 18
  • 2
    You might be interested in reading this (http://steve.yegge.googlepages.com/singleton-considered-stupid). – sand Feb 04 '10 at 15:18
  • 1
    Despite yegge's hate for singletons, they definitely server a purpose on the iPhone. But if you are simply creating a 'namespace', use class methods instead. – bentford Jul 15 '11 at 05:48
  • @bentford - I've been programming iPhones for 4 years now, old code and new. I've seen singletons used once (in some pretty lousy code). – Hot Licks Jan 03 '14 at 02:55
  • 1
    @HotLicks I use 1 or 2 singletons on almost all my projects. Two use cases are: 1) handling long running network operations and 2) managing global persistent context for CoreData. – bentford Jan 10 '14 at 00:12

5 Answers5

56

Make sure you read the discussion on this question/answer, too. Why should we separate alloc and init calls to avoid deadlocks in Objective-C?


To expand on the race condition issue; the real fix is to not have indeterminate initialization within your application. Indeterminate or lazy initialization results in behavior that can easily change due to seemingly innocuous changes -- configuration, "unrelated" code changes, etc...

Better to explicitly initialize subsystems on a known-good point in the program's lifespan. I.e. drop [MyClass sharedInstance]; into your App delegate's applicationDidFinishLaunching: method if you really need that subsystem initialized early in the program (or move it even earlier, if you want to be extra defensive).

Better still to move initialization out of that method entirely. I.e. [MyClass initializeSharedInstance]; where +sharedInstance asserts() if that method isn't called first.

As much as I'm a a fan of convenience, 25 years of ObjC programming has taught me that lazy initialization is a source of more maintenance and refactoring headaches than it is worth.


While the race condition described below exists, this code doesn't fix what is described below. It did for a couple of decades when we didn't worry about concurrency in shared instance initializers. Leaving the wrong code for prosperity.

Keep in mind that for both Colin's and Harald's otherwise correct answers, there is a very subtle race condition that could lead you to a world of woe.

Namely, if the -init of the class being allocated happens to call the sharedInstance method, it will do so before the variable is set. In both cases it will lead to a deadlock.

This is the one time that you want to separate the alloc and the init. Cribbing Colin's code because it is the best solution (assuming 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;
}

note this only works on Mac OS X; X 10.6+ and iOS 4.0+, in particular. On older operating systems, where blocks are not available, use a lock or one of the various means of doing something once that isn't blocks based.


The above pattern does not actually prevent the problem described in the text and will cause a deadlock when it is encountered. The problem is that the dispatch_once() is not re-entrant and, thus, if the init calls sharedInstance, wedge city.

Community
  • 1
  • 1
bbum
  • 162,346
  • 23
  • 271
  • 359
  • Thanks for this one, although it took me half an hour to find the correct reference to the blocks (^) syntax http://developer.apple.com/Mac/library/documentation/Cocoa/Conceptual/Blocks/Blocks.pdf, which for some reason is not included in their Objective C documentation – Harald Scheirich Feb 05 '10 at 05:18
  • Thanks. It was the first thing too I was thinking about while seeing your answer. I did not know there is a possibility to write such blocks in objective c (like anonymous class implementation in Java or delegates in C#). This is the answer for my other problem here: http://stackoverflow.com/questions/2118728/method-signature-for-a-selector Instead of using callbacks I now use blocks. :) Perhaps, I initialize my singleton in the AppDelegate and actually don't care about thread concurrency. The instance is simple there, when needed. It is a kind of cache for few things like images etc. – MacTouch Feb 05 '10 at 20:50
  • 5
    @bbum, (1) why would `-init` call `+sharedInstance`, and (2) why do you include the line `if (sharedInstance) return sharedInstance;`? – ma11hew28 Jul 10 '11 at 12:38
  • 4
    Just for completeness - the above works on iOS 4 and above when blocks were introduced on iOS. – Hunter Jul 17 '11 at 02:35
  • 3
    I would suggest removing the "if(sharedInstance) return sharedInstance", it is Double Checked Locking and does not work (see http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf) – Jochen Nov 29 '11 at 01:49
  • .... and in fact, this will still cause a deadlock if -init calls the +sharedInstance,' – bbum Nov 29 '11 at 05:58
  • @bbum so, should we just use the [solution of @ColinWheeler](http://stackoverflow.com/a/2200751/242933) and just make sure `-init` doesn't call `+sharedInstance`? – ma11hew28 Dec 04 '11 at 23:01
  • 1
    @MattDiPasquale Pretty much. I'm still thinking about this one; I suppose a conditional around the dispatch *might* help, but down that path lies potential madness. – bbum Dec 05 '11 at 03:20
  • 4
    Why did you edit your answer to remove the single one piece of information that made it valuable? Your edited code is no different than Colin's original answer, and now your contribution is meaningless, your code does not correspond to your text anymore (where is the separation between alloc and init that your answer is all about?). If, in an afterthought, you feel your answer is invalid, then (1) please add a note stating that, rather than making your answer inconsistent and (2) please explain why it would be invalid. Thanks! – KPM Sep 20 '12 at 00:05
  • 1
    @KPM You are absolutely correct. See the update. Honestly, I don't have a good clean solution for this issue. The issue is that `sharedInstance` needs to be both exclusive and re-entrant, which is completely at conflict with itself. **thinking** – bbum Sep 20 '12 at 02:33
  • Ok thanks! But if init calls sharedInstance, then we are entering a second time the class method. Therefore, dispatch_once will not fire, and sharedInstance will just return the alloc'ed but uninitialised static variable. Then when init returns, the static variable gets assigned to the initialised instance. Right? Why would we need re-entrance? – KPM Sep 20 '12 at 16:08
  • Because the dispatch_once() hasn't returned yet, so it blocks on second entry. The whole point of dispatch_once() is that you can initialize a static exactly once and, until it is initialized, nothing else will be able to execute the code after the dispatch_once(). At least the deadlock's backtrace makes it obvious what happened. – bbum Sep 20 '12 at 16:18
  • @bbum Can you please look here: http://stackoverflow.com/questions/20895214/why-should-we-separate-alloc-and-init-calls-to-avoid-deadlocks-in-objective-c your answer create a whole new discussion about separation alloc and init - some people interested in detail explanation – Oleksandr Karaberov Jan 03 '14 at 02:15
38

The fastest thread safe way to do this is with Grand Central Dispatch ( libdispatch ) and dispatch_once()

+(MyClass *)sharedInstance
{   
    static MyClass *sharedInstance = nil;
    static dispatch_once_t pred;

    dispatch_once(&pred, ^{
        sharedInstance = [[MyClass alloc] init];
    });

    return sharedInstance;
}
Colin Wheeler
  • 3,343
  • 2
  • 21
  • 23
  • why do you have to use dispatch_once? Why can't you just do an if statement like this: " if (sharedInstance == nil) { sharedInstance = [MyClass alloc] init];" – mfaani Apr 06 '16 at 19:16
  • 1
    this doesn't guarantee that it'll be allocated only once. dispatch once guarantees that the block will only be executed once even if multiple threads all call the same code at once. In your code one thread could execute the if statement, then go to start allocating the instance, then another thread at the same time evaluates the if statement and then allocates a 2nd instance and now boom you've got leaked memory you can't reclaim. Your code is functional, but it won't handle cases like what I just described. – Colin Wheeler Apr 06 '16 at 23:48
  • Thanks I get your point about being thread safe. What I don't get is why isn't JUST using dispatch_once enough? Why do I still see people do if statement + dispatch_once + static + class method? If you are using dispatch_once then why are you specifying static? – mfaani Apr 07 '16 at 02:43
  • 1
    http://stackoverflow.com/questions/4576607/what-does-static-mean-in-ansi-c I didn't use an if statement, because dispatch_once only executes once its unnecessary. static ensures the variable will live across multiple executions of the method, otherwise on the 2nd execution of this method we'd get a new pointer that doesn't point to the allocated memory we did the 1st time, and that memory would be leaked. Class method usually serves to show you are dealing with a shared instance like NSFileManager.defaultManager() – Colin Wheeler Apr 20 '16 at 15:38
12

If anyone cares, here is a Macro for the same thing:

   /*!
    * @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
Arvin
  • 2,516
  • 27
  • 30
2

This CocoaDev page can be useful for your need.

0xKayvan
  • 368
  • 1
  • 4
  • 18
Laurent Etiemble
  • 27,111
  • 5
  • 56
  • 81
  • Thanks. Hmm.. I did not think about the possibility to retain or overrelease a singleton. Hell, who does such things? :) – MacTouch Feb 04 '10 at 11:28
1

If anyone cares, here is another macro for the same thing :)

IMHO, it provides greater flexibility compared to the other variations.

#define SHARED_INSTANCE(...) ({\
    static dispatch_once_t pred;\
    static id sharedObject;\
    dispatch_once(&pred, ^{\
        sharedObject = (__VA_ARGS__);\
    });\
    sharedObject;\
})

Usage, one-line initialization:

+ (instancetype) sharedInstanceOneLine {
    return SHARED_INSTANCE( [[self alloc] init] );
}

Usage, multi-line initialization (notice curly braces around the block of code):

+ (instancetype) sharedInstanceMultiLine {
    return SHARED_INSTANCE({
        NSLog(@"creating shared instance");
        CGFloat someValue = 84 / 2.0f;
        [[self alloc] initWithSomeValue:someValue]; // no return statement
    });
}

Usage in the right part of an assignment:

- (void) someMethod {
    MethodPrivateHelper *helper = SHARED_INSTANCE( [[MethodPrivateHelper alloc] init] );
    // do smth with the helper
}
// someMethod should not call itself to avoid deadlock, see bbum's answer

This modification utilizes two language features: the GCC compound expressions extension, which is also supported by Clang, and the C99 variadic macros support.

After pre-processing, the output will look like (you can test it yourself by invoking Product > Perform Action > Preprocess "YourClassName.m" in Xcode 5):

+ (instancetype) sharedInstanceOneLine {
    return ({
        static dispatch_once_t pred;
        static id sharedObject;
        dispatch_once(&pred, ^{
            sharedObject = ( [[self alloc] init] );
        });
        sharedObject; // this object will be returned from the block
    });
}

+ (instancetype) sharedInstanceMultiLine {
    return ({
        static dispatch_once_t pred;
        static id sharedObject;
        dispatch_once(&pred, ^{
            sharedObject = ({
                NSLog(@"creating shared instance");
                CGFloat someValue = 84 / 2.0f;
                [[self alloc] initWithSomeValue:someValue];
            });
        });
        sharedObject;
    });
}

- (void) someMethod {
    MethodPrivateHelper *helper = ({
        static dispatch_once_t pred;
        static id sharedObject;
        dispatch_once(&pred, ^{
            sharedObject = ( [[MethodPrivateHelper alloc] init] );
        });
        sharedObject;
    });
}
skozin
  • 3,789
  • 2
  • 21
  • 24