12

I've been reading a lot of threads and blog articles about how to implement a singleton in objective-c, several of them maybe being a bit deprecated (year 2010 or earlier), and it seems like people have different opinions regarding this issue... Does Apple have documentation about implementing a singleton? I couldn't find it. If so, could somebody tell me where?

I need a singleton for a class that has some both public and private variables. Currently, this is the implementation I have for such class:

@interface MySingleton ()
   @property (strong, nonatomic) NSString *state;
@end

@implementation MySingleton

@synthesize state = _state;
@synthesize count = _count;

static MySingleton *sharedObject = nil;

+ (MySingleton *)sharedInstance
{
   static dispatch_once_t _singletonPredicate;

   dispatch_once(&_singletonPredicate, ^{
      sharedObject = [[super allocWithZone:nil] init];
   });

   return sharedObject;
}

+ (id)allocWithZone:(NSZone *)zone 
{
   return [self sharedInstance];
}

Should be this the recommended way? And how should I initialize the instance variables, public and private?

Another issue I'd like to make clear about singleton is: will this generate a memory leak? Is the use of singletons actually recommended in iOS?

Thanks

AppsDev
  • 12,319
  • 23
  • 93
  • 186
  • In fact ... would this work in iOS4 ? (As a curiosity.) – Fattie Nov 04 '13 at 08:39
  • And may be helpful, note the exceptionally simple presentation of the code in the popular answer here: http://codereview.stackexchange.com/questions/19829/objective-c-singleton-implementation – Fattie Nov 04 '13 at 08:52

5 Answers5

12

The above is correct, along with @miho's comment about includng the static object inside of the sharedInstance method. But there is no reason to override +allocWithZone:. Singletons in ObjC are generally "shared," not forced. You're free to create other instances of a "singleton." If it's illegal to create other instances, then you should make init perform an NSAssert rather than fooling the caller in +allocWithZone:. If your singleton is mutable (and most are), you absolutely should not override +allocWithZone: this way.

Another issue I'd like to make clear about singleton is: will this generate a memory leak?

No. This object will never be released, but it will always be accessible. That is not a leak.

Is the use of singletons actually recommended in iOS?

Yes, and it is a very common pattern, used all over the Cocoa frameworks. That said, there are various other patterns that have started to recently become somewhat popular among developers. Dependency Injection is getting some interest, though I don't see it in practice very often. Reducing your reliance on singletons can improve testability, and I have been experimenting recently with how to eliminate some of them in my code with some success. But they have a long, proud history in Cocoa, and I do not consider them a problem.

EDIT: you have one actual bug in your code. You should be calling [[self alloc] init], not [[super alloc] init]. There's no reason to ever call +allocWithZone:, just use +alloc. (The time when ...WithZone: methods were useful has long passed.)

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • I don't understand this completely myself, but in the BNR iOS book, they use `[[super allocWithZone:nil] init]` and return `[self sharedInstance]` in `+allocWithZone:`. The reason given there is that `alloc` just calls `allocWithZone:` which does the actual allocation. Using `[super allocWithZone]` avoids the code going in circles, calling its own `allocWithZone` method. – nevan king Mar 17 '13 at 18:39
  • 2
    ah. When you quit overriding `allocWithZone:` (which you should almost never override), this won't be a problem anymore. – Rob Napier Mar 17 '13 at 18:41
  • Thanks! And then, should variables be initialized by overriding `init` method, like in "regular" classes? – AppsDev Mar 17 '13 at 18:51
  • Yep. There's nothing special about singletons except that there's a class method for getting a "well-known instance" of it. Beyond that, it's just a normal class. – Rob Napier Mar 17 '13 at 20:32
  • Hi, @RobNapier, am late here, but I've heard from the grapevine that there could be rare cases when a singleton can be init'ed more than once, difficult to test, etc. I have done DI before in Java, but this topic is still vague to me regarding DI in Obj-C. I was hoping if you could be so kind as to link me a couple readings regarding DI in Obj-C, how singleton is difficult to test, rare cases involving singleton being init'ed more than once? If you yourself wrote a blog about all these, that would suffice. Been googling, but most of the result about what I mentioned here has to do w/ Java. TY. – Unheilig Apr 01 '14 at 20:21
3

In Xcode, under 'Search Documentation' enter Creating a Singleton Instance. There are lots of results (but the link above, at the bottom of the page, has example code).

Jonathan King
  • 1,528
  • 14
  • 25
GoZoner
  • 67,920
  • 20
  • 95
  • 145
  • 9
    Do not use the code at the bottom of that page lightly. It is exclusively for the creation of a "strict singleton" as it notes. These are exceedingly rare in Cocoa. Shared singletons are the norm, and do not require most of this code. That code snippet has caused a lot of confusion among new Cocoa developers. I recommend ignoring it; you are unlikely to encounter cases where it is useful. – Rob Napier Mar 17 '13 at 17:44
1

Yes, this is the recommended way. There is only one small difference of how I use it: I define the sharedObject as static variable inside the + (MySingleton *)sharedInstance method, because it shouldn't be possible to access the variable from anywhere else then from the getter method.

And no, you won't create a memory leak. When your app is terminated all reserved memory used by your app will be released anyway and there is no other situation where a static shared instance should get released. In pre-ARC area it even was common to override the release method do prevent accidentally releasing of the object.

miho
  • 11,765
  • 7
  • 42
  • 85
0

A bit of warning using gcd for singletons:

dispatch_once(&_singletonPredicate, ^{
      sharedObject = [[super allocWithZone:nil] init];
   });

if the init method for some reason addresses the singleton object directly or indirectly, there will be a deadlock. For this reason I believe that the more appropriate way to write a singleton is through the

+ (void) initialize

method

cpprulez
  • 896
  • 2
  • 9
  • 21
  • 1
    Why would the init method access the singleton instance? I can't imagine a case where that would happen. `+initialize` is not the more appropriate way to write singletons since GCD was released. (It was a good way prior to that.) – Rob Napier Mar 17 '13 at 20:34
  • Hello, Rob :) I want to sincerely thank you for your wonderful book 'iOS development: Pushing the Limits', I learned a ton from it! Well, this has happened in one of my apps: the init method instantiated an instance of another class, and that other instance addressed the singleton object, before the dispatch_once finished (which causes deadlock). Yes, I know that from design view this is not very good :) – cpprulez Mar 17 '13 at 21:31
  • Exactly. You have a design problem here, not an singleton-pattern problem. You're probably doing too much in init. You might also be using a singleton that you don't need. Sometimes people jump to singletons too quickly, when it would be better to just pass the object. – Rob Napier Mar 17 '13 at 21:45
0

I am still using the singleton header thingy from CocoaWithLove - may be a bit dated but still works like a charm. It basically does the same as described here referring to Apples documentation and I would assume at least Apple's documentation (bottom of this page) is still valid. There are people assuming it will stay valid indefinitely sine it is the official solution Apple suggested.

Christoph
  • 1,525
  • 1
  • 16
  • 27
  • Hi, yes, this is still valid, however it's not efficient, since there's a lock every time the singleton is accessed, even if only one thread is trying to access it. dispatch_once solves this problem. – cpprulez Mar 18 '13 at 07:16