0

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

我正在构建一个基于文档的 Cocoa 应用程序,它与大多数其他应用程序一样工作,只是我将 SQLCipher 用于我的数据存储(SQLite 的一种变体),因为您无法在 Core Data 中设置自己的持久数据存储,并且我也真的需要使用这个。

在我的文档子类中,我有一个NSMutableArray名为categories. 在文档 nib 中,我NSArrayController绑定了 to categories,并且NSCollectionView绑定了数组控制器。

数组中的每个模型对象(每个都是 a Category)都绑定到底层数据存储中的记录,所以当 Category 的某些属性发生变化时,我想调用[category save],当 Category 添加到集合中时,我想再次调用 ,[category save]最后,当一个类别被删除时,[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,我需要观察每个单独的类别,以便在其属性发生更改时通知文档(如名称)所以我可以立即保存更改:

- (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]从我的观察者那里调用。

一个人应该如何正确地与 NSArrayController 集成以保持对数据模型 pre-Core Data 的更改?如何解决这里的删除问题(或者这完全是错误的方法?)

提前感谢您的任何建议!

4

2 回答 2

2

根据一些最初的黑客行为,子类化 NSArrayController 似乎是这里的方法。覆盖该 API 中的各种 insertObject(s) 和 removeObject(s) 方法为我提供了一个完美的位置来添加此逻辑以弄乱数据模型。

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

感谢此解决方案是由于 Bill Garrison在可可未绑定列表中提出的建议。

于 2010-09-07T20:04:27.550 回答
1

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

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

由于您有一个额外的数组来保存类别,因此在您准备好之前不会发布它们。

于 2010-09-07T15:26:41.247 回答