项目地址:Alamofire,分析的版本:fc95610
1. 功能介绍
Alamofire 是基于 Swift 语言开发的网络操作库,是目前 Swift 里最常用的网络操作库,
2. 总体设计
2.1 总体设计图
总体设计请参考 4.1 类关系图。
2.2 Alamofire中的概念
本文默认读者已经了解 Swift
和 URLSession
等相关知识,不会具体做说明。
Alamofire:其实并没有 Alamofire
这么一个类,主要是起到一个链式调用的作用,这里起到了命名空间的作用。
Request:负责发送请求和接受响应内容,其实真正做事的是 URLSessionTask
。DataRequest
、DownloadRequest
、UploadRequest
、StreamRequest
都是它的子类,表示某种类型的请求。
Response:Response
是一个 protocol
,主要是为 URLSessionTaskMetrics
服务,这个在后面会详细讲解。DefaultDataResponse
、DataResponse
、DefaultDownloadResponse
和 DownloadResponse
,才是负责存储响应信息的对象,分别表示非序列化和可序列化。
ResponseSerialization:提供了对响应数据的序列化方式,一共有 Data
、String
、JSON
、PropertyList
四种方式。
SessionDelegate:这个类是处理对应的 session
的回调,但是实际的处理是在 TaskDelegate
中进行,这个下文会具体描述。
SessionManager:提供创建各个类型 Request
的方法,默认提供了一个单例。
2.3 阅读Tips
Alamofire 充分利用了 Swift的各种特性,比如 SessionManager
的单例使用了保留关键字 default,使用 ` 来包裹这个常量名就能实现使用保留关键字的效果。
open static let `default`: SessionManager
Alamofire 也不停的使用 extension
,比如 Request
有个 responseJSON
方法能够得到将响应数据序列化为JSON格式, responseJSON
方法的实现是在 ResponseSerialization.swift
文件中,而不是 Request.swift
,因为这个功能是属于 ResponseSerialization
的。
还有就是使用 extension
来为类拓展实现某个协议,如下代码所示。
// Response.swift
@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
extension DefaultDataResponse: Response {
#if !os(watchOS)
/// The task metrics containing the request / response statistics.
public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics }
#endif
}
@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
extension DataResponse: Response {
#if !os(watchOS)
/// The task metrics containing the request / response statistics.
public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics }
#endif
}
3. 流程图
4. 详细设计
4.1 类关系图
4.2 项目结构
4.3 详细介绍
4.3.1 Alamofire.swift
看上图得知其实就是分成如下四个类型。
- data request
- download
- upload
- stream
其实这些方法是对 SessionManager
调用的封装,下面看一个例子。
@discardableResult
public func request(
_ urlString: URLStringConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoding: ParameterEncoding = URLEncoding.default,
headers: HTTPHeaders? = nil)
-> DataRequest
{
return SessionManager.default.request(
urlString,
method: method,
parameters: parameters,
encoding: encoding,
headers: headers
)
}
如上所示,实际调用是通过调用 SessionManager
的单例的 request
方法。
这里的 @discardableResult
是用于取消警告的,如果没有使用方法的返回值,是会出现 is unused
这样的警告的,加了这个关键字之后就不会出现了。
还有使用 extension
来为 String
、URL
和 URLComponents
实现 URLStringConvertible
协议,这样在声明参数的时候就能直接使用 URLStringConvertible
了,URLRequestConvertible
也是同理。
extension String: URLStringConvertible {
/// The URL string.
public var urlString: String { return self }
}
extension URL: URLStringConvertible {
/// The URL string.
public var urlString: String { return absoluteString }
}
extension URLComponents: URLStringConvertible {
/// The URL string.
public var urlString: String { return url!.urlString }
}
4.3.2 AFError.swift
对错误类型进行封装的 struct
。
4.3.3 Notification.swift
extension Notification.Name {
/// Used as a namespace for all `URLSessionTask` related notifications.
public struct Task {
/// Posted when a `URLSessionTask` is resumed. The notification `object` contains the resumed `URLSessionTask`.
public static let DidResume = Notification.Name(rawValue: "org.alamofire.notification.name.task.didResume")
/// Posted when a `URLSessionTask` is suspended. The notification `object` contains the suspended `URLSessionTask`.
public static let DidSuspend = Notification.Name(rawValue: "org.alamofire.notification.name.task.didSuspend")
/// Posted when a `URLSessionTask` is cancelled. The notification `object` contains the cancelled `URLSessionTask`.
public static let DidCancel = Notification.Name(rawValue: "org.alamofire.notification.name.task.didCancel")
/// Posted when a `URLSessionTask` is completed. The notification `object` contains the completed `URLSessionTask`.
public static let DidComplete = Notification.Name(rawValue: "org.alamofire.notification.name.task.didComplete")
}
}
// MARK: -
extension Notification {
/// Used as a namespace for all `Notification` user info dictionary keys.
public struct Key {
/// User info dictionary key representing the `URLSessionTask` associated with the notification.
public static let Task = "org.alamofire.notification.key.task"
}
}
内容比较简单,定义4个分别对应 URLSessionTask
状态的 Notification.Name
,用于 NotificationCenter
的通知发送。还有就是一个用作 key
的定义。使用的场景就如下代码所示。
NotificationCenter.default.post(
name: Notification.Name.Task.DidResume,
object: self,
userInfo: [Notification.Key.Task: task]
)
4.3.4 ParameterEncoding.swift
这个文件跟它的名字一样是跟参数编码有关的,一共有3个编码的实现。
- URLEncoding
- JSONEncoding
- PropertyListEncoding
这三个编码都是对 ParameterEncoding
协议的实现。
public protocol ParameterEncoding {
func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest
}
以上三个的实现,后面两个都是直接对 httpbody
的写入,而 URLEncoding
则会根据 httpMethod
和 destination
来判断是直接 httpBody
写入,还是拼接参数到 url
。通过下面的代码能够清晰的看到,调用 encode
方法之后拿到处理过的 Request
。
// SessionManager.swift 的调用
@discardableResult
open func request(
_ urlString: URLStringConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoding: ParameterEncoding = URLEncoding.default,
headers: HTTPHeaders? = nil)
-> DataRequest
{
let urlRequest = URLRequest(urlString: urlString, method: method, headers: headers)
do {
let encodedURLRequest = try encoding.encode(urlRequest, with: parameters)
return request(resource: encodedURLRequest)
} catch {
let request = self.request(resource: urlRequest)
request.delegate.error = error
return request
}
}
// URLEncoding 的方法
public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
var urlRequest = urlRequest.urlRequest
guard let parameters = parameters else { return urlRequest }
if let method = HTTPMethod(rawValue: urlRequest.httpMethod ?? "GET"), encodesParametersInURL(with: method) {
guard let url = urlRequest.url else {
throw AFError.parameterEncodingFailed(reason: .missingURL)
}
if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false), !parameters.isEmpty {
let percentEncodedQuery = (urlComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters)
urlComponents.percentEncodedQuery = percentEncodedQuery
urlRequest.url = urlComponents.url
}
} else {
if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
urlRequest.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
}
urlRequest.httpBody = query(parameters).data(using: .utf8, allowLossyConversion: false)
}
return urlRequest
}
4.3.5 Request.swift
Request的类型也是有4种。
- DataRequest
- DownloadRequest
- UploadRequest
- StreamRequest
Request
实例化的时候会根据不同的 URLSessionTask
来生成不同的 TaskDelegate
,而这个 TaskDelegate
才是真正处理回调的,在后面会介绍这方面的细节。
init(session: URLSession, task: URLSessionTask, originalTask: TaskConvertible?) {
self.session = session
self.originalTask = originalTask
switch task {
case is URLSessionUploadTask:
taskDelegate = UploadTaskDelegate(task: task)
case is URLSessionDataTask:
taskDelegate = DataTaskDelegate(task: task)
case is URLSessionDownloadTask:
taskDelegate = DownloadTaskDelegate(task: task)
default:
taskDelegate = TaskDelegate(task: task)
}
delegate.queue.addOperation { self.endTime = CFAbsoluteTimeGetCurrent() }
}
还有就是 resume
、suspend
和cancel
这三个方法,分别与 URLSessionTask
的方法对应,在调用之后还会发出通知,也就是上文提到的。
4.3.6 Response.swift
这个文件主要就是放了4个类型的 Response。
- DefaultDataResponse
- DataResponse
- DefaultDownloadResponse
- DownloadResponse
上面这四个 Response 是各自独立的,没有继承关系,各自都是 struct
类型。
Response
则是一个协议的存在,并且为 Response
拓展实现了一个 add
方法,其实功能就是 set 的作用,不过我不理解的是这个方法名为啥叫 add 。
protocol Response {
/// The task metrics containing the request / response statistics.
var _metrics: AnyObject? { get set }
mutating func add(_ metrics: AnyObject?)
}
extension Response {
mutating func add(_ metrics: AnyObject?) {
#if !os(watchOS)
guard #available(iOS 10.0, macOS 10.12, tvOS 10.0, *) else { return }
guard let metrics = metrics as? URLSessionTaskMetrics else { return }
_metrics = metrics
#endif
}
}
然后就是为这4个类型都拓展实现了 Response
,让它们都有统一的行为拿到 URLSessionTaskMetrics
。
这里需要提一下的就是使用到了 URLSessionTaskMetrics
这个 WWDC 提到的新特性,简单的说就是 URLSessionTaskMetrics
能够更详细的进行网络请求的数据统计,比如 request
和 response
的各个时间点,还有 taskInterval
和 redirectCount
这两个属性来记录完成请求的时间间隔和重定向次数。
下面附上 WWDC 的图,能够更加清晰的理解 URLSessionTaskMetrics
所统计的内容。也推荐各位可以去看下 NSURLSession: New Features and Best Practices 。
我写了个简单的测试代码,可以让大家更加容易理解。
class ViewController: UIViewController, URLSessionTaskDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue())
let url = URL(string: "https://api.github.com/users/devinshine/repos")!
let task = session.dataTask(with: url) { (data: Data?, response: URLResponse?, error: Error?) in
}
task.resume()
}
func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
print("redirectCount =", metrics.redirectCount)
print("taskInterval =", metrics.taskInterval)
print("transactionMetrics =", metrics.transactionMetrics)
}
}
// 以下是控制台输出内容
redirectCount = 0
taskInterval = 2016-09-23 04:28:28 +0000 to 2016-09-23 04:28:30 +0000
transactionMetrics = [(Request) { URL: https://api.github.com/users/devinshine/repos }
(Response) { URL: https://api.github.com/users/devinshine/repos } { status code: 200, headers {
"Access-Control-Allow-Origin" = "*";
"Access-Control-Expose-Headers" = "ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval";
"Cache-Control" = "public, max-age=60, s-maxage=60";
"Content-Encoding" = gzip;
"Content-Security-Policy" = "default-src 'none'";
"Content-Type" = "application/json; charset=utf-8";
Date = "Fri, 23 Sep 2016 04:27:09 GMT";
Etag = "W/\"7fc90961687cf185d72bb971b2327d9a\"";
Server = "GitHub.com";
Status = "200 OK";
"Strict-Transport-Security" = "max-age=31536000; includeSubdomains; preload";
"Transfer-Encoding" = Identity;
Vary = "Accept, Accept-Encoding";
"X-Content-Type-Options" = nosniff;
"X-Frame-Options" = deny;
"X-GitHub-Media-Type" = "github.v3";
"X-GitHub-Request-Id" = "2D2019D0:41F2:5DCCED2:57E4AF1C";
"X-RateLimit-Limit" = 60;
"X-RateLimit-Remaining" = 55;
"X-RateLimit-Reset" = 1474607884;
"X-Served-By" = 626ed3a9050b8faa02ef5f3c540b508d;
"X-XSS-Protection" = "1; mode=block";
} }
(Fetch Start) 2016-09-23 04:28:28 +0000
(Domain Lookup Start) (null)
(Domain Lookup End) (null)
(Connect Start) (null)
(Secure Connection Start) (null)
(Secure Connection End) (null)
(Connect End) (null)
(Request Start) 2016-09-23 04:28:28 +0000
(Request End) 2016-09-23 04:28:28 +0000
(Response Start) 2016-09-23 04:28:28 +0000
(Response End) 2016-09-23 04:28:28 +0000
(Protocol Name) (null)
(Proxy Connection) NO
(Reused Connection) YES
(Fetch Type) Local Cache
, (Request) { URL: https://api.github.com/users/devinshine/repos }
(Response) { URL: https://api.github.com/users/devinshine/repos } { status code: 304, headers {
"Access-Control-Allow-Origin" = "*";
"Access-Control-Expose-Headers" = "ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval";
"Cache-Control" = "public, max-age=60, s-maxage=60";
"Content-Security-Policy" = "default-src 'none'";
Date = "Fri, 23 Sep 2016 04:28:29 GMT";
Etag = "\"7fc90961687cf185d72bb971b2327d9a\"";
Server = "GitHub.com";
Status = "304 Not Modified";
"Strict-Transport-Security" = "max-age=31536000; includeSubdomains; preload";
Vary = "Accept, Accept-Encoding";
"X-Content-Type-Options" = nosniff;
"X-Frame-Options" = deny;
"X-GitHub-Request-Id" = "2D2019D0:41F5:6FED18E:57E4AF6C";
"X-RateLimit-Limit" = 60;
"X-RateLimit-Remaining" = 55;
"X-RateLimit-Reset" = 1474607884;
"X-Served-By" = 8a5c38021a5cd7cef7b8f49a296fee40;
"X-XSS-Protection" = "1; mode=block";
} }
(Fetch Start) 2016-09-23 04:28:28 +0000
(Domain Lookup Start) 2016-09-23 04:28:28 +0000
(Domain Lookup End) 2016-09-23 04:28:28 +0000
(Connect Start) 2016-09-23 04:28:28 +0000
(Secure Connection Start) 2016-09-23 04:28:28 +0000
(Secure Connection End) 2016-09-23 04:28:29 +0000
(Connect End) 2016-09-23 04:28:29 +0000
(Request Start) 2016-09-23 04:28:29 +0000
(Request End) 2016-09-23 04:28:29 +0000
(Response Start) 2016-09-23 04:28:30 +0000
(Response End) 2016-09-23 04:28:30 +0000
(Protocol Name) http/1.1
(Proxy Connection) NO
(Reused Connection) NO
(Fetch Type) Network Load
]
4.3.7 Result.swift
Result
是一个 struct
,主要代码十分少,其作用就是返回数据的序列化的载体,使用的例子就如下代码。
// ResponseSerialization.swift
extension Request {
public static func serializeResponseJSON(
options: JSONSerialization.ReadingOptions,
response: HTTPURLResponse?,
data: Data?,
error: Error?)
-> Result
{
guard error == nil else { return .failure(error!) }
if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success(NSNull()) }
guard let validData = data, validData.count > 0 else {
return .failure(AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength))
}
do {
let json = try JSONSerialization.jsonObject(with: validData, options: options)
return .success(json)
} catch {
return .failure(AFError.responseSerializationFailed(reason: .jsonSerializationFailed(error: error)))
}
}
}
4.3.8 SessionDelegate.swift
看图就能够明白,
SessionDelegate
这个类是处理对应的
session
的回调,但是其实实际的处理是在
TaskDelegate
中处理,接着来看一段对于回调处理的代码。
open func urlSession(
_ session: URLSession,
task: URLSessionTask,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
{
guard taskDidReceiveChallengeWithCompletion == nil else {
taskDidReceiveChallengeWithCompletion?(session, task, challenge, completionHandler)
return
}
if let taskDidReceiveChallenge = taskDidReceiveChallenge {
let result = taskDidReceiveChallenge(session, task, challenge)
completionHandler(result.0, result.1)
} else if let delegate = self[task]?.delegate {
delegate.urlSession(
session,
task: task,
didReceive: challenge,
completionHandler: completionHandler
)
} else {
urlSession(session, didReceive: challenge, completionHandler: completionHandler)
}
}
如果需要重写这个方法,那么去赋值 taskDidReceiveChallengeWithCompletion
即可,self[task]?.delegate
就是之前一直提的 TaskDelegate
类型,回调的处理要交给它。
值得一提的是,这里使用了下标脚本(subscript)来管理Request
和 SessionTask
的对应关系,其实就是用 taskIdentifier
作为key,来对应 Request
。
private var requests: [Int: Request] = [:]
open subscript(task: URLSessionTask) -> Request? {
get {
lock.lock() ; defer { lock.unlock() }
return requests[task.taskIdentifier]
}
set {
lock.lock() ; defer { lock.unlock() }
requests[task.taskIdentifier] = newValue
}
}
如何使用的话,这里就看下 SessionManager
的 request
调用。
open func request(resource urlRequest: URLRequestConvertible) -> DataRequest {
let originalRequest = urlRequest.urlRequest
let originalTask = DataRequest.Requestable(urlRequest: originalRequest)
let task = originalTask.task(session: session, adapter: adapter, queue: queue)
let request = DataRequest(session: session, task: task, originalTask: originalTask)
delegate[request.delegate.task] = request
if startRequestsImmediately { request.resume() }
return request
}
SessionManager
会持有 SessionDelegate
实例化对象,在调用请求操作的时候,SessionDelegate
会持有对应的 Request
。
4.3.9 SessionManager.swift
SessionManager
默认提供了一个叫做 default
的单例和 HTTPHeader 的默认值 defaultHTTPHeaders
。
open static let `default`: SessionManager = {
let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders
return SessionManager(configuration: configuration)
}()
open static let defaultHTTPHeaders: HTTPHeaders = {
// 篇幅太长,这里略去主要代码
return [
"Accept-Encoding": acceptEncoding,
"Accept-Language": acceptLanguage,
"User-Agent": userAgent
]
}()
还有一些比较重要的属性。
session: URLSession
核心的属性,一个 session 对应多个 task,也是 WWDC 上面反复强调的。
adapter: RequestAdapter?
一个实现 RequestAdapter 协议的对象,可以对 URLRequest 做一些需求的修改。
delegate: SessionDelegate
用来处理所有的请求回调
queue: DispatchQueue
默认实现了一个串行的队列,用于创建 URLSessionDataTask
。
然后我们先看下 init
这个方法做了哪些事情。
public init(
configuration: URLSessionConfiguration = URLSessionConfiguration.default,
delegate: SessionDelegate = SessionDelegate(),
serverTrustPolicyManager: ServerTrustPolicyManager? = nil)
{
self.delegate = delegate
self.session = URLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)
commonInit(serverTrustPolicyManager: serverTrustPolicyManager)
}
其实就是3个操作,对 delegate
的持有,对 URLSession
的实例化,对 serverTrustPolicyManager
的初始化, session
的这个初始化也就使得以后的回调都将会由 self.delegate
来处理了。
SessionManager
实现了4个 request 类型的具体方法,以及一个 retry
方法用于重新请求。
- DataRequest
- DownloadRequest
- UploadRequest
- StreamRequest
下面我们选择一个 request
方法来看。
open func request(resource urlRequest: URLRequestConvertible) -> DataRequest {
let originalRequest = urlRequest.urlRequest
let originalTask = DataRequest.Requestable(urlRequest: originalRequest)
let task = originalTask.task(session: session, adapter: adapter, queue: queue)
let request = DataRequest(session: session, task: task, originalTask: originalTask)
delegate[request.delegate.task] = request
if startRequestsImmediately { request.resume() }
return request
}
流程上也比较简单,分为下面几步。
- 其实就是拿到一个
URLRequest
, 然后调用下adapter
方法看看有没需要修改的。 - 然后拿着这个
URLRequest
生成一个URLSessionTask
。 - 接着就是把
task
塞到DataRequest
实例里面。 - 然后把这个
request
让delegate
持有。 - 因为默认是立即开始请求的,所以会调用
resume
方法。
4.3.10 TaskDelegate.swift
实际的的回调处理是放在 TaskDelegate
中进行的,这里的代码和 SessionDelegate 基本类似,下面我们挑 upload
相关的代码说一说。
//Request.swift
@discardableResult
open func uploadProgress(queue: DispatchQueue = DispatchQueue.main, closure: @escaping ProgressHandler) -> Self {
uploadDelegate.uploadProgressHandler = (closure, queue)
return self
}
//TaskDelegate.swift
class UploadTaskDelegate: DataTaskDelegate {
//略去其他代码
var uploadProgressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)?
func URLSession(
_ session: URLSession,
task: URLSessionTask,
didSendBodyData bytesSent: Int64,
totalBytesSent: Int64,
totalBytesExpectedToSend: Int64)
{
if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() }
if let taskDidSendBodyData = taskDidSendBodyData {
taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend)
} else {
uploadProgress.totalUnitCount = totalBytesExpectedToSend
uploadProgress.completedUnitCount = totalBytesSent
if let uploadProgressHandler = uploadProgressHandler {
uploadProgressHandler.queue.async { uploadProgressHandler.closure(self.uploadProgress) }
}
}
}
}
UploadRequest
的 uploadProgress
方法实际上就是让 uploadDelegate
持有对应的 uploadProgressHandler
,然后这个 uploadProgressHandler
会在回调方法里面用到。
还值得一提的是 queue: OperationQueue
这个属性,那么 TaskDelegate
这个 queue
会在什么地方用呢?答案就在 ResponseSerialization.swift
,在这里面会通过 extension
为 DataRequest
拓展一些 response 相关的方法。BTW,我们知道 Request
是会持有一个 TaskDelegate
,接下来就看下代码。
//TaskDelegate.swift
init(task: URLSessionTask) {
self.task = task
self.queue = {
let operationQueue = OperationQueue()
operationQueue.maxConcurrentOperationCount = 1
operationQueue.isSuspended = true
operationQueue.qualityOfService = .utility
return operationQueue
}()
}
//ResponseSerialization.swift
@discardableResult
public func response(
queue: DispatchQueue? = nil,
responseSerializer: T,
completionHandler: @escaping (DataResponse) -> Void)
-> Self
{
delegate.queue.addOperation {
let result = responseSerializer.serializeResponse(
self.request,
self.response,
self.delegate.data,
self.delegate.error
)
let requestCompletedTime = self.endTime ?? CFAbsoluteTimeGetCurrent()
let initialResponseTime = self.delegate.initialResponseTime ?? requestCompletedTime
let timeline = Timeline(
requestStartTime: self.startTime ?? CFAbsoluteTimeGetCurrent(),
initialResponseTime: initialResponseTime,
requestCompletedTime: requestCompletedTime,
serializationCompletedTime: CFAbsoluteTimeGetCurrent()
)
var dataResponse = DataResponse(
request: self.request,
response: self.response,
data: self.delegate.data,
result: result,
timeline: timeline
)
dataResponse.add(self.delegate.metrics)
(queue ?? DispatchQueue.main).async { completionHandler(dataResponse) }
}
return self
}
//回调方法
@objc(URLSession:task:didCompleteWithError:)
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if let taskDidCompleteWithError = taskDidCompleteWithError {
taskDidCompleteWithError(session, task, error)
} else {
if let error = error {
if self.error == nil { self.error = error }
if
let downloadDelegate = self as? DownloadTaskDelegate,
let resumeData = (error as NSError).userInfo[NSURLSessionDownloadTaskResumeData] as? Data
{
downloadDelegate.resumeData = resumeData
}
}
queue.isSuspended = false
}
}
以上代码我们能够知道 queue
初始化的时候就会被悬停着,然后调用 response
相关方法的时候所添加的操作也就只能等待着,然后当请求完成之后才会把 isSuspended
设为 false
,就会恢复 queue
,继续进行序列化的操作了。
4.3.11 DispatchQueue+Alamofire.swift
extension DispatchQueue {
static var userInteractive: DispatchQueue { return DispatchQueue.global(qos: .userInteractive) }
static var userInitiated: DispatchQueue { return DispatchQueue.global(qos: .userInitiated) }
static var utility: DispatchQueue { return DispatchQueue.global(qos: .utility) }
static var background: DispatchQueue { return DispatchQueue.global(qos: .background) }
func after(_ delay: TimeInterval, execute closure: @escaping () -> Void) {
asyncAfter(deadline: .now() + delay, execute: closure)
}
func syncResult(_ closure: () -> T) -> T {
var result: T!
sync { result = closure() }
return result
}
}
这个比较简单,封装了一些项目中常用的 DispatchQueue
和两个方法。
4.3.12 MultipartFormData.swift
这个类看名字就知道是用来生成 multipart/form-data
数据,用于上传的时候使用。这个实现方式分成了两种,一种是直接内存写,这种情况是适用于数据比较小的情况,另外一种就是数据比较大的情况,从本地读取写入。
4.3.13 NetworkReachabilityManager.swift
NetworkReachabilityManager
提供了对指定域名地址的网络访问状态监听,使用方法如下。
let manager = NetworkReachabilityManager(host: "www.apple.com")
manager?.listener = { status in
print("Network Status Changed: \(status)")
}
manager?.startListening()
4.3.14 ResponseSerialization.swift
Alamofire.request("https://httpbin.org/get").responseJSON { response in
print(response.request) // original URL request
print(response.response) // HTTP URL response
print(response.data) // server data
print(response.result) // result of response serialization
if let JSON = response.result.value {
print("JSON: \(JSON)")
}
}
上面是一个官方的简单例子,以上代码我们能够知道的是,在调用 request 之后返回的是一个 Reuqest
对象,然后调用 responseJSON
方法并传入一个闭包,在闭包中能够获得到请求相应的相关数据。那么这个 responseJSON
是什么呢,我们下面来具体讲讲。
extension DataRequest {
public static func jsonResponseSerializer(
options: JSONSerialization.ReadingOptions = .allowFragments)
-> DataResponseSerializer
{
return DataResponseSerializer { _, response, data, error in
return Request.serializeResponseJSON(options: options, response: response, data: data, error: error)
}
}
@discardableResult
public func responseJSON(
queue: DispatchQueue? = nil,
options: JSONSerialization.ReadingOptions = .allowFragments,
completionHandler: @escaping (DataResponse) -> Void)
-> Self
{
return response(
queue: queue,
responseSerializer: DataRequest.jsonResponseSerializer(options: options),
completionHandler: completionHandler
)
}
}
通过阅读源码我们能够知道 responseJSON
是对 DataRequest
的 extension 方法。那么这个方法里面的 response
方法是在哪定义的呢,我们先回过头看一下这个文件的结构划分。
上图的结构清晰明了,也是分成了4个类型,每个类型下都为 Request
、DataRequest
和 DownloadRequest
进行了拓展方法。
除了那4个类型,还有一个叫 Default
的,其实最后的方法调用还是走到了 Default
定义里的 response
方法。
下面我用 JSON
来的代码来标注下大致的调用流程。
其实基本的流程都是先拿到对应类型的 ResponseSerializer
,比如是 DataRequest
的话,就生成 DataResponseSerializer
,如果是 DownloadRequest
的话就生成 DownloadResponseSerializer
。其他几个也是类似的,这里就不继续分析了。
4.3.15 ServerTrustPolicy.swift
负责网络请求的安全策略,可以使用自己的证书来做到避免中间人攻击。默认是使用 apple
的 Security framework
。
4.3.16 Timeline.swift
Timeline
这个类就是负责去计算一个 Request
从开始到完成的各个时间点。下面再祭出WWDC上的这张图。
4.3.17 Validation.swift
提供了对 statusCode
和 contentType
验证的方式
5. 杂谈
其实 Alamofire 在缓存方面省去了很多事情,因为可以直接使用 URLCache
,所以本文就没涉及到缓存方面的介绍。
还有就是 UML 和流程图方面我是非常弱的,只能表达个大概意思,如有错误,望朋友们指出我能够加以修正,我后续也会不断修改和丰富这篇文章,比如 ServerTrustPolicy
和 MultipartFormData
这两个知识点我算是理解的不是很透彻,以后理解更深刻了再继续完善。
还有就是一直也有做 Android 方面,所以我的这篇文章的目录结构是完全参考 codekk 的源码解析系列文章的结构,也推荐大家可以看看这上面优秀的文章:)
参考文档
- Volley 源码解析
- NSURLSession: New Features and Best Practices
- iOS 源代码分析 --- Alamofire
- http://www.cnblogs.com/ludashi/p/5588044.html