142

在启用 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块内对象的使用有关。

4

7 回答 7

167

回复我自己:

我对文档的理解说,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。

于 2011-08-26T13:24:07.073 回答
50

出现此问题是因为您正在为请求分配一个块,该块在其中具有对请求的强引用。该块将自动保留请求,因此原始请求不会因为循环而释放。有道理?

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

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

[request setCompletionBlock:^{
    NSDictionary *jsonDictionary = [[CJSONDeserializer deserializer] deserialize:wrequest.rawResponseData error:nil];
    // ...
    }];
于 2013-01-07T15:00:48.940 回答
14

它是由于将 self 保留在块中而导致的。Block 会从 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];
于 2014-07-11T09:12:24.180 回答
6

有时 xcode 编译器在标识符保留周期方面存在问题,因此如果您确定没有保留 completionBlock,您可以像这样放置编译器标志:

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

-(void)someMethod {
}
于 2014-11-26T20:58:53.920 回答
3

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

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

当 setCompletionBlock: 在对象“request”上调用时我的代码崩溃:请求被解除分配......

因此,此解决方案适用于调试和发布模式:

// 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];        
}];
于 2012-01-16T18:25:29.230 回答
2
ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:...
__block ASIHTTPRequest *blockRequest = request;
[request setCompletionBlock:^{
    NSDictionary *jsonDictionary = [[CJSONDeserializer deserializer] deserialize:blockRequest.responseData error:nil];
    blockRequest = nil;
// ....

}];

__weak 和 __block 引用有什么区别?

于 2013-07-24T14:42:23.863 回答
-6

查看 Apple 开发者网站上的文档:https ://developer.apple.com/library/prerelease/ios/#documentation/General/Conceptual/ARCProgrammingGuide/Introduction.html#//apple_ref/doc/uid/TP40011029

页面底部有一个关于保留周期的部分。

于 2011-08-26T13:23:35.607 回答