从backgroundThread到mainThread上下文的合并:随机删除顺序,导致NSTableView崩溃



我在删除后台线程上的几个对象时遇到了问题:

func delete(hierarchy: Hierarchy) throws {
//        let backgroundThread = self.mainThread
let hierarchies = try self.hierarchies.reloadFrom(context: backgroundThread)
let hierarchy = try hierarchy.reloadFrom(context: backgroundThread)
let hierarchyModel = hierarchy.model!
// .. doing some checks .. 
backgroundThread.performAndWait {
backgroundThread.delete(rootFile)
backgroundThread.delete(hierarchyModel)
for (index, model) in hierarchies.model!.hierarchies!.array.enumerated() {
let hierarchyModel = model as! HierarchyModel
hierarchyModel.position = Int32(index)
}
}
try backgroundThread.save()
}

然后这个循环:

func delete(hierarchies: [Hierarchy]) {
do {
for hierarchy in hierarchies {
try modelController.delete(hierarchy: hierarchy)
}
} catch {
NotificationCenter.default.post(FatalErrorNotification(error: error))
}
}

在保存时,通常的NSFetchedResultsController调用其委托。注意,他在mainThread上工作。

public func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
print(indexPath, newIndexPath)
switch type {
case .insert:
// ...
case .delete:
tableView.removeRows(at: IndexSet(integer: indexPath!.item), withAnimation: .slideRight)
case .update:
// ...
case .move:
// ...
}
}

我使用mainThread.automaticallyMergeChangesFromParent来获取从backgroundThread到mainThread的更改。到目前为止,这对我来说效果很好
我的问题是在合并过程中会发生这样的事情:

# Merging from background thread, delete order crashes.
Optional([0, 0]) nil
Optional([0, 1]) nil
Optional([0, 2]) nil

在fetchedResultsController委托调用中崩溃之前。它崩溃了,因为在第三次调用时,只剩下[0,0]行需要删除。所以它会使应用程序崩溃
如果我在主线程上进行删除,删除会一个接一个地进行:

# On mainThread: delete order works.
Optional([0, 0]) nil
Optional([0, 2]) Optional([0, 1])
Optional([0, 1]) Optional([0, 0])
going to end updates.
endUpdates
beginUpdates
Optional([0, 0]) nil
Optional([0, 1]) Optional([0, 0])
going to end updates.
endUpdates
beginUpdates
Optional([0, 0]) nil
going to end updates.
endUpdates

它不会崩溃。我尝试过的:

  • 只在所有三次删除之后调用save,而不是在每次删除之后调用
  • 颠倒删除的顺序
  • 更改合并策略(感到恼火(
  • 包装backgroundThread.performAndWait中的整个删除函数{…}
  • 试图找到一种方法,使更改在删除之前转到mainThread

更多已收集的删除订单:

# On background thread: lucky.
beginUpdates
Optional([0, 2]) nil
Optional([0, 1]) nil
Optional([0, 0]) nil
going to end updates.
endUpdates
# On background thread: lucky again.
beginUpdates
Optional([0, 2]) nil
Optional([0, 1]) nil
Optional([0, 0]) nil
going to end updates.
endUpdates
# On background thread: unlucky again.
Optional([0, 1]) nil
Optional([0, 0]) nil
Optional([0, 2]) nil
# On background thread: unlucky again.
Optional([0, 0]) nil
Optional([0, 1]) nil
Optional([0, 2]) nil
# On background thread: unlucky on 2nd delete already.
beginUpdates
Optional([0, 1]) nil
Optional([0, 2]) nil

我希望有人能帮我解决这个问题。真的很感激,我被卡住了。

//
// NSFetchedResultsControllerDelegate
//
public func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
print("beginUpdates")
tableView.beginUpdates()
}
public func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
print("going to end updates.")
tableView.endUpdates()
print("endUpdates")
}

我想我已经找到问题了。您不能删除后台线程上的对象,然后重新排序,然后期望删除的顺序与主线程正确。

最新更新