2

I have a Class that I wish to implement as Singleton.

I wish that the only way an Instance of this class may be created / accessed is via:

+(MYClass*)sharedInstance 

method. alloc and init are called within the method implementation (of course).

Is there a way to block the usage of alloc and init, or to make them 'empty', if there's an attempt to create an instance of that class NOT via the sharedInstance method (but through alloc + init directly)?

sha
  • 17,824
  • 5
  • 63
  • 98
Ohad Regev
  • 5,641
  • 12
  • 60
  • 84

2 Answers2

6

Putting this code in your header file should produce a compile time error if init is called outside the implementation:

- (id)init __attribute__((unavailable("cannot use init for this class, use +(MYClass*)sharedInstance instead")));

I found this technique here.

Update:

You won't be able to write [[MYClass alloc] init] in your .m file but you can use the following:

+ (MYClass *)sharedInstance {
    static MYClass *sharedInstance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [self new];
    });
    return sharedInstance;
}
Keith Kennedy
  • 310
  • 5
  • 11
  • wait a minute... if I do that, I block the ability to alloc and init the Singleton Instance within the sharedInstance method... Is there a way to either define this statement only externally to the class or add an exception to the exception so I can still use them in the .m file? – Ohad Regev Jan 02 '14 at 09:11
  • That works... a bit of an overkill, but it does the work. You are good, man... But if I try to do the same thing for 'alloc' as well (use the unavailable thing), I get an error. any idea what to do there? – Ohad Regev Jan 02 '14 at 09:45
  • If you are stopping alloc using something like this in the .h: `+ (id)alloc __attribute__((unavailable("no alloc")));` then, you could change the sharedInstance line in my answer to `sharedInstance = [self new]`, I'll update my answer so it does stop the use of alloc and init like you asked. – Keith Kennedy Jan 02 '14 at 09:50
0

If you are using arc, add compiler flag -fno-objc-arc to the file.

"alloc + init" will call sharedManager method.

static MyGizmoClass *sharedGizmoManager = nil;

+ (MyGizmoClass*)sharedManager
{
    if (sharedGizmoManager == nil) {
        sharedGizmoManager = [[super allocWithZone:NULL] init];
    }
    return sharedGizmoManager;
}

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

- (id)copyWithZone:(NSZone *)zone
{
    return self;
}

- (id)retain
{
    return self;
}

- (NSUInteger)retainCount
{
    return NSUIntegerMax;  //denotes an object that cannot be released
}

- (void)release
{
    //do nothing
}

- (id)autorelease
{
    return self;
}
KudoCC
  • 6,912
  • 1
  • 24
  • 53
  • Just note that this will not compile if you use ARC. – Martin R Jan 01 '14 at 09:38
  • This will most likely cause strange behavior if you have an `-init` method, since `[[MyGizmoClass alloc] init]` will call the initializer of your already instantiated singleton again. Better throw an exception in `+allocWithZone:` instead to let the programmer know that he should use the singleton accessor instead. – Alfonso Jan 01 '14 at 10:08
  • @Fönsi I agree with you, but it is ok when `-init` method doesn't do any significant thing. – KudoCC Jan 01 '14 at 10:18
  • @Fönsi There is a solution that using `dispatch_once` in init to make sure the instance in init method initial only once. – KudoCC Jan 01 '14 at 10:53
  • @KudoCC yes, you could use dispatch_once. But still I'd prefer the exception for added clarity. It's confusing if you see mixed usage of `[MyGizmoClass sharedManager]` and `[[MyGizmoClass alloc] init]` in the code. Also it probably means the programmer using the alloc/init construct does not know that he gets a singleton back, which can have all kinds of nasty effects. – Alfonso Jan 01 '14 at 11:37