Moya 阅读笔记

2,254 阅读1分钟

这是我参与8月更文挑战的第14天,活动详情查看:8月更文挑战

最近两天在阅读 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 的设计模式):

  1. MultiTarget 的具体表现由传入的 target 来负责,类似于子类重写父类的方法;
  2. MultiTarget 类似于“父类”,对外提供了统一的接口。

如果表达的不准确,欢迎指正,谢谢。

参考