KVO(キー値監視)がうまくいかない

348 views
Skip to first unread message

overisland

unread,
Jan 22, 2010, 12:41:35 PM1/22/10
to cocoa-dev-japan
いつもお世話になっています

Cocoaバインディングを使って NSMutableArray の内容を NSOutlineView に表示しています。
アプリケーション起動時にモデルクラスの init メソッド内で NSMutableArray を生成し、NSOutlineView に表示する
ところまではうまくいきました。

ところが、アプリケーション起動後にプログラムから NSMutableArray の内容を変更しても、View の表示が更新されません。
キー値監視のドキュメントを参考に、「手動監視」というものを実装してみました。
(バインディングのキーと NSArrya インスタンスの名前は同じにしてあります)

[self willChangeValueForKey:@"aArray"];

// aArray の内容をここで変えたい
[aArray removeAllObjects];
aArray= [NSArray arrayWithArray:bArray];

[self didChangeValueForKey:@"aArray"];

ところが、いっこうに表示は更新されません。
上のコードのような手動監視だけでは不十分なのでしょうか?

また、確認したいこととして、キー値コーディングでは -<key> と -set<key> のようなアクセッサメソッドがない場合でも、
<key> と同じ名前のインスタンス変数にアクセスしてくれる、という認識であっていますか。であれば、インスタンス変数へのアクセスにおいて何か固
有の実装をする必要がない場合は、アクセッサは実装しなくて良いのでしょうか。

よろしくお願いします。

MATSUMOTO Satoshi

unread,
Jan 22, 2010, 4:07:24 PM1/22/10
to cocoa-d...@googlegroups.com
松本です。

Cocoa binding のときの更新の仕方についてはよくわかりませんが、
通常、NSTableViewやNSOutlineViewのデータを更新し
たときは、

- (void)reloadData
Marks the receiver as needing redisplay, so it will reload the data
for visible cells and draw the new values.

を行わないとviewの表示は更新されません。

> --
> このグループから退会するには、次へメールをお送りください。
> cocoa-dev-jap...@googlegroups.com
> その他のオプションについては以下にアクセスしてください。
> http://groups.google.com/group/cocoa-dev-japan

-----------------------------------------------------
Satoshi Matsumoto <sat...@mac.com>
816-5 Odake, Odawara, Kanagawa, Japan 256-0802

overisland

unread,
Jan 22, 2010, 11:29:30 PM1/22/10
to cocoa-dev-japan
松本様

ご返信、ありがとうございます。

reloadData を行いましたが、やはりビューにデータの変化が通知されていないようで、表示が更新されませんでした。

> Satoshi Matsumoto <sato...@mac.com>

Sinbad

unread,
Jan 25, 2010, 2:58:36 AM1/25/10
to cocoa-d...@googlegroups.com
表示の更新の話は解りませんが

上のコードのような手動監視だけでは不十分なのでしょうか?

  setValueforKey:@"aArray" しないとキー値が更新されないです。

また、確認したいこととして、キー値コーディングでは -<key> と -set<key> のようなアクセッサメソッドがない場合でも、
<key> と同じ名前のインスタンス変数にアクセスしてくれる、という認識であっていますか。

 違います。インスタンス変数とキーは別の物です。

であれば、インスタンス変数へのアクセスにおいて何か固
有の実装をする必要がない場合は、アクセッサは実装しなくて良いのでしょうか。

 良いです、マニュアルに書いてある事は「キー値の部分集合にアクセスしたいなら、アクセッサを実装した方が良い」と云う事だと思ってます。
 
Sinbad

narumi

unread,
Jan 25, 2010, 11:54:09 AM1/25/10
to cocoa-dev-japan
こんばんわ。

KVCに関するKVOの挙動の質問ですが、以下のコードで-setValue:forKeyによるアクセスはアクセサを書かずに機能することが確認でき
ます。

@interface Test : NSObject {
NSString *test;
}
@end
@implementation Test
static NSString *TESTOBSERVE = @"TESTOBSERVE";
-(void)awakeFromNib
{
[self addObserver:self forKeyPath:@"test" options:0
context:TESTOBSERVE];
[self setValue:@"hogehoge" forKey:@"test"];
[self removeObserver:self forKeyPath:@"test"];
}
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)
object change:(NSDictionary *)change context:(void *)context
{
if (context == TESTOBSERVE) {
NSLog(@"work");
}
else {
[super observeValueForKeyPath:keyPath ofObject:object change:change
context:context];
}
}
@end


バインディングの質問については、何をどこにバインドしているのかが不明瞭で、何が起こっていそうなのかがちょっと把握できませんでした。
もうすこし詳細にわかると、具体的なアドバイスがつくかもしれません。

松本さんがreloadDataについてのアドバイスをしていますが、僕もNSOutlineViewDataSourceを使って
reloadDataで更新してみたらいいんじゃないかと感じます。その場合Bindingは使えませんが。

kimura wataru

unread,
Jan 26, 2010, 8:10:23 AM1/26/10
to cocoa-d...@googlegroups.com
木村といいます。
昔似たようなことで悩んだことあります。

NSArrayController(テーブルビュー)やNSTreeController(アウトラインビュー)
では、手動通知の方法が異なっていて
・どの位置にあるものが
・どのように変化したか(追加/削除/値の変更)
といった内容をメソッド

willChange:valuesAtIndexes:forKey:
didChange:valuesAtIndexes:forKey:

で通知するようにします。

テーブルビューでの簡単な例をつくってみました。
http://kirika.la.coocan.jp/archive/misc/TableKVO.zip

コードから関係する場所を抜き出してちょっと説明します。

- (IBAction)addRecord:(id)sender
{
NSDictionary *rec;
NSIndexSet *indexSet;

rec = [NSDictionary dictionaryWithObjectsAndKeys:
[NSString stringWithFormat:@"A-%d", [records count]], @"col1",
[NSString stringWithFormat:@"B-%@", [NSDate new]], @"col2", nil];
// この例では最後の位置にひとつ追加するので、追加前の件数=追加する場所
// の位置になる。ここで用意されるindexSetは1件だけのセット
indexSet = [NSIndexSet indexSetWithIndex:[records count]];

// KVO: insert an object
// NSArrayControllerを経由せずに項目を追加して、手動通知
[self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexSet forKey:@"records"];
[records addObject:rec];
[self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexSet forKey:@"records"];
}


- (IBAction)removeRecord:(id)sender
{
NSIndexSet *indexes;

// テーブルビューの選択されている行を調べる
indexes = [arrayController selectionIndexes];
if ([indexes count] <= 0) {
return; // no selection
}
[arrayController setSelectionIndexes:[NSIndexSet indexSet]]; // deselect all

// KVO: remove some objects
// NSArrayControllerを経由せずに選択行を削除して、手動通知
[self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:@"records"];
[records removeObjectsAtIndexes:indexes];
[self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:@"records"];
}


On Fri, 22 Jan 2010 09:41:35 -0800 (PST), overisland wrote:
> いつもお世話になっています
>
> Cocoaバインディングを使って NSMutableArray の内容を NSOutlineView に表
> 示しています。
> アプリケーション起動時にモデルクラスの init メソッド内で
> NSMutableArray を生成し、NSOutlineView に表示する
> ところまではうまくいきました。
>

> ところが、いっこうに表示は更新されません。
> 上のコードのような手動監視だけでは不十分なのでしょうか?
>

--
kimura wataru

overisland

unread,
Feb 14, 2010, 6:57:41 PM2/14/10
to cocoa-dev-japan
返信頂いた皆さん、どうもありがとうございます。

いただいた返信を読んで試行錯誤しているうちに、返信が遅くなってしまい、申し訳ありませんでした。
サンプルコードコードなども載せていただき、大変参考になりました。

結果として、KVOはうまく動作し、モデルのデータ追加や削除が問題なくビューに反映され、望んでいた通りの動作になりました。
かつ、キー値コーディングやKVOなどの知識も深められました。

重ね重ね、返信頂いた皆様に感謝申し上げます。

Reply all
Reply to author
Forward
0 new messages