使用dispatch_once为类层次结构创建单例类

时间:2012-09-27 23:05:37

标签: objective-c multithreading singleton class-hierarchy

我有一个2个子课程,继承自“MyClass”和“每个儿童班应该是一个单身人士。

当我没有继承任何其他类时,我已经使用此模式获取静态实例:

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

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

    return instance;
}

这很有用。现在,如果我添加两个新的子类,FirstClass和SecondClass,它们都继承自MyClass,我如何确保返回相应的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

执行上述操作意味着我总是回到我实例化的第一类作为我的第二类类型:

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

在不向每个子类添加getInstance方法的情况下创建相应子类单例的最佳方法是什么?

3 个答案:

答案 0 :(得分:6)

除非你有充分的理由,否则通常应该避免对单例进行子类化。它造成了一个非常令人困惑的情况。如果您创建单身MyClass,那么您可以创建单身FirstClass吗?由于FirstClass必须始终可用MyClass(Liskov),因此现在有三个“单例”MyClass对象。现在ObjC对单身人士非常宽松,这是一件好事,但这仍然很奇怪。

好的,那说,你的问题怎么样?首先是解决方案,然后是答案。解决方案是MyClass可能不应该是单身,如上所述。摆脱超类中的getInstance并在子类中定义它。

正在发生的事情的答案在于dispatch_once。在所有情况下,您都传递相同的静态once令牌。对于给定的令牌,dispatch_once的运行次数不会超过 。解决这个问题的唯一方法是为每个类传递不同的标记,并且我不知道在不重复每个文件中的dispatch_once代码的情况下这样做的便捷方法。您可以尝试为每个子类创建不同的once令牌,但这可能比复制sharedInstance方法更麻烦和代码。

顺便说一句,不要叫它getInstance。 “获取”在ObjC中具有特殊含义,你并不是指这里,所以它令人困惑。这通常称为sharedInstance,或更好sharedSomething,其中“某事”是您的类。如果你的确意味着应该有一个MyClass并且应该有一个FirstClass并且应该有SecondClass,那么你可以在这三个中实现sharedInstance

答案 1 :(得分:1)

你知道单身人士是粗暴的,对吗?许多单身人士是一个更大的问题。警告结束。


您可以使用dispatch_once创建基本单例。那么你的基础可以保存其派生类型的地图(例如,字典{key:ClassName | value:Instance})。

您正在使用dispatch_once表示这将在多线程上下文中使用。在这种情况下,您需要使用互斥锁保护与字典的交互。

那么来自子类的消息将根据消息传递类(self)确定要查找的类(如果需要,创建该实例)。

答案 2 :(得分:-1)

在firstClass的id中使用myClass 像这样

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

我认为这应该有效