当您使用属性包装器时,您可以访问两者$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
Run Code Online (Sandbox Code Playgroud)
您可以同时使用$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
Run Code Online (Sandbox Code Playgroud)
我会得到一个错误:
无法分配给属性:“$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)
}
}
Run Code Online (Sandbox Code Playgroud)
They are still both Binding<String>, so why won't the former solution work? According to Apple (https://developer.apple.com/videos/play/wwdc2019/415/) the compiler will turn a property wrapper into two things. This:
@Binding var varName: String
Run Code Online (Sandbox Code Playgroud)
becomes:
//Compiler-synthesized code
var $varName = Binding<String> = Binding<String>()
public var varName: String {
get { $varName.wrappedValue }
set { $varName.wrappedValue = newValue }
}
Run Code Online (Sandbox Code Playgroud)
$varName should be var, so why the error above? And, above all, what is that _varName? Where does it come from?
合成_varName属性是一个存储的、可设置的属性,它保存(在您的情况下)Binding<String>.
该varName属性映射到包装器的wrappedValue属性。Binding声明wrappedValue如下:
Run Code Online (Sandbox Code Playgroud)var wrappedValue: Value { get nonmutating set }
因为wrappedValue是用 声明的nonmutating set,所以合成的varName属性总是可设置的(即使self是不可变的)。
如果包装器具有属性,则合成$varName属性将映射到包装projectedValue器的projectedValue属性。Binding声明projectedValue如下:
Run Code Online (Sandbox Code Playgroud)var projectedValue: Binding<Value> { get }
由于projectedValue只是声明get,而不是get set,你永远不能分配给$varName.
Binding并不需要提供一个projectedValue属性,因为你可以使用_varName来获取Binding<String>对象。究其原因Binding声明一个projectedValue属性,使$前缀工作以同样的方式Binding,因为它确实为State,ObservedObject和EnvironmentObject。