核心数据保存竞争条件错误

Posted

技术标签:

【中文标题】核心数据保存竞争条件错误【英文标题】:Core Data saving race condition bug 【发布时间】:2011-05-17 17:59:13 【问题描述】:

我有一个NSOperation,它会更新应用数据。此操作有自己的上下文,更改通过contextDidSave 通知传递回主线程上的上下文。

更新操作删除对象。这些删除会无意中导致崩溃。当主线程在保存更新上下文的同时重新加载UITableView 时会发生崩溃。 tableview 的数据源由NSFetchedResultsController 支持。

CoreData 或 UIKit 都不输出任何日志记录。崩溃是在访问 managedObject 的属性时在tableView:cellForRowAtIndexPath: 中发生的 SIG_ABRT。这是成功更新的结果。您可以看到表格被填充一次,然后上下文保存,这反过来又导致表格被重新加载:

tableView:numberOfRowsInSection: rowCount = 29
tableView:cellForRowAtIndexPath: row: 0
tableView:cellForRowAtIndexPath: row: 1
tableView:cellForRowAtIndexPath: row: 2
tableView:cellForRowAtIndexPath: row: 3
tableView:cellForRowAtIndexPath: row: 4
tableView:cellForRowAtIndexPath: row: 5
tableView:cellForRowAtIndexPath: row: 6
tableView:cellForRowAtIndexPath: row: 7
tableView:cellForRowAtIndexPath: row: 8
tableView:cellForRowAtIndexPath: row: 9
Will saved update context
Did saved update context
Will merge update context
tableView:numberOfRowsInSection: rowCount = 58
Did merge update context
tableView:numberOfRowsInSection: rowCount = 29
tableView:cellForRowAtIndexPath: row: 0
tableView:cellForRowAtIndexPath: row: 1
tableView:cellForRowAtIndexPath: row: 2
tableView:cellForRowAtIndexPath: row: 3
tableView:cellForRowAtIndexPath: row: 4
tableView:cellForRowAtIndexPath: row: 5
tableView:cellForRowAtIndexPath: row: 6
tableView:cellForRowAtIndexPath: row: 7
tableView:cellForRowAtIndexPath: row: 8
tableView:cellForRowAtIndexPath: row: 9

这是发生崩溃时的输出:

tableView:numberOfRowsInSection: rowCount = 29
tableView:cellForRowAtIndexPath: row: 0
tableView:cellForRowAtIndexPath: row: 1
tableView:cellForRowAtIndexPath: row: 2
Will saved update context
tableView:cellForRowAtIndexPath: row: 3
tableView:cellForRowAtIndexPath: row: 4
tableView:cellForRowAtIndexPath: row: 5

我能想到两种可能的解决方案:

    在刷新表视图时锁定persitentStoreCoordinator。 (这可以通过继承 UITableView 并覆盖 reloadData 来提供锁定和解锁商店的位置) 将删除推迟到以后的日期,例如,当应用程序退出时。 (这可以通过向实体添加 isStale 属性或通过创建新的 Trash 实体并向其添加对象来完成。)

但是,这两种解决方案看起来都非常老套。我错过了什么吗?我的第一个想法是尝试让NSFetchedResultsController 预取所有对象,但这是不可能的。另一种可能的解决方案是在使用controllerDidChangeContent: 时锁定存储,但是当 fetchedResultsController 未触发表重新加载时问题仍然存在。

【问题讨论】:

@Benedict Cohen 控制台和堆栈跟踪上的崩溃显示了什么? 你确定你做对了吗? “此操作有自己的上下文,更改通过 contextDidSave 通知传递回主线程上的上下文。”这部分看起来特别狡猾developer.apple.com/library/ios/#documentation/cocoa/conceptual/… @TheBlack 我已经仔细检查过,我 99% 确定我做对了。我已经阅读并重新阅读了使用通知的其他线程中的跟踪更改以及 mergeChangesFromContextDidSaveNotification 的文档:。 developer.apple.com/library/ios/documentation/cocoa/conceptual/… @Benedict Cohen 你看过 CoreDataBooks 的例子吗?似乎有几个问题很容易忘记或错过。另外,您是否在表格视图中使用 beginUpdate/endUpdate? @Benedict Cohen SIG_ABRT 也可以建议过早释放对象。是否有可能其他对象持有对已删除对象的引用或已删除对象对对象的某些无效引用?从我所见,如果上下文合并失败,您应该得到 internalinconsistency 异常,而不是 SIG_ABRT(我不是 100% 确定,但值得检查) 【参考方案1】:

正如其他人所提到的,堆栈跟踪至关重要。没有我们只能猜测解决方案。

【讨论】:

以上是关于核心数据保存竞争条件错误的主要内容,如果未能解决你的问题,请参考以下文章

Xcode 错误地报告 Swift 访问竞争条件

核心数据保存错误

iPhone:核心数据保存错误

核心数据保存日期错误

核心数据关系在删除后导致保存错误

尝试将纬度和经度保存到核心数据会导致访问错误