Objective-CなどiOS関連の技術メモ。知識はここに投げ捨てて忘れる。

ARCでのブロックによるキャプチャと、__weakや引数を使った対処法

最終更新日 2013年07月21日 12:42

ブロック内からブロック外の自動変数(selfを含む)を利用すると、ブロックからその変数(が指すオブジェクト)への強い参照が生まれる。

ありがちなのは、あるクラスのインスタンス変数として保持されているブロックが、自分を保持しているオブジェクトのインスタンス変数にアクセスしようとするケース。インスタンス変数へのアクセスは、暗黙的にselfを利用しているので、selfへの強い参照が生じて循環参照が起こる。下にコード例を載せておく。もっとも、これはコンパイルで警告やエラーが出るので、間違って循環参照を引き起こすことは普通ない。

これを解決する方法のひとつがselfを弱参照する変数の利用。

なお、__weakな変数に対してweakSelf->_nameのようにインスタンス変数に直アクセスしようとすると、weakSelfが解放されてぶら下がりポインタになっている可能性があるからダメだと怒られる。プロパティなど、メソッドを使ってアクセスする必要がある。

よくあることかわからないが次のような場面があった。ブロック内の処理が非同期実行され、弱参照されているBlockOwnerオブジェクトにアクセスする時点でそのオブジェクトは解放済み、しかも他の場所でこのオブジェクトを強く参照することがどうしてもできない。擬似的にコードにしてみると次のような感じ。遅延を表現するためにsleepForTimeIntervalしている。ネットワークアクセスでもしていると思ってほしい。

5行目のNSLogが実行される頃にはweakSelfはとっくに解放済み、というシチュエーション。BlockOwnerオブジェクトの寿命をNSLog実行まで長らえさせた上、無事実行したあとですみやかに解放することはできないだろうか。

こういう場面では、selfが指すオブジェクト自身を引数としてわたす手が使える。

_blockブロックの引数であるargは、_block内でNSOperationとして実行される無名ブロックがargをキャプチャして強く参照するので、この無名ブロックの実行が終わるまでは解放されることはない。さらに、この無名ブロック自体はBlockOwnerに保持されていないので参照が循環することもない。うまく、必要な期間だけ参照を保持しつづけることができる。

詳解 Objective-C 2.0 第3版

必読。こんなゴミサイトより、こっちを読むべき。

Amazonでみる

iPhoneプログラミングUIKit詳解リファレンス

必読。こんなゴミサイトより、こっちを読むべき。

Amazonでみる