Swift 5.1@PropertyRapper-&x27;自我';在初始化所有存储的属性之前在属性访问中使用
我试图使用Swift 5.1属性包装器,但每次我认为我有一个很酷的用例,我最终遇到了一个问题,我不能在视图模型的初始值设定项中使用它们 举一个非常简单的例子Swift 5.1@PropertyRapper-&x27;自我';在初始化所有存储的属性之前在属性访问中使用,swift,swiftui,swift5.1,Swift,Swiftui,Swift5.1,我试图使用Swift 5.1属性包装器,但每次我认为我有一个很酷的用例,我最终遇到了一个问题,我不能在视图模型的初始值设定项中使用它们 举一个非常简单的例子 类无问题{ var foo=“ABC” 让大写:字符串 init(依赖项:AppDependencies){ self.upperCased=foo.upperCased() } } @propertyWrapper 结构盒{ 私有变量框:值 init(wrappedValue:Value){ box=wrappedValue } var
类无问题{
var foo=“ABC”
让大写:字符串
init(依赖项:AppDependencies){
self.upperCased=foo.upperCased()
}
}
@propertyWrapper
结构盒{
私有变量框:值
init(wrappedValue:Value){
box=wrappedValue
}
var wrappedValue:Value{
获取{box}
设置{box=newValue}
}
}
OhNoes类{
@框var foo=“ABC”
让大写:字符串
init(依赖项:AppDependencies){
self.upperCased=foo.upperCased()
}
}
在NoProblem
中,一切正常。但是在OhNoes
中,我得到了这个错误:在初始化所有存储的属性之前,在属性访问“foo”中使用了“self”
当然,这是一个极其简化的示例,但在为可观察属性执行@Property
包装时,或者像in这样的@Injected
包装时,我会遇到同样的问题
不幸的是,让它成为一个lay属性也不会起作用:带有包装器的属性'foo'也不能是懒惰的
这在SwiftUI中也是一个相当大的问题,请参见以下示例:
类应用商店:observeObject{
让foo=“foo”
}
结构ContentView:View{
@EnvironmentObject私有变量存储:AppStore
私有let foo:String
init(){
foo=store.foo//错误:在初始化所有存储属性之前使用了“self”
}
var body:一些观点{
文本(“你好,世界”)
}
}
编辑:
实际上,更好的解决方法是直接使用\u foo.wrappedValue.uppercased()
而不是foo.uppercased()
这也解决了双重初始化的另一个问题
深入思考这一点,这绝对是预期的行为
如果我理解正确的话,在OhNoes中,foo只是以下的缩写:
var foo: String {
get {
return self._foo.wrappedValue
}
set {
self._foo.wrappedValue = newValue
}
}
所以这是不可能以任何其他方式起作用的
我面临着与您相同的问题,我实际上认为这是某种错误/不必要的行为 无论如何,我最好的约会方式是:
@propertyWrapper
结构盒{
私有变量框:值
init(wrappedValue:Value){
box=wrappedValue
}
var wrappedValue:Value{
获取{box}
设置{box=newValue}
}
}
OhNoes类{
@框变量foo:String
让大写:字符串
init(){
let box=box(包装价值:“ABC”)
_foo=box
self.upperCased=box.wrappedValue.upperCased()
}
}
这很好(我的意思是,它没有副作用,但很难看)
这个解决方案的问题是,如果属性包装器有一个空的初始值设定项init()
,或者如果wrappedValue是可选的
,那么它实际上不起作用(没有副作用)
例如,如果您尝试使用下面的代码,您将意识到该框初始化了两次:一次在成员变量的定义中,一次在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(wrappedValue:Value?{//在本例中实际调用了两次
box=wrappedValue
}
var wrappedValue:值{
获取{box}
设置{box=newValue}
}
}
OhNoes类{
@框变量foo:字符串?
让大写:字符串?
init(){
let box=box(包装价值:“ABC”)
_foo=box
self.upperCased=box.wrappedValue?.upperCased()
}
}
我认为这绝对是我们不应该有的(或者至少我们应该能够选择退出这种行为)。不管怎样,我认为这与他们在
当属性包装类型具有无参数init()时,属性
将通过init()隐式初始化使用该包装类型的
PS:你找到其他方法了吗?最无摩擦的解决方法是使
大写
avar
而不是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()
}
}
struct-AppDependencies{}
@PropertyRapper结构框{
已装箱的专用变量:T
init(wrappedValue:T){
装箱=包装价值
}
var wrappedValue:T{
得到{装箱}
集合{boxed=newValue}
}
}
OhNoes类{
@框var foo=“abc”
var upperCased:String=”“//这是唯一真正的更改
init(依赖项:AppDependencies){
self.upperCased=foo.upperCased()
}
}
如果你真的不喜欢,那么直接参考另一个答案所建议的
\u foo.wrappedValue
。在初始化大写字母之前,你试图访问self.foo
。这与属性包装器无关。@ClausJørgensen好吧,正如您在NoProblem
示例中所看到的,当您不使用属性包装器时,同样的方法也可以正常工作。@MumtazHussain这是不可能的。当然,我可以将upperCased
设置为lazy变量,但它会抱怨lazy属性必须有一个初始值设定项。这在现实世界的例子中并不总是一个选项。“你的整个方法就是这里的错误所在”谢谢你。同样,这是一个极其简化的示例,仅用于显示编译器错误。为什么它在使用属性包装器时会抱怨,而在不使用属性包装器时却没有完成呢?这就是问题所在。正如我所说,当尝试对可观察属性或注入属性或其他任何内容使用属性包装器时,也会出现同样的问题。添加了两个示例。不,事实上,这在SwiftUI中也是一个真正的问题,请参见我编辑的问题f