2015-04-03 21 views
7

W artykule NSHiptera na temat method swizzling znajduje się następujący komunikat: "Swizzling powinien zawsze odbywać się w dispatch_once." Dlaczego jest to konieczne, ponieważ + obciążenie ma miejsce tylko raz w klasie?Korzystanie z metody dispatch_once w swizzlingu

+1

Świetne pytanie. Nie wyjaśniono tego w artykule i wydaje się, że to przesada. + (void) obciążenie powinno działać tylko raz. Nie mogę znaleźć żadnych zasobów, które mają przeciwny przykład. – KirkSpaziani

+0

Myślę jednak, że jest to wyjaśnione, jednak: * Ponownie, ponieważ swizzling zmienia stan globalny, musimy podjąć wszelkie środki ostrożności dostępne dla nas w środowisku wykonawczym. Atomowość jest jednym z takich środków ostrożności, ponieważ gwarantuje, że kod zostanie wykonany dokładnie jeden raz, nawet w różnych wątkach *. To kolejny krok zapobiegawczy, aby zapobiec warunkom wyścigu, ponieważ domyślam się, że metoda 'load' niekoniecznie gwarantuje atomowość, ale użycie' dispatch_once' robi –

Odpowiedz

6

To nie jest wymagane. +load gwarantuje bezpieczeństwo i możliwość ponownego wprowadzania. Zobacz load_images w objc-runtime-new.mm:

/*********************************************************************** 
* load_images 
* Process +load in the given images which are being mapped in by dyld. 
* Calls ABI-agnostic code after taking ABI-specific locks. 
* 
* Locking: write-locks runtimeLock and loadMethodLock 
**********************************************************************/ 
const char * 
load_images(enum dyld_image_states state, uint32_t infoCount, 
      const struct dyld_image_info infoList[]) 
{ 
    BOOL found; 

    recursive_mutex_lock(&loadMethodLock); 

    // Discover load methods 
    rwlock_write(&runtimeLock); 
    found = load_images_nolock(state, infoCount, infoList); 
    rwlock_unlock_write(&runtimeLock); 

    // Call +load methods (without runtimeLock - re-entrant) 
    if (found) { 
     call_load_methods(); 
    } 

    recursive_mutex_unlock(&loadMethodLock); 

    return nil; 
} 

Wskazówka rekurencyjne mutex, który gwarantuje, że wszystkie ładunki są wykonywane podczas blokowania i call_load_methods() zapewni + obciążenie zostanie wywołana tylko raz w realizacji +load.

Zauważ, że +load jest wyjątkowy w tym że nie może być wiele implementacje niej na zasadzie per-klasy, który jest jednym z powodów jest to korzystne dla swizzling - Twój +load, jak również oryginalnym +load jest gwarancją nazywa.

Bonus: Poniżej znajduje się odpowiednia dokumentacja call_load_methods() który bezpośrednio odnosi się dlaczego jest to bezpieczne dla wątków w taki sposób, że jest on:

/*********************************************************************** 
* call_load_methods 
* Call all pending class and category +load methods. 
* Class +load methods are called superclass-first. 
* Category +load methods are not called until after the parent class's +load. 
* 
* This method must be RE-ENTRANT, because a +load could trigger 
* more image mapping. In addition, the superclass-first ordering 
* must be preserved in the face of re-entrant calls. Therefore, 
* only the OUTERMOST call of this function will do anything, and 
* that call will handle all loadable classes, even those generated 
* while it was running. 
* 
* The sequence below preserves +load ordering in the face of 
* image loading during a +load, and make sure that no 
* +load method is forgotten because it was added during 
* a +load call. 
* Sequence: 
* 1. Repeatedly call class +loads until there aren't any more 
* 2. Call category +loads ONCE. 
* 3. Run more +loads if: 
* (a) there are more classes to load, OR 
* (b) there are some potential category +loads that have 
*  still never been attempted. 
* Category +loads are only run once to ensure "parent class first" 
* ordering, even if a category +load triggers a new loadable class 
* and a new loadable category attached to that class. 
* 
* Locking: loadMethodLock must be held by the caller 
* All other locks must not be held. 
**********************************************************************/ 
void call_load_methods(void) 
1

Myślę, że ten artykuł to sugerujący, że "Swizzling powinien być zrobiony w + obciążenie". Ale nadal możesz robić zawroty głowy gdzie indziej. W tej sytuacji powinieneś wykonać swizzling w dispatch_once dla atomicity. Myślę, że nie jest również konieczne zawijanie swizzling w dispatch_once w + load.

+0

Pokaż mi kod – dengApro