追記@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 件のコメント:
コメントを投稿