頭と尻尾はくれてやる!

パソコンおやじのiPhoneアプリ・サイト作成・運営日記


KVOによる値の変化を監視する

Cocoa MVC
Cocoa Core Competencies: Model-View-Controllerより

MVCデザインパターンを使うとMからCへ通知する、という仕組みを作るものらしい。delegateでいいんじゃねえのって思うんだけど、Mが複数のCやMに参照されうるのでdelegateではなく通知なんだそうな。なるほど、delegateだとそこへしかメッセージ送れないからね。

ということでKVO(Key-Value Observing)という仕組みを使って値の変化を監視、通知するというのを試してみたんだ。そう、今まで通知なんて使ったことなかったのよ。

調べてみたところこの通知には自動と手動の二種類あるんみたいなんだ。


それじゃあまず、自動の方ね。
ControllerクラスがModelクラス内の値を監視してて、その値が変化したらControllerへ通知が行くようにするわけだ。
まずControllerクラス内に記述するのがこんなの。
{
    [model addObserver:self forKeyPath:@"counter" options:NSKeyValueObservingOptionNew context:nil];
}
forKeyPath:ってところで文字列を指定してるけど、これなんと監視するプロパティを示すのよ。なんかダイレクトだなあって驚いたんだけどプロパティ名の文字列そのものなのよ。
optionsで指定した内容によってやってくる通知でいろんな値が拾える。上のなんちゃらNewというのを指定すると変化後のプロパティの値を後で拾うことができたりする。

同じControl内にその通知をもらう部分はこんな感じで記述↓
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if ([keyPath isEqual:@"counter"]) {
        int newValue = [[change valueForKey:@"new"] intValue];
        NSLog(@"newValue=%d",newValue);
        //viewへの処理など
    }
}
これはこういうメソッドがNSObjectにあってユーザーがテキトウに考えるメソッド名じゃないよ。ここでkeyPathって文字列を調べて該当するなら処理する、って流れ。監視の設定時にオプションで新しい値をもらう、なんてのを指定するとchangeってNSDictionaryオブジェクトでもらえる。

次に監視される方の設定。Modelクラス内の記述ね。自動か手動かを下のようなメソッドで判断する。
+(BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey
{
    BOOL automatic = NO;
        if ([theKey isEqualToString:@"counter"]) {
        automatic = YES;
    } else {
        automatic = [super automaticallyNotifiesObserversForKey:theKey];
    }
    return automatic;
}
あとは実際に値が変更する部分が当然必要でModelクラス内にこういうメソッドがあってControllerからコールされるんだろうね。
-(void)countUp
{
    self.counter++;
}
この時、_counter++;だと意図通り動かなくて上のようにsetterを通さないとだめだったよ。つまりreadonlyのプロパティだと自動の通知ができないってことになる。


readonlyのプロパティやModelクラス内のインスタンス変数の値の変化を監視するんだ!って場合には自動じゃなくて手動にしないとだめっぽい。
手動の場合は
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key
メソッドで該当するキーだとNOを返すようにする。自動の時と反対だね。
それと値変更の前後で次のような記述をするのよ。
{
    [self willChangeValueForKey:@"counter"];
    counter++;
    [self didChangeValueForKey:@"counter"];
}
ここでcounterはインスタンス変数。コードだけ見てると
willChangeValueForKey:
didChangeValueForKey:
の後にある文字列を指定してる部分はただのキーだから監視する部分で指定するキーの文字列と同じにしとけばなんでもいいのかなと思っていたら、インスタンス変数と同じにしないと動かないんだね。ちょっとはまったよ。


参考サイト Cocoaサンプル - MVC - white wheelsのメモ

<< 27インチディスプレイでXcodeを使う  TopPage  UIViewオブジェクトのみIBを使ってみる >>

コメント


管理者にだけ表示を許可する
 

トラックバック

トラックバックURL
http://ringsbell.blog117.fc2.com/tb.php/834-30df9707




Copyright ©頭と尻尾はくれてやる!. Powered by FC2 Blog. Template by eriraha.

FC2Ad