You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 20:15:59