Panther (Mac OS X 10.3)でのCocoa Bindingsの落とし穴

Cocoa BindingsはPantherから導入された仕組みでもの凄く便利なのだけど、Tigerでちょっと挙動が変わったりしている。今さらPanther向けのCocoaアプリを作る人もそういないだろうけど、Thousandを作っているときに気がついたことを残しておく。

nibを作ると、NSObjectControllerが一個も無くてもBindできる先がいくつかある。File's Ownerとか、Shared User Defaultsとか。でもFile's Ownerは鬼門
確かにMVCの「C」を経由しなくてもBindingは出来る。しかしわざわざNSObjectControllerがあるのはそれなりの理由があると見えて、Pantherではいろいろ問題が起こる。Thousandでハマったのは特にNSPopUpButtonのBindingで、これをNSArrayを持つFile's OwnerとBindしようとしてもうまく行かない。NSObjectControllerのcontentをFile's Ownerにして、一個挟むことでうまく行く。
でもNSObjectControllerのcontentをFile's Ownerにするのは、HMDTで紹介されていたように別の問題を生じる。

HMDT
http://hmdt.jp/archives/2004_10.html#sec2004101201
nib で NSController の content を接続すると、retain される
Interface Builder で、nib でインスタンス化されるオブジェクトの間でさまざまな関連を作つことができるけど、多くの接続は retain されない。たとえば、NSTableView には dataSource や delegate を関連づけることができるけど、これらは retain されない。
でも、NSController に content を接続すると、これは retain される。この場合、明示的に release を呼ばないと content が解放されないので注意。特に、content として owner を指定してしまうと、content を release しない限り、owner の dealloc が呼ばれないので、特に注意。この場合、dealloc で release を呼ぶのではなく、別のタイミングであらかじめ呼ぶようにしないといけない。
このことを考えると、Cocoa バインディングのサンプルでよく使われる、owner を content とするパターンは、あまりやらない方がいい、という結論になる。

この問題を回避するためには以下の呪文を唱えるだけで十分である。
”File's Ownerはモデルじゃねえっ!モデルじゃねえんだ!”
これこそ正当な道であり、ちゃんとしたモデルとちゃんとしたコントローラは世界平和をもたらす。しかし、例えばちっこいウインドウを表示するだけのNSWindowControllerサブクラスを作っていて、扱う値ときたらNSString数個なんてときに、パリコレに出るようなモデルを呼んでる場合かってことである。
モデルを作らない場合、HMDTの解決策では別のタイミングでreleaseを呼ぶとなっているが、なんのことはなくてNSWindowControllerサブクラスの場合だったら、ウインドウが閉じられたときにNSObjectControllerにsetContent:nilすればいい。
扱うのがNSStringとかじゃなくてNSArrayだったら、NSObjectControllerのcontentをFile's Ownerにするなんてめんどくさいことはやらずに、NSArrayControllerのcontentArrayをFile's OwnerのプロパティにBindすれば済む。冒頭のPanther問題はそうやって解決されたのでした。