追記@2010-11-30
Cocoa Bindings の使い方については以下の後続記事も参照のこと。
ブログの記事一覧を表示するようなアプリでは、記事一覧のデータは、配列(のような何か)に蓄えることになるし、画面の表示は表形式(に似た何か)を使うことになるだろう。
Cocoa アプリでこれをシンプルに実現すると、配列 (NSMutableArray
) に蓄えられたデータをテーブルビュー (NSTableView
) に表示するものになる。MVC アーキテクチャで言うなら、配列がモデル(正確には配列に蓄えられたオブジェクトがモデル)で、テーブルビューが(その名の通り)ビューになる。
NSTableView
にデータを表示させる場合、二通りの方法がある。1 つは、NSTableView
のデータソースとなるクラスを定義する方法。具体的には、AppController
というようなクラスを定義し(MVC の C)、そこで以下の 3 つのメソッドを定義するというもの。
- - numberOfRowsInTableView:
- - tableView:objectValueForTableColumn:row:
- - tableView:setObjectValue:forTableColumn:row:
3 つ目のメソッドは表示のみのアプリなら必要ない。
この方法の欠点は、NSMutableArray
の内容を NSTableView
における表示と同期させるコードは、どれも良く似ているものになることだ。
良く似たものになるなら、その仕組みを共通化してフレームワークが提供してくれればありがたい。もちろん、そのためにはある種の「抽象化」が必要になり、利用に際しては一定の「作法」に従わなければならない。この「共通化された仕組み」が Cocoa Bindings であり、「抽象化」が KVC (Key-Value Coding)、「作法」が KVO (Key-Value Observing) ということになる。
サンプルアプリ
今回、サンプルとして作ったアプリのスクショがこれ(→)だ。機能的には(意匠的にも)ヒレガス本の Chapter 8 で作るもの(→ 「MVC と Cocoaバインディング」参照)と同等だが、こちらは、コードとして書いたのはモデルとなる 2 つのクラスのみで、ビューは当然として、コントローラも Cocoa フレームワークの提供するものだけで作られている。
モデル
まずはモデルを構成する 2 つのクラスを示す。これら 2 つは、Atom 形式のフィードから生成することを想定したものだ。Feed
オブジェクトが Entry
オブジェクトの配列を抱えるようになっている。
Entry の定義
// Entry.h #import <Cocoa/Cocoa.h> @interface Entry : NSObject { NSString *title; NSDate *updated; } @property (readwrite, retain) NSString *title; @property (readwrite, retain) NSDate *updated; @end
// Entry.m #import "Entry.h" @implementation Entry - (id)init { [super init]; title = @"Hi!"; updated = [[NSDate date] retain]; return self; } - (void)dealloc { [updated release]; [title release]; [super dealloc]; } @synthesize title, updated; - (void)setTitle:(NSString *)newTitle { if (newTitle == title) return; [title release]; title = [newTitle retain]; self.updated = [[NSDate date] retain]; } @end
Feed の定義
// Feed.h #import <Cocoa/Cocoa.h> @interface Feed : NSObject { NSString *title; NSDate *updated; NSMutableArray *entries; int count; } @property (readwrite, retain) NSString *title; @property (readwrite, retain) NSDate *updated; @property (readwrite, retain) NSMutableArray *entries; @property (readwrite) int count; @end
// Feed.m #import "Feed.h" #import "Entry.h" @implementation Feed - (id)init { [super init]; title = @"My Feed"; updated = [[NSDate date] retain]; entries = [[NSMutableArray alloc] init]; [entries addObject:[[Entry alloc] init]]; count = [entries count]; return self; } - (void)dealloc { [entries release]; [updated release]; [title release]; [super dealloc]; } @synthesize title, updated, entries; - (void)setEntries:(NSMutableArray *)newArray { if (newArray == entries) return; [entries release]; entries = [newArray retain]; self.count = [entries count]; } @synthesize count; @end
コントローラ
コントローラには NSObjectController
と NSArrayController
を 1 つずつ使っている。これらのコントローラは Interface Builder から NIB (今は XIB)ファイルの中にインスタンス化し、設定を変更しているだけだ。コードは一切、書いていない。
これらコントローラの設定で肝となるのは、それぞれのコントローラが保持することになるモデルクラスとの対応づけだ。NSObjectController
は 上述のFeed
クラスのオブジェクトを保持する。NSArrayController
は Entry
クラスのオブジェクトを生成し(Add ボタンのアクション)、消滅させる(Delete ボタンのアクション)。ただし、Entry
オブジェクトたちを保持するのは Feed
が抱えた配列だ。このため、NSArrayController
は Feed
オブジェクトに対して要素となる Entry
オブジェクトの追加、編集、削除を指示する必要がある。このとき用いられるのが Cocoa Bindings だ。
NSArrayController
は配列を抱えた NSObjectController
にバインドされることになる。
ビュー
スクショを見ればわかるように、いくつかのテキストフィールド(いずれも編集不可にしてある)とテーブルビュー(タイトル欄のみ編集可)、および 2 つのボタンから構成されている。いずれも Cocoa フレームワーク標準のビューたちだ。
テキストフィールドおよびテーブルビュー(のセル)は、Cocoa Bindings によって、2 つのコントローラのプロパティを経由してモデルと結びつけられている。このため、モデル(のプロパティ)が変更されれば、KVO の通知により自動的に変更がビューに反映されることになり、ビューによる変更はコントローラを経由してモデルに反映されるようになっている。
まとめ
MVC のうちモデルとなるクラスを書くだけで後は、Interface Builder の操作でアプリが作れてしまう。標準の部品(ビューとコントローラ)を使うアプリであれば簡単にできる。ただし、Interface Builder を使って Cocoa Bindings の設定するのには、ちょっと慣れが必要だ。似たような設定項目が多く、設定する場所を間違えたら、まったく動かない。今日もそれでかなり苦労した。
参考文献
Addison-Wesley Professional ( 2008-05-15 )
ISBN: 9780321503619
関連リンク
- Cocoa Bindings Programming Topics (Mac OS X Reference Library)
0 件のコメント:
コメントを投稿