在Objective -C中子类化单例类的正确方法是什么?

时间:2010-08-03 06:25:01

标签: objective-c singleton

我创建了一个单例类,我想创建一个类,这个类是这个单例类的子类,正确的方法是什么

10 个答案:

答案 0 :(得分:20)

我特别不了解Objective-C,但一般来说,单例类应该阻止子类化。如果你有一个基类的实例一个子类的实例,那么你实际上有两个对象可以看作是基础“单例”类的实例,不是吗?

只要你有两个实例,它就不再是一个单独的实例了......而这就是抛弃多个子类的可能性,或者子类本身允许创建多个实例。

当然你可以改变你的基类,这样它就可以获得一个单独的“默认”实例,但这与使它成为一个单例并不完全相同。

答案 1 :(得分:13)

如果Jon没有说服你不这样做,你应该这样做:

在你的超类中,使用[[[self class] alloc] init]初始化你的单例实例,这样你就可以得到一个用于调用sharedInstance方法的类的实例。而且您不必覆盖子类中的sharedInstance方法。

 [SuperClass sharedInstance] //-> instance of SuperClass
 [SubClass sharedInstance] //-> instance of Class

答案 2 :(得分:8)

我为单身人士做了一个“基类”的例子,你可以在这里查看:https://github.com/stel/DOSingleton

答案 3 :(得分:6)

如果允许你实例化类及其子类,那么Jon Skeet就是否真的有一个单例是一个很好的观点。把它放在一边,这是一个你可以使用的模式,所以你只需要在父类中定义一次共享实例的getter:

// this code goes in the implementation of the superclass

static Sprocket *defaultSprocket;

+ (instancetype) defaultSprocket
{
    if (defaultSprocket == nil)
        defaultSprocket = [[[self class] alloc] init];
    return defaultSprocket;
}

这种方法具有以下优点:

  • 使用[self class]允许例如[SprocketSubclass defaultSprocket]返回SprocketSubclass而不是Sprocket
  • 的实例
  • 使用instancetype允许编译器对此方法的结果进行类型检查:当您调用Sprocket时调用它+[Sprocket defaultSprocket],但调用SprocketSubclass时调用+[SprocketSubclass defaultSprocket]它为{{1}}。

值得注意的是,您可以在基类中定义此访问器方法,然后您不必在子类中执行任何操作!

(给explaining why instancetype is so cool的NSHipster和reminding me of it recently的bbum提示。)

答案 4 :(得分:3)

如果您正在寻找的是设置新单身人士的快捷方式。这个伪抽象单例基类是我使用的:

可重复使用的基类

<强>ħ

#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

<强>中号

@implementation SharedObject
+ (instancetype)sharedInstance {
    [NSException raise:@"Call to unimplemented sharedInstance" format:@"%@ does not implement sharedInstance.", NSStringFromClass([self class])];
    return nil;
}
@end

然后是每个子类

<强>ħ

#import "SharedObject.h" 
@interface SomeSubclass : SharedObject
@end

<强>中号

@implementation SomeSubclass
CREATE_SHARED_INSTANCE 
@end

......和任何单身人士一样使用。

[[SomesSubclass SharedInstance] someMethod];

如果调用抽象基类,或者忘记在子类中包含CREATE_SHARED_INSTANCE,则会引发一个友好的异常。

通过这种方式,您可以在不受性能影响的情况下轻松设置新的单身人士。

答案 5 :(得分:1)

实现这一目标的最简单方法是在类和子类中实现标准的单例访问器。这样每个类的行为都是一个合适的单例,也就是说只有两个实例。如果您尝试在子类中重用父类的访问器,然后如果您使用这两个类,则存在访问者返回错误实例的风险,因为它们的行为将取决于它们的访问顺序。 / p>

您不应该使用instancetype作为单例访问器来帮助防止此错误。您会注意到Apple不会将它用于单身人士,例如UIApplication和CKContainer。

如果您希望访问超类的单例方法的现有代码被赋予子类的实例,那么您可能需要重新设计,请参阅MrJre的答案。

答案 6 :(得分:0)

我遇到了类似的问题,我有多个目标需要稍微不同的单例实现:每个目标都包含基类+特定的子类。这是通过编写基类来实现的:

+ (SingletonBaseClass*) sharedInstance {
    static SingletonBaseClass * sharedInstance = nil;
    if (!sharedInstance) {
        sharedInstance = [[[self class] alloc] init];
        [sharedInstance customInit];
    }
    return sharedInstance;
}

关键区别是[self class]而不是实际的班级名称。这样,当我们调用:[SingletonSubclass sharedInstance]时,实例化正确的对象。

请注意,这是一个具体案例,在一般情况下我同意以前的答案。

答案 7 :(得分:0)

我有一个类似的问题,我解决它的方法是创建一个单独的包装类,它具有所有额外的功能。这个单例类包含原始单例(将单例实例作为成员变量)。这样你可以避免肮脏的伎俩。

答案 8 :(得分:0)

我遇到了同样的问题。这是如何解决的:您需要使用静态字典来对单例进行子类化。例如:

A类:NSObject - &gt;单

B组:A

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;

}

现在您可以毫无问题地使用[B sharedInstance]和[C sharedInstance]!

答案 9 :(得分:-4)

@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