Core Data获取数据的NSFetchRequest
Core Data获取数据的NSFetchRequest

Core Data获取数据的NSFetchRequest

NSFetchRequest 是 Core Data 中用于从持久化存储中获取数据的请求对象。它允许定义查询条件(如过滤条件、排序规则等),然后执行查询来检索需要的数据。可以看作是向 Core Data 发出的 “请求” 或 “查询”,它告诉 Core Data 应该从数据存储中提取哪些数据。

主要功能和用法

1、定义查询条件:可以通过 NSFetchRequest 来指定查询的实体类型(数据模型中的对象),以及可能的排序、过滤等条件。

2、执行查询:执行请求后,将获得一个结果集,通常是一个对象数组([Entity] 类型)。

主要参数

1、Entity:要查询的数据实体。

2、Predicate:可选的过滤条件,类似 SQL 中的 WHERE 子句。

3、Sort Descriptors:可选的排序规则,类似 SQL 中的 ORDER BY 子句。

示例

假设我们有一个 ExchangeRate 实体,我们希望获取所有的汇率数据,并按照某个属性(例如汇率)升序排序:

let context = persistentContainer.viewContext
let fetchRequest: NSFetchRequest<ExchangeRate> = ExchangeRate.fetchRequest()

// 添加过滤条件(可选)
fetchRequest.predicate = NSPredicate(format: "currency == %@", "USD")

// 添加排序规则(可选)
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "rate", ascending: true)]

do {
    let results = try context.fetch(fetchRequest)
    print(results) // 处理结果
} catch {
    print("获取数据失败: \(error)")
}

核心部分

1、Entity:即查询的对象类型。ExchangeRate.fetchRequest() 创建了一个针对 ExchangeRate 实体的请求。

2、Predicate:过滤条件,用于指定要查询的记录。例如,currency == ‘USD’ 可以筛选出所有 “USD” 货币的汇率数据。

3、Sort Descriptors:定义返回结果的排序方式,例如按汇率升序或降序排序。

常见形式

NSFetchRequest<NSManagedObject>是最常用的获取实体对象(NSManagedObject)的方式。

查询结果会返回完整的 NSManagedObject 实体,可以读取所有属性,甚至进行修改、保存。

let fetchRequest: NSFetchRequest<ExchangeRate> = ExchangeRate.fetchRequest()
let results = try context.fetch(fetchRequest)
// results 是 [ExchangeRate],可直接使用对象的属性

除了NSFetchRequest<NSManagedObject>,还有以下几种常用的NSFetchRequest<T>泛型类型:

1、NSFetchRequest<NSDictionary>

let request = NSFetchRequest<NSDictionary>(entityName: "ExchangeRate")
request.resultType = .dictionaryResultType
request.propertiesToFetch = ["currencySymbol"]

适用场景:只读取部分字段、统计、聚合查询、更快更省内存。

2、NSFetchRequest<NSNumber>

let request = NSFetchRequest<NSFetchRequestResult>(entityName: "ExchangeRate")
request.resultType = .countResultType
let count = try context.count(for: request)

适用场景:快速统计某个查询条件下的数据总数。

3、NSFetchRequest<NSManagedObjectID>

let request = NSFetchRequest<NSManagedObjectID>(entityName: "ExchangeRate")
request.resultType = .managedObjectIDResultType

适用场景:拿到 ID 后再异步加载实体对象,减少初期内存占用。

4、注意

虽然可以用泛型 NSFetchRequest<T> 声明,但实际返回值的类型是由 resultType 决定的,所以:

如果用 <NSDictionary>,resultType 必须是 .dictionaryResultType。

如果用 <NSManagedObjectID>,必须是 .managedObjectIDResultType。

否则运行时会崩溃或数据不对。

NSFetchRequest<NSManagedObject>的resultType值默认是.managedObjectResultType:

let request: NSFetchRequest<ExchangeRate> = ExchangeRate.fetchRequest()
// 默认是 .managedObjectResultType
let results = try context.fetch(request)

这是最常用的情况,可以省略设置 resultType,因为:

request.resultType == .managedObjectResultType  // 默认值

常用属性

NSFetchRequest 是 Core Data 中用来向数据库查询数据的“查询模板”,它有很多可以用来自定义查询行为的属性。

1、entityName:String类型,用于      查询的实体名。

2、predicate:NSPredicate?类型,用于筛选数据(相当于 SQL 的 WHERE)。

3、sortDescriptors:[NSSortDescriptor]?类型,用于排序规则。

4、fetchLimit:Int类型,限制返回的最大数量(类似 SQL 的 LIMIT)。

5、fetchOffset:Int类型,跳过多少条再开始取(用于分页)。

6、returnsObjectsAsFaults:Bool类型,是否返回“轻量”对象(懒加载,默认 true)。

7、includesPropertyValues:Bool类型,是否包含属性值,默认 true。

8、includesSubentities:Bool类型,是否包含子实体,默认 true。

9、resultType:NSFetchRequestResultType类型,决定返回结果的类型(对象、字典、ID、数量)。

10、propertiesToFetch:[Any]?类型,指定要查询的属性字段(只在 .dictionaryResultType 有效)。

11、returnsDistinctResults:Bool类型,是否去重(配合 propertiesToFetch 使用)。

12、includesPendingChanges:Bool类型,是否包含未保存的更改,默认 true。

13、shouldRefreshRefetchedObjects:Bool类型,是否强制刷新已存在的对象。

常用属性示例

1、筛选 + 排序

let request: NSFetchRequest<ExchangeRate> = ExchangeRate.fetchRequest()
request.predicate = NSPredicate(format: "currencySymbol == %@", "USD")
request.sortDescriptors = [NSSortDescriptor(key: "date", ascending: true)]

2、分页查询

request.fetchLimit = 50
request.fetchOffset = 100  // 从第 101 条开始

3、查询字段 + 去重(dictionary 查询)

let request = NSFetchRequest<NSDictionary>(entityName: "ExchangeRate")
request.resultType = .dictionaryResultType
request.propertiesToFetch = ["currencySymbol"]
request.returnsDistinctResults = true

4、只查数量(不返回具体对象)

let request = NSFetchRequest<NSFetchRequestResult>(entityName: "ExchangeRate")
request.resultType = .countResultType

总结

NSFetchRequest 是 Core Data 中获取数据的基本工具,通过它,可以非常灵活地指定查询条件、排序方式和其他限制,从数据存储中提取出所需要的数据。

扩展知识

NSFetchRequest和@FetchRequest的区别

第一种方式:手动使用 NSFetchRequest 和 persistentContainer

static func fetchExchangeRates() -> [ExchangeRate]? {
    let context = persistentContainer.viewContext
    let fetchRequest: NSFetchRequest<ExchangeRate> = ExchangeRate.fetchRequest()
    
    do {
        let results = try context.fetch(fetchRequest)
        return results
    } catch {
        print("获取数据失败: \(error)")
        return nil
    }
}

这种方法是更传统的 Core Data 使用方式。手动创建一个 NSFetchRequest,然后通过 persistentContainer.viewContext(即 Core Data 的上下文)来执行这个请求。context.fetch(fetchRequest) 会返回请求的数据(ExchangeRate 类型的数组)。这种方式拥有更多控制权,可以自定义请求的内容,例如排序、过滤等。

第二种方式:使用 @FetchRequest 属性包装器

@FetchRequest(
    entity: ExchangeRate.entity(),
    sortDescriptors: [NSSortDescriptor(keyPath: \ExchangeRate.someProperty, ascending: true)]
)
var tasks: FetchedResults<ExchangeRate>

这是一种 SwiftUI 风格的写法,简化了很多事情。通过 @FetchRequest,可以将 NSFetchRequest 和 Core Data 的数据获取过程与视图绑定起来。这个属性包装器会自动在视图更新时重新加载数据,简化了在视图中获取数据的流程。

entity: ExchangeRate.entity():指定了想要获取的实体类型。

sortDescriptors:可以指定排序条件,类似于在手动 NSFetchRequest 中使用 sortDescriptors。

var tasks: FetchedResults<ExchangeRate>:这是绑定的变量,Core Data 会在数据变动时自动更新这个变量,触发视图更新。

为什么看起来不一样?

1、@FetchRequest 是针对 SwiftUI 的:它是为了简化与视图的绑定设计的,适合在视图模型中直接使用,不需要手动管理上下文和数据请求。

2、NSFetchRequest 是传统 Core Data 的方式:适合在更复杂的逻辑中使用,或者当不需要和 UI 直接绑定时。

总结

@FetchRequest:适用于 SwiftUI,直接与视图绑定,自动更新数据。

NSFetchRequest:传统的 Core Data 请求方式,适合更细粒度的控制和非 SwiftUI 环境。

如果做一个 iOS 应用,使用 @FetchRequest 和 SwiftUI 的集成更加流畅,但如果需要更多的控制或者在非视图层次(如数据管理类)操作数据,手动使用 NSFetchRequest 会更适合。

Core Data在dictionaryResultType的类型保护机制

在使用NSFetchRequest<NSDictionary>获取数据的过程中,例如获取date字段,在使用的过程中需要进行解包,例如:

func fetchExistingDates() -> Set<Date> {
    let fetchRequest = NSFetchRequest<NSDictionary>(entityName: "Eurofxrefhist")
    fetchRequest.resultType = .dictionaryResultType
    fetchRequest.propertiesToFetch = ["date"]   // 指定 date 字段
    fetchRequest.returnsDistinctResults = true  // 去重

    do {
        let results = try context.fetch(fetchRequest)
        let dates = results.compactMap { $0["date"] as? Date }
        return Set(dates)
    } catch {
        print("获取已有日期失败: \(error)")
        return []
    }
}

在这段代码中,当 fetchRequest.resultType 设置为 .dictionaryResultType 时,Core Data 不返回实体对象(NSManagedObject),它返回的是一个个 NSDictionary,里面的字段是以 Any 类型包装的。

因此,需要进行解包:

results.compactMap { $0["date"] as? Date }

在使用的过程中,告诉编译器date应该是Date类型。

如果不进行解包,得到的就是[Any]类型数组,没法直接用于Set<Date>,会报错:

Argument type '[Any]' expected to be an instance of a class or class-constrained type

相关文章

SwiftUI获取Core Data数据的@FetchRequest:https://fangjunyu.com/2025/03/30/swiftui获取core-data数据的fetchrequest/

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开放者。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注