Mac用USBデバイス-103 テストプロジェクトの続き [Mac用USBデバイス工作]
また先週から日にちがあいて忘れてしまった。土日も続ければいいんだけどうちに帰るとなぜかその気にならない。本当になんでだろう。
前回はNSTreeControllerを使ったCocoaバインディングのテストプロジェクトのUIとアプリのデリゲートの動作を考えた。今日はNSTreeControllerのサブクラスの具体的なコードと、NSTreeControllerのちょっと不思議な動作について。
また、TreeControllerのAttributesパネルで、
どうやらNSTreeControllerのcontentをInterface BuilderのBindingsパネルのController Contentで指定した場合、親クラスであるNSObjectControllerのinitWithContent:メソッドではなく、NSTreeControllerのawakeFromNibメソッドの中で設定されるようである。従ってrootNodeを作るのはAppDelegateのawakeFromNibではなく、initで作ればタイミングの問題はなくなる。
しかし今回なぜか動作が一致しなかった。
つまり、NSTreeControllerのcontentを
NSTreeControllerはこの両方のツリーを同じ形式で保持しているらしい。つまりNSTreeControllerのcontentは配列で、単一ルートの場合はその要素がひとつだけとして扱っている。Interface BuilderのBindingsパネルの「Controller Content」→「Content Object」で単一ルートのオブジェクトを指定して、NSTreeController(のインスタンス)にcontentメソッドを投げると、指定したオブジェクトをひとつだけ持っているNSMutableArrayが返ってくるのでわかる。
扱いを一般化して統一する、という意味では正しいが、どうやらNSTreeControllerはこの区別は設定するときにだけ意味があって、そのあとはまったく区別していないようである。ルートが単一か複数かを指定するためのメソッドも、あるいはその設定を保持するようなそれらしいインスタンス変数もない。
さきほどプログラマティックに設定した場合と、Interface Builderで設定した場合で動作が違っていて、Interface Builderでは子要素の追加メソッドを呼んでも子供が作られない、と指摘した。 実は、図-13のようにInterface BuilderのBindingsパネルで「Controller Content」→「Content Object」ではなく、「Controller Content」→「Content Array」で、単一ルートのオブジェクトをNSMutableArrayに入れたもの(図ではrootArrayがNSMutableArray)を設定すると、contentの構造は全く同じなのに、プログラマティックに設定した場合と同じ動作に変わってしまう。
結局今回、しょうがないのでプログラマティックに設定することにしたが、なぜこういう動作をするのか理解できない。どなたかこの辺りの挙動に詳しい人が教えてくれるとありがたい。
前回はNSTreeControllerを使ったCocoaバインディングのテストプロジェクトのUIとアプリのデリゲートの動作を考えた。今日はNSTreeControllerのサブクラスの具体的なコードと、NSTreeControllerのちょっと不思議な動作について。
5.3.9 NSTreeControllerのサブクラス
BranchedTreeControllerという名前のNSTreeControllerのサブクラスを作る。サブクラスはaddChild:を上書きするだけ。@interface BranchedThreeController : NSTreeController @end @implementation BranchedThreeController - (void)addChild:(id)sender { id childClass = [sender additionalObject]; [super setObjectClass:childClass]; [super addChild:sender]; } @endsenderから前回デリゲートで設定したadditionalObjectを取り出す。これをNSObjectControllerのsetObjectClass:に設定したあと、NSTreeControllerのaddChild:メソッドを呼ぶ。前回にNSMenuItemのサブクラスを作ってRefConを保持すればいいとしたが、そのRefConを返すメソッドがadditionalObjectだとしている。
5.3.10 Tree Controllerの設定
あとはInterface Builder上での設定。 まず、MainMenu.xibに乗せたNSTreeControllerを今のBranchedTreeControllerと差し替える。それはInspectorパネルのIndentityでClassの名前を書き換えればいい。また、TreeControllerのAttributesパネルで、
- 「Tree Controller」のKey Pathを「children」に(子要素を得るキーはchildrenである、ということ)
- 「Object Controller」のModeを「Class」に(Core Dataのエンティティではなく自分で用意したオブジェクトである、ということ)
- Class Nameを「Node」
5.3.11 setContent:メソッドとInterfaceBuilderでの設定の違い
さっきはプログラマティックにTreeControllerのcontentを設定したが、ここのBindingsパネルの「Content Object」でも設定できる(図-12)。 ただし今回のようにTreeControllerとcontentであるrootNodeを保持しているAppDelegateが同じxibファイルに乗っている場合(たいていそうだろう)、タイミングの問題が発生する。つまりxibファイルの上ではインスタンス化される順番がどうなるかわからないので、例えばrootNodeが作られる前にTreeControllerがインスタンス化されてしまうと初期化されていないrootNodeポインタをTreeControllerのcontentとして設定してしまう可能性がある。AppDelegateのawakeFromNibメソッドでrootNodeを作っているとすると(やはり普通はそうだろう)、場合によってはcontentとして設定されない、と言うことが起こる。どうやらNSTreeControllerのcontentをInterface BuilderのBindingsパネルのController Contentで指定した場合、親クラスであるNSObjectControllerのinitWithContent:メソッドではなく、NSTreeControllerのawakeFromNibメソッドの中で設定されるようである。従ってrootNodeを作るのはAppDelegateのawakeFromNibではなく、initで作ればタイミングの問題はなくなる。
しかし今回なぜか動作が一致しなかった。
つまり、NSTreeControllerのcontentを
- プログラマティックにNSTreeControllerのsetContent:メソッドを呼ぶ
- Interface BuilderのBindingsパネルの「Controller Content」→「Content Object」でrootNodeを設定する
- (1)プログラマティックに設定した場合
- addChild:メソッドから要素クラスのinitが呼ばれて親のchildren配列に追加される
- (2)Interface Builder上で設定した場合
- ではinitが呼ばれない。当然contentの構造に変化はない
5.3.12 NSTreeControllerのContentの保持のしかた
一般的なツリー構造とは、幹になるひとつのオブジェクトがあり、それが複数の子オブジェクトを持ち、さらに子オブジェクトが孫を、という構造のことを言う。この構造のことをここでは「単一ルート」のツリーと呼ぶことにする。NSTreeControllerはこの単一ルートのツリーと、トップレベルがすでに配列になっていて、それぞれが子供孫、を持っているさらに一般的なツリーも扱うことができるようになっている。この「複数ルート」(変な言葉だけど)のツリーはNSOutlineViewの表示方法に対応している。NSTreeControllerはこの両方のツリーを同じ形式で保持しているらしい。つまりNSTreeControllerのcontentは配列で、単一ルートの場合はその要素がひとつだけとして扱っている。Interface BuilderのBindingsパネルの「Controller Content」→「Content Object」で単一ルートのオブジェクトを指定して、NSTreeController(のインスタンス)にcontentメソッドを投げると、指定したオブジェクトをひとつだけ持っているNSMutableArrayが返ってくるのでわかる。
扱いを一般化して統一する、という意味では正しいが、どうやらNSTreeControllerはこの区別は設定するときにだけ意味があって、そのあとはまったく区別していないようである。ルートが単一か複数かを指定するためのメソッドも、あるいはその設定を保持するようなそれらしいインスタンス変数もない。
さきほどプログラマティックに設定した場合と、Interface Builderで設定した場合で動作が違っていて、Interface Builderでは子要素の追加メソッドを呼んでも子供が作られない、と指摘した。 実は、図-13のようにInterface BuilderのBindingsパネルで「Controller Content」→「Content Object」ではなく、「Controller Content」→「Content Array」で、単一ルートのオブジェクトをNSMutableArrayに入れたもの(図ではrootArrayがNSMutableArray)を設定すると、contentの構造は全く同じなのに、プログラマティックに設定した場合と同じ動作に変わってしまう。
結局今回、しょうがないのでプログラマティックに設定することにしたが、なぜこういう動作をするのか理解できない。どなたかこの辺りの挙動に詳しい人が教えてくれるとありがたい。
2010-05-24 22:01
nice!(0)
コメント(0)
トラックバック(0)
コメント 0