2013-05-16 11 views
5

Mam podstawową klasę Store z wieloma metodami dziedziczonymi przez wszystkie sklepy. Każdy sklep to singleton. Teraz, każdy sklep definiuje własny niemal identyczny sposób:Objective-C - dynamiczny inicjator singleton?

+ (Store *)instance { 
    static SubStore *store = nil; 
    if (!store) { 
     store = (SubStore *) [[super allocWithZone:nil] init]; 
     [store setupDefaults]; 
    } 
    return store; 
} 

Czy jest jakiś sposób, by stworzyć metodę singleton w taki sposób, że metoda ta może być łatwo dodana do klasy bazowej i dziedziczone przez podklasy?

+1

Próbowałem i nigdy nie znalazłem sposobu. Poza tym, aby być bezpiecznym dla wątków, powinieneś użyć bloku 'dispatch_once' zamiast' if (! Store) '. – Kevin

+0

Podążałem za kodem książki. Zajrzę do dispatch_once. –

Odpowiedz

2

Zamiast używać jednego sklepu static SubStore * store = nil;, można użyć NSMutableDictionary, który używa nazw klas jako kluczy.

skrócie:

#import <Foundation/Foundation.h> 

@interface MONStore : NSObject 
- (NSString *)nameOfMostPopularItem; 
@end 

@implementation MONStore 

+ (instancetype)sharedStore 
{ 
    // lazy population - not thread safe 
    static NSMutableDictionary * stores = nil; 
    if (nil == stores) { 
     stores = [NSMutableDictionary new]; 
    } 
    NSString * key = NSStringFromClass([self class]); 
    if (nil == [stores objectForKey:key]) { 
     [stores setObject:[self new] forKey:key]; 
    } 
    return [stores objectForKey:key]; 
} 

- (NSString *)nameOfMostPopularItem 
{ 
    return nil; 
} 

@end 

@interface MONMusicStore : MONStore 
@end 

@implementation MONMusicStore 
- (NSString *)nameOfMostPopularItem { return @"Guitar Strings"; } 
@end 

@interface MONPetStore : MONStore 
@end 

@implementation MONPetStore 
- (NSString *)nameOfMostPopularItem { return @"Puppies"; } 
@end 

int main(int argc, const char * argv[]) 
{ 
    @autoreleasepool { 
     NSLog(@"--- Shopping List ---\nMusic Store:\n\t%@\n\nPet Store:\n\t%@\n", 
        [MONMusicStore sharedStore].nameOfMostPopularItem, 
        [MONPetStore sharedStore].nameOfMostPopularItem 
        ); 
    } 
    return 0; 
} 

... nie, że ja kiedykolwiek to zrobić w moim programie.

+0

Co byś użył zamiast tego? –

+0

@StefanKendall Jestem jednym z tych, którzy narzekają/zniechęcają singletonów;) – justin

5

Trzymaj się proste/głupie, ale użyj dispatch_once.

Natychmiast po podjęciu próby generycznej staje się skomplikowana. I buggy.

Metoda sharedInstance z jawnym nazewnictwem klas jest całkowicie oczywista i jest mało prawdopodobne, aby powtórzyła się ona więcej niż kilka razy w projekcie.

Jeśli jednak masz wiele podklas jednej klasy, przejdź do modelu identyfikatora. To znaczy. pamięć podręczna w abstrakcyjnej klasie sklepu, która może wyszukiwać instancje sklepu za pomocą identyfikatora.


uniknąć przy użyciu +initialize lub, co gorsza, +load dla takiej inicjalizacji. Oba są niedeterministyczne, ponieważ ich kolejność wykonywania względem innych podsystemów w aplikacji może być dość zmienna, z pozornie nieszkodliwymi zmianami.

Znacznie lepiej mieć całkowicie deterministyczną inicjalizację. Dodaj objaśnienie w swoim applicationDidFinishLaunching: (lub jednym z pozostałych), które jawnie inicjuje ten konkretny podsystem w Twojej aplikacji. Jest łatwy do naśladowania, jednoznacznie zarówno w deklaracji, jak iw użyciu, i nie zmienia zachowania w dziwny sposób, ponieważ zmienia się baza kodów.

+0

Często umieszczam ten rodzaj kodu inicjującego w '+ initialize'. Środowisko wykonawcze ObjC zapewnia, że ​​metoda zostanie wywołana tylko raz. – zneak

+3

@zneak To nie do końca prawda. Jeśli masz klasę A z metodą 'initialize' i klasę B, która rozszerza A, a klasa B nie implementuje metody' initialize', wówczas metoda 'initialize' klasy A zostanie wywołana dwukrotnie - raz, gdy A jest pierwszym z odniesieniem i kiedy B jest pierwszym odnośnikiem. – rmaddy

+0

'+ load' jest wywoływane tylko raz dla każdej klasy/kategorii. – nielsbot