Implementação multiton elegante e 'correta' no Objetivo C?
-
22-09-2019 - |
Pergunta
Você chamaria essa implementação de um multiton no objetivo-c de 'elegante'?Eu 'proibido' programaticamente o uso de alloc
e allocWithZone:
porque a decisão de alocar ou não memória precisa ser feita com base em uma chave.
Tenho certeza de que preciso trabalhar com apenas duas instâncias, então estou usando 'switch-case' em vez de um mapa.
#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:Ainda não testei se isso funciona, mas está compilando de forma limpa :)
Solução
Acho os solteiros uma má ideia e isso parece quatro vezes mais horrível.O código é bastante complexo, você pode ter certeza de que gastará algumas horas perseguindo bugs sutis nele e provavelmente nunca se sentirá confortável com isso.Isso não é bom.Você deveria jogar fora essa abominação e conectar seus objetos de alguma outra maneira que não exija tanta reflexão.
Se você gosta de padrões, você pode usar algo parecido com o padrão Factory para conectar seus objetos.A Fábrica se encarregará de criar essas duas instâncias e passá-las sempre que necessário.E o Factory será muito mais simples que o Multiton:
@interface Factory : NSObject {
Foo *foo1, *foo2;
}
@end
@implementation Factory
- (id) init {
[super init];
foo1 = [[Foo alloc] init];
foo2 = [[Foo alloc] init];
return self;
}
Claro que você não precisa criar as duas instâncias ao mesmo tempo.Você pode fazer o que quiser lá – cache, carregamento lento, qualquer coisa.A questão é deixar o Foo
gerenciamento vitalício até a Fábrica, separado do Foo
código.Então fica muito mais fácil.¶ Todos os outros objetos que necessitam Foo
serão criados e conectados pela Fábrica e receberão seus Foo
através de um setter:
@implementation Factory
- (id) wireSomeClass {
id instance = [[SomeClass alloc] init];
[instance setFoo:foo1];
[instance setAnotherDependency:bar];
return [instance autorelease];
}
Tudo isso é muito mais simples que o código da sua pergunta.
Outras dicas
Não substitua o aloc. O problema de substituir o Alloc para retornar uma instância alocada anteriormente da classe, como você faz, é que, quando +SharedInstance chama [[Multiton alloC] init] ... +aloc retornará a instância antiga, então -Init irá reinicializar-o! A prática recomendada é substituir -init, fazendo a pesquisa de cache e chamando [auto -lançamento] antes de retornar a instância em cache.
Se vocês são verdade Preocupado com o custo desse Alloc extra +(não é muito), você também faz sua pesquisa de cache no +compartilhamento e, em seguida, garante que todos os seus clientes acessem a instância através do +SharedInstance para evitar o Allow Extra.
Ponto de ordem: Como você sabe que você só terá duas instâncias ou precisará ter duas instâncias? (Ou quer ter duas instâncias?) Qual é exatamente o ponto de ter um "multiton"? (E isso é mesmo uma palavra?)