杰瑞科技汇

CoreData教程,如何从零开始学习?

Core Data 教程:从零开始掌握 iOS 数据持久化

目录

  1. 什么是 Core Data? - 核心概念解析
  2. 为什么选择 Core Data? - 优势与适用场景
  3. 核心组件详解 - 理解 Core Data 的“五脏六腑”
  4. 实战演练:创建一个待办事项 App
    • 步骤 1:创建项目与数据模型
    • 步骤 2:生成 Core Data 代码
    • 步骤 3:实现数据的增、删、改、查
    • 步骤 4:绑定数据到 UI (UITableView)
    • 步骤 5:完成增删改查的用户交互
  5. 进阶主题与最佳实践

什么是 Core Data?

Core Data 不是一个数据库,它是一个由 Apple 提供的强大、灵活的对象图管理和持久化框架

CoreData教程,如何从零开始学习?-图1
(图片来源网络,侵删)

你可以把它想象成一个智能的“对象管家”:

  • 对象图管理:它能帮你管理你的 App 中所有对象之间的关系,一个 User 对象可以关联多个 Post 对象,Core Data 会帮你维护这种复杂的关系网络。
  • 持久化:它能将内存中的对象图自动保存到设备上(通常是 SQLite 数据库),并在下次启动 App 时重新加载回内存。

简单来说:你只需要操作普通的 Swift 对象(称为“托管对象”),Core Data 负责将它们与底层数据库进行同步,大大简化了数据存储和管理的复杂度。


为什么选择 Core Data?

  • 面向对象:你操作的是 Swift 对象,而不是 SQL 语句,更符合 Swift 的编程范式。
  • 高性能:它使用 SQLite 作为默认的存储方式,性能优异,它支持“ faulting”(惰性加载)机制,只在需要时才将数据加载到内存,节省资源。
  • 关系支持:能轻松处理对象之间的一对一、一对多、多对多关系。
  • 数据迁移:当你的数据模型(结构)发生变化时,Core Data 提供了强大的数据迁移工具,可以平滑地升级旧数据。
  • 内置功能:提供撤销/重做、数据验证、加密等高级功能。

不适用场景:对于非常简单的键值对存储(如保存几个用户设置),使用 UserDefaults 更简单直接。


核心组件详解

理解这些组件是掌握 Core Data 的关键:

CoreData教程,如何从零开始学习?-图2
(图片来源网络,侵删)
  1. Managed Object Model (托管对象模型)

    • 是什么:一个 .xcdatamodeld 文件,它定义了你的数据结构,就像数据库的“表结构设计图”。
    • 包含Entity(实体,相当于表)、Attribute(属性,相当于列,如 name, age)、Relationship(关系,相当于表之间的关联)。
  2. Persistent Store Coordinator (持久化存储协调器)

    • 是什么:Core Data 架构的“协调中心”。
    • 作用:它负责连接托管对象模型和底层的持久化存储(如 SQLite 文件),它确保数据操作的一致性和安全性。
  3. Managed Object Context (托管对象上下文)

    • 是什么你日常打交道最多的对象,它是一个“工作区”或“草稿板”。
    • 作用
      • 你在上下文中创建、查询、修改托管对象。
      • 这些修改最初只存在于内存中,尚未保存到磁盘。
      • 当你调用 context.save() 时,上下文会将所有更改“推送”给持久化存储协调器,最终写入数据库。
      • 一个 App 通常只有一个主上下文
  4. Persistent Container (持久化容器)

    • 是什么:从 iOS 10 开始引入的便利类,它将上述三个组件(NSManagedObjectModel, NSPersistentStoreCoordinator, NSManagedObjectContext)封装在一起。
    • 作用:极大地简化了 Core Data 的初始化过程,你只需要创建一个 NSPersistentContainer,它就能帮你管理好其他所有部分。
  5. Managed Object (托管对象)

    • 是什么:由 Entity 生成的 Swift 类(通常是 NSManagedObject 的子类),它代表你的数据模型中的一个具体实例。
    • 例如:如果你的实体是 TodoItemTodoItem 类的一个实例就代表一个待办事项。

实战演练:创建一个待办事项 App

我们将创建一个简单的 App,可以添加、查看、编辑和删除待办事项。

步骤 1:创建项目与数据模型

  1. 在 Xcode 中,选择 "Create a new Xcode project"。

  2. 选择 "App" 模板,点击 Next。

  3. 填写产品名称(TodoApp),选择 Team 和 Interface 为 Storyboard,Language 为 Swift

  4. 勾选 "Use Core Data",Xcode 会自动为我们设置好 Core Data 的基本框架。

  5. 创建项目后,你会看到一个 CoreDataHelper.swift 文件(或类似名称)和一个 YourAppName.xcdatamodeld 文件,我们先看 .xcdatamodeld 文件。

  6. 设计数据模型

    • 打开 TodoApp.xcdatamodeld

    • 点击 "Add Entity" 创建一个新实体,命名为 TodoItem

    • 选中 TodoItem 实体,在右侧的 Data Model Inspector 中点击 "+" 添加两个属性:

      • name: Type 为 String, Optional 为 false
      • isCompleted: Type 为 Boolean, Optional 为 false,Default Value 为 false
    • 生成托管对象类

      • 选中 TodoItem 实体,在菜单栏选择 Editor -> Create NSManagedObject Subclass...
      • 选择语言为 Swift,Xcode 会自动生成 TodoItem.swift 文件,这个文件就是我们的数据模型类。

步骤 2:生成 Core Data 代码

我们来看 AppDelegate.swift,Xcode 已经为我们生成了一些代码,但为了更好地理解,我们来手动设置一遍。

  1. AppDelegate.swift 中,我们需要确保 NSPersistentContainer 被正确初始化,代码通常在 application(_:didFinishLaunchingWithOptions:) 方法中。

  2. 我们将创建一个便利的属性来访问 NSManagedObjectContext,这样在任何地方都能轻松获取。

修改 AppDelegate.swift 如下:

import UIKit
import CoreData
// ... 其他代码 ...
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    // MARK: - Core Data Stack
    // 持久化容器,是 Core Data 的核心
    lazy var persistentContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: "TodoApp") // "TodoApp" 是你的 .xcdatamodeld 文件名
        container.loadPersistentStores { _, error in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        }
        return container
    }()
    // 便利属性,获取主上下文
    var context: NSManagedObjectContext {
        return persistentContainer.viewContext
    }
    // MARK: - App Lifecycle
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        return true
    }
    // ... applicationWillTerminate 等方法 ...
    // MARK: - Core Data Saving
    // 保存上下文的函数
    func saveContext () {
        if context.hasChanges {
            do {
                try context.save()
            } catch {
                let nserror = error as NSError
                fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
            }
        }
    }
}

解释

  • persistentContainer: 创建并加载了我们的数据模型和存储。
  • context: 一个只读计算属性,方便我们随时获取到主上下文。
  • saveContext(): 一个封装了保存逻辑的函数,避免重复代码。

步骤 3:实现数据的增、删、改、查

现在我们来创建一个 DataManager 类来处理所有的数据操作,这样可以让 ViewController 更专注于 UI。

  1. 新建一个 Swift 文件 DataManager.swift

  2. 编写 DataManager,让它持有 AppDelegate 的上下文。

import Foundation
import CoreData
class DataManager {
    static let shared = DataManager()
    private init() {} // 单例模式
    // 从 AppDelegate 获取上下文
    lazy var context: NSManagedObjectContext = {
        (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
    }()
    // MARK: - CRUD Operations
    // 创建 (Create)
    func createTodoItem(name: String) -> TodoItem {
        let newItem = TodoItem(context: context)
        newItem.name = name
        newItem.isCompleted = false
        return newItem
    }
    // 读取 (Read) - 获取所有待办事项
    func fetchTodoItems() -> [TodoItem] {
        let fetchRequest: NSFetchRequest<TodoItem> = TodoItem.fetchRequest()
        // 可以在这里添加排序,例如按创建日期排序
        // fetchRequest.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)]
        do {
            return try context.fetch(fetchRequest)
        } catch {
            print("Error fetching data: \(error)")
            return []
        }
    }
    // 更新 (Update) - 通常在编辑时调用
    func updateTodoItem(item: TodoItem, newName: String) {
        item.name = newName
        save()
    }
    // 删除 (Delete)
    func deleteTodoItem(item: TodoItem) {
        context.delete(item)
        save()
    }
    // 保存
    func save() {
        if context.hasChanges {
            do {
                try context.save()
            } catch {
                print("Error saving context: \(error)")
            }
        }
    }
}

CRUD 解释

  • Create: 创建一个新的 TodoItem 实例,设置其属性,然后它会自动被添加到上下文中。
  • Read: 使用 NSFetchRequest 来定义一个查询请求,告诉 Core Data 你想要什么数据。context.fetch() 执行这个请求并返回结果数组。
  • Update: 直接修改从上下文中取出的 TodoItem 对象的属性,这个修改是“暂存”在上下文中的。
  • Delete: 调用 context.delete(),将对象标记为“待删除”。
  • Save: 无论是创建、更新还是删除,都需要调用 save()DataManager.save() 来将上下文中的所有更改真正写入数据库。

步骤 4:绑定数据到 UI (UITableView)

  1. 打开 Main.storyboard,拖入一个 UITableViewViewController 的视图中,并设置好约束。
  2. UITableView 创建一个 Outlet,命名为 todoTableView
  3. ViewController 设置 UITableViewDataSourceUITableViewDelegate

修改 ViewController.swift

import UIKit
import CoreData
class ViewController: UIViewController {
    @IBOutlet weak var todoTableView: UITableView!
    var todoItems: [TodoItem] = []
    override func viewDidLoad() {
        super.viewDidLoad()
        // 设置 tableView 的数据源和代理
        todoTableView.dataSource = self
        todoTableView.delegate = self
        // 初始加载数据
        fetchTodoItems()
    }
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        // 每次视图出现时都重新加载数据,确保数据是最新的
        fetchTodoItems()
        todoTableView.reloadData()
    }
    private func fetchTodoItems() {
        todoItems = DataManager.shared.fetchTodoItems()
    }
    // MARK: - Navigation (点击 "+" 按钮跳转到添加页面)
    // ...
}
// MARK: - UITableViewDataSource
extension ViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return todoItems.count
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "TodoCell", for: indexPath)
        let item = todoItems[indexPath.row]
        cell.textLabel?.text = item.name
        cell.accessoryType = item.isCompleted ? .checkmark : .none
        return cell
    }
}
// MARK: - UITableViewDelegate
extension ViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
        let deleteAction = UIContextualAction(style: .destructive, title: "Delete") { _, _, completionHandler in
            let itemToDelete = self.todoItems[indexPath.row]
            DataManager.shared.deleteTodoItem(item: itemToDelete)
            self.fetchTodoItems() // 重新获取数据
            tableView.deleteRows(at: [indexPath], with: .automatic)
            completionHandler(true)
        }
        let completeAction = UIContextualAction(style: .normal, title: item.isCompleted ? "Undo" : "Done") { _, _, completionHandler in
            let itemToToggle = self.todoItems[indexPath.row]
            itemToToggle.isCompleted.toggle()
            DataManager.shared.save()
            tableView.reloadRows(at: [indexPath], with: .automatic)
            completionHandler(true)
        }
        completeAction.backgroundColor = item.isCompleted ? .systemOrange : .systemGreen
        let configuration = UISwipeActionsConfiguration(actions: [completeAction, deleteAction])
        return configuration
    }
}

代码解释

  • 我们使用 DataManager.shared 来获取数据。
  • fetchTodoItems() 调用 DataManager 的方法,并将结果存入 todoItems 数组。
  • UITableViewDataSource 的两个方法使用 todoItems 数组来显示数据。
  • UITableViewDelegate 中的 trailingSwipeActionsConfigurationForRowAt 实现了左滑显示“完成”和“删除”按钮,并调用了 DataManager 的相应方法。

步骤 5:完成增删改查的用户交互

  1. 添加待办事项

    • 在 Storyboard 中,为 ViewController 添加一个 "Add" Bar Button Item。
    • 创建一个新的 UIViewController 作为添加页面,包含一个 UITextField 和一个 "Save" 按钮。
    • ViewController 中为 Bar Button Item 创建一个 @IBAction,跳转到添加页面。
    • 在添加页面的 "Save" 按钮的 @IBAction 中,获取 UITextField 的文本,调用 DataManager.shared.createTodoItem(name: text),然后保存并返回。
  2. 编辑待办事项

    • ViewController 中,为 UITableView 的行添加点击事件(didSelectRowAt)。
    • 当用户点击一行时,可以跳转到编辑页面(可以复用添加页面的界面),并将当前 TodoItem 的数据填充到 UITextField 中。
    • 在编辑页面的 "Save" 按钮事件中,调用 DataManager.shared.updateTodoItem(...) 来更新数据。

至此,一个完整的、支持增删改查的 Core Data App 就完成了!


进阶主题与最佳实践

  • 后台上下文:对于耗时操作(如大量数据导入),应在后台队列上创建一个 NSManagedObjectContext,避免阻塞主线程,造成界面卡顿。
    let privateContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
    privateContext.parent = self.context // 设置主上下文为父上下文
  • Fetched Results Controller:当数据需要实时反映到 UI 上时(比如数据变化时 UITableView 自动更新),使用 NSFetchedResultsController 是最佳选择,它能帮你自动管理数据源和 UI 的同步。
  • 数据迁移:当你的 .xcdatamodeld 结构发生变化时(如新增一个字段),你需要创建一个“轻量级迁移”或“重量级迁移”的映射模型,以确保旧数据能正确转换到新结构。
  • 错误处理:始终对 context.save()context.fetch() 等可能抛出异常的操作进行 do-catch 处理。
  • 使用 SwiftUI:在 SwiftUI 项目中,可以使用 @Environment(\.managedObjectContext) 来轻松获取 Core Data 上下文,并结合 @FetchRequest 属性包装器实现声明式的数据绑定。

Core Data 是一个功能极其强大的框架,虽然初看起来概念较多,但一旦掌握了其核心组件(Container, Context, Model),并通过实践建立起工作流,你就会发现它在处理复杂数据时的巨大优势。

本教程带你走完了从零到一构建一个 Core Data 应用的全过程,关键在于:

  1. 设计好数据模型
  2. 正确初始化 Core Data 栈
  3. 通过 NSManagedObjectContext 管理对象的生命周期
  4. 使用 NSFetchRequest 进行数据查询
  5. 别忘了保存

希望这份教程对你有帮助!祝你编码愉快!

分享:
扫描分享到社交APP
上一篇
下一篇