5

I have a Singleton set up like this:

static Universe *instance;

+ (Universe *)instance { return instance; }

+ (void)initialize
{
    static BOOL initialized = NO;
    if(!initialized)
    {
        initialized = YES;
        instance = [[Universe alloc] init];
    }
}

- (id) init
{
    self = [super init];
    if (self != nil) {
        self.showHistory = YES;
    }
    return self;
}

but now I realize that I'd like to instantiate it from Interface Builder. I was thinking of just cutting into the init method like so

    if (instance) 
         return instance;

is this a bad idea? I'd prefer IB to pick up the instance already created in the +initialize method.

Dan Rosenstark
  • 68,471
  • 58
  • 283
  • 421

2 Answers2

6

This can be done. There is a section about it in Cocoa Design Patterns by Buck and Yachtman.

In your case you could do something along the lines of:

static Universe *instance;

+ (Universe *)instance { return instance; }

+ (id)hiddenAlloc
{
  return [super alloc];
}

+ (id)alloc
{
  return [[self instance] retain];
}

+ (void)initialize
{
    static BOOL initialized = NO;
    if(!initialized)
    {
        initialized = YES;
        instance = [[Universe hiddenAlloc] init];
    }
}

- (id)init
{
  if(instance==nil) // allow only to be called once
  {
    // your normal initialization here
  }
  return self;
}

The nib loading code will then correctly pick up the singleton via its call to [[Universe alloc] init], and you can still use instance in your code as before.

The book has more detail and recommends implementing new and allocWithZone (both simply as return [self alloc];), plus error-reporting stubs to catch copyWithZone and mutableCopyWithZone attempts for good measure.

Nick Moore
  • 15,547
  • 6
  • 61
  • 83
  • 1
    This is what I did to implement a plugin version of some networking client code that was originally in a Cocoa app. A coworker wanted it in a plugin so I went the singleton route so there'd only ever be one actual instance of the network client (internally). – ExitToShell Jan 05 '11 at 22:31
  • @invariant, great answer the kind that makes me glad to have asked the question and not just dismissed it as dumb. – Dan Rosenstark Jan 05 '11 at 22:44
  • 2
    `+alloc` should return `[[self instance] retain]`, because `alloc` returns an owned object. – Dave DeLong Jan 05 '11 at 22:47
  • The solution is clever yet simple. I still have to test whether IB is happy with it, but it sure looks right. – Dan Rosenstark Jan 05 '11 at 22:49
  • @Dave DeLong, though this is a minor point -- since the Singleton runs for the life of the app AND the interface builder instantiation will never be released -- I think you're completely right. – Dan Rosenstark Jan 05 '11 at 22:52
  • 1
    @Yar it'd be an issue if you did `alloc`-`init`-`release` in code. You'd be releasing an object that you didn't own, eventually leading to accessing deallocated memory and a crash. – Dave DeLong Jan 05 '11 at 22:54
  • @Dave DeLong, great point (and one not picked up on in the book). I've updated the answer. – Nick Moore Jan 05 '11 at 23:01
  • @invariant, @Dave DeLong, thanks for this. It's amazing to be able to wire up a singleton in IB. I promise I won't overuse it ;) – Dan Rosenstark Jan 05 '11 at 23:05
  • 2
    @invariant I just filed it on the Errata page: http://www.cocoadesignpatterns.com/errata/ – Dave DeLong Jan 05 '11 at 23:18
  • One reason why this is so cool is that the singleton really is a singleton: you can actually use it in multiple NIBs (and draw different relationships in each) and it will always be talking about the same instance. – Dan Rosenstark Jan 05 '11 at 23:27
  • NOTE: for ARC you can use `static Universe *instance;` with no modifications, since `__strong` is by default. Also, I *think* you can use `[[self hiddenAlloc] init];` instead of `[[Universe hiddenAlloc] init];` – Dan Rosenstark Aug 08 '12 at 00:03
  • TFW you Google for something and find your own answer for it that you wrote 6 years ago. – Nick Moore Dec 04 '17 at 12:07
1

That's going to leak. You can get away with it if you change it to:

if(instance) {
    [self release];
    return instance;
}

but it still smells a bit to me. I'm curious what use you have for singletons in IB; I suspect I would avoid this construct in my code.

Seamus Campbell
  • 17,816
  • 3
  • 52
  • 60
  • Hi Seamus, yeah, the code doesn't work anyway due to "This coder requires that replaced objects be returned from initWithCoder"... anyway, I'm going to checkout External Object in IB. The point is that I use one central Singleton as a bridge to all pieces of the app... so each piece registers itself with the singleton. But then I thought, why not wire some of them? – Dan Rosenstark Jan 05 '11 at 22:10
  • http://stackoverflow.com/questions/350861/what-bad-practice-do-you-do-and-why/350900#350900 – Dan Rosenstark Jan 05 '11 at 23:13