2012-06-07 9 views

Odpowiedz

42

Muszę najpierw zauważyć, że nie jest to dobrą rzeczą w innych sytuacjach niż testowanie; nawet wtedy postępuj ostrożnie - AliSoftware dostarcza kilka szczegółów i przykładowy kod w komentarzach poniżej. Zobacz także interesujące odpowiedzi na Can I declare a dispatch_once_t predicate as a member variable instead of static?, w tym kilka ważnych informacji from the horse's mouth.

dispatch_once_t to typedef d long. Jego wartość false wynosi 0. Jeśli zresetujesz tę flagę do 0, dispatch_once() zostanie uruchomione ponownie. Twoim problemem jest "po prostu", jak zmienić wartość zmiennej statycznej z innej jednostki kompilacji. Do tego, myślę, że trzeba hak testowy debug/jednostki, tak jak poniżej:

MakeWhoopie.h

#import <Foundation/Foundation.h> 

void makeWhoopie(void); 

#ifdef DEBUG 
void resetDispatchOnce(void); 
#endif 

MakeWhoopie.m

#include "MakeWhoopie.h" 

static dispatch_once_t * once_token_debug; 

void makeWhoopie(void) 
{ 

    static dispatch_once_t once_token; 
    once_token_debug = &once_token; // Store address of once_token 
             // to access it in debug function. 
    dispatch_once(&once_token, ^{ 
     NSLog(@"That's what you get, folks."); 
    }); 

    NSLog(@"Making whoopie."); 
} 

#ifdef DEBUG 
void resetDispatchOnce(void) 
{ 
    *once_token_debug = 0; 
} 
#endif 

(Można też przenieść once_token do pliku . poziom i zmienić go bezpośrednio)

Trying to uwagę:

#import <Foundation/Foundation.h> 
#import "MakeWhoopie.h" 

int main(int argc, const char * argv[]) 
{ 

    @autoreleasepool { 

     makeWhoopie(); 
     makeWhoopie(); 
     resetDispatchOnce(); 
     makeWhoopie(); 
    } 
    return 0; 
} 

Wyniki w:

2012-06-07 18: 45: 28,134 ResetDispatchOnce [8628: 403] To co masz, ludzie.
2012-06-07 18: 45: 28.163 ResetDispatchOnce [8628: 403] Dokonywanie whoopie.
2012-06-07 18: 45: 28.164 ResetDispatchOnce [8628: 403] Dokonywanie whoopie.
2012-06-07 18: 45: 28.165 ResetDispatchOnce [8628: 403] To właśnie dostajesz, ludzie.
2012-06-07 18: 45: 28.165 ResetDispatchOnce [8628: 403] Dokonywanie whoopie.

+0

Punkt 'dispatch_once' oznacza, że ​​jest bezpieczny dla wątków. Problem przez ustawienie '* once_token_debug = 0' w ten sposób jest taki, że w ogóle nie jest bezpieczny w wątku, w przypadku gdy inny wątek używa' dispatch_once (& onceToken, ...) 'podczas samodzielnego ustawiania' OnceToken'. Jak możemy zapobiec takiemu problemowi? – AliSoftware

+0

@AliSoftware: Scenariuszem w pytaniu jest testowanie jednostkowe i jest to jedyne użycie, które proponuję dla tej procedury. Token zostaje zresetowany _tutaj_, aby utworzyć czysty plan, tak jak zostałby utworzony pomiędzy przebiegami większego programu. Nawlekanie nie stanowi problemu, ponieważ program nie działa między testami. –

+0

Rozumiałem to, ale każdy musi być tego świadomy i nie być kuszonym, by używać go w inny sposób niż gdzie indziej.I może być kilka przypadków nawet w testach jednostkowych, że nie jest to bezpieczne dla wątków, szczególnie jeśli źle zakodujemy test i wykonamy asynchroniczne akcje, które będą kontynuowane nawet po zakończeniu testów (jak wywołanie 'dispatch_after (10s,^{/ * sthg, który używa sharedInstance * /} 'jeśli test osiągnie swój limit czasu, zakończy się niepowodzeniem i zostanie zatrzymany i osiągnie' tearDown', ale blok korzystający z metody sharedInstance będzie nadal wysyłany w najbliższej przyszłości ... po usunięciu łzy – AliSoftware

4

My również testujemy nasze jednostki pojedyncze i czasami musimy je zastąpić próbnymi obiektami lub zresetować je. Wziąłem odpowiedź Josha i nieco go uprościłem:

static ArticleManager *_sharedInstance = nil; 
static dispatch_once_t once_token = 0; 

+(ArticleManager *)sharedInstance { 
    dispatch_once(&once_token, ^{ 
     if (_sharedInstance == nil) { 
      _sharedInstance = [[ArticleManager alloc] init]; 
     } 
    }); 
    return _sharedInstance; 
} 

+(void)setSharedInstance:(ArticleManager *)instance { 
    if (instance == nil) once_token = 0; 
    _sharedInstance = instance; 
} 
+0

Napisałem wpis na blogu wyjaśniający, dlaczego powinieneś to zrobić w następujący sposób: http://twobitlabs.com/2013/01/objective-c-singleton-pattern-unit-testing/ – ToddH

+0

Wspomniałem o umieszczeniu razowego tokena na poziomie pliku w moja odpowiedź. Zauważ, że użycie mojego - lub kogokolwiek - kodu opublikowanego w SO, czy to dosłownym czy zmodyfikowanym, [wymaga] (http://creativecommons.org/licenses/by-sa/3.0/), że dołączasz [uznanie autorstwa] (http : //blog.stackoverflow.com/2009/06/attribution-required/). W tym przypadku mechanizm jest tak prosty, że nie warto o nim wspominać, ale proszę nie wdawać się w nawyk blogowania opartego na odpowiedziach na SO bez co najmniej powiązania z powrotem. –

+0

W rzeczywistości, sprawdzanie == zero wewnątrz dispatch_once wydaje się zbędne. Zresetowałeś tylko raz_token, jeśli pozycja _sharedInstance została ustawiona na zero. – Vitali

Powiązane problemy