不常见的CoreData崩溃" nilOutReservedCurrentEventSnapshot"

时间:2016-03-01 07:35:44

标签: ios xcode core-data swift2

我的应用程序中发生了一次崩溃,很少发生(可能每30次运行一次)。错误代码包含一个奇怪的选择器名称_nilOutReservedCurrentEventSnapshot__,我根本无法找到任何文档。这是来自我的控制台的提要:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFType _nilOutReservedCurrentEventSnapshot__]: unrecognized selector sent to instance 0x157b51e0'
*** First throw call stack:
(0x2358810b 0x22d2ee17 0x2358d925 0x2358b559 0x234bbc08 0x24cbf445 0x24ca4d99 0x249bec 0x245c90 0x19b68c 0x24a5c97 0x24b05ab 0x24a8ef9 0x24b1a8d 0x24b18e7 0x232bfb29 0x232bf718)
libc++abi.dylib: terminating with uncaught exception of type NSException

如果有人能够阐明这句话_nilOutReservedCurrentEventSnapshot__`的含义,那对我来说将是非常有帮助的。崩溃位置的屏幕截图如下:

The error

3 个答案:

答案 0 :(得分:1)

_nilOutReservedCurrentEventSnapshot__NSManagedObject内部的私有方法,用于从Core Data对象中刷新属性和值。您可以在NSManagedObject here的私有标题中看到它。

__NSCFType是Objective-C运行时内部使用的Core Foundation类型的私有包装器。您可以通过查看私有标题here了解更多信息,但没有太多可以看到的内容。

如果没有完整的回溯,则很难调试特定问题。我看到它的方式,可能有两个罪魁祸首:

  1. 您的上下文的parentObject在某种程度上无效。

  2. 您正在尝试将Core Foundation对象保存为NSManagedObject上的属性,而不是期望它。

答案 1 :(得分:1)

不幸的是,关于_nilOutReservedCurrentEventSnapshot__你找不到多少 在线..

它可能与托管对象的快照生命周期有关。

  

当Core Data从持久性存储中获取对象时,它会获取其状态的快照。快照是对象的持久属性的字典 - 通常是其所有属性以及与其具有一对一关系的任何对象的全局ID。快照参与乐观锁定。当框架保存时,它会将每个已编辑对象的快照中的值与持久性存储中当时相应的值进行比较。

没有记录_nilOutReservedCurrentEventSnapshot__的事实意味着不应该发生这种行为。

我们所知道的是它是NSManagedObject类的函数。

因此导致错误unrecognized selector sent to instance是因为在_nilOutReservedCurrentEventSnapshot__的对象上调用了NSManagedObject,因为NSManagedObject已被解除分配,而其他内容现在已填满其内存。这是事实。

关于应用程序的性质及其CoreData设置的问题没有提供上下文,但推断它使用的是父子并发模式。这很重要。

Parent-Child context pattern
[从here]检索的图像

从我发现的有关此错误的堆栈溢出问题的所有问题来看,它们似乎都使用了父子并发模式。

问题完全可能是由于采用不正确的ManagedObjects实现或处理这种模式而引起的。

可以使用父子上下文的情况是同步云数据或处理用户编辑某些内容时所做的更改,可以选择放弃或撤消所做的更改,这可以在后台线程上完成。

在问题中也提到这种情况很少见,并且每次都不会发生。这意味着在进行某种保存或更改之前,上下文可能正常,并且某种情况下上下文变得不同步,并且执行另一次保存会使应用程序崩溃。

关于在上下文之间同步更改的文档:

  

如果在应用程序中使用多个托管对象上下文,则Core Data不会自动通知一个上下文对另一个对象所做的更改。通常,这是因为上下文旨在成为一个便笺簿,您可以在其中单独更改对象,并且可以在不影响其他上下文的情况下放弃更改。

保存子项时,更改将发送到父上下文,但如果父项已单独更改,则不会发送更改。强烈建议您永远更改父上下文;只能通过保存子上下文并从那里传播更改。

Possible reason for crash



有几件事可能导致这种情况,但有两件事情可以解决:

<强> 1。从引用中释放ManagedObject,该引用由于在OSX应用程序中解除iOS应用程序或工作表中的模态视图而被处理。

可能的解决方案:在获取ManagedObjects之前将子上下文的retainsRegisteredObjects属性设置为true(然后在保存上下文后设置为false以避免进一步的潜在内存泄漏)。 warning!

例如。 ctx.retainsRegisteredObjects = true



<强> 2。未处理的上下文更改。

https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreData/ChangeManagement.html#//apple_ref/doc/uid/TP40001075-CH22-SW1

  

考虑具有两个托管对象上下文和单个持久性存储协调器的应用程序。如果用户删除第一个上下文(moc1)中的对象,则可能需要通知第二个上下文(moc2)已删除对象。在所有情况下,moc1会自动通过应用程序应注册的NSNotificationCenter发布NSManagedObjectContextDidSaveNotification通知,并将其用作触发所需的任何操作。此通知不仅包含有关已删除对象的信息,还包含有关已更改对象的信息。您需要处理这些更改,因为它们可能是删除的结果。大多数这些类型的更改都涉及瞬态关系或获取的属性。

这里可能的解决方案是:



因为此处没有其他信息可以推断导致错误的原因,我无法重现它...我建议尝试使用仪器中的Zombie profiling找到ManagedObject解除分配的位置。

  

如果消息随后被发送到其中一个解除分配的对象(现在是NSZombie对象),则会标记僵尸,应用程序崩溃,录制停止,并显示Zombie Messaged对话框。然后,您可以检查僵尸对象的保留和释放历史记录,以确定问题的确切位置。

https://github.com/iascchen/SwiftCoreDataSimpleDemo/blob/master/SwiftCoreDataSimpleDemo/CoreDataHelper.swift

希望有人可以更清楚地了解导致问题的原因。



+++++++++++++++++++++++++++++++++

旁注:
父/子上下文可能会导致UI的口吃 - “every fetch request and every save operation will fully block the main thread while data is read or written to/from disk”.这是因为每次获取都是通过父上下文(在主线程上)提取给孩子的。

supporting SO answer

如果您使用此方法,建议您将设计重新考虑为连接到同一持久性存储协调器的两个独立托管对象上下文 哪些可能会避免& #39;未来发生的问题。

Two contexts one coordinator [从here]检索的图像

您可以在linked article

中看到严重的效果差异

答案 2 :(得分:0)

一个疯狂的猜测..是否有机会从与创建上下文的实际线程不同的线程调用此save方法?为了更安全,请始终执行执行/ PerformBlockAndWait中的保存:操作。