Alamofire 源码赏析(一)基础篇

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
2
3
4
5
6
public enum ParameterEncoding {
case URL
case URLEncodedInURL
case JSON
case PropertyList(NSPropertyListFormat, NSPropertyListWriteOptions)
case Custom((URLRequestConvertible, [String: AnyObject]?) -> (NSMutableURLRequest, NSError?))

基于Swift的特性,在枚举中可以定义方法来实现“对外统一接口,内部根据自己当前的枚举值有着不同的实现”,这是Swift中很常见的写法。

1
2
3
4
5
6
7
8
9
10
public func encode(...) {
...
switch self {
case .JSON:
...
case .PropertyList(let format, let options):
...
}
...
}

再举个Result的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public enum Result<Value, Error: ErrorType> {
case Success(Value)
case Failure(Error)

/// Returns `true` if the result is a success, `false` otherwise.
public var isSuccess: Bool {
switch self {
case .Success:
return true
case .Failure:
return false
}
}
...
}

结构体

对于那些没有复杂业务逻辑,主要用来保存数据的结构,用结构体来实现,例如Response:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public struct Response<Value, Error: ErrorType> {
/// The URL request sent to the server.
public let request: NSURLRequest?

/// The server's response to the URL request.
public let response: NSHTTPURLResponse?

/// The data returned by the server.
public let data: NSData?

/// The result of response serialization.
public let result: Result<Value, Error>

/**
Initializes the `Response` instance with the specified URL request, URL response, server data and response
serialization result.

- parameter request: The URL request sent to the server.
- parameter response: The server's response to the URL request.
- parameter data: The data returned by the server.
- parameter result: The result of response serialization.

- returns: the new `Response` instance.
*/
public init(request: NSURLRequest?, response: NSHTTPURLResponse?, data: NSData?, result: Result<Value, Error>) {
self.request = request
self.response = response
self.data = data
self.result = result
}
}

泛型

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
2
3
4
5
6
7
public func request(
method: Method,
_ URLString: URLStringConvertible,
parameters: [String: AnyObject]? = nil,
encoding: ParameterEncoding = .URL,
headers: [String: String]? = nil)
-> Request

鉴权

1
2
3
4
5
public func authenticate(
user user: String,
password: String,
persistence: NSURLCredentialPersistence = .ForSession)
-> Self

进度

1
public func progress(closure: ((Int64, Int64, Int64) -> Void)? = nil) -> Self

响应

1
2
3
4
public func response(
queue queue: dispatch_queue_t? = nil,
completionHandler: (NSURLRequest?, NSHTTPURLResponse?, NSData?, NSError?) -> Void)
-> Self

验证

1
public func validate(validation: Validation) -> Self 

调用示例:

1
2
3
4
5
6
Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"])
.validate(statusCode: 200..<300)
.validate(contentType: ["application/json"])
.response { response in
print(response)
}

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
2
3
.response { response in
print(response)
}

把一个操作闭包(内部创建的闭包,不是response之后跟随的这个闭包)添加到queue,这个闭包中做了如下处理:

  1. 检查TaskDelegate中是否保存了NSError,如果有,则直接把NSError放到response中,并且回调给调用方
  2. 选择一种方式来序列化response
  3. 把序列化的结果放到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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public func request(URLRequest: URLRequestConvertible) -> Request {
var dataTask: NSURLSessionDataTask!

dispatch_sync(queue) {
dataTask = self.session.dataTaskWithRequest(URLRequest.URLRequest)
}

let request = Request(session: session, task: dataTask)
delegate[request.delegate.task] = request.delegate

if startRequestsImmediately {
request.resume()
}

return request
}

当同时有多个请求被创建时,每个Task的创建操作被同步追加到queue中,并等待它创建完成后,才执行后面的代码。

1
2
3
dispatch_sync(queue) {
dataTask = self.session.dataTaskWithRequest(URLRequest.URLRequest)
}

就是说,每个请求是按照顺序一个接一个创建的。

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的并发请求处理方式。

总结:

  1. 顺序创建Task,以Task为key保存TaskDelegate。
  2. 由Manager统一并发地处理NSURLSession的回调。
  3. 在NSURLSession的回调中根据task把回调分发给对应的TaskDelegate。

(本篇完)

下篇预告:
《Alamofire 源码赏析(二)安全篇》