To, co masz, jest w porządku, ale możesz przenieść deklarację zmiennej globalnej do metody + wystąpienia (jedyne miejsce, w którym trzeba jej użyć, chyba że zezwalasz również na jej ustawienie) i użyj nazwy takiej jak + defaultMyClass lub + sharedMyClass dla Twojej metody. + instancja nie ujawnia intencji.
Chris Hanson,
Ponieważ jest mało prawdopodobne, że „odpowiedź” na to pytanie zmieni się w najbliższym czasie, kładę na to historyczną blokadę. Dwa powody 1) Dużo wyświetleń, głosów i dobra treść 2) Aby zapobiec jo-jo otwartych / zamkniętych. To było świetne pytanie jak na swoje czasy, ale pytania tego typu nie są odpowiednie dla przepełnienia stosu. Mamy teraz przegląd kodu do sprawdzania działającego kodu. Przenieś całą dyskusję na to pytanie do tego meta pytania .
George Stocker
Odpowiedzi:
207
Inną opcją jest użycie +(void)initializemetody. Z dokumentacji:
Środowisko wykonawcze wysyła initializedo każdej klasy w programie dokładnie jeden raz tuż przed klasą lub dowolną klasą, która z niej dziedziczy, wysyłana jest pierwsza wiadomość z poziomu programu. (W związku z tym nigdy nie można wywołać metody, jeśli klasa nie jest używana.) Środowisko wykonawcze wysyła initializekomunikat do klas w sposób bezpieczny dla wątków. Nadklasy otrzymują ten komunikat przed podklasami.
Jeśli środowisko wykonawcze wywoła to tylko raz, co robi BOOL? Czy jest to środek ostrożności na wypadek, gdyby ktoś wywołał tę funkcję wprost ze swojego kodu?
Aftermathew
5
Tak, jest to środek ostrożności, ponieważ funkcję można również wywołać bezpośrednio.
Robbie Hanson,
33
Jest to również wymagane, ponieważ mogą istnieć podklasy. Jeśli nie zastąpią +initializeswojej, nadklasy będą wywoływane, jeśli podklasa zostanie użyta po raz pierwszy.
Sven
3
@Paul możesz zastąpić releasemetodę i uczynić ją pustą. :)
4
@aryaxt: Z wymienionych dokumentów jest już bezpieczny wątek. Tak więc połączenie odbywa się raz na runtime - okres. Wydaje się, że jest to prawidłowe, bezpieczne dla wątków, optymalnie wydajne rozwiązanie.
To wszystko, czego zwykle powinieneś używać do singletonów. Między innymi, utrzymywanie osobnych klas w stanie natychmiastowej dostępności ułatwia ich testowanie, ponieważ możesz testować osobne instancje zamiast sposobu resetowania ich stanu.
Chris Hanson
3
Stig Brautaset: Nie, nie można pominąć @synchronized w tym przykładzie. Ma on za zadanie poradzić sobie z możliwym stanem wyścigu dwóch wątków wykonujących tę funkcję statyczną w tym samym czasie, oba omijając jednocześnie test „if (! SharedSingleton)”, co skutkuje dwoma [alokacjami MySingleton]. .. @synchronized {blok zakresu} zmusza ten hipotetyczny drugi wątek do czekania, aż pierwszy wątek opuści {blok zakresu}, zanim będzie mógł przejść do niego. Mam nadzieję, że to pomoże! =)
MechEthan
3
Co powstrzymuje kogoś przed tworzeniem własnego wystąpienia obiektu? MySingleton *s = [[MySingelton alloc] init];
lindon lis
1
@lindonfox Jaka jest odpowiedź na twoje pytanie?
Raffi Khatchadourian,
1
@Raffi - przepraszam, chyba chyba zapomniałem wkleić odpowiedź. W każdym razie mam książkę, Pro Objective-C Design Patterns for iOSktóra wyjaśnia, w jaki sposób tworzysz „ścisły” singelton. Zasadniczo, ponieważ nie można ustawić metod inicjujących jako prywatnych, należy zastąpić metody przydzielania i kopiowania. Więc jeśli spróbujesz zrobić coś takiego [[MySingelton alloc] init], otrzymasz błąd czasu wykonywania (choć niestety błąd czasu kompilacji). Nie rozumiem, w jaki sposób wszystkie szczegóły dotyczące tworzenia obiektu, ale + (id) allocWithZone:(NSZone *)zonesharedSingleton
wdrażasz
59
Według mojej innej odpowiedzi poniżej, myślę, że powinieneś zrobić:
Nie przejmuj się wszystkim, co robisz powyżej. Uczyń swoje (miejmy nadzieję bardzo nieliczne) singletony osobno możliwe do utworzenia i po prostu skorzystaj z metody współdzielonej / domyślnej. To, co zrobiłeś, jest konieczne tylko wtedy, gdy naprawdę, naprawdę, TYLKO chcesz jednego wystąpienia swojej klasy. Którego nie robisz, zwłaszcza. do testów jednostkowych.
Chris Hanson,
Chodzi o to, że jest to przykładowy kod Apple do „tworzenia singletonu”. Ale tak, masz absolutną rację.
Colin Barrett
1
Przykładowy kod Apple jest poprawny, jeśli chcesz „prawdziwego” singletonu (tj. Obiektu, który można utworzyć tylko raz, zawsze), ale jak mówi Chris, rzadko jest to, czego chcesz lub potrzebujesz, podczas gdy pewnego rodzaju ustawiana instancja współdzielona jest tym, czego potrzebujesz zwykle chcę.
Szybki przypadek: w normalnym wykonaniu sharedInstancezostała już ustawiona, więc whilepętla nigdy nie jest wykonywana, a funkcja powraca po zwykłym sprawdzeniu istnienia zmiennej;
Wolna sprawa: jeśli sharedInstancenie istnieje, to instancja jest przydzielana i kopiowana do niej za pomocą funkcji porównania i zamiany („CAS”);
Przypadek sporny: jeśli dwa wątki próbują zadzwonić sharedInstancew tym samym czasie ORAZsharedInstance nie istnieje w tym samym czasie, oba zainicjują nowe wystąpienia singletonu i podejmą próbę umieszczenia go w CAS. Cokolwiek wygrywa, CAS natychmiast zwraca, cokolwiek traci, zwalnia właśnie przydzieloną instancję i zwraca (teraz ustawione) sharedInstance. Singiel OSAtomicCompareAndSwapPtrBarrierdziała zarówno jako bariera zapisu dla wątku ustawiającego, jak i bariera odczytu dla wątku testowego.
Jest to całkowita nadwyżka, co najmniej raz, kiedy może się to zdarzyć podczas życia aplikacji. Niemniej jednak jest on precyzyjny, a technika porównywania i wymiany jest przydatnym narzędziem, o którym należy wiedzieć, więc +1.
Steve Madsen
Dobra odpowiedź - warto wiedzieć o rodzinie OSAtomic
Bill
1
@Louis: Niesamowita, naprawdę pouczająca odpowiedź! Jedno pytanie: co moja initmetoda powinna zrobić w twoim podejściu? sharedInstanceZgadzam się, że zgłoszenie wyjątku podczas inicjalizacji nie jest dobrym pomysłem. Co zatem zrobić, aby użytkownik nie dzwonił initwiele razy bezpośrednio?
matm
2
Na ogół nie przeszkadzam. Często istnieją uzasadnione powody, aby zezwolić na zwielokrotnianie instancji tego, co zwykle jest singletonem, najbardziej wspólne są dla niektórych rodzajów testów jednostkowych. Gdybym naprawdę chciał wymusić pojedynczą instancję, prawdopodobnie sprawdziłbym metodę init, aby sprawdzić, czy glob istnieje, a jeśli tak, to muszę go zwolnić i zwrócić globalny.
Zauważyłem, że brzęk skarży się na wyciek, jeśli nie przypiszesz wyniku [[self alloc] init]do shareInst.
pix0r
Takie podważanie init jest dość brzydkim podejściem IMO. Nie zadzieraj z init i / lub faktycznym stworzeniem obiektu. Jeśli zamiast tego zdecydujesz się na kontrolowany punkt dostępu do współużytkowanej instancji, nie będąc trudnym do wypalenia singletonem w obiekcie, będziesz miał więcej czasu później, jeśli będziesz pisać testy itp. Twarde singletony są zdecydowanie zbyt nadużywane.
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:
Mam ciekawą odmianę sharedInstance, która jest bezpieczna dla wątków, ale nie blokuje się po inicjalizacji. Nie jestem jeszcze wystarczająco pewny, aby zmodyfikować najwyższą odpowiedź zgodnie z żądaniem, ale przedstawiam ją do dalszej dyskusji:
// Volatile to make sure we are not foiled by CPU cachesstaticvolatileALBackendRequestManager*sharedInstance;// There's no need to call this directly, as method swizzling in sharedInstance// means this will get called after the singleton is initialized.+(MySingleton*)simpleSharedInstance
{return(MySingleton*)sharedInstance;}+(MySingleton*)sharedInstance
{@synchronized(self){if(sharedInstance == nil){
sharedInstance =[[MySingleton alloc] init];// Replace expensive thread-safe method // with the simpler one that just returns the allocated instance.
SEL origSel =@selector(sharedInstance);
SEL newSel =@selector(simpleSharedInstance);Method origMethod = class_getClassMethod(self, origSel);Method newMethod = class_getClassMethod(self, newSel);
method_exchangeImplementations(origMethod, newMethod);}}return(MySingleton*)sharedInstance;}
+1 to naprawdę intrygujące. Mógłbym użyć, class_replaceMethodby przekształcić sharedInstancesię w klon simpleSharedInstance. W ten sposób nie będziesz musiał martwić się o @synchronizedponowne uzyskanie blokady.
Dave DeLong,
To ten sam efekt, użycie exchangeImplementations oznacza, że po inicjacji, gdy wywołujesz sharedInstance, naprawdę wywołujesz simpleSharedInstance. Właściwie zacząłem od replaceMethod, ale zdecydowałem, że lepiej będzie po prostu zmieniać implementacje, aby oryginał istniał, jeśli zajdzie taka potrzeba ...
Kendall Helmstetter Gelner
W dalszych testach nie mogłem zmusić replaceMethod do pracy - w powtarzanych wywołaniach kod nadal wywoływał pierwotną sharedInstance zamiast simpleSharedInstance. Myślę, że może tak być, ponieważ oba są metodami na poziomie klasy ... Zamiennik, którego użyłem to: class_replaceMethod (self, origSel, method_getImplementation (newMethod), method_getTypeEncoding (newMethod)); i niektóre ich odmiany. Mogę zweryfikować kod, który opublikowałem, a simpleSharedInstance jest wywoływany po pierwszym przejściu przez sharedInstance.
Kendall Helmstetter Gelner
Możesz stworzyć wersję bezpieczną dla wątków, która nie będzie ponosić kosztów blokowania po inicjalizacji bez robienia mnóstwa runtime, opublikowałem poniżej implementację.
Louis Gerbarg,
1
+1 świetny pomysł. Uwielbiam te rzeczy, które można zrobić w środowisku uruchomieniowym. Ale w większości przypadków jest to prawdopodobnie przedwczesna optymalizacja. Gdybym naprawdę musiał pozbyć się kosztów synchronizacji, prawdopodobnie użyłbym wersji bez blokady Louis.
Przeczytaj nagłówek dispatch / once.h, aby zrozumieć, co się dzieje. W takim przypadku komentarze nagłówka są bardziej odpowiednie niż dokumenty lub strona podręcznika man.
Jedynym ograniczeniem dotyczącym klasy Singleton jest to, że jest to podklasa NSObject. Ale przez większość czasu używam singletonów w kodzie, w rzeczywistości są to podklasy NSObject, więc ta klasa naprawdę ułatwia mi życie i czyni kod czystszym.
Używam twojego NSSingleton do mojego projektu i wydaje się, że jest on niezgodny z KVO. Chodzi o to, że KVO tworzy podklasę dla każdego obiektu KVO z prefiksem NSKVONotifying_ MyClass . I sprawia, że MyClass + inicjuje i -init metody, które mają być wywoływane dwukrotnie.
Oleg Trakhman
Przetestowałem to na najnowszym Xcode i nie miałem żadnych problemów z rejestracją lub odbieraniem wydarzeń KVO. Możesz to sprawdzić za pomocą następującego kodu: gist.github.com/3065038 Jak wspomniałem na Twitterze, metody inicjalizacji + są wywoływane raz dla NSSingleton i raz dla każdej podklasy. Jest to właściwość Objective-C.
kevinlawler
Jeśli dodasz NSLog(@"initialize: %@", NSStringFromClass([self class]));do +initializemetody, możesz sprawdzić, czy klasy są inicjowane tylko raz.
Nie chcesz synchronizować na sobie ... Ponieważ obiekt self jeszcze nie istnieje! W rezultacie blokujesz tymczasową wartość identyfikatora. Chcesz mieć pewność, że nikt inny nie będzie mógł uruchamiać metod klasowych (sharedInstance, alokacja, alokacjaWithZone: itd.), Więc zamiast tego musisz zsynchronizować obiekt klasy:
@implementationMYSingletonstaticMYSingleton* sharedInstance = nil;+( id )sharedInstance {@synchronized([MYSingletonclass]){if( sharedInstance == nil )
sharedInstance =[[MYSingleton alloc ] init ];}return sharedInstance;}+( id )allocWithZone:(NSZone*)zone {@synchronized([MYSingletonclass]){if( sharedInstance == nil )
sharedInstance =[ super allocWithZone:zone ];}return sharedInstance;}-( id )init {@synchronized([MYSingletonclass]){
self =[ super init ];if( self != nil ){// Insert initialization code here}return self;}}@end
Pozostałe metody, metody dostępu, metody mutatora itp. Powinny synchronizować się na sobie. Wszystkie metody i inicjalizatory klasy (+) (i prawdopodobnie -dealloc) powinny zsynchronizować się z obiektem klasy. Możesz uniknąć konieczności ręcznej synchronizacji, jeśli używasz właściwości Objective-C 2.0 zamiast metod accessor / mutator. Wszystkie object.property i object.property = foo są automatycznie synchronizowane z samym sobą.
Rob Dotson
3
Wyjaśnij, dlaczego uważasz, że selfobiekt nie istnieje w metodzie klasy. Środowisko wykonawcze określa, która implementacja metody ma zostać wywołana na podstawie dokładnie takiej samej wartości, jaką podaje selfdla każdej metody (klasy lub instancji).
dreamlax
2
Wewnątrz metody klasy selfznajduje się obiekt klasy. Spróbuj sam:#import <Foundation/Foundation.h> @interface Eggbert : NSObject + (BOOL) selfIsClassObject; @end @implementation Eggbert + (BOOL) selfIsClassObject { return self == [Eggbert class]; } @end int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSLog(@"%@", [Eggbert selfIsClassObject] ? @"YES" : @"NO"); [pool drain]; return 0; }
jscs 27.09.11
0
Chciałem to zostawić tutaj, żeby tego nie zgubić. Zaletą tego jest to, że można go używać w InterfaceBuilder, co jest OGROMNĄ zaletą. To pochodzi z innego pytania, które zadałem :
Wiem, że jest wiele komentarzy na temat tego „pytania”, ale nie widzę wielu osób sugerujących użycie makra do zdefiniowania singletonu. To taki powszechny wzorzec, a makro znacznie upraszcza singleton.
Oto makra, które napisałem na podstawie kilku implementacji Objc, które widziałem.
Singeton.h
/**
@abstract Helps define the interface of a singleton.
@param TYPE The type of this singleton.
@param NAME The name of the singleton accessor. Must match the name used in the implementation.
@discussion
Typcially the NAME is something like 'sharedThing' where 'Thing' is the prefix-removed type name of the class.
*/#defineSingletonInterface(TYPE, NAME) \
+(TYPE *)NAME;/**
@abstract Helps define the implementation of a singleton.
@param TYPE The type of this singleton.
@param NAME The name of the singleton accessor. Must match the name used in the interface.
@discussion
Typcially the NAME is something like 'sharedThing' where 'Thing' is the prefix-removed type name of the class.
*/#defineSingletonImplementation(TYPE, NAME) \
static TYPE *__ ## NAME; \
\
\
+(void)initialize \
{ \
static BOOL initialized = NO; \
if(!initialized) \
{ \
initialized = YES; \
__ ## NAME = [[TYPE alloc] init]; \} \
} \
\
\
+(TYPE *)NAME \
{ \
return __ ## NAME; \}
Po co makro interfejsu, gdy jest prawie puste? Spójność kodu między nagłówkiem a plikami kodu; łatwość konserwacji w przypadku, gdy chcesz dodać więcej automatycznych metod lub zmienić je.
Korzystam z metody inicjalizacji, aby utworzyć singleton, tak jak jest to stosowane w najpopularniejszej odpowiedzi tutaj (w momencie pisania).
Dzięki metodom klasy Objective C możemy po prostu uniknąć używania wzorca singletonu w zwykły sposób, z:
[[Librarian sharedInstance] openLibrary]
do:
[Librarian openLibrary]
przez zawinięcie klasy w inną klasę, która ma tylko metody klasowe , w ten sposób nie ma szansy na przypadkowe utworzenie duplikatów, ponieważ nie tworzymy żadnej!
Jeśli singleton jest już zainicjowany, blok LOCK nie zostanie wprowadzony. Drugim sprawdzeniem, czy (! Zainicjowano) jest upewnienie się, że nie jest jeszcze inicjalizowane, gdy bieżący wątek nabywa LOCK.
Zwykle używam kodu z grubsza podobnego do tego w odpowiedzi Bena Hoffsteina (który również dostałem z Wikipedii). Używam go z powodów podanych przez Chrisa Hansona w jego komentarzu.
Czasami jednak muszę umieścić singletona w NIB, a w takim przypadku używam:
Twój kod nie jest bezpieczny dla wątków. Używa zsynchronizowanego w metodzie alokacji, ale nie w metodzie init. Sprawdzanie zainicjowanego boola nie jest bezpieczne dla wątków.
Mecki
-5
Przyjęta odpowiedź, choć się kompiluje, jest niepoprawna.
+(MySingleton*)sharedInstance
{@synchronized(self)<-------- self does not exist at class scope
{if(sharedInstance == nil)
sharedInstance =[[MySingleton alloc] init];}return sharedInstance;}
Dokumentacja według Apple:
... Możesz zastosować podobne podejście, aby zsynchronizować metody klas powiązanych klas, używając obiektu Class zamiast self.
Nawet jeśli korzystam z self działa, nie powinno, a to wygląda na błąd kopiowania i wklejania. Prawidłowa implementacja metody klasy fabryki to:
samo z całą pewnością nie istnieje to zakres klasy. Odnosi się do klasy zamiast do instancji klasy. Klasy są (głównie) obiektami pierwszej klasy.
schwa
Dlaczego umieścisz @synchroninzowany W metodzie?
user4951
1
Schwa jak już wspomniałem, selfjest wewnątrz klasy obiektu z metody klasy. Zobacz mój komentarz do krótkiego fragmentu, który to pokazuje.
jscs
selfistnieje, ale użycie go jako przekazanego identyfikatora @synchronizedzsynchronizuje dostęp do metod instancji. Jak wskazuje @ user490696, istnieją przypadki (takie jak singletony), w których preferowane jest użycie obiektu klasy. Z Przewodnika po programowaniu Obj-C:You can take a similar approach to synchronize the class methods of the associated class, using the class object instead of self. In the latter case, of course, only one thread at a time is allowed to execute a class method because there is only one class object that is shared by all callers.
Odpowiedzi:
Inną opcją jest użycie
+(void)initialize
metody. Z dokumentacji:Możesz więc zrobić coś podobnego do tego:
źródło
+initialize
swojej, nadklasy będą wywoływane, jeśli podklasa zostanie użyta po raz pierwszy.release
metodę i uczynić ją pustą. :)[Źródło]
źródło
MySingleton *s = [[MySingelton alloc] init];
Pro Objective-C Design Patterns for iOS
która wyjaśnia, w jaki sposób tworzysz „ścisły” singelton. Zasadniczo, ponieważ nie można ustawić metod inicjujących jako prywatnych, należy zastąpić metody przydzielania i kopiowania. Więc jeśli spróbujesz zrobić coś takiego[[MySingelton alloc] init]
, otrzymasz błąd czasu wykonywania (choć niestety błąd czasu kompilacji). Nie rozumiem, w jaki sposób wszystkie szczegóły dotyczące tworzenia obiektu, ale+ (id) allocWithZone:(NSZone *)zone
sharedSingleton
Według mojej innej odpowiedzi poniżej, myślę, że powinieneś zrobić:
źródło
Ponieważ Kendall opublikował bezpieczny wątek, który próbuje uniknąć kosztów blokowania, pomyślałem, że też go podrzucę:
OK, pozwól mi wyjaśnić, jak to działa:
Szybki przypadek: w normalnym wykonaniu
sharedInstance
została już ustawiona, więcwhile
pętla nigdy nie jest wykonywana, a funkcja powraca po zwykłym sprawdzeniu istnienia zmiennej;Wolna sprawa: jeśli
sharedInstance
nie istnieje, to instancja jest przydzielana i kopiowana do niej za pomocą funkcji porównania i zamiany („CAS”);Przypadek sporny: jeśli dwa wątki próbują zadzwonić
sharedInstance
w tym samym czasie ORAZsharedInstance
nie istnieje w tym samym czasie, oba zainicjują nowe wystąpienia singletonu i podejmą próbę umieszczenia go w CAS. Cokolwiek wygrywa, CAS natychmiast zwraca, cokolwiek traci, zwalnia właśnie przydzieloną instancję i zwraca (teraz ustawione)sharedInstance
. SingielOSAtomicCompareAndSwapPtrBarrier
działa zarówno jako bariera zapisu dla wątku ustawiającego, jak i bariera odczytu dla wątku testowego.źródło
init
metoda powinna zrobić w twoim podejściu?sharedInstance
Zgadzam się, że zgłoszenie wyjątku podczas inicjalizacji nie jest dobrym pomysłem. Co zatem zrobić, aby użytkownik nie dzwoniłinit
wiele razy bezpośrednio?źródło
[[self alloc] init]
do shareInst.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.
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:
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:
Ale zastrzelicie się w stopę; lub, co gorsza, dać innemu deweloperowi możliwość zastrzelenia się w stopę.
TL; DR, oto moja implementacja
(Zamień ZAssert na własne makro asercji; lub po prostu NSAssert.)
źródło
Dokładne wyjaśnienie kodu makr Singleton znajduje się na blogu Cocoa With Love
http://cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html .
źródło
Mam ciekawą odmianę sharedInstance, która jest bezpieczna dla wątków, ale nie blokuje się po inicjalizacji. Nie jestem jeszcze wystarczająco pewny, aby zmodyfikować najwyższą odpowiedź zgodnie z żądaniem, ale przedstawiam ją do dalszej dyskusji:
źródło
class_replaceMethod
by przekształcićsharedInstance
się w klonsimpleSharedInstance
. W ten sposób nie będziesz musiał martwić się o@synchronized
ponowne uzyskanie blokady.Krótka odpowiedź: fantastycznie.
Długa odpowiedź: coś w stylu ....
Przeczytaj nagłówek dispatch / once.h, aby zrozumieć, co się dzieje. W takim przypadku komentarze nagłówka są bardziej odpowiednie niż dokumenty lub strona podręcznika man.
źródło
Przekształciłem singletona w klasę, aby inne klasy mogły dziedziczyć właściwości singletonu.
Singleton.h:
Singleton.m:
A oto przykład jakiejś klasy, w której chcesz zostać singlem.
Jedynym ograniczeniem dotyczącym klasy Singleton jest to, że jest to podklasa NSObject. Ale przez większość czasu używam singletonów w kodzie, w rzeczywistości są to podklasy NSObject, więc ta klasa naprawdę ułatwia mi życie i czyni kod czystszym.
źródło
@synchronized
jest on strasznie wolny i należy go unikać.Działa to również w środowisku bez śmieci.
źródło
Czy nie powinno to być bezpieczne dla wątków i uniknąć kosztownego blokowania po pierwszym połączeniu?
źródło
Oto makro, które zestawiłem:
http://github.com/cjhanson/Objective-C-Optimized-Singleton
Opiera się na pracy Matta Gallaghera. Ale zmiana implementacji na użycie metody swizzling, jak opisano tutaj przez Dave'a MacLachlana z Google .
Z zadowoleniem przyjmuję komentarze / uwagi.
źródło
Co powiesz na
Więc unikasz kosztów synchronizacji po inicjalizacji?
źródło
Aby uzyskać dogłębną dyskusję na temat wzoru singletonu w celu C, zajrzyj tutaj:
Używanie wzorca singletonu w celu C
źródło
KLSingleton
źródło
NSLog(@"initialize: %@", NSStringFromClass([self class]));
do+initialize
metody, możesz sprawdzić, czy klasy są inicjowane tylko raz.Nie chcesz synchronizować na sobie ... Ponieważ obiekt self jeszcze nie istnieje! W rezultacie blokujesz tymczasową wartość identyfikatora. Chcesz mieć pewność, że nikt inny nie będzie mógł uruchamiać metod klasowych (sharedInstance, alokacja, alokacjaWithZone: itd.), Więc zamiast tego musisz zsynchronizować obiekt klasy:
źródło
self
obiekt nie istnieje w metodzie klasy. Środowisko wykonawcze określa, która implementacja metody ma zostać wywołana na podstawie dokładnie takiej samej wartości, jaką podajeself
dla każdej metody (klasy lub instancji).self
znajduje się obiekt klasy. Spróbuj sam:#import <Foundation/Foundation.h> @interface Eggbert : NSObject + (BOOL) selfIsClassObject; @end @implementation Eggbert + (BOOL) selfIsClassObject { return self == [Eggbert class]; } @end int main (int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSLog(@"%@", [Eggbert selfIsClassObject] ? @"YES" : @"NO"); [pool drain]; return 0; }
Chciałem to zostawić tutaj, żeby tego nie zgubić. Zaletą tego jest to, że można go używać w InterfaceBuilder, co jest OGROMNĄ zaletą. To pochodzi z innego pytania, które zadałem :
źródło
źródło
Wiem, że jest wiele komentarzy na temat tego „pytania”, ale nie widzę wielu osób sugerujących użycie makra do zdefiniowania singletonu. To taki powszechny wzorzec, a makro znacznie upraszcza singleton.
Oto makra, które napisałem na podstawie kilku implementacji Objc, które widziałem.
Singeton.h
Przykład zastosowania:
MyManager.h
MyManager.m
Po co makro interfejsu, gdy jest prawie puste? Spójność kodu między nagłówkiem a plikami kodu; łatwość konserwacji w przypadku, gdy chcesz dodać więcej automatycznych metod lub zmienić je.
Korzystam z metody inicjalizacji, aby utworzyć singleton, tak jak jest to stosowane w najpopularniejszej odpowiedzi tutaj (w momencie pisania).
źródło
Dzięki metodom klasy Objective C możemy po prostu uniknąć używania wzorca singletonu w zwykły sposób, z:
do:
przez zawinięcie klasy w inną klasę, która ma tylko metody klasowe , w ten sposób nie ma szansy na przypadkowe utworzenie duplikatów, ponieważ nie tworzymy żadnej!
Bardziej szczegółowy blog napisałem tutaj :)
źródło
Aby rozszerzyć przykład z @ robbie-hanson ...
źródło
Moja droga jest prosta:
Jeśli singleton jest już zainicjowany, blok LOCK nie zostanie wprowadzony. Drugim sprawdzeniem, czy (! Zainicjowano) jest upewnienie się, że nie jest jeszcze inicjalizowane, gdy bieżący wątek nabywa LOCK.
źródło
initialized
jakovolatile
wystarczające. Zobacz aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf .Nie przeczytałem wszystkich rozwiązań, więc wybacz, jeśli ten kod jest zbędny.
To moim zdaniem najbardziej bezpieczna implementacja wątków.
źródło
Zwykle używam kodu z grubsza podobnego do tego w odpowiedzi Bena Hoffsteina (który również dostałem z Wikipedii). Używam go z powodów podanych przez Chrisa Hansona w jego komentarzu.
Czasami jednak muszę umieścić singletona w NIB, a w takim przypadku używam:
Implementację
-retain
(itp.) Pozostawiam czytelnikowi, chociaż powyższy kod jest wszystkim, czego potrzebujesz w środowisku śmieci.źródło
Przyjęta odpowiedź, choć się kompiluje, jest niepoprawna.
Dokumentacja według Apple:
... Możesz zastosować podobne podejście, aby zsynchronizować metody klas powiązanych klas, używając obiektu Class zamiast self.
Nawet jeśli korzystam z self działa, nie powinno, a to wygląda na błąd kopiowania i wklejania. Prawidłowa implementacja metody klasy fabryki to:
źródło
self
jest wewnątrz klasy obiektu z metody klasy. Zobacz mój komentarz do krótkiego fragmentu, który to pokazuje.self
istnieje, ale użycie go jako przekazanego identyfikatora@synchronized
zsynchronizuje dostęp do metod instancji. Jak wskazuje @ user490696, istnieją przypadki (takie jak singletony), w których preferowane jest użycie obiektu klasy. Z Przewodnika po programowaniu Obj-C:You can take a similar approach to synchronize the class methods of the associated class, using the class object instead of self. In the latter case, of course, only one thread at a time is allowed to execute a class method because there is only one class object that is shared by all callers.