この質問の一見広範に見える性質をご容赦いただければ幸いですが、かなり具体的になります。
Core Data に独自の永続データ ストアを設定できないため、データ ストア (SQLite の変形) に SQLCipher を使用していることを除いて、他のほとんどのアプリケーションと同じように機能するドキュメント ベースの Cocoa アプリケーションを構築しています。また、私は本当にこれを使用する必要があります。
ドキュメント サブクラスには、NSMutableArray
という名前のプロパティがありますcategories
。ドキュメント nib では、へのバインドがあり、配列コントローラへのバインドがありますNSArrayController
。categories
NSCollectionView
配列内の各モデル オブジェクト (それぞれがCategory
) は、基になるデータ ストア内のレコードにバインドされているため、カテゴリの一部のプロパティが変更されたときに を呼び出し[category save]
、カテゴリがセットに追加されたときに、再度 を呼び出し、[category save]
最後に、カテゴリが削除されたときに を呼び出します[category destroy]
。
私は部分的な解決策を配線しましたが、削除の要件でバラバラになり、それに関するすべてが間違ったツリーを吠えているように思えます。とにかく、ここで何が起こっているのですか:
ドキュメントと nib がすべて読み込まれたら、categories プロパティの観察を開始し、いくつかのデータを割り当てます。
[self addObserver:self
forKeyPath:@"categories"
options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld)
context:MyCategoriesContext];
self.categories = [Category getCategories];
ドキュメントが応答してデータストアを更新できるように、変更が通知されるような方法で監視メソッドを実装しました。
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
NSNumber *changeKind = (NSNumber *)[change objectForKey:@"NSKeyValueChangeKind"];
if (context == MyCategoriesContext)
{
switch ([changeKind intValue])
{
case NSKeyValueChangeInsertion:
{
Category *c = (Category *)[change objectForKey:NSKeyValueChangeNewKey];
NSLog(@"saving new category: %@", c);
[c save];
break;
}
case NSKeyValueChangeRemoval:
{
Category *c = (Category *)[change objectForKey:NSKeyValueChangeOldKey];
NSLog(@"deleting removed category: %@", c);
[c destroy];
break;
}
case NSKeyValueChangeReplacement:
{
// not a scenario we're interested in right now...
NSLog(@"category replaced with: %@", (Category *)[change objectForKey:NSKeyValueChangeNewKey]);
break;
}
default: // gets hit when categories is set directly to a new array
{
NSLog(@"categories changed, observing each");
NSMutableArray *categories = (NSMutableArray *)[object valueForKey:keyPath];
NSIndexSet *allIndexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [categories count])];
[self observeCategoriesAtIndexes:allIndexes];
break;
}
}
}
else if (context == MyCategoryContext)
{
NSLog(@"saving category for change to %@", keyPath);
[(Category *)object save];
}
else
{
// pass it on to NSObject/super since we're not interested
NSLog(@"ignoring change to %@:@%@", object, keyPath);
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
そのリストからわかるように (そして既にお気づきかもしれませんが)、categories
プロパティを観察するだけでは十分ではありません。個々のカテゴリを観察して、属性 (名前など) が変更されたときにドキュメントに通知されるようにする必要があります。その変更をすぐに保存できること:
- (void)observeCategoriesAtIndexes:(NSIndexSet *)indexes {
[categories addObserver:self
toObjectsAtIndexes:indexes
forKeyPath:@"dirty"
options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld)
context:MyCategoryContext];
}
これは私には大したことのないように見えます。私はここで Cocoa に反対しているのではないかと思いますが、ほとんどの場合はうまくいきます。
除去以外。インターフェイスにボタンを追加し、それをアレイ コントローラのremove:
アクションに割り当てるとcategories
、ドキュメントのプロパティからカテゴリが適切に削除されます。
そうすることで、カテゴリがまだ監視されている間に割り当てが解除されます。
2010-09-03 13:51:14.289 MyApp[7207:a0f] An instance 0x52db80 of class Category was deallocated while key value observers were still registered with it. Observation info was leaked, and may even become mistakenly attached to some other object. Set a breakpoint on NSKVODeallocateBreak to stop here in the debugger. Here's the current observation info:
<NSKeyValueObservationInfo 0x52e100> (
<NSKeyValueObservance 0x2f1a480: Observer: 0x2f0fa00, Key path: dirty, Options: <New: YES, Old: YES, Prior: NO> Context: 0x1a67b4, Property: 0x2f1a3d0>
...
)
[category destroy]
さらに、通知を受ける前にオブジェクトの割り当てが解除されたため、オブザーバーから呼び出す機会がありません。
Core Data より前のデータ モデルへの変更を保持するために、NSArrayController と適切に統合するにはどうすればよいでしょうか? ここで削除の問題をどのように回避しますか (または、これは完全に間違ったアプローチですか?)
アドバイスをよろしくお願いします!