问题
I have created a singleton class and I want to create a class which is subclass of this singleton class, what is the correct method to do it
回答1:
I don't know about Objective-C in particular, but in general singleton classes should prevent subclassing. If you've got an instance of the base class and an instance of the subclass, then you've effectively got two objects you can regard as instances of the base "singleton" class, haven't you?
As soon as you've got two instances, it's not really a singleton any more... and that's leaving aside the possibilities that there are multiple subclasses, or that the subclass itself allows multiple instances to be created.
Of course you can change your base class so it just has a way of getting at a single "default" instance, but that's not quite the same as making it a singleton.
回答2:
If Jon didn't convinced you to not do it, you should do it this way:
In your superclass, init your singleton instance with [[[self class] alloc] init]
so then you always get an instance of the class with which you are calling the sharedInstance method. And you don't have to overwrite the sharedInstance method in your subclass.
[SuperClass sharedInstance] //-> instance of SuperClass
[SubClass sharedInstance] //-> instance of Class
回答3:
I made an example "base class" for singleton, you can check it here: https://github.com/stel/DOSingleton
回答4:
Jon Skeet makes a good point about whether you’d really have a singleton if you’re allowed to instantiate both the class and its subclass. Putting that aside, here’s a pattern you can use so that so you only have to define the shared-instance getter once, in the parent class:
// this code goes in the implementation of the superclass
static Sprocket *defaultSprocket;
+ (instancetype) defaultSprocket
{
if (defaultSprocket == nil)
defaultSprocket = [[[self class] alloc] init];
return defaultSprocket;
}
This approach has the following advantages:
- Using
[self class]
allows e.g.[SprocketSubclass defaultSprocket]
to return an instance ofSprocketSubclass
instead ofSprocket
- Using
instancetype
allows the compiler to type-check the result of this method: it’ll beSprocket
when you invoke it as+[Sprocket defaultSprocket]
butSprocketSubclass
when you invoke it as+[SprocketSubclass defaultSprocket]
.
Notably, you can define this accessor method in the base class and then you don’t have to do anything in the subclasses!
(Hat tips to NSHipster for explaining why instancetype is so cool and bbum for reminding me of it recently.)
回答5:
If what you are looking for is a quick way to setup new singletons. This pseudo abstract singleton base class is what I use:
Reusable base class
H
#define CREATE_SHARED_INSTANCE \
+ (instancetype)sharedInstance { \
static dispatch_once_t once; \
static id instance = nil; \
dispatch_once(&once, ^{ \
instance = [[self alloc] init]; \
}); \
return instance; \
}
@interface SharedObject : NSObject
+ (instancetype)sharedInstance;
@end
M
@implementation SharedObject
+ (instancetype)sharedInstance {
[NSException raise:@"Call to unimplemented sharedInstance" format:@"%@ does not implement sharedInstance.", NSStringFromClass([self class])];
return nil;
}
@end
Then each subclass
H
#import "SharedObject.h"
@interface SomeSubclass : SharedObject
@end
M
@implementation SomeSubclass
CREATE_SHARED_INSTANCE
@end
...and use like any singleton.
[[SomesSubclass SharedInstance] someMethod];
If you call the abstract base class, or forget to include CREATE_SHARED_INSTANCE
in your subclass, you will get a friendly exception raised.
This way you can setup a new singletons easily at no performance hit.
回答6:
The simplest way to achieve this is implement the standard singleton accessor in both the class and the subclass. This way each class behaves as a proper singleton, that is there is only ever one instance of both. If you attempt to reuse the accessor of the parent class in the subclass and then if you make use of both classes, you run the risk of the accessor returning the wrong instance because their behaviour would depend on the order of how they are accessed.
You should not use instancetype for the singleton accessor to help prevent this mistake. You'll notice Apple don't use it for their singletons e.g. UIApplication and CKContainer.
If you would like existing code that accesses the super-class's singleton method be given an instance of the subclass then likely you need to redesign, see MrJre's answer.
回答7:
I had a similar problem and the way I solved it is to create a singleton wrapper class which has all the extra functionality. This singleton class contains the original singleton (has the singleton instance as a member variable). This way you can avoid dirty tricks.
回答8:
I had a similar problem, I had multiple targets that needed to have a slightly different singleton implementations: each target would include the base class + a specific subclass. This was achieved by writing the base class like so:
+ (SingletonBaseClass*) sharedInstance {
static SingletonBaseClass * sharedInstance = nil;
if (!sharedInstance) {
sharedInstance = [[[self class] alloc] init];
[sharedInstance customInit];
}
return sharedInstance;
}
The key difference is [self class]
instead of the actual class name. That way when the we call: [SingletonSubclass sharedInstance]
the correct object is instantiated.
Please note that this is a specific case, in the general case I agree with previous answers.
回答9:
I had the same problem. This is how to solve: You need to use a static dictionary to subclass a singleton. For exemple:
Class A : NSObject -> Singleton
Class B : A
Class C : A
@implementation A
// Dictionary that holds all instances of API subclasses
static NSMutableDictionary *_sharedInstances = nil;
+ (instancetype)sharedInstance
{
id sharedInstance = nil;
@synchronized(self)
{
NSString *instanceClass = NSStringFromClass(self);
if (_sharedInstances == nil)
_sharedInstances = [NSMutableDictionary dictionary];
// Looking for existing instance
sharedInstance = [_sharedInstances objectForKey:instanceClass];
// If there's no instance – create one and add it to the dictionary
if (sharedInstance == nil)
{
sharedInstance = [[super allocWithZone:nil] init];
[_sharedInstances setObject:sharedInstance forKey:instanceClass];
}
}
return sharedInstance;
}
Now you can use [B sharedInstance] and [C sharedInstance] without problems!
回答10:
@interface SingletonObjC : NSObject
+ (id) sharedInstance;
@end
@implementation SingletonObjC
+ (id) sharedInstance
{
static dispatch_once_t pred;
static id sharedInstance = nil;
dispatch_once(&pred, ^{ sharedInstance = [[self alloc] init]; });
return sharedInstance;
}
@end
来源:https://stackoverflow.com/questions/3394033/whats-the-correct-method-to-subclass-a-singleton-class-in-objective-c