是否可以在 NSFetchedResultsController 上使用后台线程进行大量读取操作以确保 UI 响应能力?

Che*_*eng 5 core-data ios swift

newBackgroundContext苹果在其官方地震示例中向我们展示了如何使用后台线程(通过使用)执行繁重的写入操作- https://github.com/yccheok/earthquakes-WWDC20

但是,对于大量的读操作呢?(数百万行用于压力测试目的)

当我们第一次启动应用程序并且应用程序正在从 CoreData 读取大量数据时,我们还希望我们的应用程序 UI 具有响应能力。

以下是使用的代码片段NSFetchedResultController


如果有很多行,UI 就没有响应能力

let controller = NSFetchedResultsController(fetchRequest: fetchRequest,
                                            managedObjectContext: persistentContainer.viewContext,
                                            sectionNameKeyPath: nil, cacheName: nil)
controller.delegate = fetchedResultsControllerDelegate

// Perform the fetch.
do {
    // This statement tooks some time to complete if you have a lot of rows.
    try controller.performFetch()
} catch {
    fatalError("Unresolved error \(error)")
}
Run Code Online (Sandbox Code Playgroud)

如果有很多行,UI 就没有响应能力

我们尝试controller.performFetch()使用后台线程来执行。尽管如此,但不知道为什么,用户界面仍然没有响应能力。我的猜测是,在NSFetchedResultsController占用UI主线程之后,要执行一些耗时的I/O读操作。

let controller = NSFetchedResultsController(fetchRequest: fetchRequest,
                                            managedObjectContext: persistentContainer.viewContext,
                                            sectionNameKeyPath: nil, cacheName: nil)
controller.delegate = fetchedResultsControllerDelegate

DispatchQueue.global(qos: .background).async {
    // Perform the fetch.
    do {
        // This statement tooks some time to complete if you have a lot of rows.
        try controller.performFetch()
    } catch {
        fatalError("Unresolved error \(error)")
    }
}
Run Code Online (Sandbox Code Playgroud)

UI 现在已响应。但解决方案是错误的...

我想,我们也需要放在NSFetchedresultController后台线程下。因此,我们进行如下修改。

let backgroundContext = persistentContainer.newBackgroundContext()
let controller = NSFetchedResultsController(fetchRequest: fetchRequest,
                                            managedObjectContext: backgroundContext,
                                            sectionNameKeyPath: nil, cacheName: nil)
controller.delegate = fetchedResultsControllerDelegate

backgroundContext.perform {
    // Perform the fetch.
    do {
        // This statement tooks some time to complete if you have a lot of rows.
        try controller.performFetch()
    } catch {
        fatalError("Unresolved error \(error)")
    }
}
Run Code Online (Sandbox Code Playgroud)

UI 在获取过程中具有响应能力,一段时间后即可获取并显示数据。

但是,如果我们使用启动参数进一步调查

-com.apple.CoreData.ConcurrencyDebug 1
Run Code Online (Sandbox Code Playgroud)

controller.performFetch()执行完成后我们会遇到以下崩溃。

CoreData`+[NSManagedObjectContext __Multithreading_Violation_AllThatIsLeftToUsIsHonor__]:
Run Code Online (Sandbox Code Playgroud)

NSFetchedResultController我想知道,当我们用来加载大量数据(根据测试,只有几百万行)时,是否可以使 UI 响应?可以NSFetchedResultController在后台线程下运行吗?

Mun*_*ndi 1

我认为您的问题可能是fetchedResultsControllerDelegate新控制器的分配。这似乎可以从您遇到多线程违规的地方推断出来。

从您提供的代码来看,尚不清楚委托是什么类型的对象,但推测它管理 UI,因此位于主线程上。您可以看到谁这可能会导致线程问题。

感谢您从我的答案中发布到此问题的链接/sf/answers/1029733281/datasource我仍然认为,如果您在完成后将FRC 指定为您的表,然后reloadData.

附带说明:虽然我发现这个问题从工程角度来看很有趣且具有挑战性,但我怀疑直接在移动设备上处理数百万条记录是否是一个好的架构。