@ObservedObject 和 @StateObject 的区别
在 SwiftUI 中,@ObservedObject 和 @StateObject 都用于订阅遵循 ObservableObject 协议的对象(即当对象的 @Published 属性变化时,刷新视图)。
它们的核心区别在于 生命周期(Lifecycle) 和 所有权(Ownership)。
简单的一句话总结:谁负责创建对象,谁就用 @StateObject;谁只是被动接收并观察对象,谁就用 @ObservedObject。
1. @StateObject (iOS 14+)
- 含义:表示视图拥有这个对象。
- 生命周期:SwiftUI 会负责创建并保持这个对象的存活。即使视图(View Struct)因为父视图的更新而被重新创建,
@StateObject里的数据不会丢失,对象实例也不会被销毁重建。 - 使用场景:当你在视图内部初始化(
= ViewModel())一个对象时使用。
plaintext
struct ContentView: View {
// ContentView 拥有这个 ViewModel,负责它的创建
// 即使 ContentView 刷新,myModel 依然是同一个实例
@StateObject var myModel = MyViewModel()
var body: some View {
// ...
}
}
2. @ObservedObject
- 含义:表示视图观察这个对象,但不拥有它。
- 生命周期:它的生命周期不归当前视图管理。如果当前视图被父视图重新渲染(销毁并重新创建 View Struct),而在视图内部初始化的
@ObservedObject也会被重新初始化,导致数据丢失。 - 使用场景:当对象是从父视图通过参数传递进来时使用。
plaintext
struct DetailView: View {
// DetailView 不拥有这个对象,它是从外面传进来的
@ObservedObject var model: MyViewModel
var body: some View {
Text(model.title)
}
}
3. 经典错误示例(为什么不能混用)
如果你在应该使用 @StateObject 的地方使用了 @ObservedObject,会发生什么?
plaintext
class CounterModel: ObservableObject {
@Published var count = 0
}
struct BadView: View {
// 错误用法!这里应该用 @StateObject
@ObservedObject var model = CounterModel()
var body: some View {
VStack {
Text("Count: \(model.count)")
Button("Add") { model.count += 1 }
}
}
}
struct ParentView: View {
@State var refreshFlag = false
var body: some View {
VStack {
// 每次点击这个按钮,ParentView 会刷新 body
Button("Refresh Parent") { refreshFlag.toggle() }
// BadView 会被重新创建结构体
BadView()
}
}
}
后果:
- 当你在
BadView里点击 "Add",数字变成 1。 - 当你点击父视图的 "Refresh Parent",父视图重绘,导致
BadView的结构体被重新初始化。 - 因为
BadView使用的是@ObservedObject,它没有“记忆”功能,model = CounterModel()会再次执行,创建一个新的实例。 BadView里的数字瞬间变回 0(数据丢失)。
修正:将 BadView 里的 @ObservedObject 改为 @StateObject,SwiftUI 就会知道:“哦,这个对象是我存起来的,虽然 View 结构体重建了,但我还是把旧的那个实例塞给你。”
4. 总结对比表
| 特性 | @StateObject | @ObservedObject |
|---|---|---|
| 所有权 | 视图拥有该对象 (Owner) | 视图借用/依赖该对象 (Dependent) |
| 初始化位置 | 通常在当前视图内 (= ViewModel()) |
通常由父视图传递进来 |
| 生命周期 | 独立于 View 结构体的更新,数据持久化 | 随 View 结构体销毁而销毁(若在此处初始化) |
| 主要用途 | Source of Truth (数据源头) | 传递数据 |
最佳实践口诀
- 如果是你创建的 (
= ViewModel()),用@StateObject。 - 如果是别人传给你的 (
: ViewModel),用@ObservedObject。