1

更新:当集合视图的表示对象是 NSManagedObjects 时,此线程识别 NSCollectionView 中的错误。该错误触发如下;

(a) 从 NSArrayController 中删除对象 (b) 在删除之后和 NSCollectionView 完成其动画之前的任何时间对相关的 nsmanagedobjectcontext 执行保存。

github 上的这些项目证明了这个问题。

https://github.com/artifacts/NSCollectionViewCoreDataBug https://github.com/iracooke/CoreDataCollectionViewCrashing

下面的原始问题

我有一个 NSCollectionView 设置,其内容绑定绑定到核心数据实体的 NSArrayController 的排列对象。在我的集合项目视图(NSCollectionView 视图的原型)中,我有几个控件通过我的集合视图项目的代表对象绑定到我的核心数据实体。

在大多数情况下,这可以正常工作。

当我尝试从 ArrayController 中删除实体时遇到 objc_exception。我只需调用即可删除这些实体;

[myArrayController removeObject:managedObjectToDelete];

不幸的是,当我这样做时,我经常收到“CoreData 无法完成故障”错误。负责的实体是由 NSArrayController 管理的实体之一。

抛出异常时对调用堆栈的检查表明,当 NSCollectionView 接收到它的 _endOfAnimation 方法时发生了崩溃。这反过来又启动了其他方法来解除绑定(在我看来,可能是我的实体的属性与控件)。

还有一点信息是,我正在使用的实体与我模型中的其他实体没有关系。

在我看来,好像正在发生以下问题;

  • 当我从我的 NSArrayController 中删除对象时,它们又会从上下文中删除。
  • 从上下文中删除后,对象变成了故障
  • NSCollectionView 保留了对对象的引用(现在是故障)。它试图在动画结束时清理它们(解除绑定等)。
  • 当 NSCollectionView 尝试清理与对象的绑定时,它会导致核心数据尝试在对象上触发错误(希望我的术语就在那里)。这会导致错误,因为该对象尚未保存到磁盘。

我能想到的防止这种情况的唯一方法是在删除之前将对象保留在存储中(通过保存它们)。这可行,但只能以一种骇人听闻的方式,因为我需要确保在再次保存之前完成一轮删除......并且由于在动画期间发生错误......经过一段时间的延迟......并且连续两次保存会导致同样的错误再次发生。

这是否意味着我不能使用支持核心数据的 NSArrayController 来填充 NSCollectionView?如果不是我做错了什么?有没有更好的方法来解决这个问题?

4

2 回答 2

2

一个直接但无聊的答案:我可以确认一个支持 Core Data 的 NSArrayController 可以填充一个 NSCollectionView,集合 NSView 项中的不同 GUI 对象绑定到生成的“Collection View Item”并引用路径上的各种 Core Data 对象。以编程方式删除(和重新排序) NSArrayController 的元素(免费提供动画)。

也许您的 Collection View 中的某些绑定或其他依赖项导致了问题?还是托管对象上下文的线程问题?

于 2011-06-28T03:17:18.757 回答
1

您可以通过向指向相应 ArrayController 的 CollectionItem 添加(分配)属性来规避此问题。

该属性可以在 -[YourNSCollectionViewSubClass newItemWithRepresentedObject:] 中设置。

然后您可以观察每个项目中的排列对象。当它发生变化并且 item.representedObject 不再包含在排列对象中时,您将 item.representedObject 设置为 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];
    }
}
于 2011-08-05T03:30:50.620 回答