【问题标题】:Struct initialization in SwiftUI: 'self' used before all stored properties are initializedSwiftUI 中的结构初始化:在初始化所有存储的属性之前使用“self”
【发布时间】:2021-09-06 08:51:40
【问题描述】:

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

很遗憾,我无法初始化 VM,因为我在初始化 VM self.jobsViewModel = JobsViewModel(jobFilter: $jobFilter) 的行中收到错误 'self' used before all stored properties are initialized

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 匹配的作业

【问题讨论】:

  • 您在初始化之前访问了该值,但即使您在初始化时没有问题,也可以在出现时使用
  • 非常感谢您的及时回答! onAppear 是什么意思?我应该在哪里做,在JobsTab 或JobsViewModel?
  • 我更喜欢从超级视图注入 ViewModelError 的模式。你可能应该注入一个JobsTabViewModel,你已经用JobsModel和CategoriesModel或其他东西初始化了它;这个视图不应该知道其他视图视图模型的任何信息
  • 使用自定义初始化在 SwiftUI 中效果不佳。远离自定义初始化。您的 Viewmodel 可能会泄漏,并且您的状态变量未正确初始化。 developer.apple.com/documentation/swiftui/…
  • @Cristik 对此感到抱歉;我现在已将其添加到原始问题中

标签: ios swift swiftui


【解决方案1】:

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

但不管怎样,你实际上问:为什么我们不能在初始化jobsViewModel之前使用$jobFilter?

让我们从简化示例开始:

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") }
}

【讨论】:

  • 对错误的方法给出正确的答案,他会感到困惑!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-04-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多