web-dev-qa-db-fra.com

Correction de l'avertissement "Capturer [un objet] fortement dans ce bloc est susceptible d'entraîner un cycle de conservation" dans le code activé par ARC

Dans le code activé par ARC, comment résoudre un avertissement concernant un cycle de rétention potentiel lors de l'utilisation d'une API par bloc?

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

produit par cet extrait de code:

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

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

L'avertissement est lié à l'utilisation de l'objet request à l'intérieur du bloc.

139
Guillaume

Me répondant:

Ma compréhension de la documentation indique que l’utilisation du mot clé block et la définition de la variable sur nil après l’avoir utilisé à l’intérieur du bloc devraient être correctes, mais cela indique toujours l’avertissement.

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

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

    }];

Mise à jour: l'a fait fonctionner avec le mot clé '_ faible "au lieu de' _ block ', et en utilisant une variable temporaire:

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

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

Si vous souhaitez également cibler iOS 4, utilisez __unsafe_unretained au lieu de __weak. Même comportement, mais le pointeur reste en suspens au lieu d'être automatiquement défini sur nil lorsque l'objet est détruit.

164
Guillaume

Le problème se produit parce que vous affectez un bloc à une demande qui contient une référence forte. Le bloc conservera automatiquement la demande afin que la demande d'origine ne soit pas désallouée à cause du cycle. Avoir un sens?

C'est bizarre parce que vous marquez l'objet de requête avec __block afin qu'il puisse se référer à lui-même. Vous pouvez résoudre ce problème en créant une référence faible à côté de le.

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

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

Cela a pour effet de garder le soi dans le bloc. Le bloc sera accessible à partir de soi, et le moi est référé en bloc. cela créera un cycle de conservation.

Essayez de résoudre ce problème en créant une faible référence de 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];
12
HDdeveloper

Parfois, le compilateur xcode rencontre des problèmes d'identificateur pour les cycles de conservation. Par conséquent, si vous êtes certain de ne pas conserver le completionBlock, vous pouvez placer un indicateur de compilation comme celui-ci:

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

-(void)someMethod {
}
6
GOrozco58

Quand j'essaie la solution fournie par Guillaume, tout va bien en mode débogage mais cela plante en mode libération.

Notez que n'utilisez pas __weak mais __unsafe_unretained car ma cible est iOS 4.3.

Mon code se bloque lorsque setCompletionBlock: est appelé sur l'objet "request": la requête a été désallouée ...

Donc, cette solution fonctionne à la fois en modes Debug et 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];        
}];
3
squall2022
ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:...
__block ASIHTTPRequest *blockRequest = request;
[request setCompletionBlock:^{
    NSDictionary *jsonDictionary = [[CJSONDeserializer deserializer] deserialize:blockRequest.responseData error:nil];
    blockRequest = nil;
// ....

}];

quelle est la différence entre __weak et __block reference?

2
Emil Marashliev