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

时间:2018-07-15 02:33:30

标签: swift core-data nsfetchedresultscontroller 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

不会崩溃。我尝试过的:

  • 仅在全部三个删除操作之后调用保存,而不是在每个删除操作之后调用保存。
  • 颠倒删除的顺序。
  • 更改了合并政策(惹恼了)
  • 将整个删除功能包装在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")
}

1 个答案:

答案 0 :(得分:0)

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