SwiftUI 最新数据模型完整解析:@Observable、@State、@Bindable(iOS17+ 全新范式)
自 iOS 17 起,SwiftUI 引入了 全新的 Observation 模型。
它用三个核心工具彻底重塑了数据管理方式:
-
@Observable —— 定义可观察的状态模型
-
@State —— 持有模型实例,等价于旧时代的 @StateObject
-
@Bindable —— 在视图中实现对 Observable 模型的双向绑定
如果你还在用 ObservableObject、@Published、@StateObject、@ObservedObject、@EnvironmentObject,是时候升级了:新范式更简单、更 Swift、更高性能。
本文将系统梳理 SwiftUI 最新的数据管理体系。
🧱 一、旧数据体系的问题
iOS 16 及以前,我们管理状态基本依赖:
-
ObservableObject
-
@Published
-
@StateObject
-
@ObservedObject
-
@EnvironmentObject
这些机制的问题:
-
装饰器太多,容易混乱
-
生命周期容易搞错(尤其是 @StateObject vs @ObservedObject)
-
@Published 对属性执行全局广播,性能不够优雅
-
环境写法不够类型安全
新模型的目标:让 SwiftUI 更简单、更自动、更智能。
🚀 二、@Observable:新时代核心
新系统中的任何可观察模型,只要声明:
@Observable
class UserModel {
var name = "HanQiu"
var age = 23
}
不再需要:
-
ObservableObject
-
@Published
-
手动发布变更
所有存储属性都是可观察的,SwiftUI 会精确追踪变化来源。
🧩 三、@State取代@StateObject
在旧时代,创建页面级别持久的模型需要:
@StateObject var vm = UserModel()
在新系统中:
@State var vm = UserModel()
是的, @State 自动完成以前 @StateObject 的作用:
-
保持引用类型实例生命周期
-
在视图重建中保持稳定
-
触发视图刷新
只要你的模型是 @Observable 的,就可以用 @State 持有。
🧠 四、那@ObservedObject呢?—— 不需要了
旧写法(子视图):
struct ProfileView: View {
@ObservedObject var vm: UserModel
}
新写法:
struct ProfileView: View {
var vm: UserModel
}
SwiftUI 会自动观察视图中“被使用的属性”。
你不需要告诉它“这个对象可观察”,它本身就知道(因为模型是 @Observable)。
🌿 五、环境注入方式的升级
旧写法:
@EnvironmentObject var settings: SettingsModel
新写法更强、更明确:
注入
struct AppRoot: View {
@State var settings = SettingsModel()
var body: some View {
MainView()
.environment(settings)
}
}
获取
@Environment(SettingsModel.self) var settings
减少误用,也更符合 Swift 语言本身的表达。
⭐ 六、重点:@Bindable的出现解决了什么?
@Observable 模型虽然自动可观察,但 UI 控件(如 TextField)需要 双向绑定:
TextField("Name", text: $vm.name)
新模型中,属性只是普通 stored property,不是 Published,不具备 Binding 能力。
于是 Swift 引入:
✔@Bindable
为 View 提供 绑定视角的模型访问。
🧲 七、@Bindable的标准用法
模型
@Observable
class UserModel {
var name = ""
var age = 18
}
视图(可编辑 UI)
struct EditUserView: View {
@Bindable var user: UserModel
var body: some View {
Form {
TextField("Name", text: $user.name)
Stepper("Age: \(user.age)", value: $user.age)
}
}
}
只需标记 @Bindable,模型属性即可自动得到 $binding。
🧩 八、为什么不是所有时候都用@Bindable?
是否需要取决于:
| 情况 | 是否需要 @Bindable |
|---|---|
| 仅用于展示,不会修改模型 | ❌ No |
| 需要用 TextField / Toggle / Stepper 修改模型 | ✔ Yes |
| 子视图要修改父模型 | ✔ Yes |
| 完全只读视图 | ❌ No |
越“表单”风格的页面,越需要 @Bindable。
🚦 九、@Bindable的局部绑定写法(推荐技巧)
你也可以只在 body 内使用 Bindable:
var body: some View {
@Bindable var b = user // 局部绑定
VStack {
TextField("Name", text: $b.name)
Stepper("Age: \(b.age)", value: $b.age)
}
}
不会污染结构体属性定义,适合仅局部可编辑的 UI。
🧭 十、三者关系总结(最重要)
@Observable —— 使模型可观察
@State —— 在 View 中持有模型(生命周期 = 旧 @StateObject)
@Bindable —— 提供绑定能力,允许 UI 修改模型
一个“完整数据流”的表达式:
@Observable 定义状态 → @State 持有 → @Bindable 编辑 → SwiftUI 自动刷新
🧪 十一、完整示例:新 Paradigm 最佳实践
@Observable
class ProfileModel {
var name = "HanQiu"
var level = 1
}
struct ProfileView: View {
@State var profile = ProfileModel()
var body: some View {
VStack {
Text("Name: \(profile.name)")
Text("Level: \(profile.level)")
EditSection(profile: profile)
}
}
}
struct EditSection: View {
@Bindable var profile: ProfileModel
var body: some View {
VStack {
TextField("Name", text: $profile.name)
Stepper("Level: \(profile.level)", value: $profile.level)
}
.padding()
}
}
无需 @Published,不用 @StateObject,不需要 @ObservedObject。
SwiftUI 的数据管理彻底简化。
🧾 十二、迁移指南(旧 → 新)
| 旧 API | 新 API |
|---|---|
| ObservableObject | @Observable |
| @Published | 不需要 |
| @StateObject | @State |
| @ObservedObject | 删除,直接传模型 |
| @EnvironmentObject | .environment(model) + @Environment(Model.self) |
| 双向绑定属性 | 使用 @Bindable |
🎉 总结
SwiftUI 从 iOS17 开始进入 Observation 时代:
-
@Observable → 自动观察
-
@State → 管理模型生命周期
-
@Bindable → 构建表单/编辑 UI 的关键
-
更少的装饰器
-
更精准的性能优化
-
更符合 Swift 语言设计哲学
如果你写 SwiftUI,这套新范式未来几年都会是主流。