Correggi l'avviso "Catturare [un oggetto] con forza in questo blocco probabilmente porterà a un ciclo di mantenimento" nel codice abilitato per ARC


141

Nel codice abilitato ARC, come correggere un avviso su un potenziale ciclo di conservazione, quando si utilizza un'API basata su blocco?

L'avviso:
Capturing 'request' strongly in this block is likely to lead to a retain cycle

prodotto da questo frammento di codice:

ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:...

[request setCompletionBlock:^{
    NSDictionary *jsonDictionary = [[CJSONDeserializer deserializer] deserialize:request.rawResponseData error:nil];
    // ...
    }];

L'avvertimento è collegato all'uso dell'oggetto requestall'interno del blocco.


1
Probabilmente dovresti usare responseDatainvece di rawResponseData, controlla la documentazione ASIHTTPRequest.
0

Risposte:


165

Rispondendo a me stesso:

La mia comprensione della documentazione afferma che l'uso della parola chiave blocke l'impostazione della variabile su zero dopo averla utilizzata all'interno del blocco dovrebbe essere ok, ma mostra comunque l'avvertimento.

__block ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:...

[request setCompletionBlock:^{
    NSDictionary *jsonDictionary = [[CJSONDeserializer deserializer] deserialize:request.responseData error:nil];
    request = nil;
// ....

    }];

Aggiornamento: ha funzionato con la parola chiave '_ weak' invece di ' _block', e usando una variabile temporanea:

ASIHTTPRequest *_request = [[ASIHTTPRequest alloc] initWithURL:...
__weak ASIHTTPRequest *request = _request;

[request setCompletionBlock:^{
    NSDictionary *jsonDictionary = [[CJSONDeserializer deserializer] deserialize:request.responseData error:nil];
    // ...
    }];

Se desideri scegliere come target anche iOS 4, utilizza __unsafe_unretainedinvece di __weak. Stesso comportamento, ma il puntatore rimane penzolante invece di essere automaticamente impostato su zero quando l'oggetto viene distrutto.


8
Basato sui documenti ARC, sembra che tu debba usare __unsafe_unretained __block insieme per ottenere lo stesso comportamento di prima quando usi ARC e blocchi.
Hunter,

4
@SeanClarkHess: Quando unisco le prime due righe, ricevo questo avviso: "Assegnare l'oggetto trattenuto a una variabile debole; l'oggetto verrà rilasciato dopo l'assegnazione"
Guillaume,

1
@Guillaume grazie per la risposta, in qualche modo ho trascurato la variabile temporanea, l'ho provato e gli avvisi sono spariti. Sai perché funziona? Sta solo ingannando il compilatore per sopprimere gli avvisi o l'avvertimento in realtà non è più valido?
Chris Wagner,

2
Ho postato un follow-up domanda: stackoverflow.com/questions/8859649/...
barfoon

3
Qualcuno può spiegare perché hai bisogno delle parole chiave __block e __weak? Immagino che sia stato creato un ciclo di mantenimento, ma non lo vedo. E in che modo la creazione di una variabile temporanea risolve il problema?
user798719

50

Il problema si verifica perché stai assegnando un blocco alla richiesta che ha un forte riferimento alla richiesta in esso. Il blocco manterrà automaticamente la richiesta, quindi la richiesta originale non verrà allocata a causa del ciclo. Ha senso?

È strano perché stai tag l'oggetto richiesta con __block in modo che possa fare riferimento a se stesso. È possibile risolvere questo problema creando un riferimento debole accanto ad esso.

ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:...];
__weak ASIHTTPRequest *wrequest = request;

[request setCompletionBlock:^{
    NSDictionary *jsonDictionary = [[CJSONDeserializer deserializer] deserialize:wrequest.rawResponseData error:nil];
    // ...
    }];

__weak ASIHTTPRequest * wrequest = request; Non ha funzionato per me. Dando errore ho usato __block ASIHTTPRequest * blockRequest = request;
Ram G.

13

Causa a causa del mantenimento del sé nel blocco. Al blocco si accederà da sé e il sé viene riferito in blocco. questo creerà un ciclo di mantenimento.

Prova a risolverlo creando un riferimento debole di self

__weak typeof(self) weakSelf = self;

operationManager = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operationManager.responseSerializer = [AFJSONResponseSerializer serializer];
[operationManager setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {

    [weakSelf requestFinishWithSucessResponseObject:responseObject withAFHTTPRequestOperation:operation andRequestType:eRequestType];

} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    [weakSelf requestFinishWithFailureResponseObject:error withAFHTTPRequestOperation:operation andRequestType:eRequestType];
}];
[operationManager start];

Questa è la risposta corretta e dovrebbe essere annotata come tale
Benjamin

6

Alcune volte il compilatore xcode ha problemi nell'identificare i cicli di conservazione, quindi se sei sicuro di non conservare il completamentoBlock puoi mettere un flag di compilatore come questo:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
#pragma clang diagnostic ignored "-Wgnu"

-(void)someMethod {
}

1
Alcuni potrebbero obiettare che si tratta di una cattiva progettazione, ma a volte creo oggetti indipendenti che restano in memoria fino a quando non hanno finito con un'attività asincrona. Sono mantenuti da una proprietà completamentoBlock che contiene un forte riferimento a sé, creando un ciclo di conservazione intenzionale. CompletamentoBlock contiene self.completionBlock = nil, che rilascia il completamentoBlock e interrompe il ciclo di conservazione, consentendo all'oggetto di essere rilasciato dalla memoria una volta completata l'attività. La tua risposta è utile per aiutare a calmare gli avvisi che si verificano quando lo faccio.
iperspasmo

1
a dire il vero, le possibilità che uno abbia ragione e che il compilatore sia sbagliato sono molto piccole. Quindi direi che superare gli avvertimenti è un affare rischioso
Max MacLeod

3

Quando provo la soluzione fornita da Guillaume, tutto va bene in modalità Debug ma si blocca in modalità Release.

Nota che non uso __weak ma __unsafe_unretained perché il mio target è iOS 4.3.

Il mio codice si arresta in modo anomalo quando setCompletionBlock: viene chiamato sull'oggetto "richiesta": la richiesta è stata deallocata ...

Quindi, questa soluzione funziona sia in modalità Debug che Release:

// Avoiding retain cycle :
// - ASIHttpRequest object is a strong property (crashs if local variable)
// - use of an __unsafe_unretained pointer towards self inside block code

self.request = [ASIHttpRequest initWithURL:...
__unsafe_unretained DataModel * dataModel = self;

[self.request setCompletionBlock:^
{
    [dataModel processResponseWithData:dataModel.request.receivedData];        
}];

Soluzione interessante. Hai capito perché si blocca in modalità di rilascio e non in debug?
Valerio Santinelli,

2
ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:...
__block ASIHTTPRequest *blockRequest = request;
[request setCompletionBlock:^{
    NSDictionary *jsonDictionary = [[CJSONDeserializer deserializer] deserialize:blockRequest.responseData error:nil];
    blockRequest = nil;
// ....

}];

qual è la differenza tra __weak e __block reference?


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.