首页 > 解决方案 > SwiftUI 中的结构初始化:在初始化所有存储的属性之前使用“self”

问题描述

我正在尝试将绑定传递给我的虚拟机,它应该是一个过滤器,以便虚拟机根据参数传递的过滤器获取对象。

不幸的是,我无法初始化虚拟机,因为我在'self' used before all stored properties are initialized初始化虚拟机的行中遇到了错误self.jobsViewModel = JobsViewModel(jobFilter: $jobFilter)

struct JobsTab: View {
    @ObservedObject var jobsViewModel: JobsViewModel
    @ObservedObject var categoriesViewModel: CategoriesViewModel
    
    @StateObject var searchText: SearchText = SearchText()
    
    @State private var isEditing: Bool
    @State private var showFilter: Bool
    @State private var jobFilter: JobFilter
    
    init() {
        self.categoriesViewModel = CategoriesViewModel()
        self.jobFilter = JobFilter(category: nil)
        self.showFilter = false
        self.isEditing = false
        self.jobsViewModel = JobsViewModel(jobFilter: $jobFilter)
    }

我想我正在初始化所有的变量,并且self.searchText不在 init 块中,因为编译器抱怨它是一个仅获取的属性。

有没有其他方法可以做到这一点?

谢谢!

编辑:这是我的虚拟机:

class JobsViewModel: ObservableObject {
    @Published var isLoading: Bool = false
    @Published var jobs: [Jobs] = []
    @Binding var jobFilter: JobFilter
    
    init(jobFilter: Binding<JobFilter>) {
        _jobFilter = jobFilter
    }
...
}

struct JobFilter {
    var category: Category?
}

我的想法是将作业过滤器作为 JobsTab 中的一个状态,并且每次该状态发生变化时,VM 都会尝试获取与JobFilter

标签: iosswiftswiftui

解决方案


您不应该@ObservedObject在初始化程序中创建值。这样做会导致错误,因为每次重新创建视图时都会创建新实例。jobsViewModel并且categoriesViewModel应该作为参数传递给,或者init您应该使用@StateObject这些属性。

但无论如何,您实际上问:为什么我们不能$jobFilter在初始化之前使用jobsViewModel

让我们从简化示例开始:

struct JobsTab: View {
    @State var jobFilter: String
    var jobFilterBinding: Binding<String>

    init() {
        jobFilter = ""
        jobFilterBinding = $jobFilter
                       //  ^  'self' used before all stored properties are initialized
    }

    var body: some View { Text("hello") }
}

那么,这里发生了什么?如果我们“去糖”使用@State. 编译器将 的声明jobFilter转换为三个属性:

struct JobsTab: View {
    private var _jobFilter: State<String>

    var jobFilter: String {
        get { _jobFilter.wrappedValue }
        nonmutating set { _jobFilter.wrappedValue = newValue }
    }

    var $jobFilter: Binding<String> {
        get { _jobFilter.projectedValue }
    }

    var jobFilterBinding: Binding<String>

    init() {
        _jobFilter = State<String>(wrappedValue: "")
        jobFilterBinding = $jobFilter
                       //  ^  'self' used before all stored properties are initialized
    }

    var body: some View { Text("hello") }
}

现在请注意,这$jobFilter不是存储属性。它是一个计算属性。所以访问$jobFilter意味着调用它的“getter”,这是一个方法self。但是我们不能在完全初始化self之前调用方法。这就是为什么我们在初始化所有存储属性之前self尝试使用会出错的原因。$jobFilter

解决方法是避免使用$jobFilter. 相反,我们可以_jobFilter.projectedValue直接使用:

struct JobsTab: View {
    @State var jobFilter: String
    var jobFilterBinding: Binding<String>

    init() {
        jobFilter = ""
        jobFilterBinding = _jobFilter.projectedValue
    }

    var body: some View { Text("hello") }
}

推荐阅读