Edit: Cette implémentation obsolète avec ARC. Veuillez consulter Comment mettre en œuvre un singleton Objective-C compatible avec ARC? pour une mise en œuvre correcte.
Toutes les implémentations d'initialisation que j'ai lues dans d'autres réponses partagent une erreur commune.
+ (void) initialize {
_instance = [[MySingletonClass alloc] init] // <----- Wrong!
}
+ (void) initialize {
if (self == [MySingletonClass class]){ // <----- Correct!
_instance = [[MySingletonClass alloc] init]
}
}
La documentation Apple vous recommande de vérifier le type de classe dans votre bloc d'initialisation. Parce que les sous-classes appellent l'initialisation par défaut. Il existe un cas non évident où des sous-classes peuvent être créées indirectement via KVO. Car si vous ajoutez la ligne suivante dans une autre classe:
[[MySingletonClass getInstance] addObserver:self forKeyPath:@"foo" options:0 context:nil]
Objective-C créera implicitement une sous-classe de MySingletonClass entraînant un deuxième déclenchement de +initialize
.
Vous pouvez penser que vous devriez implicitement vérifier l'initialisation en double dans votre bloc init en tant que tel:
- (id) init { <----- Wrong!
if (_instance != nil) {
// Some hack
}
else {
// Do stuff
}
return self;
}
Mais vous vous tirerez une balle dans le pied; ou pire, donner à un autre développeur la possibilité de se tirer une balle dans le pied.
- (id) init { <----- Correct!
NSAssert(_instance == nil, @"Duplication initialization of singleton");
self = [super init];
if (self){
// Do stuff
}
return self;
}
TL; DR, voici mon implémentation
@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
(Remplacez ZAssert par notre propre macro d'assertion; ou simplement NSAssert.)