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のドキュメントを確認してください。
0xced

回答:


165

自分に返信:

ドキュメンテーションについての私の理解では、キーワードblockを使用し、ブロック内で使用した後に変数をnilに設定することは問題ないはずですが、それでも警告が表示されます。

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

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

    }];

更新:「_ block」の代わりに「 _weak」というキーワードを使用し、一時変数を使用するようにしました。

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のドキュメントに基づいて、ARCとブロックを使用する場合の以前と同じ動作を得るには、__ unsafe_unretained __blockを一緒に使用する必要があるようです。
ハンター

4
@SeanClarkHess:最初の2行を組み合わせると、次の警告が表示されます:「保持されたオブジェクトを弱い変数に割り当てています。オブジェクトは割り当て後に解放されます」
Guillaume

1
@Guillaumeの応答に感謝します。一時変数を見落とし、それを試してみましたが、警告は消えました。これが機能する理由を知っていますか?コンパイラをだまして警告を抑制しているだけなのか、それとも警告が実際に無効になったのか?
Chris Wagner、

2
:私は質問のフォローアップを投稿したstackoverflow.com/questions/8859649/...
barfoon

3
__blockおよび__weakキーワードが必要な理由を誰かが説明できますか?保持サイクルが作成されているようですが、表示されません。また、一時変数を作成するとどのように問題が解決されますか?
user798719

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 =リクエスト; 私にとってはうまくいきませんでした。エラーを与える私は__block ASIHTTPRequest * blockRequest = request;を使用しました。
ラムG.

13

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

の弱い参照を作成してこれを解決してみてください 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];

これは正解であり、そのように注意する必要があります
ベンジャミン

6

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

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

-(void)someMethod {
}

1
設計が悪いと主張する人もいるかもしれませんが、非同期タスクで終了するまでメモリ内にたむろする独立したオブジェクトを作成することがあります。それらは、自己への強い参照を含むcompletionBlockプロパティによって保持され、意図的な保持サイクルを作成します。completionBlockにはself.completionBlock = nilが含まれています。これは、completionBlockを解放して保持サイクルを中断し、タスクが完了するとオブジェクトをメモリから解放できるようにします。あなたの答えは、私がこれを行うときに発生する警告を静めるのに役立ちます。
ハイパー痙攣

1
正直に言うと、1つが正しく、コンパイラが間違っている可能性は非常に小さいです。したがって、警告を超えるだけではリスクが高いビジネスだと思います
Max MacLeod

3

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

ターゲットはiOS 4.3であるため、__ weakではなく__unsafe_unretainedを使用しないでください。

オブジェクト「リクエスト」でsetCompletionBlock:が呼び出されるとコードがクラッシュします:リクエストの割り当てが解除されました...

したがって、このソリューションはデバッグモードとリリースモードの両方で機能します。

// 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年


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.