Correction de l’avertissement «La capture fortement dans ce bloc est susceptible de conduire à un cycle de conservation» dans le code compatible ARC

Dans le code ARC activé, comment corriger un avertissement concernant un cycle de rétention potentiel lors de l’utilisation d’une API basée sur des blocs?

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’alerte est liée à l’utilisation de la request object dans le bloc.

En me répondant:

Ma compréhension de la documentation indique que l’utilisation du mot-clé block et la définition de la variable à zéro après l’avoir utilisée dans le bloc devrait être correcte, mais elle affiche toujours l’avertissement.

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

Mise à jour: a fonctionné 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 rest en suspens au lieu d’être automatiquement mis à zéro lorsque l’object est détruit.

Le problème se produit car vous affectez un bloc à la requête qui a une référence forte à demander. Le bloc conservera automatiquement la requête, de sorte que la demande d’origine ne sera pas désallouée à cause du cycle. Avoir du sens?

C’est juste bizarre parce que vous étiquetez l’object de requête avec __block pour 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é .

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

Cela provoque la rétention de soi dans le bloc. La volonté de bloc accessible depuis soi-même et soi-même est référencé en bloc. Cela créera un cycle de conservation.

Essayez de résoudre ceci 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]; 

Parfois, le compilateur xcode a des problèmes pour identifier les cycles de conservation, donc si vous êtes sûr de ne pas conserver le terminéeBlock, vous pouvez placer un indicateur de compilation comme ceci:

 #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-retain-cycles" #pragma clang diagnostic ignored "-Wgnu" -(void)someMethod { } 

Lorsque j’essaie la solution fournie par Guillaume, tout se passe bien en mode Debug, mais il se bloque en mode Release.

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’object “request”: la requête a été libérée …

Donc, cette solution fonctionne à la fois dans les 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]; }]; 
 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 la référence __block?

Consultez la documentation sur le site Web des développeurs Apple: https://developer.apple.com/library/prerelease/ios/#documentation/General/Conceptual/ARCProgrammingGuide/Introduction.html#//apple_ref/doc/uid/TP40011029

Il y a une section sur les cycles de rétention au bas de la page.