2012-04-08 23 views
5

Znalazłem ten singletonowy wzór w sieci. Wydaje mi się, że ma wiele rzeczy, które można zoptymalizować.Objective-C - Optymalizacja tego singletonowego wzoru?

-In sharedMySingleton metoda, nie trzeba wywoływać zatrzymania? Nie jestem pewien ...
-Jeśli nie, to dlaczego jest przechowywany w allocWithZone?
-jaki jest pożytek z @synchronized. W NSAssert myślę, że blok może być wywołany wiele razy, więc jeśli tak, powinien istnieć jakiś kod do wydania poprzedniej pamięci lub wyjść z klocka wyraźnie, nie tylko przez NSAsserting, a jeśli nie, to dlaczego istnieje ten NSAssert?
-wiązanie beetween sharedMySingleton i alloc wydaje się dziwne. ja bym napisał coś takiego:

+(MySingleton*)sharedMySingleton 
{ 
    @synchronized([MySingleton class]) 
    { 
     if (_sharedMySingleton == nil) _sharedMySingleton = [[self alloc] init]; 
     return _sharedMySingleton; 
    } 

    return nil; 
} 

+(id)alloc 
{ 
    @synchronized([MySingleton class]) 
    { 
     return [super alloc]; 
    } 

    return nil; 
} 

Singleton

#import "MySingleton.h" 

@implementation MySingleton 

// ########################################################################################################## 
// ######################################## SINGLETON PART ################################################## 
// ########################################################################################################## 
static MySingleton* _sharedMySingleton = nil; 

// ================================================================================================= 
+(MySingleton*)sharedMySingleton 
// ================================================================================================= 
{ 
    @synchronized([MySingleton class]) 
    { 
     if (_sharedMySingleton == nil) [[self alloc] init]; 
     return _sharedMySingleton; 
    } 

    return nil; 
} 

// ================================================================================================= 
+(id)alloc 
// ================================================================================================= 
{ 
    @synchronized([MySingleton class]) 
    { 
     NSAssert(_sharedMySingleton == nil, @"Attempted to allocate a second instance of a singleton."); 
     _sharedMySingleton = [super alloc]; 
     return _sharedMySingleton; 
    } 

    return nil; 
} 

+ (id)allocWithZone:(NSZone *)zone { return [[self sharedMySingleton] retain]; } 
- (id)copyWithZone:(NSZone *)zone { return self; } 
- (id)retain      { return self; } 
- (NSUInteger)retainCount   { return NSUIntegerMax; /* denotes an object that cannot be released */} 
- (oneway void)release    { /* do nothing */ } 
- (id)autorelease     { return self; } 

// ########################################################################################################## 
// ########################################################################################################## 
// ########################################################################################################## 

// ================================================================================================= 
-(id)init 
// ================================================================================================= 
{ 
    if (!(self = [super init])) return nil; 

    return self; 
} 

// ================================================================================================= 
-(void) dealloc 
// ================================================================================================= 
{ 
    [super dealloc]; 
} 

// ================================================================================================= 
-(void)test 
// ================================================================================================= 
{ 
    NSLog(@"Hello World!"); 
} 

@end 

Odpowiedz

17

Nie należy stosować tego wzoru w ogóle (to na bardzo szczególny przypadek Singleton, że prawie nigdy nie trzeba, a nawet w takim przypadku nie powinieneś go używać).

Istnieje wiele dobrych wzorców omówionych na What should my Objective-C singleton look like?, ale większość z nich jest nieaktualna od czasu wydania GCD. W nowoczesnych wersjach Mac i iOS, należy użyć następującego wzoru, nadany przez Colin Barrett w połączonej pytanie:

+ (MyFoo *)sharedFoo 
{ 
    static dispatch_once_t once; 
    static MyFoo *sharedFoo; 
    dispatch_once(&once, ^{ sharedFoo = [[self alloc] init]; }); 
    return sharedFoo; 
} 

ja tylko skopiować go tutaj raczej niż oznakowanie pytanie powielać bo najwyższych oceniane odpowiedzi na odwieczne pytanie za są przestarzałe.

+0

Co z innymi metodami: zachowaj, zatrzymaj, zwolnij, copyWithZone, ...? Dlaczego mówisz o "nowoczesnych" wersjach? Co masz na myśli przez współczesność? Czy jest jakiś pomysł, dlaczego Apple nie zaktualizował swojego singleton snippet z tego rodzaju kodu? – Oliver

+1

Przez nowoczesne, mam na myśli od czasu wprowadzenia GCD. Nie powinieneś zastępować tych metod. Jedyny przykładowy kod, który pokazuje ich nadpisanie, znajduje się tutaj: https://developer.apple.com/library/mac/#documentation/Cocoa/Conoa/CocoaFundamentals/CocoaObjects/CocoaObjects.html Jak zauważa, jest to * ścisłe * wdrożenie singletonu, który powyższy tekst wyjaśnia, jest generalnie niepotrzebny. Otworzyłem przypadek doc, aby ulepszyć tę dokumentację, ponieważ jest niejasna dla wielu programistów. Mike Ash ma dobry post na ten temat: http://www.mikeash.com/pyblog/friday-qa-2009-10-02-care-and-feeding-of-singletons.html –

+0

Co to jest odpowiednik 10,6 w warunki iOS? – Oliver

0

Nie trzeba dzwonić pod numer retain, ponieważ istnieje alloc. Zadzwoń pod numer retain, co może spowodować wyciek pamięci. Zachowuje się na allocWithZone, ponieważ jest to singleton, więc nie chcemy tworzyć dwóch różnych wystąpień klasy. Zamiast alokować nową instancję, zwiększamy liczbę zatrzymań instancji singleton. Dlaczego? Prawdopodobnie uniemożliwi to komuś, kto nie zdaje sobie sprawy z pojedynczego typu klasy. Jeśli zadzwoni pod numer allocWithZone, a następnie zwolni instancję, wszystko będzie działało bez zarzutu, a on rzeczywiście uzyska dostęp do współużytkowanej instancji singleton.

@synchronized służy do uniemożliwienia dwóch wywołań z dwóch różnych wątków do wprowadzenia w instrukcji if w tym samym czasie. Kod jest więc bezpieczny dla wątków.

Prawdopodobnie NSSAssert powoduje awarię aplikacji, jeśli kiedykolwiek utworzono dwa wystąpienia singletonu. Jest to kod "just-to-sure-sure", zwany również programowaniem defensywnym.

W odniesieniu do łańcucha beetween sharedMySingleton i alloc, myślę, że jest w porządku.

0

W metodzie sharedMySingleton nie trzeba wywoływać funkcji zatrzymywania?

alloc zwraca nową instancję z liczbą referencyjną wynoszącą jeden, więc nie jest konieczne zatrzymanie.

Jeśli nie, to dlaczego w przydziale ze Strefą czasową występuje zatrzymanie?

Według zasady, kiedy zadzwonić allocWithZone, jesteś właścicielem odniesienia, dlatego allocWithZone znacznie zwiększyć liczbę odniesienia dla Ciebie. Ale ta implementacja allocWithZone zwraca pojedynczą instancję, która została już utworzona i jest własnością kogoś innego (metoda sharedMySingleton). Tak więc metoda sharedMySingleton tworzy obiekt z alloc, dlatego staje się właścicielem. A następnie dostajesz tę samą instancję przez allocWithZone, dlatego stajesz się drugim właścicielem tej samej instancji. Tak więc liczba zatrzymań musi wzrosnąć, ponieważ teraz jest dwóch właścicieli. Dlatego należy zachować allocWithZone.

Jaki jest pożytek @synchronized?

@synchronized pozwala na jednoczesne wywoływanie kodu przez wiele wątków. Jeśli nigdy nie zadzwonisz pod numer sharedMySingleton z więcej niż jednego wątku, nie jest to konieczne i możesz go pominąć.

NSAssert sprawiają że blok może być wywoływana wiele razy, więc jeśli tak nie powinno być trochę więcej kodu, aby zwolnić poprzedni pamięci lub wyjście bloku wyraźnie nie tylko NSAsserting, a jeśli nie, dlaczego to jest tam ten NSAssert?

Ponieważ klasa ma być singletonem, alloc powinno być wywoływane tylko raz. NSAssert() kończy program, jeśli alloc jest wywoływany więcej niż jeden raz. Od NSAssert() kończy program, gdy alloc nazywa się po raz drugi, nie jest konieczne zarządzanie pamięcią.