web-dev-qa-db-ja.com

ARC、ブロックおよび保持サイクル

ARCを使用して、4.0および5.0を対象とするiOSプロジェクトに取り組んでいます。

ブロック、ARC、およびブロック外からのオブジェクトの参照に関連する問題が発生しました。ここにいくつかのコードがあります:

 __block AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
   [operation setCompletionBlock:^ {
       if ([operation isCancelled]) {
           return;
       }

... do stuff ...

operation = nil;
}];

この場合、コンパイラは、ブロックで「操作」を使用すると保持サイクルが発生することを警告します。 ARCでは、__ blockが変数を保持するようになりました。

__unsafe_unretainedを追加すると、コンパイラはオブジェクトをすぐに解放するため、明らかにそれは機能しません。

4.0をターゲットにしているため、__ weakを使用できません。

私はこのようなことをしてみました:

AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
__block __unsafe_unretained AFHTTPRequestOperation *weakOperation = operation;

ただし、weakOperationはnilではありませんが、ブロック内にある場合、そのプロパティはいずれも入力されません。

上記のプロジェクトの制約を考えると、この状況を処理するための最良の方法は何ですか?

28
Hunter

進捗が保証されていると仮定すると、保持サイクルはまさにあなたが望むものかもしれません。ブロックの最後で保持サイクルを明示的に中断するため、永続的な保持サイクルではありません。ブロックが呼び出されると、サイクルが中断されます。

ただし、操作を維持する何かが他にある場合は、参照を__weakまたは__unsafe_unretained変数のいずれかに格納し、それをブロック内から使用できます。何らかの理由でブロック中に変数のバインディングを変更する必要がない限り、__block-変数を修飾する必要はありません。これ以上中断するための保持サイクルがないため、弱い変数に何も割り当てる必要はありません。

23

これは、Conrad Stollが Blocks、Operations、and Retain Cycles で説明している問題のようですが、彼の記事にはいくつかの重要な点がありません。

  • __block MRCモードでキャプチャされた変数への強い参照を回避するApple推奨の方法のように見えますが、ARCモードでは完全に不要です。この場合、ARCモードでは完全に不要です。軽量の回避策ははるかに冗長ですが、MRCモードでも不要です:void * unretainedOperation = operation; ... ^{ AFHTTPRequestOperation * op = unretainedOperation; }
  • ARCモードでは、強力な参照(キューに追加できるようにするため)と弱い/安全でない_unretained参照の両方が必要です。

最も簡単な解決策は次のようになります。

AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
AFHTTPRequestOperation * __unsafe_unretained unretainedOperation = operation;

[operation setCompletionBlock:^ {
  if ([unretainedOperation isCancelled]) {
    return;
  }
  ... do stuff ...
}];

参照サイクルを中断しても、ブロックが最初にAFHTTPRequestOperationを保持する理由はありません(操作が完了するまでそれ自体が存続していると仮定します)ハンドラーが完了します。これは常に保証されているわけではありませんが、通常はtrueであり、コールスタックのさらに上でselfを使用して参照された場合、ARCによって想定されます)。

最善の修正は、 最新のAFNetworking に更新することであるように見えます。これは、引数として操作をブロックに渡します。

1
tc.