【发布时间】:2020-02-04 00:36:27
【问题描述】:
我正在尝试使用 Swift 5.1 属性包装器,但每次我认为我有一个很酷的用例时,我最终都会遇到无法在 View Model 的初始化程序中使用它们的问题。
举这个极其简单的例子。
class NoProblem {
var foo = "ABC"
let upperCased: String
init(dependencies: AppDependencies) {
self.upperCased = foo.uppercased()
}
}
@propertyWrapper
struct Box<Value> {
private var box: Value
init(wrappedValue: Value) {
box = wrappedValue
}
var wrappedValue: Value {
get { box }
set { box = newValue }
}
}
class OhNoes {
@Box var foo = "ABC"
let upperCased: String
init(dependencies: AppDependencies) {
self.upperCased = foo.uppercased()
}
}
在NoProblem 中,一切正常。然而在OhNoes 我得到这个错误:'self' used in property access 'foo' before all stored properties are initialized。
当然,这是一个极其简化的示例,但在为可观察属性执行 @Property 包装器或 @Injected 包装器(如 this article 等)时,我遇到了同样的问题。
不,遗憾的是,将其作为非专业人士也行不通:Property 'foo' with a wrapper cannot also be lazy。
这在 SwiftUI 中也是一个相当大的问题,看这个例子:
class AppStore: ObservableObject {
let foo = "foo"
}
struct ContentView: View {
@EnvironmentObject private var store: AppStore
private let foo: String
init() {
foo = store.foo // error: 'self' used before all stored properties are initialized
}
var body: some View {
Text("Hello world")
}
}
【问题讨论】:
-
您正在尝试在
upperCased初始化之前访问self.foo。这与属性包装无关。 -
@ClausJørgensen 好吧,正如您在
NoProblem示例中看到的那样,当您不使用属性包装器时,同样可以正常工作。 -
@MumtazHussain 那是不可能的。当然,我可以将
upperCased设为惰性 var,但随后它会抱怨Lazy properties must have an initializer。在更多现实世界的例子中,这并不总是一种选择。 -
“你的整个方法都是错的”,谢谢你。同样,这是一个非常简化的示例,只是为了显示编译器错误。为什么在使用属性包装器时会抱怨,但在不使用属性包装器时却没有完成 - 这就是问题所在。就像我说的,当尝试将属性包装器用于可观察属性或注入属性或其他任何东西时,也会发生同样的问题。
-
又添加了两个示例。