Skip to content

Commit

Permalink
Update README
Browse files Browse the repository at this point in the history
  • Loading branch information
fanbaoying committed Oct 8, 2023
1 parent 4ddd4bc commit 7547bf8
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 4 deletions.
Binary file modified .DS_Store
Binary file not shown.
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,11 @@

## 最新

[WWDC 23 之后的 SwiftUI 有哪些新功能](https://mp.weixin.qq.com/s/X7BGxRYZq1WpKlGvNCFZQQ)
[回顾每一代 iPhone 的特性升级和创新](https://mp.weixin.qq.com/s/jGIqteUg1PRWxgQWdDmcIQ)

[Swift 单元测试入门](https://mp.weixin.qq.com/s/ik26DGr7Rye-14Q4-jLp2w)

[使用 HSB 而不是 RGB 来定义颜色](https://mp.weixin.qq.com/s/4I54IYunntZBV0sc8jUWVg)
[如何使用 SwiftUI 中新地图框架 MapKit](https://mp.weixin.qq.com/s/ISjtU6llF8z_byNCvH0qKQ)

[WWDC 23 之后的 SwiftUI 有哪些新功能](https://mp.weixin.qq.com/s/X7BGxRYZq1WpKlGvNCFZQQ)

## <a name="1">Swift 进阶</a>

Expand Down Expand Up @@ -121,6 +120,8 @@
<details>
<summary>点击查看内容</summary>

[Swift 单元测试入门](https://mp.weixin.qq.com/s/ik26DGr7Rye-14Q4-jLp2w)

[成为更好的 Swift 开发者的 10 个 Tips](https://mp.weixin.qq.com/s/9p7s15ndgnLDqjrSX0PW_A)

[了解 Swift 调度器](https://mp.weixin.qq.com/s/VS0nB2f9hf8jMAW33Qal6g)
Expand Down Expand Up @@ -238,6 +239,8 @@
<details>
<summary>点击查看内容</summary>

[使用 HSB 而不是 RGB 来定义颜色](https://mp.weixin.qq.com/s/4I54IYunntZBV0sc8jUWVg)

[Swift 开发者常犯的十大错误](https://mp.weixin.qq.com/s/Q9TFFEUFu_3qnnTE-hXAvA)

[App UI 设计灵感](https://github.com/fanbaoying/AppUIDesign/blob/main/README.md)
Expand Down
Binary file modified resource/.DS_Store
Binary file not shown.
111 changes: 111 additions & 0 deletions resource/Swift 中的动态成员查找.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
## 前言

我最喜欢 Swift 语言的一个特性是动态成员查找(dynamic member lookup)。虽然我们并不经常使用它,但它通过改进我们访问特定类型数据的方式,显著改善了所提供类型的 API。

> Glassfy:简化构建、管理和推广应用内购买。从订阅管理 SDK 到付费墙等完整的货币化工具。立即免费构建。
## 基础知识

假设我们正在开发一个提供缓存功能的类型,并将其建模为名为 Cache 的结构体。

```Swift
struct Cache {
var storage: [String: Data] = [:]
}
```

为了访问缓存的数据,我们调用存储属性的下标,该存储属性是 Dictionary 类型提供的。

```Swift
var cache = Cache()
let profile = cache.storage["profile"]
```

在这里没有什么特别之处。我们像以前一样通过 Dictionary 类型的下标访问字典。让我们看看如何使用 `@dynamicMemberLookup` 属性改进 Cache 类型的 API。

```Swift
@dynamicMemberLookup
struct Cache {
private var storage: [String: Data] = [:]

subscript(dynamicMember key: String) -> Data? {
storage[key]
}
}
```

如上例所示,我们使用 `@dynamicMemberLookup` 属性标记了 Cache 类型。我们必须实现具有 `dynamicMember` 参数并返回我们需要的任何内容的下标。

```Swift
var cache = Cache()
let profile = cache.profile
```

现在,我们可以更方便地访问 Cache 类型的配置文件数据。我们的 API 的使用者可能会认为配置文件是 Cache 类型的属性,但事实并非如此。

此特性完全在运行时工作,并利用了在点符号后键入的任何属性名称来访问 Cache 类型的下标,该下标具有 dynamicMember 参数。

整个逻辑在运行时运行,编译期间的结果是不确定的。在运行时,您完全可以决定应该从下标返回哪些数据以及如何处理 dynamicMember 参数。

## 使用 KeyPath 的编译时安全性

我们唯一能找到的缺点是缺乏编译时安全性。我们可以将 Cache 类型视为代码中键入的任何属性名称。幸运的是,`@dynamicMemberLookup` 下标的参数不仅可以是 String 类型,还可以是 KeyPath 类型。

```Swift
@dynamicMemberLookup
final class Store\<State, Action>: ObservableObject {
typealias ReduceFunction = (State, Action) -> State

@Published private var state: State
private let reduce: ReduceFunction

init(
initialState state: State,
reduce: @escaping ReduceFunction
) {
self.state = state
self.reduce = reduce
}

subscript<T>(dynamicMember keyPath: KeyPath<State, T>) -> T {
state[keyPath: keyPath]
}

func send(_ action: Action) {
state = reduce(state, action)
}

}
```

如上例所示,我们定义了接受强类型 KeyPath 实例的 dynamicMember 参数下标。在这种情况下,我们允许 State 类型的 KeyPath,这有助于我们获得编译时安全性。因为每当我们传递与 State 类型无关的错误 KeyPath 时,编译器都会显示错误。

```Swift
struct State {
var products: \[String] = \[]
var isLoading = false
}

enum Action {
case fetch
}

let store: Store\<State, Action> = .init(initialState: .init()) { state, action in
var state = state
switch action {
case .fetch:
state.isLoading = true
}
return state
}

print(store.isLoading)
print(store.products)
print(store.favorites) // Compiler error
```

在上例中,我们通过接受 KeyPath 的下标访问 Store 的私有 state 属性。这看起来与前面的例子类似,但在这种情况下,只要您尝试访问 State 类型的不可用属性,编译器就会显示错误。

## 总结

今天我们学习了如何使用 `@dynamicMemberLookup` 属性改进特定类型的 API。虽然并不是每个类型都需要它,但您可以谨慎使用它来改善 API。

0 comments on commit 7547bf8

Please sign in to comment.