Alamofire 5.x 源码阅读分析(Source Code reading and analysis)

本文最后更新于 2021年4月4日 晚上

iOS端常用的三方框架, 很少有时间去阅读它们的源码, 目前正好有机会可以看看. 这次先来看 Alamofire, 一个基于苹果原生 URLSession 的网络通信框架.

文档

先看文档, 然后再分析源码, 循序渐进.

简介

下面从整体上介绍 Alamofire 的相关内容.

主要功能

  • 链式的请求/响应方法
  • URL/JSON/PList 参数编码
  • 文件/流/MultiPart表单数据上传
  • 文件下载/恢复
  • 使用 URLCredential 进行认证
  • HTTP 响应验证(Validation)
  • 提供上传/下载进度回调
  • cURL 命令输出
  • 动态适应(Adapt)/重试(Retry)请求
  • TLS 证书/公钥 Pinning(主动选择信任 CA 的能力)
  • 网络可达性检测
  • 大量的单元/集成测试覆盖
  • 完善的文档支持

组件库

Alamofire 只是核心库, 在其上还开发了若干组件:

  • AlamofireImage: 一个图片库, 提供图片响应序列化器, 以及 UIImage/UIImageView 扩展, 自定义图片过滤器, 自动清理图片缓存, 基于优先级的图片下载系统.

  • AlamofireNetworkActivityIndicator: 通过 Alamofire 来控制 iOS 的网络活动指示器. 并支持独立的 URLSession.

需求

  • iOS 10 以上/ macOS 10.12 以上/ tvOS 10 以上/ watchOS 3 以上
  • Xcode 10.2 以上
  • Swift 5 以上

支持

Alamofire 是基于苹果的 URLSession 的, 即URL Loading System.

在 swift.org 论坛中有专门的版块提供 Alamofire 支持.

在 Stack Overflow 上也有.

安装

支持如下安装方式:

  • CocoaPods: pod 'Alamofire', '~> 5.0.0-rc.3'
  • Carthage: github "Alamofire/Alamofire" "5.0.0-rc.3"
  • SPM:
    1
    2
    3
    dependencies: [
    .package(url: "https://github.com/Alamofire/Alamofire.git", from: "5.0.0-rc.3")
    ]
  • 手动集成: 基于 git submodule 方式.

基本使用

基本使用包括 HTTP 请求/响应, 数据的上传和下载, 以及一些实用工具的介绍.

Alamofire 基本实现概述

Alamofire 建立在苹果提供的 URL Loading System 上, 核心是 URLSessionURLSessionTask 类族. 并且在 Alamofire 中网络通信都是异步进行的, 不会阻塞主线程.

新版的 Alamofire 仍然提供默认的 Session(之前的 SessionManager), 只是把它挪到了 AF 名空间下, AF 是一个 enum.

基本使用的内容大多基于 AF 名空间下的静态方法实现.

进行请求

进行简单的请求:

1
2
3
AF.request("https://httpbin.org/get").response { response in
debugPrint(response)
}

其中 request 方法声明如下:

1
2
3
4
5
6
open func request<Parameters: Encodable>(_ convertible: URLConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoder: ParameterEncoder = URLEncodedFormParameterEncoder.default,
headers: HTTPHeaders? = nil,
interceptor: RequestInterceptor? = nil) -> DataRequest

方法返回一个 DataRequest 对象. 新版 API 中都推荐使用这个泛型的 request 重载, 而不推荐使用老的那个带 ParameterEncoding 参数的非泛型重载方法.

另外还有一个更简洁的 request 非泛型重载方法:

1
2
open func request(_ urlRequest: URLRequestConvertible, 
interceptor: RequestInterceptor? = nil) -> DataRequest

这个方法中提供了一个全新的 URLRequestConvertible 协议对象参数, 可以把所有请求参数都封装到里面, 极大地方便了开发者, 我们现在就可以利用这个协议来实现自定义请求参数类. 具体的使用方式详见高级功能用法一节.

HTTP 方法

根据 RFC 7231 第 4.3 节定义了如下的 HTTP 方法(使用 struct 而非 enum 定义的):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public struct HTTPMethod: RawRepresentable, Equatable, Hashable {
public static let connect = HTTPMethod(rawValue: "CONNECT")
public static let delete = HTTPMethod(rawValue: "DELETE")
public static let get = HTTPMethod(rawValue: "GET")
public static let head = HTTPMethod(rawValue: "HEAD")
public static let options = HTTPMethod(rawValue: "OPTIONS")
public static let patch = HTTPMethod(rawValue: "PATCH")
public static let post = HTTPMethod(rawValue: "POST")
public static let put = HTTPMethod(rawValue: "PUT")
public static let trace = HTTPMethod(rawValue: "TRACE")

public let rawValue: String

public init(rawValue: String) {
self.rawValue = rawValue
}
}

通过 request API 传入它们:

1
2
3
4
AF.request("https://httpbin.org/get")   // 默认是 get
AF.request("https://httpbin.org/post", method: .post)
AF.request("https://httpbin.org/put", method: .put)
AF.request("https://httpbin.org/delete", method: .delete)

由于不同的请求方法可能会需要不同的参数编码方式, 比如 post 可能把参数编码到请求体, 且请求体中可以包含 JSON 或表单数据等, 另外 苹果的 URL Loading System 中 URLSession 不允许在使用 Get 方法时携带请求体(Request Body)参数. 此时如果在 Get 请求中指定请求体参数, 将会报错.

在 Alamofire 的内部, 通过如下代码获取 URLRequest 中的请求方法字符串对应的 HTTPMethod:

1
2
3
4
5
6
7
public extension URLRequest {
/// Returns the `httpMethod` as Alamofire's `HTTPMethod` type.
var method: HTTPMethod? {
get { return httpMethod.flatMap(HTTPMethod.init) }
set { httpMethod = newValue?.rawValue }
}
}

另外, 如果需要使用自定义的 HTTP 方法(很少见, 但也可能), 此时可以像下面这样对 HTTPMethod 进行扩展:

1
2
3
extension HTTPMethod {
static let custom = HTTPMethod(rawValue: "CUSTOM")
}

请求参数和参数序列化器

Alamofire 支持对 Encodable 类型参数直接进行序列化. 目前 Alamofire 提供了两个 ParameterEncoder 实现类, 包括 JSONParameterEncoderURLEncodedFormParameterEncoder. 使用上非常方便.

其中 JSONParameterEncoder 可以将参数对象序列化为 JSON 字符串, 若请求头没有指定 ContentType, 则默认设置 application/json.

Post 请求传递 JSON 参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
struct Login: Encodable {
let email: String
let password: String
}

let login = Login(email: "[email protected]", password: "testPassword")

AF.request("https://httpbin.org/post",
method: .post,
parameters: login,
encoder: JSONParameterEncoder.default).response { response in
debugPrint(response)
}

可以在请求时对 JSON 序列化器进行配置:

1
2
3
4
5
6
7
8
9
10
11
let parameters: [String: [String]] = [
"foo": ["bar"],
"baz": ["a", "b"],
"qux": ["x", "y", "z"]
]

AF.request("https://httpbin.org/post", method: .post, parameters: parameters, encoder: JSONParameterEncoder.default)
AF.request("https://httpbin.org/post", method: .post, parameters: parameters, encoder: JSONParameterEncoder.prettyPrinted)
AF.request("https://httpbin.org/post", method: .post, parameters: parameters, encoder: JSONParameterEncoder.sortedKeys)

// HTTP body: {"baz":["a","b"],"foo":["bar"],"qux":["x","y","z"]}

可以设置自定义的 JSON 序列化器:

1
2
3
4
let encoder = JSONEncoder()
encoder.dateEncoding = .iso8601
encoder.keyEncodingStrategy = .convertToSnakeCase
let parameterEncoder = JSONParameterEncoder(encoder: encoder)

URLEncodedFormParameterEncoder 则支持将对象序列化为 query 字符串或请求体表单数据. 展开说就是在 Get 请求时, 默认将参数序列化为 query string, 在 Post 请求时, 将参数序列化为请求体中携带的参数. 通过 destination 可设置参数的具体位置:

  • .methodDependent: 在 get/head/delete 请求时,设置为 query string, 在其他情况下, 设置为请求体参数.

  • .queryString: 序列化为字符串并设置为 query string 参数

  • .httpBody: 序列化为字符串并设置为请求体参数

如果没有指定 Content-Type, 则 Alamofire 自动设置其为 application/x-www-form-urlencoded; charset=utf-8.

URLEncodedFormParameterEncoder 内部使用 URLEncodedFormEncoder 进行序列化操作.

Get 请求:

1
2
3
4
5
6
7
8
let parameters = ["foo": "bar"]

// All three of these calls are equivalent
AF.request("https://httpbin.org/get", parameters: parameters) // encoding defaults to `URLEncoding.default`
AF.request("https://httpbin.org/get", parameters: parameters, encoder: URLEncodedFormParameterEncoder.default)
AF.request("https://httpbin.org/get", parameters: parameters, encoder: URLEncodedFormParameterEncoder(destination: .methodDependent))

// https://httpbin.org/get?foo=bar

Post 请求时:

1
2
3
4
5
6
7
8
9
10
11
12
let parameters: [String: [String]] = [
"foo": ["bar"],
"baz": ["a", "b"],
"qux": ["x", "y", "z"]
]

// All three of these calls are equivalent
AF.request("https://httpbin.org/post", method: .post, parameters: parameters)
AF.request("https://httpbin.org/post", method: .post, parameters: parameters, encoder: URLEncodedFormParameterEncoder.default)
AF.request("https://httpbin.org/post", method: .post, parameters: parameters, encoder: URLEncodedFormParameterEncoder(destination: .httpBody))

// HTTP body: "qux[]=x&qux[]=y&qux[]=z&baz[]=a&baz[]=b&foo[]=bar"

可以通过 alphabetizeKeyValuePairs 设置是否将参数按照字母顺序排序, 默认情况下会自动按字母顺序排序(由于swift的字典是哈希字典, 如果不排序, 可能对缓存等产生影响, 因为每次的参数序列化字符串都可能不同):

1
let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(alphabetizeKeyValuePairs: false))

还有 Array/Bool/Data/Date等的序列化选项, 详见文档.

特别是常用的 Date 序列化, 可以指定 .iso8601/.millisecondsSince1970/.secondsSince1970 或者 .custom((Date) throws -> String) 等选项.

对于参数中的 Key 风格, 在序列化时也可以进行设置(默认是原样进行序列化):

1
2
// 设置 oneTwoThree 被序列化为 one_two_three
let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(keyEncoding: .convertToSnakeCase))

指定空格的序列化选项(默认是序列化为 %20):

1
2
// 将空格序列化时替换为加号 +
let encoder = URLEncodedFormParameterEncoder(encoder: URLEncodedFormEncoder(spaceEncoding: .plusReplaced))

HTTP Header 设置

Alamofire 提供了 HTTPHeaders 类型表示请求/响应头, 它能够保持头部数据的顺序, 并且忽略大小写.

在请求时使用 HTTPHeaders:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let headers: HTTPHeaders = [
"Authorization": "Basic VXNlcm5hbWU6UGFzc3dvcmQ=",
"Accept": "application/json"
]

AF.request("https://httpbin.org/headers", headers: headers).responseJSON { response in
debugPrint(response)
}

// 或者使用它提供的枚举:

let headers: HTTPHeaders = [
.authorization(username: "Username", password: "Password"),
.accept("application/json")
]

AF.request("https://httpbin.org/headers", headers: headers).responseJSON { response in
debugPrint(response)
}

最佳实践: 针对一直不变的请求头数据, 可以直接在初始化 Session 时设置到 URLSessionConfiguration 中.

Alamofire 中会设置如下默认请求头数据:

  • Accept-Encoding: 默认值 br;q=1.0, gzip;q=0.8, deflate;q=0.6.

  • Accept-Language: 默认值为系统中优先级最高的 6 种语言, 格式为: en;q=1.0

  • User-Agent: 根据当前 APP 的信息生成, 格式为: iOS Example/1.0 (com.alamofire.iOS-Example; build:1; iOS 13.0.0) Alamofire/5.0.0

创建新的 Session 实例后, 如果要自定义统一的请求头信息, 有两条路:

  • 新建 URLSessionConfiguration, 然后设置.

  • 获取 URLSessionConfiguration.af.default, 然后修改上面的内容并传递到 Session 的构造函数(构造函数中默认使用的就是它).

响应验证(Validation)

默认情况下, Alamofire 认为只要接收到响应就表示整个请求过程是成功的. 但有时希望对响应进行验证, 这个时候就可以使用 validate() 相关方法.

  • 自动验证: 链入空的 validate() 方法, 它认为响应状态码在 200..<300 范围内, 且如果设置了 Accept 头时响应的 Content-Type 和其相符的表示响应成功.

    1
    2
    3
    AF.request("https://httpbin.org/get").validate().responseJSON { response in
    debugPrint(response)
    }
  • 手动验证: 手动指定相关验证条件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    AF.request("https://httpbin.org/get")
    .validate(statusCode: 200..<300)
    .validate(contentType: ["application/json"])
    .responseData { response in
    switch response.result {
    case .success:
    print("Validation Successful")
    case let .failure(error):
    print(error)
    }
    }

响应处理

Alamofire 中的请求对象 或 DownloadRequest 对应的响应类型都是

Alamofire 中请求对象和响应对象的对应关系:

  • DataRequest 对应 DataResponse<Success, Failure: Error>
  • DownloadRequest 对应 DownloadResponse<Success, Failure: Error>

其中 Error 都是 AFError.

针对公共 API, Alamofire 提供的则是 AFDataResponse<Success>AFDownloadResponse<Success>. 实际定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
/// Default type of `DataResponse` returned by Alamofire, with an `AFError` `Failure` type.
public typealias AFDataResponse<Success> = DataResponse<Success, AFError>
/// Default type of `DownloadResponse` returned by Alamofire, with an `AFError` `Failure` type.
public typealias AFDownloadResponse<Success> = DownloadResponse<Success, AFError>

public struct DataResponse<Success, Failure: Error> {
// ...
}

public struct DownloadResponse<Success, Failure: Error> {
// ...
}

DataResponse 处理, 都是在其上调用 response 组方法, 比如 responseJSON:

1
2
3
4
AF.request("https://httpbin.org/get").responseJSON { response in
// response 为 AFDataResponse 类型
debugPrint(response)
}

对于 responseJSON 内部使用 JSONResponseSerializer 对响应数据进行序列化.

Alamofire 有如下 6 种 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
// Response Handler - Unserialized Response
func response(queue: DispatchQueue = .main,
completionHandler: @escaping (AFDataResponse<Data?>) -> Void) -> Self

// Response Serializer Handler - Serialize using the passed Serializer
func response<Serializer: DataResponseSerializerProtocol>(queue: DispatchQueue = .main,
responseSerializer: Serializer,
completionHandler: @escaping (AFDataResponse<Serializer.SerializedObject>) -> Void) -> Self

// Response Data Handler - Serialized into Data
func responseData(queue: DispatchQueue = .main,
completionHandler: @escaping (AFDataResponse<Data>) -> Void) -> Self

// Response String Handler - Serialized into String
func responseString(queue: DispatchQueue = .main,
encoding: String.Encoding? = nil,
completionHandler: @escaping (AFDataResponse<String>) -> Void) -> Self

// Response JSON Handler - Serialized into Any Using JSONSerialization
func responseJSON(queue: DispatchQueue = .main,
options: JSONSerialization.ReadingOptions = .allowFragments,
completionHandler: @escaping (AFDataResponse<Any>) -> Void) -> Self

// Response Decodable Handler - Serialized into Decodable Type
func responseDecodable<T: Decodable>(of type: T.Type = T.self,
queue: DispatchQueue = .main,
decoder: DataDecoder = JSONDecoder(),
completionHandler: @escaping (AFDataResponse<T>) -> Void) -> Self
  1. response: 不会对响应数据进行任何处理, 它直接将响应的 Data 返回. 在实际开发中推荐使用其他几个方法.
  2. responseData: 使用 DataResponseSerializer 处理并返回服务端响应的 Data 形式.
  3. responseString: 使用 StringResponseSerializer 将服务端响应 Data 转换为 String. 如果没有指定 Encoding, Alamofire 会自动使用响应中的编码格式反序列化 Data 为 String, 如果没有, 则默认为 .isoLatin1.
  4. responseJSON: 使用 JSONResponseSerializer 将 Data 序列化为 Any, 它内部使用的是 Foundation 中的 JSONSerialization API 进行 JSON 反序列化. 这个方法的返回值实际上可能是数组([[String: Any]])或字典([String: Any]). 且内部序列化失败的情况下会抛出 jsonSerializationFailed 错误.
  5. responseDecodable<T: Decodable>: 使用 DecodableResponseSerializer 将响应数据序列化为传入的类型, 可以将响应数据直接序列化为指定类型的模型. 在内部使用的是 JSONDecoder 来进行 JSON 反序列化.

默认情况下, response 处理块的代理是在 .main 线程执行的, 可以在调用时传入其他线程:

1
2
3
4
5
6
let utilityQueue = DispatchQueue.global(qos: .utility)

AF.request("https://httpbin.org/get").responseJSON(queue: utilityQueue) { response in
print("Executed on utility queue.")
debugPrint(response)
}

对数据的反序列化操作始终都是在后台线程进行的.

缓存响应

响应的缓存是通过 URLCache 在系统框架级别上进行的. 默认情况下, Alamofire 使用 URLCache.shared 对象进行缓存. 可以在 Session 创建时对缓存行为进行配置.

cURL 命令输出

借由这个功能可以非常有效地进行 Debug:

1
2
3
4
5
6
7
AF.request("https://httpbin.org/get")
.cURLDescription { description in
print(description)
}
.responseJSON { response in
debugPrint(response.metrics)
}
1
2
3
4
5
6
$ curl -v \
-X GET \
-H "Accept-Language: en;q=1.0" \
-H "Accept-Encoding: br;q=1.0, gzip;q=0.9, deflate;q=0.8" \
-H "User-Agent: Demo/1.0 (com.demo.Demo; build:1; iOS 13.0.0) Alamofire/1.0" \
"https://httpbin.org/get"

高级功能用法

高级使用包括基于 URLSession 的封装介绍, Routing, 响应序列化, 连接检测等内容的介绍.

在进入高级功能之前, 先要弄懂 iOS 平台上的 URL Loading System.

高级使用里面首先介绍了 Session 和 Session 配置的东西, 然后后续的在日常开发中看了会比较有帮助.

Routing

在 Alamofire 中, 进行请求的入口就是那 2 个 request 方法, 而接收参数的时候传入的要么是 URLConvertible 和若干请求参数, 要么是 URLRequestConvertible 包装的所有参数, 所以在网络栈实现时, 也可以有两条路可选:

  1. URLConvertible 路线:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    extension User: URLConvertible {
    static let baseURLString = "https://example.com"

    func asURL() throws -> URL {
    let urlString = User.baseURLString + "/users/\(username)/"
    return try urlString.asURL()
    }
    }

    // ...

    let user = User(username: "mattt")
    Alamofire.request(user) // https://example.com/users/mattt
  2. URLRequestConvertible 路线:

    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
    enum Router: URLRequestConvertible {
    case search(query: String, page: Int)

    static let baseURLString = "https://example.com"
    static let perPage = 50

    // MARK: URLRequestConvertible

    func asURLRequest() throws -> URLRequest {
    let result: (path: String, parameters: Parameters) = {
    switch self {
    case let .search(query, page) where page > 0:
    return ("/search", ["q": query, "offset": Router.perPage * page])
    case let .search(query, _):
    return ("/search", ["q": query])
    }
    }()

    let url = try Router.baseURLString.asURL()
    let urlRequest = URLRequest(url: url.appendingPathComponent(result.path))

    return try URLEncoding.default.encode(urlRequest, with: result.parameters)
    }
    }

    // ...

    Alamofire.request(Router.search(query: "foo bar", page: 1)) // https://example.com/search?q=foo%20bar&offset=50

针对后面一种, 更完整的例子如下:

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import Alamofire

enum Router: URLRequestConvertible {
case createUser(parameters: Parameters)
case readUser(username: String)
case updateUser(username: String, parameters: Parameters)
case destroyUser(username: String)

static let baseURLString = "https://example.com"

var method: HTTPMethod {
switch self {
case .createUser:
return .post
case .readUser:
return .get
case .updateUser:
return .put
case .destroyUser:
return .delete
}
}

var path: String {
switch self {
case .createUser:
return "/users"
case .readUser(let username):
return "/users/\(username)"
case .updateUser(let username, _):
return "/users/\(username)"
case .destroyUser(let username):
return "/users/\(username)"
}
}

// MARK: URLRequestConvertible

func asURLRequest() throws -> URLRequest {
let url = try Router.baseURLString.asURL()

var urlRequest = URLRequest(url: url.appendingPathComponent(path))
urlRequest.httpMethod = method.rawValue

switch self {
case .createUser(let parameters):
urlRequest = try URLEncoding.default.encode(urlRequest, with: parameters)
case .updateUser(_, let parameters):
urlRequest = try URLEncoding.default.encode(urlRequest, with: parameters)
default:
break
}

return urlRequest
}
}

// ...

Alamofire.request(Router.readUser("mattt")) // GET https://example.com/users/mattt

请求加工和重试

RequestAdapter 解决了一个老大难问题: 如何实现当更新 accessToken 后的认证头更新问题.

可以使用 RequestAdapter 来处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class AccessTokenAdapter: RequestAdapter {
private let accessToken: String

init(accessToken: String) {
self.accessToken = accessToken
}

func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
var urlRequest = urlRequest

if let urlString = urlRequest.url?.absoluteString, urlString.hasPrefix("https://httpbin.org") {
urlRequest.setValue("Bearer " + accessToken, forHTTPHeaderField: "Authorization")
}

return urlRequest
}
}

// ...

let sessionManager = SessionManager()
sessionManager.adapter = AccessTokenAdapter(accessToken: "1234")

sessionManager.request("https://httpbin.org/get")

另外有 RequestRetrier 可以实现请求错误时重试, 详见文档.

自定义的响应转换

可以在 response 类型上使用 map 和 flatmap, 详见文档, 特别是这样可以帮助实现自定义的响应处理块.

还有很多有用的内容详见文档.

网络可达性观察

1
2
3
4
5
6
7
8
// 需要持有这个对象
let manager = NetworkReachabilityManager(host: "www.apple.com")

manager?.listener = { status in
print("Network Status Changed: \(status)")
}

manager?.startListening()

源码阅读

基于上述知识准备, 我们就可以开始源码的阅读了. Alamofire 的源码不多, 阅读和实验相对比较容易.

Alamofire 文件夹组织如下所示:

  • Alamofire.swift: 提供 AF 名空间的定义, 在其中实现 Data/Download/Upload 组的请求方法. 均使用 Session.default 进行请求.
  • Core 文件夹: 包含 Session/Request/Response, 以及请求参数序列化等功能实现.
  • Extensions 文件夹: 包含任务调度相关的扩展, URLRequest 的相关扩展, Swift Result 扩展, 以及 URLSessionConfiguration 的 default 实现.
  • Features 文件夹: 包含响应序列化实现, 便捷响应序列化处理等.

从文档中可以知道, Alamofire 的公共 API 入口主要就是 AF 名空间下的便捷方法, 或者是使用自己创建的 Session 上的 request, response, validate 方法. 以及请求的 adaptor 和 retrier. 下面从这几个角度去看源码, 骨架完善后再补充细枝末节.

AF 名空间

在 Alamofire 中, 请求的类型被限定为三大类: Data/Download/Upload. 在 AF 名空间下, 使用 Session.default 进行上述三类请求.

Session.default 的配置如下:

1
2
3
4
open class Session {
public static let `default` = Session()
// ...
}

创建 default 时使用默认配置, 和文档描述一致, 不再赘述.

调用两个主要的 request 方法都会返回 DataRequest 对象.

这样就引出了疑问1: DataRequest 对象的创建过程?

请求的时候, 最终会在 Sessionrequest 中创建 DataRequest. 创建时会传入root队列和序列化队列, 两个队列有关联关系, 所以需要明白 iOS 中的 DispatchQueue 为什么会有 target 参数?

1
2
3
/*
A custom dispatch queue doesn’t execute any work, it just passes work to the target queue. By default, target queue of custom dispatch queue is a default-priority global queue. Since Swift 3, once a dispatch queue is activated, it cannot be mutated anymore. The target queue of a custom queue can be set by the setTarget(queue:) function. Setting a target on an activated queue will compile but then throw an error in runtime. Fortunately, DispatchQueue initializer accepts other arguments. If for whatever reason, you still need to set the target on an already created queue, you can do that by using the initiallyInactive attribute available since iOS 10.
*/

Alamofire 5.x 源码阅读分析(Source Code reading and analysis)
https://blog.rayy.top/2019/12/09/2019-2019-12-09-alamofire/
作者
貘鸣
发布于
2019年12月9日
许可协议