Alamofire是Swift编写的网络库,如果你的项目使用Swift编写的,则可以用它来替代AFNetworking。Alamofire和AFNetworking都是由Mattt Thompson发起的。Alamofire相比AFNetworking而言,利用了Swift语言的特性,来使得代码更加优雅,并具有一些强大的特性。
下面我们从源码中来一一赏析这些。
结构设计
基于Swift语言的简洁,Alamofire的文件并不多,分成了两个目录Core和Feature,非常的清晰。
Core目录中包含了核心的2个类和3个枚举和2个结构体。
2个核心类:
类名 | 职责 |
---|---|
Manager | 提供对外接口,处理NSURLSession的delegate |
Request | 对请求的处理 |
3个枚举:
枚举名 | 职责 |
---|---|
Method | HTTP Method |
ParameterEncoding | 参数编码方式(JSON、PropertyList) |
Result | 定义了请求成功或失败的数据结构 |
2个结构体:
结构体名 | 职责 |
---|---|
Response | 封装了响应需要包含的数据结构 |
Error | 错误码定义;创建NSError对象; |
Features目录中则是对这些核心数据结构的扩展:
文件名 | 职责 |
---|---|
Upload | 处理上传相关操作,是Manager类的扩展 |
Download | 处理下载相关操作,是Manager类的扩展 |
Stream | 处理输入输出流,是Manager类的扩展 |
Validation | 对Response数据进行校验,是Request类的扩展 |
ResponseSerialization | Response的序列化处理,是Request类的扩展 |
MultipartFormData | 自己是一个类,被Request的Upload引用,处理MultipartFormData |
ServerTrustPolicyManager | 自己是一个类,被NSURLSession引用,处理安全策略 |
可以看到并不是每个文件都是一个类,Upload、Download、Stream这些操作都以extension的形式放到单独的文件中,做到了不仅由Manager类来统一管理,也按职责进行了拆分。
同样Validation和ResponseSerialization也以Request类的扩展的形式存在,并支持Request类的链式调用。
类图如下:
职责划分明确,有着很好的内聚性。
枚举
凡是需要类型划分的都用枚举来实现,例如:
1 | public enum ParameterEncoding { |
基于Swift的特性,在枚举中可以定义方法来实现“对外统一接口,内部根据自己当前的枚举值有着不同的实现”,这是Swift中很常见的写法。
1 | public func encode(...) { |
再举个Result的例子:
1 | public enum Result<Value, Error: ErrorType> { |
结构体
对于那些没有复杂业务逻辑,主要用来保存数据的结构,用结构体来实现,例如Response:
1 | public struct Response<Value, Error: ErrorType> { |
泛型
Alamofire中有着大量对泛型的使用
1 | public func validate<S: SequenceType where S.Generator.Element == Int>(statusCode acceptableStatusCode: S) -> Self { |
1 | public enum Result<Value, Error: ErrorType> { |
命名空间
Alamofire是一个Framework,享有独立的命名空间,因此所有的类、结构体、枚举等的命名都不需要加前缀。
链式调用
对于Request类,其中的很多方法都使用了返回-> Self
的方式。
请求
1 | public func request( |
鉴权
1 | public func authenticate( |
进度
1 | public func progress(closure: ((Int64, Int64, Int64) -> Void)? = nil) -> Self |
响应
1 | public func response( |
验证
1 | public func validate(validation: Validation) -> Self |
调用示例:
1 | Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"]) |
Alamofire.request(...)
这一步首先会创建一个Request实例,这个实例中启动了一个NSURLSessionTask,这时,一个请求已经被发出。
Request类中有个内部类TaskDelegate用来处理与这个Task相对应的NSURLSession回调,TaskDelegate维护了一个Serial的 NSOperationQueue(maxConcurrentOperationCount = 1),TaskDelegate会随着Request一起创建,这个queue同时也被加载到内存中。
接下来的链式调用创建的操作闭包都会追加到这个queue中。
.validate(statusCode: 200..<300)
把一个用来验证StatusCode的操作闭包追加到queue,如果验证失败,会在TaskDelegate中保存一个NSError
.validate(contentType: ["application/json"])
把一个用来验证contentType的操作闭包追加到queue,如果验证失败,会在TaskDelegate中保存一个NSError
1 | .response { response in |
把一个操作闭包(内部创建的闭包,不是response之后跟随的这个闭包)添加到queue,这个闭包中做了如下处理:
- 检查TaskDelegate中是否保存了NSError,如果有,则直接把NSError放到response中,并且回调给调用方
- 选择一种方式来序列化response
- 把序列化的结果放到response参数中,并在主线程调用response之后跟随的这个闭包。
至此链式调用完成。
有个小细节
是,request之后追加到queue中的这些操作闭包都是对response进行处理的。所以在queue创建的时候要把queue挂起。
1 | operationQueue.suspended = true |
在NSURLSession回调结束(URLSession(_:task:didCompleteWithError:))后恢复queue。
1 | queue.suspended = false |
一句话总结:
Alamofire使用了-> Self
和顺序队列
来实现链式调用。
处理多请求并发
Alamofire的最外层是一个Manager,通过这个Manager来为每一个请求创建Request实例(Request实例背后是NSURLSessionTask)。
Manager中有个实例变量queue,是一个Serial Dispatch Queue。
1 | public func request(URLRequest: URLRequestConvertible) -> Request { |
当同时有多个请求被创建时,每个Task的创建操作被同步追加到queue中,并等待它创建完成后,才执行后面的代码。
1 | dispatch_sync(queue) { |
就是说,每个请求是按照顺序一个接一个创建的。
Manager有个内部类SessionDelegate,用来处理NSURLSession的回调方法。Manager在创建时,把Manager引用的NSURLSession对象的delegate赋值为这个SessionDelegate对象。
前面说到,每个请求创建的同时,一个对应的TaskDelegate实例也会创建,这个实例中保存了请求对应的NSURLSessionTask,
请求被创建后,会把相应的TaskDelegate以task为key保存在SessionDelegate中的一个实例变量字典中。
(这里有点绕,如果难以理解可以去参考下源码。)
当发出多次请求后,SessionDelegate中会保存多个TaskDelegate,每个TaskDelegate中都有一个NSURLSessionTask。
每次NSURLSession的回调回来以后,Manager会根据回调参数中的task(例如URLSession(_:task:didCompleteWithError:)中的task参数),以这个task为key,取出对应的TaskDelegate,并把回调分发给它。
(以task为key通过Swift语法的中下标subscript来实现。)
这就是Alamofire的并发请求处理方式。
总结:
- 顺序创建Task,以Task为key保存TaskDelegate。
- 由Manager统一并发地处理NSURLSession的回调。
- 在NSURLSession的回调中根据task把回调分发给对应的TaskDelegate。
(本篇完)
下篇预告:
《Alamofire 源码赏析(二)安全篇》