【问题标题】:What's the difference between $varName and _varName in SwiftUISwiftUI 中的 $varName 和 _varName 有什么区别
【发布时间】:2019-09-24 22:42:06
【问题描述】:

当您使用属性包装器时,您可以访问$varName_varName,但我并没有真正理解其中的区别。比如这里

import SwiftUI

struct ContentView: View {
    @Binding var varName: String

    var body: some View {
        TextField("", text: $varName) //here you can also use `_varName`
    }
}

#if DEBUG
struct ContentView_Previews: PreviewProvider {
  static var previews: some View {
    ContentView(varName: .constant("Hello world!"))
  }
}
#endif

您可以同时使用$varName_varName。这两种解决方案似乎是等价的。两个变量都是Binding<String>。但如果我需要这样的东西:

import SwiftUI

struct ContentView: View {
    @Binding var varName: String

    init(varName: Binding<String>) {
        self.$varName = varName //ERROR
    }

    var body: some View {
        TextField("", text: $varName)
    }
}

#if DEBUG
struct ContentView_Previews: PreviewProvider {
  static var previews: some View {
    ContentView(varName: .constant("Hello world!"))
  }
}
#endif

我会得到一个错误:

无法分配给属性:“$varName”是不可变的

我必须使用_varName 来抑制错误:

struct ContentView: View {
    @Binding var varName: String

    init(varName: Binding<String>) {
        self._varName = varName //this works fine
    }

    var body: some View {
        TextField("", text: _varName)
    }
}

他们仍然都是Binding&lt;String&gt;,那么为什么以前的解决方案不起作用呢?根据 Apple (https://developer.apple.com/videos/play/wwdc2019/415/) 的说法,编译器会将属性包装器变成两件事。这个:

@Binding var varName: String

变成:

//Compiler-synthesized code
var $varName = Binding<String> = Binding<String>()

public var varName: String {
    get { $varName.wrappedValue }

    set { $varName.wrappedValue = newValue }
} 

$varName 应该是var,那为什么上面的错误呢?而且,最重要的是,_varName 是什么?它来自哪里?

【问题讨论】:

    标签: ios swift swiftui


    【解决方案1】:

    合成的_varName 属性是一个存储的、可设置的属性,它包含(在您的情况下)Binding&lt;String&gt; 的一个实例。

    varName 属性映射到包装器的 wrappedValue 属性。 Binding declares wrappedValue 像这样:

    var wrappedValue: Value { get nonmutating set }
    

    由于wrappedValue 是用nonmutating set 声明的,因此合成的varName 属性始终是可设置的(即使self 不可变)。

    如果包装器具有projectedValue 属性,则合成的$varName 属性将映射到包装器的projectedValue 属性。 Binding declares projectedValue 像这样:

    var projectedValue: Binding<Value> { get }
    

    由于projectedValue 仅声明为get,而不是get set,因此您永远不能分配给$varName

    Binding 不需要需要提供projectedValue 属性,因为您可以使用_varName 来获取Binding&lt;String&gt; 对象。 Binding 声明 projectedValue 属性的原因是为了使 $ 前缀对 Binding 的工作方式与对 StateObservedObjectEnvironmentObject 的工作方式相同。

    【讨论】:

    • 谢谢 Rob,现在我明白我为什么在这里搞混了。一旦有时间,我会添加更多细节以更好地解释这个错误。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-25
    • 1970-01-01
    • 1970-01-01
    • 2020-03-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多