December 23, 2011

ARCでweak参照が使えないクラスについて:NSWindowとか

ARC便利ですよね。オブジェクトの確保や解放に関するコードがなくなるので全体の見通しが良くなってスッキリしますよね。え、GC?知らん。

で、ARCを有効にするとweak参照な変数が使えます。旧来のassignプロパティと同じような弱い参照なわけですが、参照先のオブジェクトが解放されると自動的にnilになってくれるイカしたやつです。便利ですね。

このweak参照、実はすべてのオブジェクトに使えるわけではありません。例えば、以下のようなプロパティを用意したとします。

@property (weak) IBOutlet NSWindow *window;

ウインドウへの参照をアウトレットとして持とうとしているわけですね。しかしこのプロパティを含むコードを実行すると、以下のようなメッセージが出力されます。

objc[2242]: cannot form weak reference to instance (0x100410770) of class NSKVONotifying_NSWindow

NSKVONotifying_NSWindowってのはCocoaのKVOシステムが勝手に作ったNSWindowのサブクラスなので、ここでは要するにNSWindowはweak参照できねーよって言われています。

実は、ARCでは-allowsWeakReferenceというメソッドを定義してNOを返すようにするとweak参照ができなくなります。NSWindowのオブジェクトもNOを返してくるのでさっきのようなエラーが出たわけですね。

weak参照を禁止する場面としては、MRCなライブラリで独自のメモリ管理をしていて、それをARCなアプリから使われるときなんかがあるでしょうか。AppleのTransitioning to ARC Release Notesでも、retain/releaseを改変している場合はweak参照を禁止すべきであると示されています。ちなみにこのドキュメントでは-supportsWeakPointersでNOを返せと書かれていますが、これはドキュメントの誤りです。

というわけで、weak参照を禁止されたオブジェクトに対しては、かわりにunsafe_unretained参照を用いましょう。冒頭のプロパティは次のように修正します。

@property (unsafe_unretained) IBOutlet NSWindow *window;

もちろん、この場合は自動nil化が行われないのでMRC同様の注意が必要となります。

© Shun Takebayashi