FetchDescriptor
是苹果在 Swift Data 框架(WWDC23 推出)中引入的一个关键类型,用于定义数据查询的配置(过滤、排序、分页等)。它是 Swift Data 中替代 Core Data 的 NSFetchRequest
的现代化方案,完全基于 Swift 并发模型设计,支持类型安全和 Swift 原生语法。
一、核心功能
#
功能 | 说明 |
---|
数据过滤 | 通过 Predicate 宏定义查询条件(类型安全,支持 Swift 原生类型) |
数据排序 | 使用 SortDescriptor 定义排序规则(支持多字段排序) |
分页查询 | 通过 fetchLimit 和 offset 实现分页加载 |
关系预加载 | 通过 relationshipsToPrefetch 预加载关联数据,优化性能 |
结果去重 | 使用 propertiesToFetch 指定返回字段,结合 returnsDistinctResults 去重 |
变更跟踪 | 通过 includePendingChanges 控制是否包含未保存的临时数据 |
二、基本用法
#
1. 简单查询
#
// 查询所有 User 对象
let descriptor = FetchDescriptor<User>()
let users = try await context.fetch(descriptor)
2. 过滤 + 排序
#
// 查询年龄 >= 18 的用户,按姓名升序排列
let predicate = #Predicate<User> { $0.age >= 18 }
let sort = SortDescriptor(\User.name, order: .forward)
let descriptor = FetchDescriptor(
predicate: predicate,
sortBy: [sort]
)
3. 分页查询
#
// 每页 20 条数据,加载第 3 页
let descriptor = FetchDescriptor<User>(
fetchLimit: 20,
offset: 40 // (3-1)*20 = 40
)
4. 预加载关联数据
#
// 预加载用户的订单数据
let descriptor = FetchDescriptor<User>(
relationshipsToPrefetch: [\.orders]
)
三、对比 Core Data 的 NSFetchRequest
#
特性 | FetchDescriptor (Swift Data) | NSFetchRequest (Core Data) |
---|
类型安全 | ✅ 基于 Swift 泛型和 KeyPath | ❌ 依赖字符串 Key |
并发支持 | ✅ 原生支持 Swift async/await | ❌ 需手动管理线程上下文 |
语法简洁性 | ✅ 使用 Swift 宏 (#Predicate ) | ❌ 依赖 NSPredicate 字符串 |
关系处理 | ✅ 直接通过 KeyPath 访问关联对象 | ❌ 需要手动配置 relationshipKeyPaths |
内存优化 | ✅ 自动优化延迟加载 | ✅ 类似但需手动配置 |
四、高级用法示例
#
1. 动态排序
#
// 根据用户选择动态切换排序字段
func dynamicSortDescriptor(by keyPath: KeyPath<User, String>) -> FetchDescriptor<User> {
FetchDescriptor(sortBy: [SortDescriptor(keyPath, order: .forward)])
}
2. 聚合查询
#
// 计算用户平均年龄
let descriptor = FetchDescriptor<User>(
propertiesToFetch: [\.age]
)
let ages = try await context.fetch(descriptor)
let averageAge = ages.compactMap { $0.age }.reduce(0, +) / ages.count
3. 去重查询
#
// 获取所有不重复的用户所在城市
let descriptor = FetchDescriptor<User>(
propertiesToFetch: [\.city],
returnsDistinctResults: true
)
五、性能优化建议
#
- 索引加速:对频繁查询的字段添加
@Attribute(.unique)
或 @Attribute(.indexed)
- 批量加载:合理使用
fetchLimit
+ offset
避免一次性加载过多数据 - 选择性加载:通过
propertiesToFetch
仅加载必要字段 - 内存优化:设置
includesPropertyValues = false
当仅需对象引用时