Элегантная и "правильная" многотонная реализация в Objective C?
Вы бы назвали эту реализацию мультитона в target-c "элегантной"? Я программно "запретил" использование alloc
а также allocWithZone:
потому что решение о выделении или не выделении памяти должно приниматься на основе ключа.
Я точно знаю, что мне нужно работать только с двумя экземплярами, поэтому вместо карты я использую 'switch-case'.
#import "Multiton.h"
static Multiton *firstInstance = nil;
static Multiton *secondInstance = nil;
@implementation Multiton
+ (Multiton *) sharedInstanceForDirection:(enum KeyName)direction {
return [[self allocWithKey:direction] init];
}
+ (id) allocWithKey:(enum KeyName)key {
return [self allocWithZone:nil andKey:key];
}
+ (id) allocWithZone:(NSZone *)zone andKey:(enum KeyName)key {
Multiton **sharedInstance;
@synchronized(self) {
switch (key) {
case KEY_1:
sharedInstance = &firstInstance;
break;
case KEY_2:
sharedInstance = &secondInstance;
break;
default:
[NSException raise:NSInvalidArgumentException format:@"Invalid key"];
break;
}
if (*sharedInstance == nil)
*sharedInstance = [super allocWithZone:zone];
}
return *sharedInstance;
}
+ (id) allocWithZone:(NSZone *)zone {
//Do not allow use of alloc and allocWithZone
[NSException raise:NSObjectInaccessibleException format:@"Use allocWithZone:andKey: or allocWithKey:"];
return nil;
}
- (id) copyWithZone:(NSZone *)zone {
return self;
}
- (id) retain {
return self;
}
- (unsigned) retainCount {
return NSUIntegerMax;
}
- (void) release {
return;
}
- (id) autorelease {
return self;
}
- (id) init {
[super init];
return self;
}
@end
PS: я не пробовал, если это работает, но его компиляция чисто:)
3 ответа
Я считаю синглеты плохой идеей, и это выглядит примерно в четыре раза ужаснее. Код довольно сложный, вы можете быть уверены, что потратите немало часов на то, чтобы отследить тонкие ошибки в нем, и вы, вероятно, никогда не будете чувствовать себя комфортно. Это не хорошо. Вы должны выбросить эту мерзость и соединить свои объекты другим способом, который не требует большого количества размышлений.
Если вам нравятся шаблоны, вы можете использовать что-то похожее на шаблон Factory для привязки ваших объектов. Фабрика позаботится о создании этих двух экземпляров и передаче их по мере необходимости. И Фабрика будет намного проще, чем Multiton:
@interface Factory : NSObject {
Foo *foo1, *foo2;
}
@end
@implementation Factory
- (id) init {
[super init];
foo1 = [[Foo alloc] init];
foo2 = [[Foo alloc] init];
return self;
}
Конечно, вам не нужно создавать оба экземпляра одновременно. Там вы можете делать все что угодно - кеш, ленивая загрузка, все что угодно. Дело в том, чтобы оставить Foo
управление сроком службы до завода, отдельно от Foo
код. Тогда это становится намного проще. ¶ Все остальные предметы, которые нужны Foo
будет создан и подключен через фабрику и получит их Foo
через сеттер:
@implementation Factory
- (id) wireSomeClass {
id instance = [[SomeClass alloc] init];
[instance setFoo:foo1];
[instance setAnotherDependency:bar];
return [instance autorelease];
}
Это все гораздо проще, чем код из вашего вопроса.
Не переопределяйте alloc. Проблема с переопределением alloc для возврата ранее выделенного экземпляра класса, как вы делаете, заключается в том, что когда +sharedInstance вызывает [[Multiton alloc] init]... +alloc вернет старый экземпляр, тогда -init будет повторно инициализирован Это! Лучше всего переопределить -init, выполнить поиск в кэше и вызвать [self release] перед возвратом кэшированного экземпляра.
Если вы действительно обеспокоены стоимостью этого дополнительного +alloc (это не так много), вы также выполняете поиск в кэше в +sharedInstance и затем гарантируете, что все ваши клиенты обращаются к экземпляру через +sharedInstance, чтобы избежать дополнительного выделения.
Точка порядка: откуда вы знаете, что у вас будет только два экземпляра или вам понадобится два экземпляра? (Или хотите иметь два экземпляра?) Какой смысл иметь "Мультитон"? (И это даже слово?)