前言
在前几篇文章我们介绍了 SwiftData 的基本使用、增删改查操作以及数据的排序和过滤。这篇文章主要介绍 SwiftData 几个主要的类。
ModelContainer、ModelContext 与 ModelConfiguration
SwiftData 有三个比较重要的类:ModelContainer、ModelContext 与 ModelConfiguration。它们的名称虽然比较相似,但功能却是截然不同。
ModelContainer
负责创建和管理用于所有 SwiftData 存储需求的实际数据库文件。ModelContext
负责跟踪内存中已创建、修改和删除的所有对象,以便稍后将它们全部保存到模型容器中。ModelConfiguration
确定数据的存储方式和位置,包括使用哪个 CloudKit 容器(如果有)以及是否应启用保存。此配置提供给你的模型容器以确定其行为方式。
每个使用 SwiftData 的项目都需要至少有一个模型容器,以便操作系统知道在哪里读取和写入数据。该容器通常放置在磁盘中,但也可能存储在内存中,以便在应用程序终止时自动清除。
但并非所有内容都需要立即存储在磁盘上。因为,如果我们总是在磁盘读写我们正在使用的每一条数据,那是非常消耗性能的。所以,SwiftData 会更喜欢将数据从存储读取到内存中,然后从那里使用它,这样速度更快。当我们使用 @Query
或类似方法加载数据时,它会存储在这个模型上下文中以便快速访问。这样我们就可以进行多次更改,并一次性将它们生效,这是更加高效的一种做法。
模型上下文提供了一些非常有用的额外功能,包括撤消或重做更改的能力以及启用或禁用自动保存的能力。因此,模型上下文存储所有内存中的对象和更改,而模型容器则长期存储它。
因为所有 SwiftData 的项目都必须至少有一个模型容器,因此你的主 App 结构中通常会包含这样的代码:
php
.modelContainer(for: XXModel.self)
这还为我们创建了一个称为 main context
的模型上下文,并将该上下文放入 SwiftUI 的环境中供我们使用。这个 main context
始终在 Swift 的 main actor
上运行,因此我们可以安全地从用户界面使用。
Tips:模型容器可以在程序中自由传递,但模型上下文必须保留在创建它们的线程上。
我们可以使用 SwiftUI
的 @Environment
属性包装器从环境中读取此内容:
java
@Environment(\.modelContext) var modelContext
@Query
会自动在 SwiftUI 视图中使用相同的上下文。
如何使用 ModelConfiguration 配置自定义的 ModelContainer
当你使用 modelContainer(for:)
获取默认配置时,就意味着 SwiftData 会决定你数据库的存储位置、启用自动保存、禁用撤销等等。你可以重载 modelContainer()
修饰符来修改一些默认配置。
但如果你想完全控制,则需要自己创建一个 ModelConfiguration
对象,然后使用它去创建你的容器。
例如,如果你想将数据库放在 documents 目录下,则可以使用如下代码:
swift
@main
struct SwiftDataDemoApp: App {
var container: ModelContainer
init() {
do {
let storeURL = URL.documentsDirectory.appending(path: "database.sqlite")
let config = ModelConfiguration(url: storeURL)
container = try ModelContainer(for: Item.self, configurations: config)
} catch {
fatalError("Failed to configure SwiftData container.")
}
}
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(container)
}
}
手动创建配置的一大优势是,如果你有特别敏感的数据,或者你可能已经发送了一些在任何情况下都不应更改的模板数据,它允许我们完全禁用保存:
ini
let config = ModelConfiguration(allowsSave: false)
手动创建配置的优势在于,你可以使用多个 ModelConfiguration
对象来配置单个模型容器。比如,你希望将 Item
存储在一个文件中,而将用户数据存储在另一个文件中,或者其中一个应该备份到CloudKit,而另一个不应该等等。