SwiftUI中StateObject被两次初始化,如何保证仅初始化一次?
问题根因
你遇到的ProjectViewModel初始化多次的问题是由两个错误用法共同导致的:
- SwiftUI的
View是值类型,每当视图树状态变化需要重新计算布局时,都会重新生成ProjectView结构体实例,对应的初始化方法会被多次调用。 - 你在自定义
init中主动执行了ProjectViewModel的构造闭包,同时提前访问了未被托管的@StateObject的wrappedValue,导致每次ProjectView初始化时都会生成新的ProjectViewModel实例,哪怕@StateObject本身最终只会保留第一个实例,多余的实例也会被创建出来。 StateObject的初始化方法接收的是@autoclosure类型的参数,本意是延迟到视图第一次加载时才执行构造逻辑,你提前调用构造闭包的写法绕过了这个延迟机制。
修复方案
推荐调整逻辑,将子ViewModel放到主ViewModel中管理,同时修正@StateObject的初始化写法:
第一步:修改ProjectViewModel,内置子ViewModel
class ProjectViewModel: ObservableObject { // 原有属性保留 @Published var tonalityViewModel: TonalityViewModel! @Published var tempoViewModel: TempoViewModel! init(recording: RecordingFile) { // 原有初始化逻辑保留 tonalityViewModel = createTonalityViewModel() tempoViewModel = createTempoViewModel() } init(filePath: String?) { // 原有初始化逻辑保留 tonalityViewModel = createTonalityViewModel() tempoViewModel = createTempoViewModel() } // 原有createTonalityViewModel、createTempoViewModel方法保留 }
第二步:修改ProjectView的初始化逻辑
struct ProjectView: View { @StateObject private var viewModel: ProjectViewModel private init(viewModel: @autoclosure @escaping () -> ProjectViewModel) { // 直接将autoclosure传给StateObject,不提前执行构造 _viewModel = StateObject(wrappedValue: viewModel()) } init(recording: RecordingFile) { self.init(viewModel: ProjectViewModel(recording: recording)) } init(filePath: String?) { self.init(viewModel: ProjectViewModel(filePath: filePath)) } var body: some View { // 需要使用子ViewModel时直接调用viewModel.tonalityViewModel、viewModel.tempoViewModel即可 } }
修复后效果
ProjectViewModel的构造闭包只会在ProjectView第一次被加载到视图树时执行一次,后续视图重新计算生成ProjectView实例时不会再触发构造逻辑,完全满足仅初始化一次的要求。
内容的提问来源于stack exchange,提问作者Semyon Tikhonenko
相关产品推荐
相关产品推荐

