更新: 当集合视图的表示对象是NSManagedObjects时,此线程标识NSCollectionView中的错误。该错误触发如下;
(a)从NSArrayController中删除对象 (b)在删除后和NSCollectionView完成动画之前的任何时候执行相关的nsmanagedobjectcontext保存。
github上的这些项目证明了这个问题。
https://github.com/artifacts/NSCollectionViewCoreDataBug https://github.com/iracooke/CoreDataCollectionViewCrashing
我有一个NSCollectionView设置,其内容绑定绑定到核心数据实体的NSArrayController的arrangeObjects。在我的Collection Item View(NSCollectionView视图的原型)中,我有几个控件通过我的collectionview项的representObject绑定到我的核心数据实体。
在大多数情况下,这可行。
当我尝试从ArrayController中删除实体时遇到objc_exception。我只是通过调用删除这些实体;
[myArrayController removeObject:managedObjectToDelete];
不幸的是,当我这样做时,我经常得到“CoreData无法解决错误”错误..负责实体是由NSArrayController管理的实体之一。
抛出异常时检查调用堆栈会发现崩溃是在NSCollectionView收到其_endOfAnimation方法时发生的。这反过来启动了其他方法来解除绑定(在我的视图中可能是我的实体的属性与控件)。
另外一点信息是我正在使用的实体与我模型中的其他实体没有任何关系。
我认为好像发生了以下问题;
我能想到的唯一方法就是在删除之前将对象保存在存储区中(通过保存它们)。这是有效的,但只是以一种hackish方式,因为我需要确保在再次保存之前完成一轮删除...并且因为错误发生在动画期间...经过一些延迟...并且两次连续保存将会导致同样的错误再次发生。
这是否意味着我不能使用Core-Data支持的NSArrayController来填充NSCollectionView?如果不是我做错了什么?有没有更好的解决这个问题的方法?
答案 0 :(得分:2)
直接但无聊的答案:我可以确认Core Data支持的NSArrayController可以填充NSCollectionView,集合NSView项中的不同GUI对象绑定到生成的“Collection View Item”并引用沿着它的各种Core Data对象路径。以编程方式删除(和重新排序)NSArrayController的元素(免费动画)。
也许集合视图中的某些绑定或其他依赖项导致了问题?或托管对象上下文的线程问题?
答案 1 :(得分:1)
您可以通过向CollectionItem添加(assign)属性来指出相应的ArrayController来解决此问题。
此属性可以在 - [YourNSCollectionViewSubClass newItemWithRepresentedObject:]中设置。
然后您可以在每个项目中观察arrangeObjects。 当它发生变化时,item.representedObject不再包含在arrangeObjects中, 你将item.representedObject设置为nil。在我的测试(10.6.8)中,这会在CoreData将对象转换为错误之前触发绑定清理。 (我的对象有关系,项视图绑定了它们。)
btw:此问题不仅限于在动画期间保存, 撤消/重做/保存组合也可以触发它。
我正在寻找一个在项目内开始观察的好地方但是 我想出的唯一一个是copyWithZone:我想避免。 (-awakeFromNib仅针对第一项调用,-view太早了。)
因此我(不情愿地)决定在-newItemWithRepresentedObject中开始观察: 并在项目的-dealloc中停止它。
您还应该注意由对象触发的任何控件或其他操作 在项目视图中 - 通过快速单击我可能会导致消息被解除分配的对象, 大概是因为仍在反应的按钮,在鼠标下动画。 我的解决方案是在将representObject设置为nil时禁用控件。 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];
}
}