SwiftUI中@Observable下View无法稳定持有ViewModel的原因及优化
SwiftUI @Observable + @State 重复初始化ViewModel的问题解析与优化方案
原理解释
1. @StateObject的核心特性
@StateObject是SwiftUI专为ObservableObject设计的属性包装器,它将ViewModel的生命周期与View的身份深度绑定。即使父View刷新导致子View的结构体实例被重建,@StateObject持有的ViewModel也只会初始化一次——SwiftUI会自动在后台保留并复用这个实例,不会随View结构体的重建而重新创建。
2. @State + @Observable的问题根源
你遇到的重复初始化问题,核心是错误的@State初始化方式:
- @State虽然支持存储引用类型(包括@Observable类),但它的生命周期管理逻辑和@StateObject不同,更依赖SwiftUI的自动存储管理。
- 你在
SubView的自定义init中手动执行_viewModel = State(wrappedValue: SubViewModel()),这会导致每次父View刷新、SubView结构体被重建时,都触发这个初始化代码,创建新的ViewModel实例。因为自定义init中的显式赋值会绕过SwiftUI对@State的复用逻辑,强制每次都生成新的对象。
优化方案
方案1:直接声明初始化@State属性
去掉SubView的自定义init,直接在@State属性声明时完成初始化,让SwiftUI自动管理ViewModel的生命周期:
struct ContentView: View { @State var text = "" var body: some View { TextField("Enter text", text: $text) SubView() } } @Observable class SubViewModel { var text: String = "Hello, World!" init() { print("init SubViewModel") } } struct SubView: View { @State private var viewModel = SubViewModel() var body: some View { Text(viewModel.text) } }
方案2:父View持有+子View@Bindable绑定
如果需要从父View传递ViewModel实例,可让父View用@State持有ViewModel,子View用@Bindable绑定:
struct ContentView: View { @State var text = "" @State private var subViewModel = SubViewModel() var body: some View { TextField("Enter text", text: $text) SubView(viewModel: $subViewModel) } } @Observable class SubViewModel { var text: String = "Hello, World!" init() { print("init SubViewModel") } } struct SubView: View { @Bindable var viewModel: SubViewModel var body: some View { Text(viewModel.text) } }
方案3:保留自定义init的正确写法
如果必须保留自定义init,不要直接创建State实例,而是通过初始值参数传递:
struct SubView: View { @State private var viewModel: SubViewModel init(viewModel: SubViewModel = SubViewModel()) { self._viewModel = State(initialValue: viewModel) } var body: some View { Text(viewModel.text) } }
这种写法会让SwiftUI正确识别@State的复用逻辑,避免重复初始化ViewModel。
内容的提问来源于stack exchange,提问作者Max
相关产品推荐
相关产品推荐

