【问题标题】:Infrequent CoreData crash "nilOutReservedCurrentEventSnapshot"罕见的 CoreData 崩溃“nilOutReservedCurrentEventSnapshot”
【发布时间】:2016-06-13 12:43:16
【问题描述】:

我的应用程序中发生了崩溃,这种情况很少发生(可能每 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__` 的含义,那将极大地帮助我。坠机位置截图如下:

【问题讨论】:

  • 自从 iOS 9.3 在我的 CoreData 代码中发布以来,我已经开始遇到这种崩溃。想了解如何调试?
  • 这个运气好吗?我也有同样的问题
  • 能否请您发布一些调用 saveContext() 的代码的 sn-ps。我怀疑您创建的上下文没有正确保留。也许上下文是在块中捕获的,或者是从错误的线程访问的?

标签: ios xcode core-data swift2


【解决方案1】:

很遗憾,您找不到关于_nilOutReservedCurrentEventSnapshot__ 的信息 在线..

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

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

_nilOutReservedCurrentEventSnapshot__ 未记录在案这一事实意味着不应发生此类行为。

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

因此,导致错误unrecognized selector sent to instance 是因为在不是NSManagedObject 的对象上调用了_nilOutReservedCurrentEventSnapshot__,因为NSManagedObject 已被释放并且其他东西现在填充了它的内存。这是事实。

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


[图片取自here]

从我能找到的有关此错误的所有堆栈溢出问题中,看来它们都使用父子并发模式。

这个问题完全有可能是由于采用了这种模式而对 ManagedObjects 的实现或处理不当造成的。

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

问题中还提到这是一种罕见的情况,并非每次都会发生。这意味着在进行特定保存或更改之前上下文可能很好,并且上下文以某种方式变得不同步,并且执行另一个保存会使应用程序崩溃。

来自有关在上下文之间同步更改的文档:

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

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

Possible reason for crash



有几件事可能会导致这种情况,但有两件事很突出:

1.由于关闭 iOS 应用程序中的模态视图或 OSX 应用程序中的工作表而从已处理的引用中释放 ManagedObject。

可能的解决方案:在获取 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 通知,您的应用程序应该注册并用作它需要采取的任何操作的触发器。此通知不仅包含有关已删除对象的信息,还包含有关已更改对象的信息。您需要处理这些更改,因为它们可能是删除的结果。大多数这些类型的更改都涉及瞬态关系或获取的属性。

这里可能的解决方案是:



因为这里没有其他信息可以推断导致错误的原因,而且我无法重现它。我建议尝试使用以下方法找到您的 ManagedObject 被释放的位置Instruments 中的僵尸分析。

如果随后将消息发送到这些已释放对象之一(现在是 NSZombie 对象),则会标记僵尸,应用程序崩溃,录制停止,并出现僵尸消息对话框。然后,您可以检查僵尸对象的保留和释放历史,以确定问题发生的确切位置。

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

如果您使用这种方法,建议您将设计重新考虑为类似连接到同一个持久存储协调器的两个独立托管对象上下文 这可能会“避免”该问题将来发生。

[图片取自here]

您可以在linked article 中看到巨大的性能差异

【讨论】:

  • 谢谢。我不能确定我是否修复了它,但对我来说,触发问题的实体可以在几个地方访问。我的模型使用 B 的“标识符”在实体 A 和 B 之间建立了弱关系。实体 A 通过获取请求实现访问 B 的属性,并将其保留在成员变量中以供重复访问。我猜这不适用于托管对象上下文如何管理内存。再次查看代码,我决定最好保留 B 的 objectID 而不是 B 本身,并在每次访问属性时解析它。
【解决方案2】:

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

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

没有完整的回溯,很难调试具体问题。在我看来,可能有两个罪魁祸首:

  1. 您的上下文的 parentObject 不知何故无效。

  2. 您正在尝试将 Core Foundation 对象保存为 NSManagedObject 上的属性,这并不符合预期。

【讨论】:

    【解决方案3】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-04-11
      • 1970-01-01
      • 2020-02-24
      • 2016-12-23
      • 1970-01-01
      • 2020-08-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多