Cóż, łatwym sposobem obejścia init
jest po prostu nie pisać, aby wywołać domyślną implementację NSObject (która zwraca tylko self
). Następnie, dla swojej funkcji sharedInstance
, zdefiniuj i wywołaj prywatną funkcję, która wykonuje pracę podobną do init podczas tworzenia singletonu. (Pozwala to uniknąć przypadkowego ponownego zainicjowania Twojego singletonu przez użytkownika.)
Jednak !!! Główny problem polega na tym, że użytkownik alloc
jest wywoływany przez użytkownika twojego kodu! Do tego ja osobiście polecam trasę Apple z nadrzędnego allocWithZone:
...
+ (id)allocWithZone:(NSZone *)zone
{
return [[self sharedInstance] retain];
}
Oznacza to, że użytkownik będzie nadal uzyskać singleton instancji i mogą błędnie używać jak gdyby przydzielono go i bezpiecznie zwolnić go kiedyś od tego custom alloc wykonuje retain na singleton. (Uwaga: alloc
dzwoni allocWithZone:
i nie trzeba go oddzielnie zastępować.)
Nadzieję, że pomaga! Daj mi znać, jeśli chcesz uzyskać więcej informacji ~
EDIT: Rozszerzanie odpowiedź dostarczenie przykład i więcej szczegółów -
Biorąc odpowiedź Catfish_Man jest pod uwagę, że to często nie ważne, aby stworzyć kuloodporną Singleton, a zamiast tego po prostu napisz jakieś sensowne komentarze w nagłówkach/dokumentacji i umieść je w assert
.
Jednak w moim przypadku chciałem mieć bezpieczny wątek typu single-leniwy - to znaczy, że nie jest przydzielany do momentu użycia, zamiast automatycznego przydzielania przy uruchomieniu aplikacji. Po tym, jak nauczyłem się tego robić bezpiecznie, pomyślałem, że równie dobrze mogę z nim iść.
EDIT # 2: Teraz używam GCD na dispatch_once(...)
dla podejścia wątku bezpieczny alokacji singleton obiektu tylko raz na całe życie wniosku. Zobacz Apple Docs: GCD dispatch_once. Ja też jeszcze dodać allocWithZone:
override trochę od starego singleton przykład Apple, i dodał prywatny Init nazwie singletonInit
aby zapobiec przypadkowemu zwanych wiele razy:
//Hidden/Private initialization
-(void)singletonInit
{
//your init code goes here
}
static HSCloudManager * sharedInstance = nil;
+ (HSCloudManager *) sharedManager {
static dispatch_once_t dispatchOncePredicate = 0;
dispatch_once(&dispatchOncePredicate, ^{
sharedInstance = [[super allocWithZone:NULL] init];
[sharedInstance singletonInit];//Only place you should call singletonInit
});
return sharedInstance;
}
+ (id) allocWithZone:(NSZone *)zone {
//If coder misunderstands this is a singleton, behave properly with
// ref count +1 on alloc anyway, and still return singleton!
return [[HSCloudManager sharedManager] retain];
}
HSCloudManager
podklasy NSObject
i nie zastępują init
pozostawiając jedynie domyślne implementacja w NSObject
, która zgodnie z dokumentacją Apple zwraca tylko siebie. Oznacza to, że [[HSCloudManager alloc] init]
jest taki sam jak [[[HSCloud Manager sharedManager] retain] self]
, dzięki czemu jest bezpieczny zarówno dla zagmatwanych użytkowników, jak i aplikacji wielowątkowych, jako leniwy singleton.
Jeśli chodzi o twoje obawy dotyczące podklasowania twojego singletonu przez użytkownika, powiedziałbym po prostu komentarz/udokumentuj to wyraźnie. Każdy na ślepo podklasy bez czytania na klasie jest prosząc o za ból!
EDIT # 3: Dla kompatybilność ARC, po prostu usunąć część zatrzymywania z allocWithZone:
ręcznym, ale zachować nadpisanie.
@yAaak, wydaje się dość sprawiedliwy , ale czuję się jak w nieskończonej pętli i muszę to przetrawić;) Jak mogę zapewnić, że proces tworzenia takiego singletona przez prywatną metodę jest bezpieczny dla wątków? Inny problem: jeśli podążę za pomysłem Apple 'allocWithZone', zostanie wywołany domyślny' init' NSObject (czy po prostu zwraca 'self' lub robi coś innego?) ... i wtedy znowu użytkownik próbuje utworzyć instancję za pomocą' alloc' i ' init' czy zmienia coś o moich właściwościach/inicjalizacji ivars i NSObject pobierającym 'init' ponownie? – matm
YAak, masz rację: rozsądne podejście, które sugerujesz, na razie wystarcza. Będę grał z singletonami nieco bardziej przy użyciu twojego kodu :) Myślę, że twoja odpowiedź jest dość obszerna i bierze pod uwagę kontrpropozycję Catfish_man, więc akceptuję to. Dzięki jeszcze raz! – matm
Proponuję dispatch_once() zamiast OSAtomic *. Mniej wybredny, równie szybki lub szybszy. –