Edit: Esta implementação obsoleta com o ARC. Veja como implementar um singleton Objective-C que seja compatível com o ARC? para implementação correta.
Todas as implementações de inicialização que eu li em outras respostas compartilham um erro comum.
+ (void) initialize {
_instance = [[MySingletonClass alloc] init] // <----- Wrong!
}
+ (void) initialize {
if (self == [MySingletonClass class]){ // <----- Correct!
_instance = [[MySingletonClass alloc] init]
}
}
A documentação da Apple recomenda que você verifique o tipo de classe no seu bloco de inicialização. Porque as subclasses chamam a inicialização por padrão. Existe um caso não óbvio em que subclasses podem ser criadas indiretamente através do KVO. Pois se você adicionar a seguinte linha em outra classe:
[[MySingletonClass getInstance] addObserver:self forKeyPath:@"foo" options:0 context:nil]
O Objective-C criará implicitamente uma subclasse de MySingletonClass, resultando em um segundo disparo de +initialize
.
Você pode pensar que deve verificar implicitamente a inicialização duplicada no seu bloco init, como tal:
- (id) init { <----- Wrong!
if (_instance != nil) {
// Some hack
}
else {
// Do stuff
}
return self;
}
Mas você vai dar um tiro no próprio pé; ou pior, dê a outro desenvolvedor a oportunidade de dar um tiro no próprio pé.
- (id) init { <----- Correct!
NSAssert(_instance == nil, @"Duplication initialization of singleton");
self = [super init];
if (self){
// Do stuff
}
return self;
}
TL; DR, aqui está minha implementação
@implementation MySingletonClass
static MySingletonClass * _instance;
+ (void) initialize {
if (self == [MySingletonClass class]){
_instance = [[MySingletonClass alloc] init];
}
}
- (id) init {
ZAssert (_instance == nil, @"Duplication initialization of singleton");
self = [super init];
if (self) {
// Initialization
}
return self;
}
+ (id) getInstance {
return _instance;
}
@end
(Substitua o ZAssert por nossa própria macro de asserção; ou apenas NSAssert.)