一、四句话(所有权模型一句话版)
- @State
👉 “这个值的所有权在 View 自己,生命周期由 SwiftUI 管理。” - @StateObject
👉 “这个引用类型对象由当前 View 创建并拥有,只创建一次。” - @ObservedObject
👉 “这个对象不是我创建的,我只是临时观察它。” - @EnvironmentObject
👉 “这个对象由更高层统一拥有,我只是从环境里拿来用。”
如果你只记住这四句,80% 场景已经不会踩雷。
二、把“所有权”翻译成三个问题(最关键)
每次选属性包装器,只问自己这三件事:
- 是谁创建的?
- 谁负责活多久?
- View 消失时,它要不要跟着消失?
三、完整对照表(所有权 + 生命周期)
| 包装器 | 谁创建 | 谁拥有 | 生命周期绑定 | 典型用途 |
|---|---|---|---|---|
@State | View | SwiftUI State Store | View identity | 简单值类型状态 |
@StateObject | View | View | View identity(一次) | ViewModel / Store |
@ObservedObject | 外部 | 外部 | 外部决定 | 子 View 观察 |
@EnvironmentObject | 根容器 | 全局环境 | App / Scene | 全局共享状态 |
四、最容易混淆的两个(重点)
1️⃣ @StateObject vs @ObservedObject
一句话区别:
谁
init的,谁就用@StateObject;
没init的,就用@ObservedObject。
// 正确
struct ParentView: View {
@StateObject var vm = UserViewModel()
}
// 正确
struct ChildView: View {
@ObservedObject var vm: UserViewModel
}
❌ 错误用法:
@ObservedObject var vm = UserViewModel() // 会被反复创建
2️⃣ @EnvironmentObject 的本质
不是“魔法全局变量”,而是:
由更高层 View 显式注入的依赖
@main
struct App: App {
let session = SessionStore()
var body: some Scene {
WindowGroup {
RootView()
.environmentObject(session)
}
}
}
- View 不负责创建
- View 假定“环境中一定存在”
- 缺失会直接 crash(设计使然)
五、一个超级好记的心智模型(推荐)
把它们当成 资源管理声明:
@State = 我有一个小本子
@StateObject = 我养了一只宠物
@ObservedObject = 我只是帮别人看宠物
@EnvironmentObject= 社区公共设施
六、错误使用的典型后果(速查)
| 错误 | 后果 |
|---|---|
| 用 @ObservedObject 创建对象 | View 重建 → 对象重建 |
| 用 @StateObject 接收外部对象 | 所有权混乱 |
| 滥用 @EnvironmentObject | 隐式依赖难以维护 |
| 用 @State 存引用类型 | 生命周期不可控 |
最终一句话(压轴版)
SwiftUI 的属性包装器不是“状态类型”,而是“所有权声明”:你在告诉系统——这个状态是谁创建的、谁负责活多久。