NSCollectionView 与 CoreData 兼容吗
更新: 当集合视图的表示对象是 NSManagedObjects 时,此线程识别出 NSCollectionView 中的错误。该错误的触发方式如下;
(a) 从 NSArrayController 中删除对象 (b) 在删除之后和 NSCollectionView 完成其动画之前的任何时间对相关的 nsmanagementobjectcontext 执行保存。
github 上的这些项目演示了这个问题。
https://github.com/artifacts/NSCollectionViewCoreDataBug https://github.com/iracooke/CoreDataCollectionViewCrashing
下面的原始问题
我有一个 NSCollectionView 设置,其内容绑定绑定到核心数据实体的 NSArrayController 的排列对象。在我的集合项视图(NSCollectionView 视图的原型)中,我有几个控件通过我的集合视图项的representedObject 绑定到我的核心数据实体。
在大多数情况下,这工作正常。
当我尝试从 ArrayController 中删除实体时,遇到 objc_exception。我只需调用即可删除这些实体;
[myArrayController removeObject:managedObjectToDelete];
不幸的是,当我这样做时,我经常收到“CoreData 无法完成故障”错误。负责实体是 NSArrayController 管理的实体之一。
引发异常时对调用堆栈的检查表明,崩溃是在 NSCollectionView 接收其 _endOfAnimation 方法时发生的。这反过来会启动其他方法来解除绑定(可能是我的视图中带有控件的实体的属性)。
进一步的一点信息是,我正在使用的实体与模型中的其他实体没有关系。
在我看来似乎正在发生以下问题;
- 当我从 NSArrayController 中删除对象时,它们也会从上下文中删除。
- 从上下文中删除后,对象将变成错误
- NSCollectionView 保留了对对象的引用(现在是错误)。它尝试在动画结束时清理它们(解除绑定等)。
- 当 NSCollectionView 尝试清理与对象的绑定时,它会导致核心数据尝试在对象上引发错误(希望我的术语就在那里)。这会导致错误,因为该对象尚未保存到磁盘。
我能想到的防止这种情况的唯一方法是在删除之前将对象保留在存储中(通过保存它们)。这是可行的,但只能以一种黑客的方式,因为我需要确保在再次保存之前完成一轮删除......并且由于错误发生在动画期间......经过一段延迟......并且连续两次保存将导致同样的错误再次发生。
这是否意味着我不能使用 Core-Data 支持的 NSArrayController 来填充 NSCollectionView?如果不是我做错了什么?有更好的方法解决这个问题吗?
Update:
This thread identifies a bug in NSCollectionView when the represented objects of the collection view are NSManagedObjects. The bug is triggered as follows;
(a) Delete objects from the NSArrayController
(b) Perform save on the related nsmanagedobjectcontext any time after the deletion and before the NSCollectionView has finished its animation.
These projects on github demonstrate the issue.
https://github.com/artifacts/NSCollectionViewCoreDataBug
https://github.com/iracooke/CoreDataCollectionViewCrashing
Original question below
I have an NSCollectionView setup with its content binding bound to arrangedObjects of an NSArrayController of core-data entities. In my Collection Item View (the prototype for the NSCollectionView view) I have several controls bound via representedObject of my collectionview item to my core-data entities.
For the most part this works OK.
I encounter an objc_exception when I try to delete entities from my ArrayController. I delete these entities simply by calling;
[myArrayController removeObject:managedObjectToDelete];
Unfortunately, when I do this I frequently get a "CoreData could not fulfill a fault" Error .. with the responsible entity being one of the entities being managed by the NSArrayController.
Examination of the call-stack when the exception is thrown reveals that the crash originates when the NSCollectionView receives its _endOfAnimation method. This in turn initiates other methods to do with unbinding (likely of properties of my entity with controls in my view).
One further bit of info is that the Entity I'm working with has no relationships to other entities in my model.
It looks to me as though the following problem is occurring;
- When I delete objects from my NSArrayController they are in turn deleted from the context.
- After deletion from the context the objects are turned into faults
- The NSCollectionView has retained references to the objects (now faults). It tries to clean them up at the end of its animation (unbinding etc).
- When NSCollectionView tries to cleanup bindings to the object it causes core data to attempt to fire a fault on the object (hope I got my terminology right there). This causes an error because the object has not yet been saved to disk.
The only way I can think of to prevent this, is to persist the objects in the store (by saving them) prior to deletion. This works, but only in a hackish kind of way since I need to ensure that one round of deletion is complete before saving again ... and since the error occurs during an animation .. after some delay .. and two saves in succession would result in the same error occuring again.
Does this mean that I cannot used a Core-Data backed NSArrayController to populate an NSCollectionView? If not what am I doing wrong? Is there a better way around this issue?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
一个直接但无聊的答案:我可以确认支持 Core Data 的 NSArrayController 可以填充 NSCollectionView,集合 NSView 项中的不同 GUI 对象绑定到生成的“Collection View Item”并引用路径上的各种 Core Data 对象。以编程方式删除(并重新排序) NSArrayController 的元素是可行的(免费提供动画)。
也许集合视图中的某些绑定或其他依赖项导致了问题?或者托管对象上下文的线程问题?
A direct yet boring answer: I can confirm that a Core Data backed NSArrayController can populate an NSCollectionView, with different GUI objects in the collection NSView item bound to the generated "Collection View Item" and referring to various Core Data objects along the path. Programmatically deleting (and re-ordering) elements of the NSArrayController works (with animation for free).
Perhaps some bindings or other dependencies inside your Collection View are causing the problem? Or a threading issue with the Managed Object Context(s)?
您可以通过向 CollectionItem 添加指向相应 ArrayController 的(分配)属性来规避此问题。
该属性可以在 -[YourNSCollectionViewSubClass newItemWithRepresentedObject:] 中设置。
然后你可以观察每个item中的arrangedObjects。
当它发生变化并且 item.representedObject 不再包含在arrangedObjects中时,
您将 item.representedObject 设置为 nil。在我的测试 (10.6.8) 中,这会在 CoreData 将对象变成错误之前触发绑定清理。
(我的对象具有关系,并且项目视图具有与它们的绑定)。
顺便说一句:这个问题不仅限于动画期间保存,
撤消/重做/保存组合也可以触发它。
我一直在寻找一个开始观察物品内部的好地方,但是
我想出的唯一一个是 copyWithZone: 我想避免它。
(-awakeFromNib 仅针对第一项调用,-view 为时过早)。
因此我(不情愿地)决定在-newItemWithRepresentedObject中开始观察:
并在项目的 -dealloc 中停止它。
您还应该注意由对象触发的任何控件或其他操作
在项目视图中 - 通过快速单击我可以导致消息释放到对象,
大概是因为仍在反应的按钮在鼠标下动画消失。
我的解决方案是在将representedObject 设置为nil 时禁用控件。 YMMV。
这是我的观察者代码:
You can circumvent this problem by adding a (assign) property to your CollectionItem which points to the corresponding ArrayController.
This property can be set in -[YourNSCollectionViewSubClass newItemWithRepresentedObject:].
Then you can observe arrangedObjects in each item.
When it changes and item.representedObject is not contained in arrangedObjects any more,
you set item.representedObject to nil. In my testing (10.6.8), this triggers the binding cleanup before CoreData turns the objects into faults.
(My objects have relationships and the item view has bindings into them).
btw: This problem is not restricted to saving during animation,
undo/redo/save combinations can trigger it too.
I was looking for a good place to start the observation inside the item but
the only one I came up with was copyWithZone: which I wanted to avoid.
(-awakeFromNib is called only for the first item, -view is too early).
Therefore I (reluctantly) decided to start the observation in -newItemWithRepresentedObject:
and stop it in the item's -dealloc.
You should also watch out for any controls or other actions triggered by objects
in the item view - by rapid clicking I could cause messages to deallocated objects,
presumably because the still reacting buttons where animating away under the mouse.
My solution is to disable the controls when setting representedObject to nil. YMMV.
Here is my observer code: