先理解这三个为什么要一起讲
它们是一套组合拳,缺一不可:
| 角色 | 是什么 |
|---|---|
ObservableObject |
一个协议,贴在 class 上,宣告"我是可被观察的数据源" |
@Published |
一个 Property Wrapper,贴在属性上,宣告"这个属性变化时要通知订阅者" |
@ObservedObject |
一个 Property Wrapper,贴在 View 的属性上,宣告"我订阅这个数据源,它变化我就刷新" |
为什么需要这套东西?@State 不够用吗?
@State 适合简单的值类型,但现实中你的数据模型往往是一个 class ,有很多属性和方法,且需要被多个平级 View 共享。
swift
// 一个用户信息模型,多个页面都要用
class UserModel {
var name: String = "Tom"
var age: Int = 18
var score: Int = 0
// ... 还有很多方法
}
把这个 class 塞进 @State 是行不通的------@State 是为值类型设计的,对 class 的引用地址变化不敏感,属性改了 UI 也不会刷新。
三件套的用法
swift
// 第一步:让你的 class 遵守 ObservableObject 协议
class UserModel: ObservableObject {
// 第二步:在需要触发 UI 刷新的属性上加 @Published
@Published var name: String = "Tom"
@Published var score: Int = 0
var internalCache: String = "" // 不加 @Published,改它不会刷新 UI
}
// 第三步:在 View 里用 @ObservedObject 订阅这个模型
struct ProfileView: View {
@ObservedObject var user: UserModel
var body: some View {
VStack {
Text(user.name)
Text("\(user.score)")
Button("加分") {
user.score += 1 // 改 @Published 属性 → 触发 UI 刷新
}
}
}
}
// 使用:顶层 View 用 @StateObject 持有并创建模型
struct ContentView: View {
@StateObject var user = UserModel()
var body: some View {
ProfileView(user: user)
}
}
三件套的本质
@Published 本质上是:
swift
@propertyWrapper
public struct Published<Value> {
// 每次 wrappedValue 被 set,就通过 objectWillChange 发出通知
public var wrappedValue: Value
// $score 拿到的是一个 Combine Publisher,可以接链式操作
public var projectedValue: Publisher
}
ObservableObject 协议本质上是:
swift
public protocol ObservableObject: AnyObject {
// 编译器会自动合成这个,你的 @Published 属性改变时,它会发出信号
var objectWillChange: ObservableObjectPublisher { get }
}
@ObservedObject 本质上是:View 订阅了 user.objectWillChange,只要它 emit,SwiftUI 就重新计算这个 View 的 body。
整个流程: user.score += 1 → @Published 的 setter 触发 → user.objectWillChange.send() → 订阅了它的 @ObservedObject 感知到 → SwiftUI 重新渲染对应的 View
@ObservedObject vs @StateObject
这是一个非常容易踩的坑:
| @ObservedObject | @StateObject | |
|---|---|---|
| 数据归属 | 不拥有,由外部传入 | 拥有,由这个 View 创建和持有 |
| 生命周期 | 跟随外部,不负责销毁 | 跟随 View,View 消失时销毁 |
| 典型场景 | 子 View 接收父 View 传来的模型 | 根 View 或顶层 View 创建模型 |
经验法则 :谁创建,谁用 @StateObject;谁接收,谁用 @ObservedObject。
使用时需要关心的问题
-
只有 class 能用 :
ObservableObject是AnyObject的子协议,struct 和 enum 无法遵守,这套机制天生是为引用类型设计的。 -
@Published 要精准 :不是所有属性都需要
@Published,只给真正需要驱动 UI 的属性加,滥加会导致不必要的 View 重渲染,影响性能。 -
objectWillChange 是"将要改变" :SwiftUI 在属性改变之前 就会收到通知,你通常不需要手动调用它,但在某些手动控制的场景可以用
objectWillChange.send()主动触发刷新。