是否与CoreData兼容NscollectionView
-
12-10-2019 - |
题
更新:此线程在集合视图的表示对象为NSManageBocts时标识NScollectionView中的错误。该错误触发如下;
(a)从NSarrayController(b)删除对象(b)在删除后的任何时间和NscollectionView完成动画之前的任何时候,在相关的NSMANAGEDOBJECTCONTEXT上删除对象。
Github上的这些项目证明了这个问题。
https://github.com/artifacts/nscollectionViewCoredataBughttps://github.com/iracooke/coredatacollectionviewcrashing
下面的原始问题
我有一个NscollectionView设置,其内容绑定与Core-Data实体的NSarrayController的布置结合。在我的收集项目视图(NscollectionView视图的原型)中,我有几个通过表示我的CollectionView项目绑定到Core-Data实体的控件。
在大多数情况下,这一切都可以。
当我尝试从ArrayController删除实体时,我会遇到OBJC_EXCEPTION。我只是通过打电话删除这些实体;
[myArrayController removeObject:managedObjectToDelete];
不幸的是,当我这样做时,我经常会得到“柯达塔无法实现的错误”错误。.负责任的实体是由NSarrayController管理的实体之一。
当抛出异常时,检查呼叫堆栈的检查表明,当NscollectionView收到其_EndoFanimation方法时,崩溃起源于此。反过来,这又启动了与解关的其他方法(在我看来,我的实体的属性可能具有控制属性)。
另一个信息是,我与我合作的实体与模型中的其他实体没有关系。
在我看来,以下问题正在发生。
- 当我从nsarrayController中删除对象时,它们又从上下文中删除。
- 从上下文删除后,对象变成故障
- NscollectionView保留了对对象(现在故障)的引用。它试图在动画结束时清理它们(未连接等)。
- 当NscollectionView试图清理对象绑定,它会导致核心数据试图在对象上发出故障(希望我将术语放在那里)。这会导致错误,因为该对象尚未保存到磁盘上。
我能想到的唯一方法是在删除之前(通过保存)将对象持续存在。这起作用,但仅以一种黑客的方式进行导致再次发生相同的错误。
这是否意味着我不能使用核心数据支持的NSArrayController来填充NscollectionView?如果不是,我做错了什么?在这个问题上有更好的方法吗?
解决方案
一个直接但无聊的答案:我可以确认核心数据支持的NSArrayController可以填充NscollectionView,在集合中,nsview项目中的不同GUI对象绑定到生成的“集合视图项目”,并指向路径沿路径的各种核心数据对象。编程删除NSArrayController作品的元素(并免费使用动画)。
也许您的收集视图中的某些绑定或其他依赖性引起了问题?还是托管对象上下文的线程问题?
其他提示
您可以通过在指向相应的ArrayController指向的CollectionItem中添加(分配)属性来解决此问题。
可以将此属性设置为 - [YournScollectionViewSubClass newitemwithRepresentedObject:]。
然后,您可以观察到每个项目中的布置对象。当它更改并进行项目时,不再包含在“布置”中包含的内容,您将项目设置为nil。在我的测试(10.6.8)中,这会触发Coredata之前的绑定清理,使对象变成故障。 (我的对象具有关系,项目视图中具有绑定)。
顺便说一句:此问题不仅限于在动画期间保存,撤消/重做/保存组合也可以触发它。
我一直在寻找一个很好的地方来开始观察物品,但是我想出的唯一一个是CopyWithzone:我想避免。 (-awakefromnib仅适用于第一项,-View为时过早)。
因此,我(勉强)决定在-newitemwithrepresentedObject中启动观察结果:并将其停在项目的-dealloc中。
您还应提防项目视图中对象触发的任何控件或其他操作 - 快速单击我可以导致消息交易对象,这大概是因为仍在反应按钮,该按钮在鼠标下进行动画。我的解决方案是在设置表示零时禁用控件。 YMMV。
这是我的观察者代码:
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if( object == self.arrayController && [keyPath isEqualToString: @"arrangedObjects"] ) {
if( NO == [self.arrayController.arrangedObjects containsObject: self.representedObject] ) {
self.representedObject = nil;
for(NSView *subview in [self.view subviews]) {
if( [subview isKindOfClass: [NSControl class]] ) {
[(NSControl *)subview setEnabled: NO];
}
}
}
} else {
[super observeValueForKeyPath:keyPath ofObject: object change:change context:context];
}
}