最近两天在阅读 Moya,一个在基于 Alamofire 的对网络请求进行封装的框架。下面是我的阅读笔记。
范围表达式 RangeExpression
下面是过滤网络状态(statusCodes)的filter方法:
func filter<R: RangeExpression>(statusCodes: R) throws -> Response where R.Bound == Int {
guard statusCodes.contains(statusCode) else {
throw MoyaError.statusCode(self)
}
return self
}
在表示可以接收的 statusCodes 的范围时,使用了 RangeExpression。
Swift 中表示范围时有多种类型:
一、范围(Range):
- 元素类型要求满足 Comparable半开范围(Range):比如 0..<10;
- 闭合范围(ClosedRange):比如 0...10;
二、可数范围,和 Range 相比,它的元素类型需要遵守 Strideable 协议 (以整数为步长):
- 可数半开范围(CountableRange):。 比如 0..<10。
- 可数闭合范围(CountableClosedRange):。 比如 0...10。
三、部分范围 (partial range):指的是将 ... 或 ..< 作为前置或者后置运算符来使用时所构造的范围:
- ... 作为后置,比如 Character("a")...
- ... 作为前置,比如 ...Character("z")
- ..< 作为后置,比如 ..<10
- ..< 作为前置,比如 10..<
上面的八种类型都符合 RangeExpression 协议。
标签语句——提前结束循环语句和条件语句
在 Moya 中 Response 类中,有一段代码使用了标签语句:
let jsonData: Data
keyPathCheck: if let keyPath = keyPath {
guard let jsonObject = (try mapJSON(failsOnEmptyData: failsOnEmptyData) as? NSDictionary)?.value(forKeyPath: keyPath) else {
if failsOnEmptyData {
throw MoyaError.jsonMapping(self)
} else {
jsonData = data
break keyPathCheck
}
}
if let data = try serializeToData(jsonObject) {
jsonData = data
} else {
// 省略部分代码
}
} else {
jsonData = data
}
// 省略下面针对 jsonData 的操作
在循环中,经常会用到 break 语句,来提前结束循环。
其实,在 Swift 的循环语句和条件语句中,都可以使用 break + 标签语句来提前结束它们的执行。上面的代码是一个很好的例子,在 if 条件中,需要提前结束 if 语句,继续执行下面的内容时,就用到了 break keyPathCheck。如果此处不使用 break + 标签的方式,后继针对 jsonData 的操作将会出现重复的代码。
使用 break 可以简化代码,同时势必会增加阅读代码的难度。
为了避免重复代码的出现,也可以通过其他方式:
- 将代码进行抽离,新增一个实例方法;
- 直接在方法内部再定义一个方法或者闭包,处理相似的逻辑。
给枚举(enum)加个“父类”
Moya 的核心类 MoyaProvider 的定义如下:
open class MoyaProvider<Target: TargetType>: MoyaProviderType {
}
在初始化 MoyaProvider 时,要明确泛型 Target 的具体类型。实现 TargetType 协议使用的是枚举类型(enum)。假如我们需要把网络请求分成多个模块,放到不同的枚举中,那应该将 Target 指定为什么呢?
如果使用类(class)来实现 TargetType 协议,我们可以创建一个父类,指定为父类类型。但是枚举(enum)类型无法继承。
Moya 的解决方案是 MultiTarget:
public enum MultiTarget: TargetType {
/// The embedded `TargetType`.
case target(TargetType)
/// Initializes a `MultiTarget`.
public init(_ target: TargetType) {
self = MultiTarget.target(target)
}
// 省略部分代码
}
MultiTarget 的设计非常简单,仅仅是保存了初始化传入的 target: TargetType,并用 target 实现 TargetType 协议。它达到了类的多态(我没办法说清楚 MultiTarget 的设计模式):
- MultiTarget 的具体表现由传入的 target 来负责,类似于子类重写父类的方法;
- MultiTarget 类似于“父类”,对外提供了统一的接口。
如果表达得不准确,欢迎指正,谢谢。