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.
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.
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];
// ...
}];
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];
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 {
}
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];
}];
ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:...
__block ASIHTTPRequest *blockRequest = request;
[request setCompletionBlock:^{
NSDictionary *jsonDictionary = [[CJSONDeserializer deserializer] deserialize:blockRequest.responseData error:nil];
blockRequest = nil;
// ....
}];