iugo / candev

Can development be self-taught? 开发能自学吗?
The Unlicense
0 stars 0 forks source link

Swift 代码 `\.self` 中的 `\` 是什么意思? Key-Path 表达式. #11

Open iugo opened 1 year ago

iugo commented 1 year ago

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/expressions#Key-Path-Expression 中有这种表达式的详细说明.

  1. Key-Path 表达式的意义.
  2. 根据上下文省略类型的 Key-Path 表达式.
  3. 使用 Key-Path 表达式快捷创建 identity key.
  4. 使用 Key-Path 表达式访问深层路径.
  5. 使用 Key-Path 表达式访问数组.
  6. Key-Path 是一个表达式, 而不会实时计算.

Key-Path 表达式的意义

在 JS 中, 我们常会用到这样的代码:

const user = { name: 'Joe', age: 18 };
const key = 'name';
const v = user[key];

而在 Swift 中, 实现类似功能的方法为 Key-Path 表达式.

struct User {
    var name: String
    var address: String
}

let u = User(name: "Joe", address: "NY")
var pathToProperty = \User.name

if (Int.random(in: 1...10) > 5) {
  pathToProperty = \User.address
}

let value = u[keyPath: pathToProperty]

print("value", value);

不过从上述例子中我们也能看到, Swift 为了保证后续使用 path 时的类型安全, 在定义 pathToProperty 时已经推断类型为 WritableKeyPath<User, String>, 而不是 WritableKeyPath<User, String or Int>.

根据上下文省略类型的 Key-Path 表达式

Key-Path 表达式有两大部分组成:

  1. 类型
  2. 路径

其中, 前者在上下文充分时是可以省略的.

比如官方例子中:

class SomeClass: NSObject {
    @objc dynamic var someProperty: Int
    init(someProperty: Int) {
        self.someProperty = someProperty
    }
}

let c = SomeClass(someProperty: 10)
c.observe(\.someProperty) { object, change in
    // ...
}

在对 c.observe 传参的时候, 我们只希望传入 "路径", 类型对于这个方法来说是已知的, 就是 Self. 此时可以省略.

Key-Path 是一个表达式, 而不会实时计算

var index = 2
let path = \[String].[index]
let fn: ([String]) -> String = { strings in strings[index] }

print(greetings[keyPath: path])
// Prints "bonjour"
print(fn(greetings))
// Prints "bonjour"

// Setting 'index' to a new value doesn't affect 'path'
// 如果想要影响, 应该再设置一遍 path = \[String].[index]
index += 1
print(greetings[keyPath: path])
// Prints "bonjour"

// Because 'fn' closes over 'index', it uses the new value
print(fn(greetings))
// Prints "안녕"

代码:

struct ContentView: View {
    @State private var data = [Int]()

    var body: some View {
        List(data, id: \.self) { item in
            Text(item)
        }
        .onAppear {
            // 在视图首次显示时加载数据
            self.data = [1, 2, 3, 4, 5]
        }
    }
}

在 SwiftUI 中,\ 是一个符号,用于引用当前的视图或属性。

如果不使用 \ 符号,则需要使用 self.data 来引用当前视图的 data 属性。例如,以下代码与上述代码等效:

struct ContentView: View {
    @State private var data = [Int]()

    var body: some View {
        List(data, id: self.data) { item in
            Text(item)
        }
        .onAppear {
            // 在视图首次显示时加载数据
            self.data = [1, 2, 3, 4, 5]
        }
    }
}

使用 \ 符号可以使代码更简洁、更易于阅读。