Dalle Transitioning to ARC Release NotesPerché dobbiamo impostare __block variable su nil?
Qualificazioni consumo una tantum per evitare forti cicli di riferimento
È possibile utilizzare qualificazioni a vita per evitare forti cicli di riferimento. Per esempio , in genere se si dispone di un grafico di oggetti disposti in una gerarchia padre-figlio e i genitori devono rivolgersi ai propri figli e viceversa, quindi rendere la relazione genitore-figlio forte e il figlio-a -parent relationship debole. Altre situazioni potrebbero essere più sottili , in particolare quando coinvolgono oggetti di blocco.
Nella modalità di conteggio del riferimento manuale,
__block id x;
ha l'effetto di non trattenendox
. In modalità ARC, il valore predefinito di__block id x;
è il mantenimento dix
(solo come tutti gli altri valori). Per ottenere la modalità di conteggio del riferimento manuale sotto ARC, è possibile utilizzare__unsafe_unretained __block id x;
. Come il nome__unsafe_unretained
implica, tuttavia, avere una variabile non mantenuta è pericolosa (perché può penzolare) ed è quindi scoraggiata. Due opzioni migliori sono l'uso di__weak
(se non è necessario supportare iOS 4 o OS X v10.6) o impostare il valore sunil
per interrompere il ciclo di conservazione.
Ok, quindi cosa c'è di diverso nella variabile __block
?
Perché impostare su nil
qui? La variabile __block
viene mantenuta due volte? Chi detiene tutto il riferimento? Il blocco? Il mucchio? Lo stack? Il filo? Il cosa?
Il seguente frammento di codice illustra questo problema utilizzando un motivo che viene talvolta utilizzato nel conteggio dei riferimenti manuale.
MyViewController *myController = [[MyViewController alloc] init…];
// ...
myController.completionHandler = ^(NSInteger result) {
[myController dismissViewControllerAnimated:YES completion:nil];
};
[self presentViewController:myController animated:YES completion:^{
[myController release];
}];
Come descritto, invece, è possibile utilizzare un qualificatore __block
e impostare la variabile myController al nil
nel gestore di completamento:
MyViewController * __block myController = [[MyViewController alloc] init…]; //Why use __block. my controller is not changed at all
// ...
myController.completionHandler = ^(NSInteger result) {
[myController dismissViewControllerAnimated:YES completion:nil];
myController = nil; //Why set to nil here? Is __block variable retained twice? Who hold all the reference? The block? The heap? The stack? The thread? The what?
};
Anche perché myController
non è impostato su nil
dal compilatore. Perché dobbiamo farlo? Sembra che il compilatore sappia quando myController non sarà più riutilizzato, ovvero quando il blocco scadrà.
"Ma il blocco stesso manterrà anche l'oggetto perché è fortemente referenziato all'interno del blocco." Perché? Chiusura. –
In che modo l'aggiunta di __block fa comunque la differenza? –
Quando un blocco cattura un puntatore a un oggetto obiettivo-c, quell'oggetto verrà mantenuto a meno che non si usi '__weak' o' __unsafe_unretained' (o '__block' in un codice non ARC). –