web-dev-qa-db-ja.com

ARC対応コードの「このブロックで[オブジェクト]を強くキャプチャすると、保持サイクルが発生する可能性が高い」という警告を修正

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の使用にリンクされています。

139
Guillaume

自分への返信:

ドキュメントの私の理解では、キーワード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に設定されるのではなく、ぶら下がります。

164
Guillaume

この問題は、リクエストへの強い参照を持つリクエストにブロックを割り当てるために発生します。ブロックはリクエストを自動的に保持するため、元のリクエストはサイクルのために割り当て解除されません。理にかなっていますか?

リクエストオブジェクトに__blockのタグを付けて、自分自身を参照できるようにしているため、奇妙です。これを修正するには、弱参照とともに itを作成します。

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

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

ブロックに自己を保持するために発生します。ブロックはselfからアクセスされ、selfはブロックで参照されます。これにより、保持サイクルが作成されます。

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];
12
HDdeveloper

Xcodeコンパイラには保持サイクルの識別子に問題がある場合があるため、completionBlockを保持していないことが確実な場合は、次のようにコンパイラフラグを設定できます。

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

-(void)someMethod {
}
6
GOrozco58

Guillaumeが提供するソリューションを試してみると、デバッグモードではすべて問題ありませんが、リリースモードではクラッシュします。

ターゲットがiOS 4.3であるため、__ weakではなく__unsafe_unretainedを使用しないことに注意してください。

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

}];

__ weakと__blockの参照の違いは何ですか?

2
Emil Marashliev