首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >SWIFT5.1 @propertyWrapper --在初始化所有存储的属性之前,在属性访问中使用“self”

SWIFT5.1 @propertyWrapper --在初始化所有存储的属性之前,在属性访问中使用“self”
EN

Stack Overflow用户
提问于 2019-10-06 12:36:08
回答 2查看 3.6K关注 0票数 6

我正在尝试使用SWIFT5.1属性包装器,但是每次我认为我有一个很酷的用例时,我最终会遇到一个问题,就是我无法在我的View的初始化器中使用它们。

以这个非常简单的例子为例。

代码语言:javascript
复制
class NoProblem {
  var foo = "ABC"
  let upperCased: String

  init(dependencies: AppDependencies) {
    self.upperCased = foo.uppercased()
  }
}
代码语言:javascript
复制
@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中也是一个相当大的问题,请参见下面的示例:

代码语言:javascript
复制
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")
  }
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-02-14 15:12:19

编辑:

实际上,更好的解决办法是直接使用_foo.wrappedValue.uppercased()而不是foo.uppercased()

这也解决了双重初始化的另一个问题。

更深入地考虑这一点,这绝对是有意的行为。

如果我正确理解它,在OhNoes中,foo只是以下的缩写:

代码语言:javascript
复制
var foo: String {
  get {
    return self._foo.wrappedValue
  }
  set {
    self._foo.wrappedValue = newValue
  }
}

所以这不可能以任何其他方式起作用。

我正面临着同样的问题,我认为这是某种错误/不想要的行为。

不管怎么说,我能和你约会的最好方式是:

代码语言:javascript
复制
@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中,然后将替换前者。

代码语言:javascript
复制
@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()隐式初始化。

你有没有找到其他方法来做这件事?

票数 3
EN

Stack Overflow用户

发布于 2020-02-14 16:22:37

最无摩擦的解决方法是使upperCased成为var而不是let。好的,这可能不可取,但至少意味着您可以保留所有代码并立即使用生成的OhNoes实例:

代码语言:javascript
复制
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

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58257290

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档