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

发布于 2024-11-08 00:49:23 字数 2348 浏览 0 评论 0原文

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

更新操作会删除对象。这些删除无意中导致了崩溃。当主线程在保存更新上下文的同时重新加载 UITableView 时,会发生崩溃。表视图的数据源由 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

stacktrace

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

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

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

I have an NSOperation which updates the apps data. This operation has its own context and changes are passed back to the context on the main thread via the contextDidSave notification.

The update operation deletes objects. These deletions are inadvertently causing a crash. The crash occurs when the main thread is reloading a UITableView at the same time that the update context is saving. The datasource for the tableview is backed by a NSFetchedResultsController.

Neither CoreData or UIKit output any logging. The crash is a SIG_ABRT that occurs in tableView:cellForRowAtIndexPath: while accessing an attribute of managedObject. Here's the result of a successful update. You can see that the table is populate once, then the context saves, which in turn causes the table to be reloaded:

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

And here's an the output when a crash occurred:

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

stacktrace

I can think of two potential solutions:

  1. lock the persitentStoreCoordinator while the table view is refreshing. (This could be done by subclassing UITableView and overriding reloadData to provide a location to lock and unlock the store)
  2. Defer the deletion to a later date, eg, when the app quits. (This could be done by adding a isStale attrib to the entities or by creating a new Trash entity and adding objects to that.)

However, both of these solutions seem really hacky. Am I missing something? My first though was to try and make the NSFetchedResultsController pre fetch all objects, but this isn't possible. Another possible solution was to lock the store when using controllerDidChangeContent: but the problem still occurs when the table reload is not triggered by the fetchedResultsController.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

彩扇题诗 2024-11-15 00:49:23

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

As others have mentioned the stack trace is vital. Without we can only guess at the solution.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文