我正在尝试使用SWIFT5.1属性包装器,但是每次我认为我有一个很酷的用例时,我最终会遇到一个问题,就是我无法在我的View的初始化器中使用它们。
以这个非常简单的例子为例。
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包装器(如这篇文章 )时,我也遇到了同样的问题。
而且不,不幸的是,让它成为一个lay属性也是行不通的: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")
}
}发布于 2020-02-14 15:12:19
编辑:
实际上,更好的解决办法是直接使用_foo.wrappedValue.uppercased()而不是foo.uppercased()。
这也解决了双重初始化的另一个问题。
更深入地考虑这一点,这绝对是有意的行为。
如果我正确理解它,在OhNoes中,foo只是以下的缩写:
var foo: String {
get {
return self._foo.wrappedValue
}
set {
self._foo.wrappedValue = newValue
}
}所以这不可能以任何其他方式起作用。
我正面临着同样的问题,我认为这是某种错误/不想要的行为。
不管怎么说,我能和你约会的最好方式是:
@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 : String
let upperCased: String
init() {
let box = Box(wrappedValue: "ABC")
_foo = box
self.upperCased = box.wrappedValue.uppercased()
}
}这很好(我的意思是,它没有副作用,但很难看)。
这个解决方案的问题是,如果您的属性包装器有一个空的初始化器init()或如果wrappedValue是Optional,那么它实际上不能工作(没有副作用)。
例如,如果您尝试使用下面的代码,您将发现Box被初始化了两次:一次在成员变量的定义处,一次在OhNoes‘init中,然后将替换前者。
@propertyWrapper
struct Box<Value> {
private var box: Value?
init(wrappedValue: Value?) { // Actually called twice in this case
box = wrappedValue
}
var wrappedValue: Value? {
get { box }
set { box = newValue }
}
}
class OhNoes {
@Box var foo : String?
let upperCased: String?
init() {
let box = Box(wrappedValue: "ABC")
_foo = box
self.upperCased = box.wrappedValue?.uppercased()
}
}我认为这绝对是我们不应该拥有的东西(或者至少我们应该能够选择退出这种行为)。不管怎样,我认为这和他们在这个音高里说的话有关
当属性包装器类型具有无参数init()时,使用该包装类型的属性将通过init()隐式初始化。
你有没有找到其他方法来做这件事?
发布于 2020-02-14 16:22:37
最无摩擦的解决方法是使upperCased成为var而不是let。好的,这可能不可取,但至少意味着您可以保留所有代码并立即使用生成的OhNoes实例:
struct AppDependencies {}
@propertyWrapper struct Box<T> {
private var boxed: T
init(wrappedValue: T) {
boxed = wrappedValue
}
var wrappedValue: T {
get { boxed }
set { boxed = newValue }
}
}
class OhNoes {
@Box var foo = "abc"
var upperCased: String = "" // this is the only real change
init(dependencies: AppDependencies) {
self.upperCased = foo.uppercased()
}
}如果您真的不喜欢这样做,那么按照另一个答案的建议,直接参考_foo.wrappedValue。
https://stackoverflow.com/questions/58257290
复制相似问题