2011-10-13 2 views
28

Lavorare su un progetto iOS che ha come target 4.0 e 5.0, utilizzando ARC.ARC, Blocks and Retain Cycles

Esecuzione in un problema relativo ai blocchi, ARC e riferimento a un oggetto dall'esterno del blocco. Ecco po 'di codice:

__block AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request]; 
    [operation setCompletionBlock:^ { 
     if ([operation isCancelled]) { 
      return; 
     } 

... do stuff ... 

operation = nil; 
}]; 

In questo caso, il compilatore dà un avvertimento che l'utilizzo di 'funzionamento' nel blocco sta per portare ad un ciclo di trattenere. Sotto ARC, __block ora mantiene la variabile.

Se aggiungo __unsafe_unretained, il compilatore rilascia immediatamente l'oggetto, quindi ovviamente non funzionerà.

Ho scelto come target 4.0, quindi non riesco a utilizzare __weak.

Ho provato a fare qualcosa di simile:

AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request]; 
__block __unsafe_unretained AFHTTPRequestOperation *weakOperation = operation; 

ma mentre weakOperation non è pari a zero, nessuna delle sue proprietà sono popolate quando all'interno del blocco.

Qual è il modo migliore per gestire questa situazione dati i vincoli di progetto elencati sopra?

risposta

23

Supponendo le garanzie di avanzamento, un ciclo di conservazione potrebbe essere esattamente ciò che si desidera. Si interrompe esplicitamente il ciclo di mantenimento alla fine del blocco, quindi non è un ciclo di conservazione permanente: quando viene chiamato il blocco, il ciclo si interrompe.

Se si dispone di qualcos'altro che mantiene l'operazione, è possibile memorizzare un riferimento in una variabile __weak o __unsafe_unretained e quindi utilizzarlo dall'interno del blocco. Non è necessario per __block -qualificare la variabile a meno che per qualche motivo non sia necessario modificare l'associazione della variabile durante il blocco; dal momento che non hai più un ciclo di conservazione per rompere di più, non dovresti aver bisogno di assegnare nulla alla variabile debole.

+1

Ho la cosa del "ciclo di conservazione" martellata nella mia mente, non ci ho nemmeno pensato come hai descritto. Duh. Domanda successiva: un modo per disattivare l'avviso del compilatore? Mi farà impazzire. – Hunter

+1

Vedere ["Controlling Diagnostics via Pragmas"] (http://clang.llvm.org/docs/UsersManual.html#diagnostics_pragmas) nel manuale dell'utente di Clang. Dovrai solo capire quale flag di avviso ignorare. –

+4

È '#pragma clang diagnostic ignored" -Warc-retain-cycles "', di by. –

1

Questo sembra essere il problema descritto da Conrad Stoll in Blocks, Operations, and Retain Cycles, ma la sua interessante resoconto manca alcuni punti importanti:

  • __block assomiglia al modo in cui Apple raccomandato di evitare un forte riferimento a variabili imprigionati nel MRC modalità, ma è completamente inutile in modalità ARC. In questo caso, è completamente inutile in modalità ARC; è anche inutile in modalità MRC anche se la soluzione più leggera è molto più verboso: void * unretainedOperation = operation; ... ^{ AFHTTPRequestOperation * op = unretainedOperation; }
  • In modalità ARC, è necessario sia un riferimento forte (in modo da poter aggiungere alla coda) e una debole di riferimento/unsafe_unretained

la soluzione più semplice è simile al seguente:

AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request]; 
AFHTTPRequestOperation * __unsafe_unretained unretainedOperation = operation; 

[operation setCompletionBlock:^ { 
    if ([unretainedOperation isCancelled]) { 
    return; 
    } 
    ... do stuff ... 
}]; 

Anche se si interrompe il ciclo di riferimento, non v'è alcun motivo per il blocco di mantenere la AFHTTPRequestOperation in primo luogo (supponendo che l'operazione mantiene in sé vivo fino a t Il gestore di completamento è completo, che non è sempre garantito ma in genere è vero e assunto da ARC se si fa riferimento all'utilizzo di self oltre lo stack di chiamate).

La correzione migliore sembra essere l'aggiornamento a latest AFNetworking, che passa l'operazione nel blocco come argomento.

+0

__block non è completamente inutile. Per impostazione predefinita, tutte le variabili, che sono state mantenute per blocco, saranno const all'interno di esso, quindi non è possibile cambiarne i valori. Qui __block viene in soccorso. –