Создание одноэлементного класса с 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;
}
я думаю, что это должно работать