Создание одноэлементного класса с dispatch_once для класса hiearchy

У меня есть 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

Выполнение вышеизложенного означает, что я всегда получаю обратно тот класс, который я создал 1-м как мой тип второго класса:

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

Каков наилучший способ создания синглетонов соответствующих дочерних классов без добавления getInstance метод для каждого из дочерних классов?

3 ответа

Решение

Если у вас нет веских причин, вам следует избегать подклассов синглетонов. Это создает очень запутанную ситуацию. Если вы создаете синглтон MyClass Можете ли вы создать синглтон? FirstClass? поскольку FirstClass всегда должен использоваться везде MyClass можно использовать (по Лискову), сейчас есть три "синглтона" MyClass объекты. Теперь ObjC очень свободен с одиночками, и это хорошо, но это все еще очень странно.

Хорошо, что сказал, как насчет вашей проблемы? Сначала решение, потом ответ. Решение состоит в том, что MyClass вероятно, не должно быть синглтоном, как обсуждалось выше. Избавляться от getInstance в суперклассе и просто определить его в подклассах.

Ответ на то, что происходит в вашем dispatch_once, Вы проходите ту же статику once жетон во всех случаях. dispatch_once будет работать не более одного раза за данный токен. Единственный способ обойти это - передать разные токены для каждого класса, и я не знаю удобного способа сделать это без дублирования кода dispatch_once в каждом файле. Вы можете попробовать создать разные once токены для каждого подкласса, но это, вероятно, будет больше проблем и кода, чем просто дублирование sharedInstance метод.

Кстати, не называйте это getInstance, "get" имеет особое значение в ObjC, и вы не имеете в виду это здесь, так что это сбивает с толку. Это обычно называется sharedInstance, или лучше sharedSomething где "что-то" - твой класс Если вы действительно имеете в виду, что должно быть MyClass и там должно быть FirstClass и там должно быть SecondClass Вы можете реализовать sharedInstance во всех трех.

Вы знаете, синглтоны корявые, верно? много синглетонов - большая проблема. конец предостережения.


Вы можете просто использовать dispatch_once создать базовый синглтон. тогда ваша база может содержать карту (например, словарь {key:ClassName|value:Instance}) ее производных типов.

что вы используете dispatch_once предполагает, что это будет использоваться в многопоточном контексте. в этом случае вам нужно будет защищать взаимодействия со словарем, используя мьютекс.

тогда ваши сообщения из подклассов будут определяться на основе класса сообщений (self) какой класс искать (создавая этот экземпляр, если необходимо).

Использование id в вашем myClass в первом классе, как это

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

я думаю, что это должно работать

Другие вопросы по тегам