相关文档:
apple - Key Path Member Lookup
SwiftUI 和 Swift 5.1 新特性(3) Key Path Member Lookup
Protected
定义一个协议 Lock,让实现该协议的类拥有lock()、unlock()、around()能力,其中lock()、unlock()需要遵循协议的类自定义实现。
|
|
自定义锁 UnfairLock 遵循 Lock 协议,并自定义实现lock()、unlock()能力。
|
|
使用 @propertyWrapper 和 @dynamicMemberLookup 两个批注,自定义属性包裹器 Protected,实现属性的读写安全。
|
|
应用:
|
|
@propertyWrapper
属性包装器是一种通用结构,用于封装属性的读写访问,或添加其他行为。比如限制可用的属性值、向读写访问添加额外逻辑、添加方法等。
Swift 5.1 为创建属性包装器提供了一种新的解决方案,使用@propertyWrapper批注标记属性包装器。
wrappedValue:使用@propertyWrapper的包装器,要求包装器对象必须包含一个被包装的值的非静态属性。
|
|
projectedValue:@propertyWrapper 提供了另一种语法糖——投影值,通过在属性前加$前缀来访问属性值。

@dynamicMemberLookup
@dynamicMemberLookup 称为动态成员查找,是Swift的一项功能特性,允许动态成员查找调用看起来像范文类型属性的常规调用。
|
|
name是从字典中查找的,而不是作为 dic 的属性访问的。
实现支持动态查找的People类:
- 使用
@dynamicMemberLookup标注People的类型; - 实现
subscript(dynamicMember key:)方法。
|
|
没有使用 @dynamicMemberLookup 标注的情况会报错:

1. KeyPath
KeyPath<Root, Value> 泛型类型,用来表示从 Root 类型到 Value 属性的访问路径。
|
|
这里用到了一个语法 \Person.name,它的类型是 KeyPath<Person, String>

同一类型
KeyPath<Root, Value>可以代表多种获取路径:KeyPath<Person, Int>既可以表示\Person.age,也可以表示\Person.name.count。两个 KeyPath 可以拼接成一个新的 KeyPath:
KeyPath1的类型是KeyPath<Person, String>,\String.count的类型是KeyPath<String, Int>,调用appending函数后变成了KeyPath<Person, Int>类型。
|
|
KeyPath是WriteableKeyPath的父类,WriteableKeyPath是ReferenceWritableKeyPath的父类。KeyPath的能力是最弱的,只能以只读的方式访问属性。
|
|
WriteableKeyPath可以对可变属性进行读写操作。将let改成var,则person.name的类型就变成了WriteableKeyPath,可以直接修改p[keyPath: keyPath] = "lisi"
|
|
ReferenceWritableKeyPath可以对引用类型的可变属性进行写入。将struct改成class,则Person.name的类型变成ReferenceWritableKeyPath
|
|
2. 动态成员查找 Dynamic Member Lookup
Swift4.2 引入了动态成员查找特性,实现使用静态的语法做动态的查找。
|
|
支持动态成员查找的类型首先需要用 @dynamicMemberLookup 来修饰,然后实现动态查找方法subscript(dynamicMember member: String)。
直接使用 .property 的语法访问属性貌似静态实则动态,该语法实际上调用的是subscript(dynamicMember member: String)方法。
3. 成员查找 Key Path Member Lookup
Swift5.1 引入的成员查找。
编译器可以从 KeyPath 查询所有的目标,以及它们的类型:
|
|
person.info.name 可以直接写成 person.name。
@dynamicMemberLookup 非常适用于包装类型:
|
|
链式调用
通过回传 self,实现链式调用:
|
|
这种方法被动性比较大,如果 Label 的属性变化,则对应的 setter 方法也得改变。
使用动态成员查找实现:
|
|
创建一个 UILabel:

线程安全的属性 MutableState
在 Request.swift 文件中,Alamofire 使用 @Protected 标注,实现了一个线程安全的对象 mutableState:
|
|
MutableState 中定义了很多变量,这些变量可能在异步线程调用:
|
|
比如:
|
|
或者
|
|
AlamofireExtended
Alamofire 扩展包裹器,用来对系统类进行扩展时包裹使用,避免方法入侵。
第一种方式
通过添加全局方法的方式扩展两个新的方法:
|
|
通过这种方式添加的方法,在所有能访问到该扩展的地方都可以调用。调用时,扩展的方法直接出现在 UIButton 提示的方法列表中。这有一个问题,一方面会给调用者造成困惑,另一方发面会存在同名的风险,造成方法污染👇:

第二种方式
写一个包裹器,将 UIButton 包裹起来,在包裹器中添加需要扩展的方法。
|
|
通过实现一个包裹器wrapper,扩展的方法只会出现在wrapper的调用提示方法列表里,UIButton的调用提示方法列表里只新增了一个 wrapper,从而避免了对UIButton的方法污染。
UIButtonWrapper 是针对UIButton设计的一个包裹器,只适用于UIButton。可以借用Swift泛型与扩展约束的力量,实现一个泛型包裹器,从而实现为需要的任何类型扩展方法。
第三种方式
使用泛型和扩展约束,实现一个泛型包裹器:
|
|
通过泛型 AlamofireExtension<ExtendedType> 实现了对UIButton和UILabel的同时支持,扩展约束 where ExtendedType == UIButton 实现了对UIButton和UILabel扩展方法的区分,这种区分可以从调用提示方法列表中看出来:
UILabel提示方法列表:
UIButton提示方法列表:
通过UIButton和UILabel的扩展方式可以看出,在为类型扩展方法时,首先需要添加 af 计算属性来返回泛型包裹器AlamofireExtension<ExtendedType>。
|
|
可以使用泛型协议和扩展的方式,添加默认实现,从而省略掉这一步。
第四种方式
使用泛型协议和扩展的,实现af计算属性:
|
|
应用:
|
|
静态包裹器扩展了类型方法:
|
|
扩展的类型方法,也有对应的提示方法列表:

EventMonitor
protocol EventMonitor 协议概述了 Alamofire 生命周期里的内部事件。EventMonitor 既包括从各种 URLSession 委托协议接收的事件,也包括Request 及其子类生命周期内的各种事件。
|
|
实例代码中只展示了URLSessionDelegate协议相关方法的声明,其它协议的声明省略。
在 EventMonitor 协议声明完方法后,还要对这些方法提供默认实现:
|
|
因为 EventMonitor 协议包含了 URLSession 和 Request 的各种事件,所以方法比较多。实现中什么也没做,只是提供了默认实现。
EventMonitor 协议就像一个显示器,完整且全面的展示了 Alamofire 内部发生的各种事件,透过它可以大概了解 Alamofire 都做了些什么。然而,EventMonitor 只是一个抽象类,在Alamofire的逻辑代码部分用到的并不是它,而是遵循了 EventMonitor 协议的类 CompositeEventMonitor,负责处理 Alamofire 中的各种事件。
|
|
CompositeEventMonitor
CompositeEventMonitor,负责处理 Alamofire 中的各种事件。从命名就可以看出,这是一个批量处理事件的“管理类”,可以称为事件监视器集合。
|
|
CompositeEventMonitor 遵循了 EventMonitor 协议,并且实现了全部方法。这里也只提供两个方法,其它方法省略。
CompositeEventMonitor 实现这些方法为了是使用队列来调度方法的调用,所以方法实现部分都是一样的,都调用了 performEvent 方法:
- 在 Session 和 Request 事件回调时,最终都会来到
performEvent方法; - 依次取出数组里的事件监视器,放到同步队列里执行。
|
|
event(monitor) 调用了闭包,闭包内部实现👇,$0 是参数 monitor:
|
|
defaultEventMonitors
在 Session 的初始化方法里👆🏻,eventMonitors 数组里包含两个部分,一个是默认的 defaultEventMonitors,另一个是调用者添加的自定义事件监听器:
|
|
defaultEventMonitors 的定义:
|
|
AlamofireNotifications 提供Alamofire通知的事件监视器。
- 遵循 EventMonitor 协议;
- 实现需要监听的方法;
|
|
在 CompositeEventMonitor 的 performEvent 方法里,event(monitor) 会调用 AlamofireNotifications,触发对应的方法。
ClosureEventMonitor
事件监视器——允许将可选闭包设置为接收事件。
|
|
1、CompositeEventMonitor 的 performEvent,获取到 ClosureEventMonitor,将事件放入队列中;
|
|
2、ClosureEventMonitor 调用实现的代理方法;
|
|
3、从而调用对应的闭包
|
|
Concurrency
async-await
|
|
打印结果:
|
|
增加测试数据:
|
|
打印结果:
|
|
使用 Task.sleep(nanoseconds:):
|
|
打印结果:
|
|
await Task.sleep(nanoseconds:) 方法有可能会调用失败,所以需要加 try: