Duy*_*Anh 3 core-data swift swiftui
几天来我一直在试图理解和弄清楚它,但仍然无法想出解决方案。
例如,有一个包含 100 万行的核心数据实体,我想在视图中显示“滚动时加载”的所有记录,以提高性能,因为加载时加载 100 万行会很慢,而且浪费。
从文档来看,基本上.fetchBatchSize
只会在需要时加载“批量”数据,这是问题的完美解决方案。用户滚动列表,SwiftUI 在需要时批量加载数据。
这是我所拥有的(简化的):
ContextView.swift
struct ContentView: View {
@FetchRequest private var items: FetchedResults<Item>
init() {
_items = FetchRequest<Item>(fetchRequest: request())
}
var body: some View {
List(items) {
Text($0.name)
}
}
func request() -> NSFetchRequest<Item> {
let request = Item.fetchRequest()
request.sortDescriptors = []
request.fetchBatchSize = 5
return request
}
}
Run Code Online (Sandbox Code Playgroud)
问题: @FetchRequest 在没有批处理的情况下同时加载所有记录和/或它确实有效,但同时检索所有批次并破坏了批量检索的整个目的。
我尝试实际加载 100 万行,并花费大量时间来显示视图(因为它正在检索和准备所有 100 万行数据)。如果“.fetchBatchSize”有效,它将仅加载前 5 个,并且随着列表滚动而缓慢加载。
*注意:我知道有.fetchLimit
& .fetchOffset
,但它需要实现一个单独的逻辑 *
您不能使用fetchBatchSize
,因为List
需要 SwiftUI 比较的所有objectID
s 来检测插入、删除和移动。这就是为什么如果您设置的批处理大小比没有批处理慢得多,它会加载所有批处理。
不过,您可以禁用includesPropertyValues以防止Core Data 将所有数据加载到其行缓存中。
禁用它后,SQL 查询将变为:
SELECT 0, t0.Z_PK FROM ZITEM t0
Run Code Online (Sandbox Code Playgroud)
而不是默认启用它获取所有字段:
SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZNAME, t0.ZTIMESTAMP FROM ZITEM t0
Run Code Online (Sandbox Code Playgroud)
要查看查询,请使用启动参数-com.apple.CoreData.SQLDebug 4
(数字越大,日志输出越多)。
您还可以利用 List 的惰性行为,将 移动$0.name
到子视图的主体中,以便核心数据仅访问数据库来读取数据,并在对象滚动到屏幕上时触发对象的错误,并且您可以将在日志中看到这一点:
CoreData: details: SQLite: EXPLAIN QUERY PLAN SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZTIMESTAMP, t0.ZTITLE FROM ZITEM t0 WHERE t0.Z_PK = ?
2 0 0 SEARCH t0 USING INTEGER PRIMARY KEY (rowid=?)
CoreData: annotation: fault fulfilled from database for : 0xa17723cef738c1a0 <x-coredata://FA6F121D-805F-4558-AAAC-F5F60A117256/Item/p9778> with row values: <NSSQLRow: 0x600001745b60>{Item 1-9778-1 timestamp=2022-02-24 13:58:39 +0000 title=NULL and to-manys=0x0}
Run Code Online (Sandbox Code Playgroud)
这些改进可能会带来足够大的性能提升,如下所示:
extension Item {
static func myFetchRequest() -> NSFetchRequest<Item> {
let fr = Self.fetchRequest()
fr.includesPropertyValues = false
fr.sortDescriptors = [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)]
return fr
}
}
struct MyView: View {
@ObservedObject var item: Item
var body: some View {
Text(item.timestamp!, formatter: itemFormatter)
}
}
struct ContentView: View {
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest(
fetchRequest: Item.myFetchRequest(),
animation: .default)
private var items: FetchedResults<Item>
var body: some View {
NavigationView {
List {
ForEach(items) { item in
MyView(item: item)
...
Run Code Online (Sandbox Code Playgroud)
如果您使用@SectionedFetchRequest,那么您将需要获取分段中使用的字段,这可以通过保留includesPropertyValues
其默认值 true 并设置propertiesToFetch
为用于分段的属性名称数组来完成。
例如
extension Item {
static func myFetchRequest() -> NSFetchRequest<Item> {
let fr = Self.fetchRequest()
fr.propertiesToFetch = ["title"]
fr.sortDescriptors = [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)]
return fr
}
}
Run Code Online (Sandbox Code Playgroud)
初始查询结果为:
CoreData: sql: SELECT 1, t0.Z_PK, t0.ZTITLE FROM ZITEM t0 ORDER BY t0.ZTIMESTAMP
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,没有选择timestamp
. 当列表滚动并且访问对象时,将执行其他查询来检索所有字段。
归档时间: |
|
查看次数: |
784 次 |
最近记录: |