From 7547bf8d8002c4a4e11bb066807b7faad26ef97b Mon Sep 17 00:00:00 2001 From: fanbaoying Date: Sun, 8 Oct 2023 14:47:30 +0800 Subject: [PATCH] Update README --- .DS_Store | Bin 8196 -> 8196 bytes README.md | 11 +- resource/.DS_Store | Bin 12292 -> 14340 bytes ...20\345\221\230\346\237\245\346\211\276.md" | 111 ++++++++++++++++++ 4 files changed, 118 insertions(+), 4 deletions(-) create mode 100644 "resource/Swift \344\270\255\347\232\204\345\212\250\346\200\201\346\210\220\345\221\230\346\237\245\346\211\276.md" diff --git a/.DS_Store b/.DS_Store index 53c3cda9f44c9cf4b1d3d7a8fe2a781d51a6c2d6..3518b0a41e3e929087aaa50e228c6a7ef1fe054c 100644 GIT binary patch delta 174 zcmZp1XmQxEP-t?sK$(c8v5tbFsbQ^-LbaiRp^=V)nT5e*Rzc&*vBGXl(o&O`3g3~A z$V|@93(l-cjThiA&d4wK1abq4Qp++^%O}qf(a>ao01go-14c21_}?vFaZFNYAiJX delta 154 zcmZp1XmQxEP>6BIxCsxgr`&4GSh87m?-SRCWkp0E5Zz vL=+h%Cku#*GiGhp5WUZ|nN8w5ix9(HhV=})8BQ?VWq8lSwift 进阶 @@ -121,6 +120,8 @@
点击查看内容 +[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) @@ -238,6 +239,8 @@
点击查看内容 +[使用 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) diff --git a/resource/.DS_Store b/resource/.DS_Store index cbcaef4fedbc7210fec9e44f10f8ef799f51825f..168b41f199945de3a52412952870e627e2a53356 100644 GIT binary patch delta 287 zcmZokXem%&U|?W$DortDU@!nOIe-{M3-ADmb_NCoo{0+jqUJyjGf)u7X9#8}XUJqo zVMR`JkxS=8Yn@%p9_jP5z<&^`(<1s*6gbZU{FB`R<(8Akg3j60XP=ZWd&G&pesm#FqzXFvvX&K-yq)hDjAOL^l9F CKOfHk diff --git "a/resource/Swift \344\270\255\347\232\204\345\212\250\346\200\201\346\210\220\345\221\230\346\237\245\346\211\276.md" "b/resource/Swift \344\270\255\347\232\204\345\212\250\346\200\201\346\210\220\345\221\230\346\237\245\346\211\276.md" new file mode 100644 index 0000000..1b4692b --- /dev/null +++ "b/resource/Swift \344\270\255\347\232\204\345\212\250\346\200\201\346\210\220\345\221\230\346\237\245\346\211\276.md" @@ -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\: 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(dynamicMember keyPath: KeyPath) -> 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\ = .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。