Core Data — Apple 用于管理 iOS 和 macOS 应用中对象图的框架。它提供数据持久化、变更跟踪、操作撤销以及通过 NSFetchedResultsController 与 UI 的集成。根据 Apple Developer (2025) 文档,Core Data 不是数据库——它是一个对象建模层,默认使用 SQLite 作为持久存储来加载和保存对象。
要点
Core Data — 是 Apple Cocoa Touch SDK 中的一个对象图管理和持久化框架。它提供了面向对象的数据操作接口:开发者处理实体(Entity)、属性和关系(Relationship),而 Core Data 在底层将这些对象转换为关系型数据库记录。
Core Data 最早在 Mac OS X 10.4 Tiger (2005) 中为 macOS 推出,并于 iOS 3.0 (2009) 移植到 iOS。在 20 多年的发展中,该框架从 SQLite 之上的简单抽象层演变为一个完整的栈,支持通过 NSPersistentCloudKitContainer 进行云同步、通过自动上下文管理实现多线程、以及通过 Swift Concurrency 实现异步加载。
根据 Slack Community (2025) 的 iOS 开发者调查,Core Data 被用于 68% 的商业 iOS 应用中进行本地数据存储。尽管因其复杂性和多层架构而受到批评,但由于与系统的紧密集成、零成本(内置于 SDK)以及对 iCloud 同步的支持,该框架仍然是 Apple 应用的标准。
一个常见的误解 — 将 Core Data 视为数据库。该框架不直接执行 SQL 查询,也不是 DBMS。Core Data 是一个对象管理(object graph management)层,可以使用 SQLite、Binary 或 In-Memory 进行持久化。类比:Core Data 就像 Hibernate 或 Entity Framework 这样的 ORM,但适用于 Apple 生态系统,其底层的 SQLite 就像 Hibernate 底层的 MySQL。
Core Data 栈由四个相互关联的组件组成:NSManagedObjectModel(数据模式)、NSPersistentStoreCoordinator(存储协调器)、NSManagedObjectContext(工作上下文)和 NSPersistentContainer(从 iOS 10 开始统一三者的容器)。NSPersistentContainer 自动创建和配置栈。
每个组件执行严格定义的功能。NSManagedObjectModel 加载包含实体描述的 .xcdatamodeld 文件。NSPersistentStoreCoordinator 将模型与物理存储文件(SQLite)连接起来。NSManagedObjectContext 提供用于处理对象的临时区域。Container 将所有内容整合到一个初始化调用中。
SQLite (NSSQLiteStoreType) — 大多数应用中使用的标准存储。数据保存在单个 .sqlite 文件中,支持 ACID 事务。Binary (NSBinaryStoreType) — 用于小数据集(最多几百个对象)的二进制格式存储。In-Memory (NSInMemoryStoreType) — RAM 中的临时存储,不保存到磁盘,用于测试和缓存。
| 存储类型 | 格式 | 性能 | 何时使用 |
|---|---|---|---|
| SQLite | .sqlite | 高 | 生产环境的标准选择 |
| Binary | .binary | 中 | 小数据集 |
| In-Memory | RAM | 最高 | 测试、缓存、临时数据 |
| CloudKit | iCloud | 取决于网络 | 设备间同步 |
存储选择在初始化 NSPersistentStoreDescription 时用一行代码设置。开发者可以将存储从 SQLite 切换到 In-Memory(用于单元测试)或切换到 CloudKit(用于 iCloud 同步),而无需更改处理对象的代码——Core Data 通过统一的上下文 API 抽象了存储类型之间的差异。
NSManagedObject — 所有 Core Data 对象的基类,代表一个实体记录。每个托管对象都有一个唯一的 NSManagedObjectID(持久标识符),绑定到一个上下文,并通过 KVO(Key-Value Observing)跟踪其更改。开发者创建 NSManagedObject 的子类来定义实体的类型化属性。
NSManagedObjectContext — Core Data 的核心组件,为所有对象操作提供工作区。上下文跟踪对象的添加、删除和修改(变更跟踪),通过 undoManager 支持操作撤销,并在收到保存通知时自动合并其他上下文的更改。
私有队列规则:NSManagedObjectContext 以 .privateQueueConcurrencyType 或 .mainQueueConcurrencyType 类型创建。主上下文绑定到主 UI 线程,私有上下文在后台队列上执行。每个上下文只能在其自己的队列中使用——从另一个线程访问托管对象会导致崩溃。parentContext 允许组织上下文层次结构以实现异步写入。
struct CoreDataStack {
let container: NSPersistentContainer
init(name: String) {
container = NSPersistentContainer(name: name)
container.loadPersistentStores { _, error in
if let error = error {
fatalError("Failed to load store: \(error)")
}
}
container.viewContext.automaticallyMergesChangesFromParent = true
}
func backgroundContext() -> NSManagedObjectContext {
let context = container.newBackgroundContext()
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
return context
}
}NSPersistentContainer 自动创建 viewContext(主队列)并提供 newBackgroundContext() 用于后台操作。automaticallyMergesChangesFromParent = true 属性使 viewContext 在后台上下文保存时自动获取更改,无需手动重新查询数据即可更新 UI。
NSPersistentStoreCoordinator 管理物理数据存储:打开文件、基于模型创建 SQLite 表、在模式更改时执行迁移。当使用 NSSQLiteStoreType 类型初始化 NSPersistentStoreDescription 时,Core Data 会创建与 .xcdatamodeld 模型对应的 SQLite 文件。
Core Data 不使用标准的 SELECT/INSERT/UPDATE SQL 查询。相反,它基于模型和通过 NSFetchRequest 的查询生成内部 SQL 命令。开发者可以通过启动参数 -com.apple.CoreData.SQLDebug 1 启用 SQL 日志记录来调试查询性能。
轻量级迁移(Lightweight Migration)——在添加新属性、更改 optional/required 或使用 renamingID 重命名时自动更新 SQLite 模式的过程。当模式发生根本性变化(如合并或拆分实体)时,需要进行重量级迁移,并通过自定义的 NSMigrationManager 执行。
let description = NSPersistentStoreDescription()
description.url = FileManager.default
.urls(for: .documentDirectory, in: .userDomainMask)
.first?
.appendingPathComponent("Model.sqlite")
description.setOption(true as NSNumber,
forKey: NSMigratePersistentStoresAutomaticallyOption)
description.setOption(true as NSNumber,
forKey: NSInferMappingModelAutomaticallyOption)
let container = NSPersistentContainer(name: "AppModel")
container.persistentStoreDescriptions = [description]
container.loadPersistentStores { _, error in
if let error = error { print("Migration error: \(error)") }
}通过 NSMigratePersistentStoresAutomaticallyOption 和 NSInferMappingModelAutomaticallyOption 配置自动迁移,允许 Core Data 在新版本模型中添加属性或实体时独立更新 SQLite 文件。如果迁移不可能进行,存储协调器会抛出带有原因描述的错误——开发者必须通过 NSMigrationManager 实现自定义迁移。
NSFetchRequest — 从 Core Data 获取对象的主要工具。查询包含实体名称、谓词(过滤器)、排序、限制和偏移量(offset)。结果作为 NSManagedObject 或类型化子类的数组返回。NSPredicate 支持带有 AND、OR、IN、LIKE 和子查询的复杂条件。
NSBatchDeleteRequest — 一种无需将每个对象加载到内存的批量删除方法。该查询在 SQLite 级别执行,绕过托管对象上下文,并在完成后才更新上下文。类似的批量查询也存在于更新(NSBatchUpdateRequest)和插入(NSBatchInsertRequest)。
CRUD(Create, Read, Update, Delete)在 Core Data 中通过上下文的方法执行:insert、fetch、save 和 delete。所有更改在调用 context.save() 之前都是临时的——该方法将更改保存到持久 SQLite 存储。如果保存出错,上下文会保持在修改状态以供重试。
let context = container.viewContext
// 创建
let user = User(context: context)
user.id = 42
user.name = "Alice"
// 读取
let request = User.fetchRequest()
request.predicate = NSPredicate(format: "name CONTAINS %@", "Ali")
request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
let results = try context.fetch(request)
// 更新
results.first?.name = "Alice Updated"
// 删除
if let first = results.first { context.delete(first) }
// 保存
try context.save()上下文的保存(context.save())——一个关键操作。如果不调用保存,所有更改仅保留在内存中。上下文跟踪 hasChanges 状态,可以在保存前进行检查。对于后台操作,使用带自有保存的 newBackgroundContext;对于 UI,使用基于定时器或在应用进入后台时自动保存的 viewContext。
第一实践——使用 NSPersistentCloudKitContainer 通过 iCloud 在用户设备之间同步数据。云同步通过在存储描述中添加 CloudKit 选项来启用。Core Data 在同步期间自动管理冲突并合并来自其他设备的更改。
第二实践——避免在大表上使用没有谓词的 fetchRequest。每次无条件获取都会将实体的所有对象加载到内存中,导致高 RAM 消耗和 UI 变慢。始终使用谓词和限制。对于分页,在 NSFetchRequest 中使用 fetchLimit 和 fetchOffset。
第三实践——配置 mergePolicy 以解决多线程访问时的冲突。NSMergeByPropertyObjectTrumpMergePolicy 从最后保存的上下文更新冲突属性。NSRollbackMergePolicy 在冲突时撤销当前上下文的更改。策略的选择取决于应用的业务逻辑。
第四实践——使用 NSFetchedResultsController 与表格和集合进行集成。它自动订阅 NSManagedObjectContextDidSave 通知,仅加载必要的对象(faulting),并通过相应的索引路径通知委托关于插入、删除和移动,以便 UITableView 动画。
Faulting——Core Data 对象的懒加载机制。由 fetch 查询返回的托管对象处于 fault 状态——其属性未完全加载,仅加载了标识符。完全加载(fire fault)在首次访问任何属性时发生。Relationship prefetching(setRelationshipKeyPathsForPrefetching)预先加载相关对象,避免 N+1 查询。
extension UserRepository {
func fetchUsersWithPosts() throws -> [User] {
let request = User.fetchRequest()
request.predicate = NSPredicate(format: "isActive == YES")
request.relationshipKeyPathsForPrefetching = ["posts"]
request.returnsObjectsAsFaults = false
request.fetchBatchSize = 20
let context = container.viewContext
return try context.fetch(request)
}
}fetchBatchSize = 20 强制 Core Data 以 20 个对象为一批加载数据(用于屏幕显示),而不是一次性加载整个表。returnsObjectsAsFaults = false 标志确保所有用户的属性立即加载,这对直接显示很有用。预取 "posts" 关系可避免在访问帖子时为每个用户执行单独的查询。
常见问题
不,Core Data 是一个对象图管理框架。它提供了用于处理对象、跟踪更改和保存的 API。不应将 Core Data 底层的数据库(默认为 SQLite)与框架本身混淆。Core Data 是一个 ORM,而不是 DBMS。
可以,Core Data 支持三种存储类型:SQLite、Binary 和 In-Memory。存储选择通过 NSPersistentStoreDescription 设置。In-Memory 存储不将数据保存到磁盘,适用于单元测试。Binary 存储是一种用于紧凑对象集合的过时格式。
对于轻量级迁移,启用 NSMigratePersistentStoresAutomaticallyOption 和 NSInferMappingModelAutomaticallyOption。对于复杂更改,通过 Xcode 创建 Mapping Model(.xcmappingmodel)。CloudKit 存储(NSPersistentCloudKitContainer)在模式与 iCloud 服务器同步时自动支持迁移。
SwiftData——是 Apple 的新框架(iOS 17+),使用 Swift Macros 和 Swift Concurrency 建立在 Core Data 之上。SwiftData 语法更简单:实体用 @Model 宏描述,上下文用 @Environment(\.modelContext) 描述。在底层,SwiftData 使用相同的 Core Data 栈和 SQLite。
启用启动参数 -com.apple.CoreData.SQLDebug 1 — Core Data 将在 Xcode 控制台中显示所有 SQL 查询及其持续时间。对于性能分析,使用 Instruments 的 Core Data 模板,该模板显示 fault 查询次数、对象加载量和上下文保存时间。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。