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.