在启用ARC的代码中修复警告“在此块中强烈捕获[对象]可能会导致保留周期”


141

在启用ARC的代码中,当使用基于块的API时,如何解决有关潜在保留周期的警告?

警告:
Capturing 'request' strongly in this block is likely to lead to a retain cycle

由以下代码片段生成:

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

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

警告与request块内对象的使用有关。


1
您可能应该使用responseData而不是rawResponseData,请查看ASIHTTPRequest文档。
2011年

Answers:


165

回复自己:

我对文档的理解是,在模块中使用关键字block并将变量设置为nil后应该可以使用该关键字,但仍会显示警告。

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

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

    }];

更新:使它可以使用关键字 _weak” 而不是“ _block”,并使用一个临时变量:

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

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

如果您也要定位iOS 4,请使用__unsafe_unretained代替__weak。行为相同,但是在对象被销毁时,指针保持悬空状态,而不是自动设置为nil。


8
根据ARC文档,听起来您需要一起使用__unsafe_unretained __block来获得与使用ARC和块时相同的行为。
猎人

4
@SeanClarkHess:当我合并前两行时,会收到以下警告:“将保留的对象分配给弱变量;对象将在分配后释放”
Guillaume

1
@Guillaume感谢您的答复,有些我忽略了临时变量,尝试了一下,警告消失了。你知道为什么这样吗?只是在欺骗编译器以抑制警告,还是警告实际上不再有效?
克里斯·瓦格纳

2
我已经发布了一个跟进的问题:stackoverflow.com/questions/8859649/...
barfoon

3
有人可以解释为什么您需要__block和__weak关键字吗?我猜正在创建一个保留周期,但是我看不到它。创建临时变量如何解决该问题?
user798719 2012年

50

发生此问题的原因是,您正在为要分配的块分配对其中的请求具有强引用的块。该块将自动保留请求,因此原始请求不会由于周期而取消分配。合理?

这很奇怪,因为您正在使用__block标记请求对象,以便它可以引用自身。您可以通过旁边创建一个弱引用来解决此问题。

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

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

__weak ASIHTTPRequest * wrequest = request; 没有为我工作。给我我使用__block ASIHTTPRequest * blockRequest = request;的错误;
Ram G.

13

这是由于将自身保留在块中引起的。块将从self访问,并且self在block中被引用。这将创建一个保留周期。

尝试通过创建弱引用来解决此问题 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];

这是正确的答案,应予以注意
Benjamin

6

有时xcode编译器在标识符保留周期方面存在问题,因此,如果您确定不保留completionBlock,则可以放置一个编译器标志,如下所示:

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

-(void)someMethod {
}

1
有人可能会认为这是不好的设计,但有时我会创建独立的对象,这些对象会一直挂在内存中,直到完成异步任务为止。它们由completeBlock属性保留,该属性包含对self的强引用,从而创建了有意的保留周期。completeBlock包含self.completionBlock = nil,它将释放completeBlock并中断保留周期,从而一旦任务完成就可以从内存中释放对象。您的回答有助于使我在执行此操作时发出的警告安静下来。
痉挛

1
老实说,正确的人和编译器错误的人的机会很小。因此,我想说,超越警告是危险的生意
Max MacLeod

3

当我尝试Guillaume提供的解决方案时,在Debug模式下一切正常,但在Release模式下崩溃。

请注意,不要使用__weak而是__unsafe_unretained,因为我的目标是iOS 4.3。

在对象“ request”上调用setCompletionBlock:时,我的代码崩溃:请求被释放...

因此,此解决方案在Debug和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];        
}];

有趣的解决方案。您是否弄清楚了为什么它在“发布”模式而不是“调试”中崩溃?
Valerio Santinelli,2012年


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.