Edycja: Ta implementacja jest przestarzała z ARC. Zobacz, jak zaimplementować singleton Objective-C zgodny z ARC? dla prawidłowego wdrożenia.
Wszystkie implementacje inicjalizacji, które przeczytałem w innych odpowiedziach, mają wspólny błąd.
+ (void) initialize {
_instance = [[MySingletonClass alloc] init] // <----- Wrong!
}
+ (void) initialize {
if (self == [MySingletonClass class]){ // <----- Correct!
_instance = [[MySingletonClass alloc] init]
}
}
Dokumentacja Apple zaleca sprawdzenie typu klasy w bloku inicjalizacji. Ponieważ podklasy domyślnie wywołują inicjalizację. Istnieje nieoczywisty przypadek, w którym podklasy można tworzyć pośrednio za pośrednictwem KVO. Bo jeśli dodasz następujący wiersz w innej klasie:
[[MySingletonClass getInstance] addObserver:self forKeyPath:@"foo" options:0 context:nil]
Cel C domyślnie utworzy podklasę MySingletonClass, co spowoduje drugie wyzwolenie +initialize
.
Możesz pomyśleć, że niejawnie powinieneś sprawdzić duplikat inicjalizacji w swoim bloku init jako taki:
- (id) init { <----- Wrong!
if (_instance != nil) {
// Some hack
}
else {
// Do stuff
}
return self;
}
Ale zastrzelicie się w stopę; lub, co gorsza, dać innemu deweloperowi możliwość zastrzelenia się w stopę.
- (id) init { <----- Correct!
NSAssert(_instance == nil, @"Duplication initialization of singleton");
self = [super init];
if (self){
// Do stuff
}
return self;
}
TL; DR, oto moja implementacja
@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
(Zamień ZAssert na własne makro asercji; lub po prostu NSAssert.)