CoreData 中的异步读取 - 使用 newBackgroundContext + FetchRequest 与 newBackgroundContext + NSAsynchronousFetchRequest 的区别?
Asynchronous read in CoreData - Difference in using newBackgroundContext + FetchRequest vs newBackgroundContext + NSAsynchronousFetchRequest?
似乎有两种方法可以在不阻塞主线程的情况下在 CoreData 中执行异步读取 UI。
newBackgroundContext + NSFetchRequest
来源:https://www.advancedswift.com/core-data-background-fetch-save-create/
// Create a new background managed object context
let context = persistentContainer.newBackgroundContext()
// If needed, ensure the background context stays
// up to date with changes from the parent
context.automaticallyMergesChangesFromParent = true
// Perform operations on the background context
// asynchronously
context.perform {
do {
// Create a fetch request
let fetchRequest: NSFetchRequest<CustomEntity>
fetchRequest = CustomEntity.fetchRequest()
fetchRequest.fetchLimit = 1
let objects = try context.fetch(fetchRequest)
// Handle fetched objects
}
catch let error {
// Handle error
}
}
newBackgroundContext + NSAsynchronousFetchRequest
来源:https://www.marcosantadev.com/coredata_crud_concurrency_swift_2/
let privateManagedObjectContext = persistentContainer.newBackgroundContext()
// Creates a fetch request to get all the dogs saved
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Dog")
// Creates `asynchronousFetchRequest` with the fetch request and the completion closure
let asynchronousFetchRequest = NSAsynchronousFetchRequest(fetchRequest: fetchRequest) { asynchronousFetchResult in
// Retrieves an array of dogs from the fetch result `finalResult`
guard let result = asynchronousFetchResult.finalResult as? [Dog] else { return }
// Dispatches to use the data in the main queue
DispatchQueue.main.async {
// Do something
}
}
do {
// Executes `asynchronousFetchRequest`
try privateManagedObjectContext.execute(asynchronousFetchRequest)
} catch let error {
print("NSAsynchronousFetchRequest error: \(error)")
}
但是,请注意,如果我要启用标志 -com.apple.CoreData.ConcurrencyDebug 1
,上面的代码将很不幸地导致致命错误。到目前为止,我还没有很好的解决方案。详情请参考Why I am getting Multithreading_Violation_AllThatIsLeftToUsIsHonor for this simplest NSAsynchronousFetchRequest use case?
请问newBackgroundContext + NSFetchRequest
和newBackgroundContext + NSAsynchronousFetchRequest
有什么区别?
我应该如何取舍?谢谢。
NSAsynchronousFetchRequest
有两个主要特点:
我们不需要单独的上下文(背景,任何),这意味着您可以在主视图上下文中执行它,其他所有内容(比如在需要时创建背景上下文等)将由API。
注意:您仍然需要在完成块中重定向到主线程,因为它可以在任何队列上调用。
如果初始请求设置为fetchLimit
,我们可以直接通过NSAsynchronousFetchResult.progress
跟踪获取数据的进度。
1.关于 __Multithreading_Violation_AllThatIsLeftToUsIsHonor__
异常:
本帖有详细讨论:
CoreData asynchronous fetch causes concurrency debugger error
一致认为这是 CoreData 中的错误。
有一个错误报告:https://openradar.appspot.com/30692722 在撰写本文时 8 年后仍然开放。
2。如何正确使用NSAsynchronousFetchRequest
API 于 2014 年推出,并在此 WWDC 视频 225_sd_whats_new_in_core_data 中进行了讨论。
没有提到 NSAsynchronousFetchRequest
应该在主(视图)上下文或后台上下文中使用。
我查看了在 GitHub 上使用 NSAsynchronousFetchRequest
的几个随机实现,并找到了主要和背景上下文的示例。
然而,在使用后台上下文时,您必须做的一件事是将提取执行包装在perform
块中(documentation)。
在您链接的文章和上面摘录的示例中,缺少此内容!
它应该是这样的:
privateManagedObjectContext.perform {
do {
try privateManagedObjectContext.execute(asynchronousFetchRequest)
} catch let error {
print("error trying to fetch saving objects:", error.localizedDescription)
}
}
同一篇文章还有另一个潜在的问题,所以持保留态度:
DispatchQueue.main.async {
// here the objects in result (belongs to private context) are
// accessed on the main queue – the whole point is to *not* do that!
// one might get away with it because it is only read access to id
// but good luck debugging this...
let dogs: [Dog] = result.lazy
.flatMap { [=11=].objectID }
.flatMap { mainManagedObjectContext.object(with: [=11=]) as? Dog }
// ...
我的理解NSAsynchronousFetchRequest
是最好从主上下文中使用,其目的其实是对你隐藏后台上下文业务
所以:mainContext + NSAsynchronousFetchRequest
3。我该如何取舍?
在我看来,创建 NSAsynchronousFetchRequest
的初衷是为了简化我们的异步核心数据获取。为此,您可以充分利用它,尤其是当您需要处理进度和取消时。
但是我可能不会在我的项目中使用它,因为
- 文档稀疏
- 维护得不好(永远打开错误)
- 它没有被很好地采用(例如流行和优秀的 CoreData 包装器 CoreStore 没有使用它)
最后一个想法 - 在开始异步获取之前,请确保您确实需要它。最好先优化查询、数据模型或批处理设置的性能。
似乎有两种方法可以在不阻塞主线程的情况下在 CoreData 中执行异步读取 UI。
newBackgroundContext + NSFetchRequest
来源:https://www.advancedswift.com/core-data-background-fetch-save-create/
// Create a new background managed object context
let context = persistentContainer.newBackgroundContext()
// If needed, ensure the background context stays
// up to date with changes from the parent
context.automaticallyMergesChangesFromParent = true
// Perform operations on the background context
// asynchronously
context.perform {
do {
// Create a fetch request
let fetchRequest: NSFetchRequest<CustomEntity>
fetchRequest = CustomEntity.fetchRequest()
fetchRequest.fetchLimit = 1
let objects = try context.fetch(fetchRequest)
// Handle fetched objects
}
catch let error {
// Handle error
}
}
newBackgroundContext + NSAsynchronousFetchRequest
来源:https://www.marcosantadev.com/coredata_crud_concurrency_swift_2/
let privateManagedObjectContext = persistentContainer.newBackgroundContext()
// Creates a fetch request to get all the dogs saved
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Dog")
// Creates `asynchronousFetchRequest` with the fetch request and the completion closure
let asynchronousFetchRequest = NSAsynchronousFetchRequest(fetchRequest: fetchRequest) { asynchronousFetchResult in
// Retrieves an array of dogs from the fetch result `finalResult`
guard let result = asynchronousFetchResult.finalResult as? [Dog] else { return }
// Dispatches to use the data in the main queue
DispatchQueue.main.async {
// Do something
}
}
do {
// Executes `asynchronousFetchRequest`
try privateManagedObjectContext.execute(asynchronousFetchRequest)
} catch let error {
print("NSAsynchronousFetchRequest error: \(error)")
}
但是,请注意,如果我要启用标志 -com.apple.CoreData.ConcurrencyDebug 1
,上面的代码将很不幸地导致致命错误。到目前为止,我还没有很好的解决方案。详情请参考Why I am getting Multithreading_Violation_AllThatIsLeftToUsIsHonor for this simplest NSAsynchronousFetchRequest use case?
请问newBackgroundContext + NSFetchRequest
和newBackgroundContext + NSAsynchronousFetchRequest
有什么区别?
我应该如何取舍?谢谢。
NSAsynchronousFetchRequest
有两个主要特点:
我们不需要单独的上下文(背景,任何),这意味着您可以在主视图上下文中执行它,其他所有内容(比如在需要时创建背景上下文等)将由API。 注意:您仍然需要在完成块中重定向到主线程,因为它可以在任何队列上调用。
如果初始请求设置为
fetchLimit
,我们可以直接通过NSAsynchronousFetchResult.progress
跟踪获取数据的进度。
1.关于 __Multithreading_Violation_AllThatIsLeftToUsIsHonor__
异常:
本帖有详细讨论:
CoreData asynchronous fetch causes concurrency debugger error
一致认为这是 CoreData 中的错误。
有一个错误报告:https://openradar.appspot.com/30692722 在撰写本文时 8 年后仍然开放。
2。如何正确使用NSAsynchronousFetchRequest
API 于 2014 年推出,并在此 WWDC 视频 225_sd_whats_new_in_core_data 中进行了讨论。
没有提到 NSAsynchronousFetchRequest
应该在主(视图)上下文或后台上下文中使用。
我查看了在 GitHub 上使用 NSAsynchronousFetchRequest
的几个随机实现,并找到了主要和背景上下文的示例。
然而,在使用后台上下文时,您必须做的一件事是将提取执行包装在perform
块中(documentation)。
在您链接的文章和上面摘录的示例中,缺少此内容!
它应该是这样的:
privateManagedObjectContext.perform {
do {
try privateManagedObjectContext.execute(asynchronousFetchRequest)
} catch let error {
print("error trying to fetch saving objects:", error.localizedDescription)
}
}
同一篇文章还有另一个潜在的问题,所以持保留态度:
DispatchQueue.main.async {
// here the objects in result (belongs to private context) are
// accessed on the main queue – the whole point is to *not* do that!
// one might get away with it because it is only read access to id
// but good luck debugging this...
let dogs: [Dog] = result.lazy
.flatMap { [=11=].objectID }
.flatMap { mainManagedObjectContext.object(with: [=11=]) as? Dog }
// ...
我的理解NSAsynchronousFetchRequest
是最好从主上下文中使用,其目的其实是对你隐藏后台上下文业务
所以:mainContext + NSAsynchronousFetchRequest
3。我该如何取舍?
在我看来,创建 NSAsynchronousFetchRequest
的初衷是为了简化我们的异步核心数据获取。为此,您可以充分利用它,尤其是当您需要处理进度和取消时。
但是我可能不会在我的项目中使用它,因为
- 文档稀疏
- 维护得不好(永远打开错误)
- 它没有被很好地采用(例如流行和优秀的 CoreData 包装器 CoreStore 没有使用它)
最后一个想法 - 在开始异步获取之前,请确保您确实需要它。最好先优化查询、数据模型或批处理设置的性能。