核心数据保存竞争条件错误
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】:正如其他人所提到的,堆栈跟踪至关重要。没有我们只能猜测解决方案。
【讨论】:
以上是关于核心数据保存竞争条件错误的主要内容,如果未能解决你的问题,请参考以下文章