如何在没有Core Data或NSKeyedArchiver的情况下持久保存由NSArrayController管理的数据?

时间:2010-09-03 21:16:22

标签: objective-c cocoa cocoa-design-patterns object-persistence

我希望你能原谅这个问题看似广泛的性质,但它非常具体。

我正在构建一个基于文档的Cocoa应用程序,除了我正在使用SQLCipher作为我的数据存储(SQLite的一种变体)之外,它与大多数其他应用程序一样,因为你没有设置自己的 persistent < / em> Core Data中的数据存储,我也真的需要使用这个。

在我的文档子类中,我有一个名为NSMutableArray的{​​{1}}属性。在文档笔尖中,我有categories绑定到NSArrayController,并且我有一个绑定到数组控制器的categories

数组中的每个模型对象(每个都是NSCollectionView)绑定到底层数据存储中的记录,因此当类别的某些属性发生更改时,我想调用Category ,当一个类别被添加到集合中时,我想再次呼叫[category save],最后,当删除某个类别时,[category save]

我已经连接了一个部分解决方案,但它在移除要求方面有所分离,而且关于它的一切似乎在我看来,好像我正在咆哮错误的树。无论如何,这是正在发生的事情:

一旦文档和笔尖都被加载,我开始观察categories属性,并为它分配一些数据:

[category destroy]

我已经实施了观察方法,因为我了解了更改,以便文档可以响应并更新数据存储。

    [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

这对我来说就像一个大笨蛋,我怀疑我在这里反对Cocoa,但在大多数情况下它都有效。

除去。当您向界面添加按钮并将其分配给阵列控制器的- (void)observeCategoriesAtIndexes:(NSIndexSet *)indexes { [categories addObserver:self toObjectsAtIndexes:indexes forKeyPath:@"dirty" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:MyCategoryContext]; } 操作时,它将从我的文档上的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> ... )

如何正确地与NSArrayController集成以保持对核心数据之前的数据模型的更改?如何解决这里的删除问题(或者这完全是错误的方法?)

提前感谢任何建议!

2 个答案:

答案 0 :(得分:2)

基于一些初步的黑客攻击,似乎是继承NSArrayController的方法。覆盖该API中的各种insertObject(s)和removeObject(s)方法,为我提供了在这个逻辑中添加数据模型的理想位置。

从那里我也可以开始观察内容数组中的各个项目的变化等,在销毁/解除分配之前停止观察等,让父类处理其余部分。

感谢这个解决方案归功于Bill Garrison suggested it在可可未绑定列表上。

答案 1 :(得分:1)

我会观察类别列表的更改,当列表更改时,使用mutableCopy将类别数组存储在辅助NSArray中,即“已知类别”。下次列表更改时,将“已知”列表与新列表进行比较;你可以知道哪些类别缺失,哪些是新的等等。对于每个被删除的类别,请停止观察并释放它。

然后为“已知”类别列表获取新的可变副本,为下次调用做好准备。

由于您有一个包含类别的附加数组,因此在您准备好之前不会释放它们。