2012-09-27 8 views
8

Mam 2 klasy potomne, które dziedziczą po "MyClass", a każda klasa powinna być singletonem.Tworzenie klasy singleton z dispatch_once dla klasy hiearchy

Użyłem tego wzoru dla uzyskania statycznego instancji, gdy nie mam żadnych innych klas dziedziczących:

+ (MyClass *)getInstance 
{ 
    static dispatch_once_t once; 
    static MyClass *instance; 

    dispatch_once(&once, ^{ 
     instance = [[MyClass alloc] init]; 
    }); 

    return instance; 
} 

Działa to po prostu świetne. Teraz, jeśli dodaję dwie nowe klasy potomne, FirstClass i SecondClass, obie odziedziczone po MyClass, w jaki sposób mogę upewnić się, że otrzymam odpowiedni program ChildClass?

dispatch_once(&once, ^{ 
    // No longer referencing 'MyClass' and instead the correct instance type 
    instance = [[[self class] alloc] init]; 
}); 

FirstClass *firstClass = [FirstClass getInstance]; // should be of FirstClass type 
SecondClass *secondClass = [SecondClass getInstance]; // should be of SecondClass type 

Doing powyższe oznacza, że ​​zawsze wrócić niezależnie od klasy I instancja 1st jako mojego drugiego typu Klasa:

first: <FirstClass: 0x884b720> 
second: <FirstClass: 0x884b720> 
// Note that the address and type as identical for both. 

Jaki jest najlepszy sposób na tworzenie odpowiednich pojedynczych klas dziecko bez dodawania getInstance metodę każda z klas dzieci?

Odpowiedz

6

O ile nie masz dobrego powodu, powinieneś ogólnie unikać podklas pojedynczych. Stwarza to bardzo zagmatwaną sytuację. Jeśli utworzysz singleton MyClass, możesz utworzyć singleton FirstClass? Od FirstClass musi być zawsze przydatny wszędzie, gdzie MyClass jest użyteczny (przez Liskov), teraz są trzy obiekty "singleton" MyClass. Teraz ObjC jest bardzo wolny od singletonów i to dobrze, ale to wciąż jest bardzo dziwne.

OK, powiedział, a twój problem? Najpierw rozwiązanie, a następnie odpowiedź. Rozwiązaniem jest to, że MyClass prawdopodobnie nie powinien być singletonem, jak omówiono powyżej. Pozbądź się getInstance w superklasie i po prostu zdefiniuj ją w podklasach.

Odpowiedź na to, co się dzieje, znajduje się w numerze dispatch_once. W każdym przypadku przekazujesz ten sam statyczny znacznik once. dispatch_once będzie działać nie więcej niż jeden raz kiedykolwiek dla danego tokena. Jedynym sposobem na to jest przekazanie różnych tokenów dla każdej klasy i nie znam wygodnego sposobu na to, bez duplikowania kodu dispatch_once w każdym pliku. Możesz spróbować utworzyć różne tokeny once dla każdej podklasy, ale to prawdopodobnie będzie więcej problemów i kodu niż tylko powielanie metody sharedInstance.

BTW, nie nazywaj tego getInstance. "get" ma specjalne znaczenie w ObjC i nie masz na myśli tego tutaj, więc jest to mylące. Nazywa się to zwykle sharedInstance lub lepiej sharedSomething, gdzie "coś" jest twoją klasą.Jeśli naprawdę masz na myśli, że powinno być MyClass i powinno być FirstClass i powinno być SecondClass, możesz wdrożyć sharedInstance we wszystkich trzech.

+0

Powodem, dla którego mam tę hierarchię jest to, że mam numer wspólnego wspólnego kodu i danych, które mają wszystkie moje klasy potomne (poza "sharedInstance".) Jest to również powód, dla którego używam dziedziczenia w porównaniu do protokołów. nie powtarzaj tego samego kodu instancji dla wszystkich trzech klas pochodnych: –

+0

+1 o nazwie "get". –

1

wiesz, że singletony są gnarly, prawda? wiele singletonów to większy problem. koniec ostrożności.


można po prostu użyć dispatch_once, aby utworzyć singleton podstawowy. wtedy twoja baza może przechowywać mapę (np. słownik {klucz: ClassName | wartość: Instancja}) jej typów pochodnych.

Który używasz dispatch_once sugeruje, że będzie to używane w kontekście wielowątkowym. w takim przypadku musisz zabezpieczyć interakcje ze słownikiem za pomocą muteksu.

wtedy twoje wiadomości z podklas będą określać na podstawie klasy wiadomości (self), która klasa do wyszukiwania (tworzenie tej instancji, jeśli to konieczne).

+1

Ludzie się nauczyć używać pojedynczych a następnie wkręca przez nich wiedzieć, dlaczego na myśli, że są "większy problem". – mskw

+0

@mskw Nie chciałem zbyt mocno uderzać w Biblię, kiedy większość ludzi słyszała pierwsze zdanie = p, ale masz rację. typowym problemem jest to, że eliminacja globalnego/singleton wpływa na wiele źródeł/implementacji. kiedy wielu wchodzi w interakcje, staje się wielkim splotem globalnego stanu, który jest często niemożliwy do ponownego wykorzystania w sposób inny niż pierwotny zamiar (IOW, oddzielić). oznacza to, że wiele z tych klas i zależności, nie tylko pojedyncze, ale także implementacje, które je wykorzystują, są bezużyteczne w innych programach - poprawianie zajmuje dużo czasu i wiele testów. – justin

0

użycie id w swojej myClass w Firstclass jak ten

+(instancetype)sharedInstance 
{ 
    static id sharedInstance; 
    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken, ^{ 
     sharedInstance = [[[self class] alloc] init]; 
    }); 
    return sharedInstance; 
} 

myślę, że to powinno działa

Powiązane problemy