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

如何避免@State变量初始化时SwiftUI初始视图被多次初始化

问题根源

SwiftUI的视图是值类型,系统会根据状态变化、布局计算等场景多次创建视图结构体的副本,所以你看到init方法打印3次是正常现象,本身不会带来性能问题。
你遇到的CoreDataViewModel多次初始化、fetchItems()执行3次的核心问题是:你用@ObservedObject修饰并直接在视图内部初始化了ViewModel。@ObservedObject本身不负责持有对象实例,仅作为观察者存在,每次视图结构体重建时,赋值表达式CoreDataViewModel()都会执行,生成新的ViewModel实例,触发多次init和fetch操作。

解决方法

按你的使用场景二选一即可:

  • 场景1:这个CoreDataViewModel仅在GroceryList视图内部使用
    直接把@ObservedObject改为@StateObject即可。@StateObject是SwiftUI专门用来在视图内部持有可观察对象的属性包装器,对象实例只会在第一次创建视图时初始化一次,后续视图结构体重建时会复用已有的实例,不会重复生成。
  • 场景2:这个CoreDataViewModel需要在多个视图间共享
    不要在GroceryList内部初始化ViewModel,改在父视图、App入口结构体中用@StateObject初始化,再通过environmentObject注入到视图树中,GroceryList内部用@EnvironmentObject接收即可,全局只会有一个实例。

额外优化:可以把ViewModel init中的fetchItems()调用移到视图的.onAppear/.task修饰符中,同时在ViewModel内部加是否已拉取过数据的标记,避免意外重复拉取。

代码示例

单视图使用ViewModel写法

struct GroceryList: View {
  // 改为@StateObject持有,仅初始化一次
  @StateObject var coreDataViewModel: CoreDataViewModel = CoreDataViewModel() 
  @StateObject var userPreferences: UserPreferences = UserPreferences()
  @State private var selectedItemsInList: [GroceryItem] = []
  @State private var selection = Set<UUID>()
  @State private var activeTab: Int = 0

  init() {
    print("Grocery List View 结构体初始化,多次打印属于正常现象")
  }

  var body: some View {
    // 你的视图布局代码
    List(coreDataViewModel.items) { item in
      // 列表项代码
    }
  }
}

多视图共享ViewModel写法

先在App入口初始化并注入:

@main
struct YourGroceryApp: App {
  // 全局持有ViewModel实例
  @StateObject private var coreDataViewModel = CoreDataViewModel()

  var body: some Scene {
    WindowGroup {
      GroceryList()
        // 注入到视图树
        .environmentObject(coreDataViewModel)
    }
  }
}

GroceryList内接收注入的实例:

struct GroceryList: View {
  // 接收环境中的ViewModel实例,不会重复初始化
  @EnvironmentObject var coreDataViewModel: CoreDataViewModel
  @StateObject var userPreferences: UserPreferences = UserPreferences()
  @State private var selectedItemsInList: [GroceryItem] = []
  @State private var selection = Set<UUID>()
  @State private var activeTab: Int = 0
}

内容的提问来源于stack exchange,提问作者udGlobal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 16:57:03