NSCollectionView 是不是与 CoreData 兼容
Posted
技术标签:
【中文标题】NSCollectionView 是不是与 CoreData 兼容【英文标题】:Is NSCollectionView compatible with CoreDataNSCollectionView 是否与 CoreData 兼容 【发布时间】:2011-01-19 10:02:50 【问题描述】:更新: 当集合视图的表示对象是 NSManagedObjects 时,此线程识别 NSCollectionView 中的错误。该bug触发如下;
(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?如果不是我做错了什么?有没有更好的方法解决这个问题?
【问题讨论】:
我已经建立了另一个项目来演示这个问题并提交了一个错误,它是雷达#9903150。您可以在此处找到项目和错误描述:github.com/artifacts/NSCollectionViewCoreDataBug 谢谢。您的 github 项目比我的更好地再现了该错误。我猜苹果现在从这个帖子中对这个问题有一个很好的描述。 【参考方案1】:一个直接但无聊的答案:我可以确认一个支持 Core Data 的 NSArrayController 可以填充一个 NSCollectionView,集合 NSView 项中的不同 GUI 对象绑定到生成的“Collection View Item”并引用各种 Core Data 对象小路。以编程方式删除(和重新排序) NSArrayController 的元素(免费提供动画)。
也许您的 Collection View 中的某些绑定或其他依赖项导致了问题?还是托管对象上下文的线程问题?
【讨论】:
谢谢。原型视图中的任何 GUI 对象是否具有动画效果?我的一个是进度指示器。从内存中解除绑定导致崩溃的对象。 也许我应该将我的问题改写为“NSCollectionView 是否与绑定和核心数据兼容”。您的回答“可能是某些绑定导致了问题”几乎可以肯定是正确的……但是我们应该能够使用绑定。 +1 回答,但它并不能完全解决我的问题。 我的工作 NSCollectionViewItem 代码包括一个动态的且经常从网络源更新 (30Hz) 的指示器,所有连接都通过与 Interface Builder 的绑定完成。据我所知,无论集合动画时间如何,我都无法让事情崩溃。你只通过IB绑定吗?当您删除与进度指示器的绑定时会发生什么? (以确保它确实是问题的原因)。此时,重现您的问题的一些修剪代码将有助于准确识别发生了什么。 感谢您的跟进。昨晚我建立了一个最小的项目,并向自己证明它确实如你所说的那样完美。我还不能重现我的错误,但我接受了这个答案,因为我的代码中一定有其他东西导致了这个问题。当我发现它是什么时,我会在这里发帖以供后代使用。 我认为问题在于您在执行动画时保存了托管对象上下文(删除保存消息会删除错误)。从 NSArrayController 中删除一个对象会导致它从 Core Data 对象图中删除并标记为删除,但实际上并未删除。对象的删除是在保存时执行的。 developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/…【参考方案2】:您可以通过将(分配)属性添加到指向相应 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];
【讨论】:
这听起来是个不错的解决方案,感谢分享。不过,在我看来,很明显 NSCollectionView 被核心数据破坏了,如果我们需要竭尽全力防止它崩溃。我想我应该就这个主题向 Apple 提交错误报告。以上是关于NSCollectionView 是不是与 CoreData 兼容的主要内容,如果未能解决你的问题,请参考以下文章