我在SwiftUI应用程序中有几个地方需要从CoreData获取(不断增长的)信息集,并在更新UI之前对其进行处理。
我使用MVVM方法来实现这一点,因此有许多NSFetchedResultsController
示例负责获取和更新结果。
为了停止阻塞主线程,我尝试将NSFetchedResultsController
转换为在后台线程上执行其工作。
只有当我禁用-com.apple.CoreData.ConcurrencyDebug 1
参数以确保我没有违反线程规则时,这才有效。
我已经确保所有的CD访问都是在后台线程上完成的,但是当SwiftUI视图从CD对象访问属性时,它似乎是在主线程上进行的,因此导致崩溃。
现在我可以想到几个可能的解决方案:
- 确保
NSFetchedResultsController
获取的数据可以在很短的时间内获取/处理,以确保UI不会挂起 - 创建“DTO”对象,以便将获取的数据插入到后台线程内的另一个类示例中,并让UI使用它。
- 这种方法的问题是,在对象上进行编辑或对更新做出React变得更加复杂,因为您需要手动保持CD和DTO对象同步。
**编辑:**添加了我使用的ViewModel示例
class ContentViewModel: NSObject, ObservableObject, NSFetchedResultsControllerDelegate {
@Published var items: [Item] = []
private let viewContext = PersistenceController.shared.container.viewContext
private let resultsController: NSFetchedResultsController<Item>!
private let backgroundContext: NSManagedObjectContext!
override init() {
let fetchRequest: NSFetchRequest<Item> = Item.fetchRequest()
let sort = NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)
fetchRequest.sortDescriptors = [sort]
fetchRequest.propertiesToFetch = ["timestamp"]
backgroundContext = PersistenceController.shared.container.newBackgroundContext()
backgroundContext.automaticallyMergesChangesFromParent = true
resultsController = NSFetchedResultsController(
fetchRequest: fetchRequest,
managedObjectContext: backgroundContext,
sectionNameKeyPath: nil,
cacheName: nil)
super.init()
resultsController.delegate = self
try? resultsController.performFetch()
DispatchQueue.main.async { [self] in
items = resultsController.fetchedObjects ?? []
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
DispatchQueue.main.async { [self] in
withAnimation {
items = (controller.fetchedObjects as? [Item]) ?? []
}
}
}
}
1条答案
按热度按时间eoigrqb61#
核心问题是您在后台队列中获取项目,然后在主队列中使用它们。这是不允许的核心数据。如果没有并发调试标志,它可能不会立即崩溃,但它正在设置一个可能在某个时候发生的崩溃--这就是并发调试抱怨的原因。
要在一个队列上获取但在另一个队列上使用结果,可以使用对象ID。它们是线程安全的。你会用类似
接着是这样的
然后在主队列中,按对象的ID查找对象。对于一个对象数组,您可以通过在 predicate 类似
NSPredicate(format: "self in %@", itemIDs)
的地方执行fetch来实现这一点。它应该是快的,因为在这一点上没有必要做任何过滤,因为内部核心数据缓存。