Blocks の循環参照の話。

私は Blocks が登場する前 (iOS 3) から Objective-C であれこれやっていたのですが、Blocks が登場してから今まで理解できていませんでした。似非プログラマーにはクロージャという概念がまったく理解できず、変態的な書きかただなあ delegate でいいじゃん、と思っていました。

幸い、仕事では (残念ながら) レガシー 1 なコードしか触っていないので、Blocks を使う機会もあまりない 2 のですが、iOS の開発でメシを食っている人間として、いまだに理解できていないのはどうかという気がしていました。

特に、Blocks を使うときに循環参照に気をつけないといけないと言われる意味がわかっていませんでした。別に Blocks の中で何使っても中の処理が終わったら解放されるんじゃないの?と思っていましたが、知らないうちに知恵がついたのか、ようやく理解することができました。(たぶん)

要は、ブロックの呼び出し元ブロック を強参照していて、ブロック呼び出し元 を強参照してしまうので、循環参照になるのですね。なので、ブロックのなかで呼び出し元を参照しなければ OK。参照したい情報は、ブロックの引数で返せばよいと。

もう一つは、ブロックをコピーした時に、自分自身 (self) を参照した場合。これも self がブロックを強参照して、ブロックが self を強参照するので循環参照になります。 この場合は、__weak typeof(self) *weakself = self; のように弱参照するローカル変数を作っておき、それをブロック内で使うことで回避できます。

また、ブロックをコピーせずに - (void)updateWithCompletion:(void(^)(NSDictionary *result, NSError *error)):completion のようなメソッドを用意する形にすれば、循環参照は回避できます。

参考

ARC & Blocks 時代の ベストプラクティスを考える / SSSSLIDE http://sssslide.com/speakerdeck.com/kishikawakatsumi/arc-blocks

Objective-Cのメモリ管理おさらいと解放tips | 株式会社インフィニットループ技術ブログ http://www.infiniteloop.co.jp/blog/2014/03/objective-c-memory-tips/


  1. ARC すら使っていない… 

  2. なのに WatchKit で Apple Watch アプリの開発をしていたりする