2011-10-22 11 views
6

Ho un controller di visualizzazione che desidero inizializzare lentamente e, una volta inizializzato, utilizzare la stessa copia quando possibile (non uso un singleton poiché desidero rimuoverlo dalla memoria) , io uso il getter di farlo, il mio codice simile a questa:Inizializzazione pigro sicuro di thread su iOS

@property (retain) UIViewController *myController 

... 

@synthesize myController = _myController; 


... 


- (UIViewController *)myController 
{ 
    if (!_myController) {         // Evaluation 
     _myController = [[MyViewController alloc] init]; // Object Creation 
    } 
    return _myController; 
} 

questo funziona, ma non è thread-safe, e se più di un thread valutare a true prima che venga creato l'oggetto, dovrò una perdita di memoria. Una soluzione che ho provato è di sincronizzare il codice @, ma non sono sicuro del modo corretto di farlo.

Questo sembra funzionare, (lockForMyController è un semplice NSString) ma rende questa sezione di codice molto più lento:

- (UIViewController *)myController 
{ 
    @synchronized(self.lockForMyController){ 
     if (!_myController) { 
      _myController = [[MyViewController alloc] init]; 
     } 
    } 
    return _myController; 
} 

mi chiedevo se c'è qualche altro modo per raggiungere un pigro inizializzato, filo sicuro, proprietà?

risposta

10

questa soluzione funziona

Si noti che questa soluzione funziona solo se myController si accede su un thread in background la prima volta. Sarà deadlock se chiamato sul thread principale.

Si desidera utilizzare gcd. La chiave è serializzare la creazione dell'oggetto, in modo che, indipendentemente dai thread che iniziano il blocco, venga sempre creata esattamente una sola volta.

- (UIViewController *)myController 
    if (_myController == nil) { 
     dispatch_sync(dispatch_get_main_queue(),^{ if (_myController == nil) _myController = [[MyViewController alloc] init]; }); 
    } 
    return _myController; 
} 

Qui, anche se più thread eseguono il blocco, l'esecuzione del blocco viene serializzato sul thread principale e solo uno MyViewController può mai essere creato.

Non si vedrà un risultato di prestazioni qui a meno che l'oggetto non sia zero.

Poiché la proprietà è implicitamente atomica, ciò significa che nel setter il valore sarà autorelasciato. Questo dovrebbe renderlo adatto per socializzare con il tuo recupero personalizzato, dato che autorizzerà qualsiasi modifica di valore a _myController.

http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProperties.html#//apple_ref/doc/uid/TP30001163-CH17-SW2

Tuttavia, è ancora possibile entrare in una condizione di competizione in cui si imposta il valore su un filo, ma l'accesso su un altro. Ogni volta che imposti il ​​valore, probabilmente vuoi assicurarti di fare qualcosa del genere:

dispatch_sync (dispatch_get_main_queue(),^{self.myController = {newValueOrNil}});

Questo farà in modo di serializzare le chiamate dei metodi del setter senza dover reinventare la ruota per i setter atomici, il che è molto difficile da ottenere.

Questa soluzione non funziona

Si desidera utilizzare MCD.

http://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html#//apple_ref/c/func/dispatch_once

vedere questo post su single. So che non vuoi un singleton, ma questo dimostra come usare il metodo. Puoi facilmente adattarlo.

Create singleton using GCD's dispatch_once in Objective C

+0

Ho problemi ad adattarlo. Posso farlo funzionare come un singleton, ma poi ho problemi quando uso '[_myController release]; _myController = nil; 'e poi provo a crearlo nuovamente, dal momento che non posso resettare la variabile' dispatch_once_t once' statica. – Yamanqui

+0

Hmm lascia che aggiorni la mia domanda con un suggerimento che dovrebbe funzionare. – logancautrell

+0

Grazie mille, la soluzione aggiornata funziona perfettamente. – Yamanqui