从 backgroundThread 合并到 mainThread 上下文:删除顺序随机所以崩溃 NSTableView

Merging from backgroundThread to mainThread context: delete order random so crashes 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] 行要删除。所以它使应用程序崩溃。
如果我在 mainThread 上执行删除操作,则删除操作一个接一个地完成:

# 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

不会崩溃。我尝试了什么:

再收集一些删除命令:

# 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")
}

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