catturare fortemente se stessi in questo blocco può portare a un ciclo di conservazione

Come posso evitare questo avviso in xcode. Ecco lo snippet di codice:

[player(AVPlayer object) addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100) queue:nil usingBlock:^(CMTime time) { current+=1; if(current==60) { min+=(current/60); current = 0; } [timerDisp(UILabel) setText:[NSString stringWithFormat:@"%02d:%02d",min,current]];///warning occurs in this line }]; 

La cattura di self qui sta entrando con il tuo accesso implicito alla proprietà di self.timerDisp – non puoi riferirti a self o alle proprietà su self da un blocco che sarà fortemente mantenuto da self .

Puoi aggirare questo creando un riferimento debole a self prima di accedere a timerDisp all’interno del tuo blocco:

 __weak typeof(self) weakSelf = self; [player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100) queue:nil usingBlock:^(CMTime time) { current+=1; if(current==60) { min+=(current/60); current = 0; } [weakSelf.timerDisp setText:[NSString stringWithFormat:@"%02d:%02d",min,current]]; }]; 
 __weak MyClass *self_ = self; // that's enough self.loadingDidFinishHandler = ^(NSArray *receivedItems, NSError *error){ if (!error) { [self_ showAlertWithError:error]; } else { self_.items = [NSArray arrayWithArray:receivedItems]; [self_.tableView reloadData]; } }; 

E una cosa molto importante da ricordare: non usare le variabili di istanza direttamente nel blocco, usale come proprietà dell’object debole, esempio:

 self.loadingDidFinishHandler = ^(NSArray *receivedItems, NSError *error){ if (!error) { [self_ showAlertWithError:error]; } else { self_.items = [NSArray arrayWithArray:receivedItems]; [_tableView reloadData]; // BAD! IT ALSO WILL BRING YOU TO RETAIN LOOP } }; 

e non dimenticare di fare:

 - (void)dealloc { self.loadingCompletionHandler = NULL; } 

un altro problema può comparire se si passa una copia debole di oggetti non mantenuti da nessuno:

 MyViewController *vcToGo = [[MyViewCOntroller alloc] init]; __weak MyViewController *vcToGo_ = vcToGo; self.loadingCompletion = ^{ [vcToGo_ doSomePrecessing]; }; 

se vcToGo sarà deallocato e poi questo blocco vcToGo , credo che si verificherà un arresto anomalo con selettore non riconosciuto in un cestino che contiene ora variabile vcToGo_ . Prova a controllarlo.

Migliore versione

 __strong typeof(self) strongSelf = weakSelf; 

Crea un forte riferimento a quella versione debole come prima linea nel tuo blocco. Se il sé esiste ancora quando il blocco inizia ad essere eseguito e non è tornato indietro a zero, questa linea assicura che permanga per tutta la durata dell’esecuzione del blocco.

Quindi il tutto sarebbe come questo:

 // Establish the weak self reference __weak typeof(self) weakSelf = self; [player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100) queue:nil usingBlock:^(CMTime time) { // Establish the strong self reference __strong typeof(self) strongSelf = weakSelf; if (strongSelf) { [strongSelf.timerDisp setText:[NSString stringWithFormat:@"%02d:%02d",min,current]]; } else { // self doesn't exist } }]; 

Ho letto questo articolo molte volte. Questo è un eccellente articolo di Erica Sadun su Come evitare i problemi quando si usano i blocchi e NSNotificationCenter


Aggiornamento rapido:

Ad esempio, in swift un metodo semplice con blocco di successo potrebbe essere:

 func doSomeThingWithSuccessBlock(success: () -> ()) { success() } 

Quando chiamiamo questo metodo e abbiamo bisogno di usare self nel blocco di successo. Useremo il [weak self] e guard let funzioni.

  doSomeThingWithSuccessBlock { [weak self] () -> () in guard let strongSelf = self else { return } strongSelf.gridCollectionView.reloadData() } 

Questa cosiddetta danza strong-weak è utilizzata dal popolare progetto open source Alamofire .

Per maggiori informazioni consulta la guida rapida

In un’altra risposta, Tim ha detto:

non puoi riferirti a sé o alle proprietà su se stessi da un blocco che sarà fortemente mantenuto da sé.

Questo non è del tutto vero. È giusto che tu lo faccia fino a quando rompi il ciclo ad un certo punto. Ad esempio, supponiamo di avere un timer che emette un blocco che mantiene il sé e che mantenga anche un forte riferimento al timer in sé. Questo va benissimo se sai sempre che distruggerai il timer ad un certo punto e interromperai il ciclo.

Nel mio caso, ho ricevuto questo avvertimento per il codice che ha fatto:

 [x setY:^{ [x doSomething]; }]; 

Ora capisco che clang produrrà questo avvertimento solo se rileva che il metodo inizia con “set” (e un altro caso speciale che non menzionerò qui). Per quanto mi riguarda, so che non c’è pericolo che ci sia un ciclo di mantenimento, quindi ho cambiato il nome del metodo in “useY:” Naturalmente, potrebbe non essere appropriato in tutti i casi e di solito si vorrà usare un riferimento debole, ma Ho pensato che valesse la pena notare la mia soluzione nel caso in cui aiuti gli altri.

Aggiungendo due centesimi per migliorare precisione e stile. Nella maggior parte dei casi userete solo uno o un paio di membri di self in questo blocco, molto probabilmente solo per aggiornare un cursore. Il cast di self è eccessivo. Invece, è meglio essere espliciti e lanciare solo gli oggetti di cui hai veramente bisogno all’interno del blocco. Ad esempio, se si tratta di un’istanza di UISlider* , ad esempio _timeSlider , _timeSlider come segue prima della dichiarazione di blocco:

 UISlider* __weak slider = _timeSlider; 

Quindi basta usare il slider all’interno del blocco. Tecnicamente questo è più preciso poiché limita il potenziale ciclo di conservazione solo all’object di cui hai bisogno, non tutti gli oggetti all’interno di self .

Esempio completo:

 UISlider* __weak slider = _timeSlider; [_embeddedPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:nil usingBlock:^(CMTime time){ slider.value = time.value/time.timescale; } ]; 

Inoltre, molto probabilmente l’object lanciato su un puntatore debole è già un puntatore debole all’interno di self , minimizzando o eliminando completamente la probabilità di un ciclo di mantenimento. Nell’esempio sopra, _timeSlider è in realtà una proprietà memorizzata come riferimento debole, ad esempio:

 @property (nonatomic, weak) IBOutlet UISlider* timeSlider; 

In termini di stile di codifica, come con C e C ++, le dichiarazioni variabili sono meglio leggere da destra a sinistra. Dichiarando SomeType* __weak variable in questo ordine legge più naturalmente da destra a sinistra come: variable is a weak pointer to SomeType .