29

I found some information in the net to create a singleton class using GCD. Thats cool because it's thread-safe with very low overhead. Sadly I could not find complete solutions but only snippets of the sharedInstance method. So I made my own class using the trial and error method - and et voila - the following came out:

@implementation MySingleton

// MARK: -
// MARK: Singleton Pattern using GCD

+ (id)allocWithZone:(NSZone *)zone { return [[self sharedInstance] retain]; }
- (id)copyWithZone:(NSZone *)zone { return self; }
- (id)autorelease { return self; }
- (oneway void)release { /* Singletons can't be released */ }
- (void)dealloc { [super dealloc]; /* should never be called */ }
- (id)retain { return self; }
- (NSUInteger)retainCount { return NSUIntegerMax; /* That's soooo non-zero */ }

+ (MySingleton *)sharedInstance
{
    static MySingleton * instance = nil;

    static dispatch_once_t predicate;   
    dispatch_once(&predicate, ^{
        // --- call to super avoids a deadlock with the above allocWithZone
        instance = [[super allocWithZone:nil] init];
    });

    return instance;
}

// MARK: -
// MARK: Initialization

- (id)init
{
    self = [super init];
    if (self) 
    {
        // Initialization code here.
    }
    return self;
}

@end

Please feel free to comment and tell me if I've missing something or doing something completely wrong ;)

Cheers Stefan

vikingosegundo
  • 52,040
  • 14
  • 137
  • 178
blackjacx
  • 9,011
  • 7
  • 45
  • 56
  • I'd be tempted to add a `-(void)dealloc` that throws an exception, that way you should be able to track down the offending actor if someone is getting a singleton instance and then releasing it. Apart from being an abuse of the pattern, that'd leave you with a dangling pointer. – Tommy Sep 29 '11 at 14:47
  • meta question: should this be at [http://codereview.stackexchange.com/]? – Joren Sep 29 '11 at 14:50
  • 3
    apple strongly advises not to create singletons overriding retain/release! this will break applications transitioning to ARC – Martin Ullrich Sep 29 '11 at 14:53
  • Possible duplicate/closely related: [What does your Objective-C singleton look like?](http://stackoverflow.com/questions/145154/what-does-your-objective-c-singleton-look-like) – jscs Sep 29 '11 at 19:44

3 Answers3

83

Keep it simple:

+(instancetype)sharedInstance
{
    static dispatch_once_t pred;
    static id sharedInstance = nil;
    dispatch_once(&pred, ^{
        sharedInstance = [[self alloc] init];
    });
    return sharedInstance;
}

- (void)dealloc
{
    // implement -dealloc & remove abort() when refactoring for
    // non-singleton use.
    abort();
}

That is it. Overriding retain, release, retainCount and the rest is just hiding bugs and adding a bunch of lines of unnecessary code. Every line of code is a bug waiting to happen. In reality, if you are causing dealloc to be called on your shared instance, you have a very serious bug in your app. That bug should be fixed, not hidden.

This approach also lends itself to refactoring to support non-singleton usage modes. Pretty much every singleton that survives beyond a few releases will eventually be refactored into a non-singleton form. Some (like NSFileManager) continue to support a singleton mode while also supporting arbitrary instantiation.

Note that the above also "just works" in ARC.

gaussblurinc
  • 3,642
  • 9
  • 35
  • 64
bbum
  • 162,346
  • 23
  • 271
  • 359
  • thanks for this...just a question here regarding the statically created object. Shouldnt you release that object i dealloc? i am always confused about the ownership of static objects. So shouldnt you have something like [[MyClass sharedInstance] release]; in dealloc? – Abolfoooud Aug 25 '12 at 20:06
  • 5
    Singletons exist from the moment they are requested until the app terminates. They aren't deallocated and re-instantiated. As there is no reason to deallocate anything on application termination, there is no reason to `release` the singleton. Because it is unlikely that the singleton's destruction has ever been tested, implementing the `dealloc` as shown is a purely defensive measure to remind future you that past you didn't think about memory management for this class. – bbum Aug 25 '12 at 21:47
  • one small addition, the (id) should be (instanceType)..for details read [this question](http://stackoverflow.com/questions/8972221/would-it-be-beneficial-to-begin-using-instancetype-instead-of-id) – Tushar Koul Jun 23 '14 at 09:36
  • Hello, can you do that using swift language? Thanks. – Allan Macatingrao Nov 26 '14 at 09:07
  • @Allan, I've added another [answer](http://stackoverflow.com/a/27739016/947342) for creating a singleton with Swift. – pxpgraphics Jan 02 '15 at 07:45
  • why do you use `[[self class] alloc]` instead of `[self alloc]` in `+` method? – gaussblurinc Oct 15 '15 at 15:59
19
// See Mike Ash "Care and Feeding of Singletons"
// See Cocoa Samurai "Singletons: You're doing them wrong"
+(MySingleton *)singleton {
    static dispatch_once_t pred;
    static MySingleton *shared = nil;
    dispatch_once(&pred, ^{
        shared = [[MySingleton alloc] init];
        shared.someIvar = @"blah";
    });
    return shared;
}

Be aware that dispatch_once is not reentrant, so calling itself from inside the dispatch_once block will deadlock the program.

Don't try to code defensively against yourself. If you are not coding a framework, treat your class as normal then stick the singleton idiom above. Think of the singleton idiom as a convenience method, not as a defining trait of your class. You want to treat your class as a normal class during unit testing, so it's OK to leave an accessible constructor.

Don't bother using allocWithZone:

  • It ignores its argument and behaves exactly like alloc. Memory zones are no longer used in Objective-C so allocWithZone: is only kept for compatibility with old code.
  • It doesn't work. You can't enforce singleton behavior in Objective-C because more instances can always be created using NSAllocateObject() and class_createInstance().

A singleton factory method always returns one of these three types:

  • id to indicate the return type is not fully known (case where you are building a class cluster).
  • instancetype to indicate that the type returned is an instance of the enclosing class.
  • The class name itself (MySingleton in the example) to keep it simple.

Since you tagged this iOS, an alternative to a singleton is saving the ivar to the app delegate and then using a convenience macro that you can redefine if you change your mind:

#define coreDataManager() \
        ((AppDelegate*)[[UIApplication sharedApplication] delegate]).coreDataManager
Community
  • 1
  • 1
Jano
  • 62,815
  • 21
  • 164
  • 192
1

If you want to unit test your singleton you also have to make it so that you can replace it with a mock singleton and/or reset it to the normal one:

@implementation ArticleManager

static ArticleManager *_sharedInstance = nil;
static dispatch_once_t once_token = 0;

+(ArticleManager *)sharedInstance {
    dispatch_once(&once_token, ^{
        if (_sharedInstance == nil) {
            _sharedInstance = [[ArticleManager alloc] init];
        }
    });
    return _sharedInstance;
}

+(void)setSharedInstance:(ArticleManager *)instance {
    once_token = 0; // resets the once_token so dispatch_once will run again
    _sharedInstance = instance;
}

@end
ToddH
  • 2,801
  • 2
  • 28
  • 28
  • I walk through each line of that code sample in a blog post here: http://twobitlabs.com/2013/01/objective-c-singleton-pattern-unit-testing/ – ToddH Jan 25 '13 at 01:29