8

I have a 2 child classes that inherit from 'MyClass' and each child class should be a singleton.

I've used this pattern for getting a static instance when I don't have any other classes inheriting:

+ (MyClass *)getInstance
{
    static dispatch_once_t once;
    static MyClass *instance;

    dispatch_once(&once, ^{
        instance = [[MyClass alloc] init];
    });

    return instance;
}

This works just great. Now, if I add two new child classes, FirstClass and SecondClass, both of which inherit from MyClass, how do I ensure that I get back the respective ChildClass?

dispatch_once(&once, ^{
    // No longer referencing 'MyClass' and instead the correct instance type
    instance = [[[self class] alloc] init];
});

FirstClass *firstClass = [FirstClass getInstance]; // should be of FirstClass type
SecondClass *secondClass = [SecondClass getInstance]; // should be of SecondClass type

Doing the above means that I always get back whatever class I instantiated 1st as my second class type:

first: <FirstClass: 0x884b720>
second: <FirstClass: 0x884b720>
// Note that the address and type as identical for both.

What's the best way to create the respective child class singletons without adding getInstance method to each of the child classes?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Howard Spear
  • 551
  • 1
  • 5
  • 14

3 Answers3

6

Unless you have a good reason, you should generally avoid subclassing singletons. It creates a very confusing situation. If you create a singleton MyClass, can you then create a singleton FirstClass? Since FirstClass must always be useable anywhere MyClass is usable (by Liskov), now there are three "singleton" MyClass objects. Now ObjC is very loose with singletons, and that's a good thing, but this is still very strange.

OK, that said, what about your problem? First the solution, then the answer. The solution is that MyClass probably shouldn't be a singleton, as discussed above. Get rid of getInstance in the superclass and just define it in the subclasses.

The answer to what is happening is in your dispatch_once. You're passing the same static once token in all cases. dispatch_once will run no more than one time ever for a given token. The only way around this is to pass different tokens for each class, and I don't know of a convenient way to do that without duplicating the dispatch_once code in each file. You could try to create different once tokens for each subclass, but that's likely to be more trouble and code than just duplicating the sharedInstance method.

BTW, don't call it getInstance. "get" has special meaning in ObjC, and you don't mean that here, so it's confusing. This is typically called sharedInstance, or better sharedSomething where "something" is your class. If you really do mean that there should be a MyClass and there should be a FirstClass and there should be a SecondClass, you can implement sharedInstance in all three.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • The reason I have this hierarchy is that I have number of shared common code & data that all of my child classes have (outside of the 'sharedInstance'. This is also the reason why I am using inheritance vs protocols. I also wanted to not repeat the same instantiation code for all three of the derived classes. – Howard Spear Sep 28 '12 at 16:08
1

you know singletons are gnarly, right? many singletons are a bigger problem. end of caution.


you can just use dispatch_once to create the base singleton. then your base could hold a map (e.g. dictionary{key:ClassName|value:Instance}) of its derived types.

that you're using dispatch_once suggests this will be used in a multithreaded context. in that case, you would need to guard interactions with the dictionary using a mutex.

then your messages from subclasses would determine based on the messaging class (self) which class to lookup (creating that instance, if necessary).

justin
  • 104,054
  • 14
  • 179
  • 226
  • 1
    People got to learn to use singletons and then get screwed by them to know why you meant by they are a 'bigger problem'. – mskw Dec 02 '12 at 21:29
  • @mskw i didn't want to bang the bible too hard, when most people have heard the first sentence =p but you're right. one typical problem is that eradicating a global/singleton affects many sources/implementations. when many interact, it becomes a big intertwined global state which is often impossible to reuse in a way different from the initial intent (IOW, to separate). that means many of those classes and dependencies, not just the singletons, but also the implementations which use them, are unusable to other programs -- fixing that takes much time and much testing. – justin Dec 03 '12 at 16:46
-1

use id in your myClass in firstClass like this

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

i think this should works

Dennis Farandy
  • 277
  • 1
  • 5
  • 9
  • The same token would still be used for each class and subclasses and so, the sharedinstance variable will be initialized just once for the first iteration. – Nicolas Buquet Apr 20 '18 at 13:30