基于本文回答

播面 播面

刷题像听歌,多听自然懂
0
评论

@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() 
        }
    }
}

后果

  1. 当你在 BadView 里点击 "Add",数字变成 1。
  2. 当你点击父视图的 "Refresh Parent",父视图重绘,导致 BadView 的结构体被重新初始化。
  3. 因为 BadView 使用的是 @ObservedObject,它没有“记忆”功能,model = CounterModel() 会再次执行,创建一个的实例。
  4. BadView 里的数字瞬间变回 0(数据丢失)。

修正:将 BadView 里的 @ObservedObject 改为 @StateObject,SwiftUI 就会知道:“哦,这个对象是我存起来的,虽然 View 结构体重建了,但我还是把旧的那个实例塞给你。”


4. 总结对比表

特性 @StateObject @ObservedObject
所有权 视图拥有该对象 (Owner) 视图借用/依赖该对象 (Dependent)
初始化位置 通常在当前视图内 (= ViewModel()) 通常由父视图传递进来
生命周期 独立于 View 结构体的更新,数据持久化 随 View 结构体销毁而销毁(若在此处初始化)
主要用途 Source of Truth (数据源头) 传递数据

最佳实践口诀

  • 如果是你创建的 (= ViewModel()),用 @StateObject
  • 如果是别人传给你的 (: ViewModel),用 @ObservedObject
00:00
00:00