调试 NSObjectInaccessibleException - ID 为 0x123456789 的 NSManagedObject 已失效
Posted
技术标签:
【中文标题】调试 NSObjectInaccessibleException - ID 为 0x123456789 的 NSManagedObject 已失效【英文标题】:Debugging NSObjectInaccessibleException - The NSManagedObject with ID:0x123456789 has been invalidated 【发布时间】:2009-08-09 07:24:59 【问题描述】:我有一个我正在努力追查的错误。我相信正在发生的事情是,我正在从底层数据库中删除一个对象,而另一个托管对象上下文(在另一个线程中)有一个错误,并在它试图完成错误时得到“NSObjectInaccessibleException”。
场景是我有一个视图通过一个上下文访问数据,同时在后台,另一个威胁是从存储中清除过时的记录。后台线程应该只清除视图不需要的对象——这显然不是这种情况,但我无法准确追踪发生了什么。当我看到这个缺陷时,已经为时已晚,这是一个相对罕见的缺陷,主要只发生在现场。
因此我的问题是:在调试 CoreData 时我是否缺少任何技巧 - 我可以在另一个上下文中跟踪对象的生命周期吗? IE。当我删除我的对象时,是否有一种简单的方法可以查看是否有任何其他上下文引用了同一个对象?使用它,我可以构建一些测试代码来检查我的逻辑并找到错误。
【问题讨论】:
吉姆,你为什么丢掉我的 iPhone 标签?我正在使用 iPhone,所以我没有 CoreData 绑定。 Roger - 我把 iPhone 标签放回去了。但这确实是一个通用的核心数据问题,可能发生在桌面或移动平台上。 所以我已经诊断出这是一个 iPhone 问题,尽管可能发生在任何地方。 Ben,为什么要删除 iPhone 特定标签并添加 cocoa-touch(iPhone 特定标签)? 【参考方案1】:我早些时候遇到了这个错误,罪魁祸首是我正在清理(完成释放)我的上下文,然后尝试访问(以前)由该上下文管理的对象。
在我的例子中,上下文是一个“临时”上下文,当视图关闭时会消失。但是,我有一个后台作业,该视图已经产生了想要更新对象的视图。
当 [managedObject isFault] 为真时,我最终为返回 nil 的托管对象创建了一个访问器。然后在我的代码中,我检查了访问器选择器的值,以确保我有一个有效的对象可以使用(比如当我的后台工作最终完成它的工作时)。
我对 Core Data 很陌生,所以可能有更好/更智能的方法来做到这一点,但我认为它为我解决了这个问题。
【讨论】:
Jason,感谢您再次引起我的注意。我最近修复了我的错误,它是清理和 MapKit 的组合 - 地图视图让他们的委托太长了。在您的情况下,您可能不应该从后台线程访问相同的 NSManagedObject 上下文 - CoreData 不是线程安全的。相反,您应该将 NSManagedObjectReference 传递给您的后台任务。 是的,你绝对是对的。在我的情况下,我在实际编辑对象之前跳回到主线程(我创建所有上下文),但是当它的上下文已经再见时,这仍然没有好处。 你说的这个“NSManagedObjectReference”是什么?不存在,根据文档... 他的意思可能是NSManagedObjectID
s。【参考方案2】:
我也遇到了这个问题。我做了一些重构,以遵循 Apple 的 “find-or-create” 模式来批量导入数据。通过按照建议将 undomanager 设置为 nil,我创建了一个专用于导入的新上下文。 所以:
// create a new special context for the bulk import of data
NSManagedObjectContext *importContext = [[NSManagedObjectContext alloc] init];
[importContext setPersistentStoreCoordinator:_persistentStoreCoordinator];
// avoid tracking for undo/redo operations
[importContext setUndoManager:nil];
然后我创建了一个 fetchRequest 来检索存储在数据库中的对象的 id,并在导入循环中测试了对象的 id 以查找它是否包含在检索到的 id 数组中......问题是在给定的时间间隔内我正在保存 importContext 并重置它。由于我确实错误地引用了 importContext 而不是 defaultContext 我得到了那个错误。我只是通过更改来修复:
NSArray *storedObjects = [importContext executeFetchRequest:checkRequest error:&fetchError];
与:
NSArray *storedObjects = [defaultContext executeFetchRequest:checkRequest error:&fetchError];
【讨论】:
只是给 ios 用户的一点小提示。在 iOS 上,undoManager
属性默认为 nil
。【参考方案3】:
当第二个上下文试图在已从持久存储中删除的对象中出错时,它在做什么?
这听起来像是一个错误,可能有两部分:您没有合并对等上下文中的更改,并且您有一个逻辑错误导致您在线程 B 中使用已在线程 A 中删除的对象。
通常,您会希望使用 -[NSManagedObjectContext mergeChangesFromContextDidSaveNotification:]
合并来自对等上下文的更改。
【讨论】:
我认为这里有两个错误,我同意它们的含义:-(我需要的是一种追踪它们的方法。 您是否正在合并来自同行上下文的更改?出于调试目的,您可以在保存上下文时记录已删除的对象。如果您知道所有其他现存的上下文是什么,您还可以检查使用这些上下文注册的对象,但您必须以线程安全的方式进行。 你是对的。我决定我需要修复整个线程情况来解决这个缺陷。这不是微不足道的,但可能对我的健康有益。【参考方案4】:解决方案是清理和this mapkit bug 的组合。在我发布了我的 NSManagedObjectContext 之后,地图视图正在保留它的委托。 Mapkit 向委托询问注释的坐标,而我的委托对象尝试查询处于已发布上下文中的对象(类似于 Jason 的问题)。
修复如 Jake 的博客文章中所述 - 完成地图视图后将代理设置为 nil。
【讨论】:
链接已损坏。你有新的吗,或者你能解释一下吗? 我以为我做到了。问题是一些类对它们的委托持有一个弱引用,并在它被释放后向它发送消息。如果您遇到此问题,请确保在您是委托的任何类中都没有委托(或数据源)引用。 感谢您的澄清。 这也是我在 iOS 8.0 上的问题。在委托中,我正在访问一个已删除的托管对象。以上是关于调试 NSObjectInaccessibleException - ID 为 0x123456789 的 NSManagedObject 已失效的主要内容,如果未能解决你的问题,请参考以下文章