Alamofire 源码解析

项目地址:Alamofire,分析的版本:fc95610

1. 功能介绍

Alamofire 是基于 Swift 语言开发的网络操作库,是目前 Swift 里最常用的网络操作库,

2. 总体设计

2.1 总体设计图

总体设计请参考 4.1 类关系图

2.2 Alamofire中的概念

本文默认读者已经了解 SwiftURLSession 等相关知识,不会具体做说明。

Alamofire:其实并没有 Alamofire 这么一个类,主要是起到一个链式调用的作用,这里起到了命名空间的作用。

Request:负责发送请求和接受响应内容,其实真正做事的是 URLSessionTaskDataRequestDownloadRequestUploadRequestStreamRequest 都是它的子类,表示某种类型的请求。

ResponseResponse 是一个 protocol,主要是为 URLSessionTaskMetrics 服务,这个在后面会详细讲解。DefaultDataResponseDataResponseDefaultDownloadResponseDownloadResponse,才是负责存储响应信息的对象,分别表示非序列化和可序列化。

ResponseSerialization:提供了对响应数据的序列化方式,一共有 DataStringJSONPropertyList 四种方式。

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. 流程图

Alamofire 源码解析_第1张图片

4. 详细设计

4.1 类关系图

Alamofire 源码解析_第2张图片

4.2 项目结构

Alamofire 源码解析_第3张图片

4.3 详细介绍

4.3.1 Alamofire.swift
Alamofire 源码解析_第4张图片

看上图得知其实就是分成如下四个类型。

  • 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 来为 StringURLURLComponents 实现 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 则会根据 httpMethoddestination 来判断是直接 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() }
    }

还有就是 resumesuspendcancel这三个方法,分别与 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 能够更详细的进行网络请求的数据统计,比如 requestresponse 的各个时间点,还有 taskIntervalredirectCount 这两个属性来记录完成请求的时间间隔和重定向次数。

下面附上 WWDC 的图,能够更加清晰的理解 URLSessionTaskMetrics 所统计的内容。也推荐各位可以去看下 NSURLSession: New Features and Best Practices 。

Alamofire 源码解析_第5张图片
WWDC

我写了个简单的测试代码,可以让大家更加容易理解。

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

Alamofire 源码解析_第6张图片

看图就能够明白, 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)来管理RequestSessionTask 的对应关系,其实就是用 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
   }
}

如何使用的话,这里就看下 SessionManagerrequest 调用。

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 实例里面。
  • 然后把这个 requestdelegate 持有。
  • 因为默认是立即开始请求的,所以会调用 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) }
            }
        }
    }
}

UploadRequestuploadProgress 方法实际上就是让 uploadDelegate 持有对应的 uploadProgressHandler,然后这个 uploadProgressHandler 会在回调方法里面用到。

还值得一提的是 queue: OperationQueue 这个属性,那么 TaskDelegate 这个 queue 会在什么地方用呢?答案就在 ResponseSerialization.swift,在这里面会通过 extensionDataRequest 拓展一些 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 方法是在哪定义的呢,我们先回过头看一下这个文件的结构划分。

Alamofire 源码解析_第7张图片

上图的结构清晰明了,也是分成了4个类型,每个类型下都为 RequestDataRequestDownloadRequest 进行了拓展方法。

除了那4个类型,还有一个叫 Default 的,其实最后的方法调用还是走到了 Default 定义里的 response 方法。

下面我用 JSON 来的代码来标注下大致的调用流程。

Alamofire 源码解析_第8张图片

其实基本的流程都是先拿到对应类型的 ResponseSerializer,比如是 DataRequest 的话,就生成 DataResponseSerializer ,如果是 DownloadRequest 的话就生成 DownloadResponseSerializer。其他几个也是类似的,这里就不继续分析了。

4.3.15 ServerTrustPolicy.swift

负责网络请求的安全策略,可以使用自己的证书来做到避免中间人攻击。默认是使用 appleSecurity framework

4.3.16 Timeline.swift

Timeline 这个类就是负责去计算一个 Request 从开始到完成的各个时间点。下面再祭出WWDC上的这张图。

Alamofire 源码解析_第9张图片
WWDC

4.3.17 Validation.swift

提供了对 statusCodecontentType 验证的方式

5. 杂谈

其实 Alamofire 在缓存方面省去了很多事情,因为可以直接使用 URLCache,所以本文就没涉及到缓存方面的介绍。

还有就是 UML 和流程图方面我是非常弱的,只能表达个大概意思,如有错误,望朋友们指出我能够加以修正,我后续也会不断修改和丰富这篇文章,比如 ServerTrustPolicyMultipartFormData 这两个知识点我算是理解的不是很透彻,以后理解更深刻了再继续完善。

还有就是一直也有做 Android 方面,所以我的这篇文章的目录结构是完全参考 codekk 的源码解析系列文章的结构,也推荐大家可以看看这上面优秀的文章:)

参考文档

  1. Volley 源码解析
  2. NSURLSession: New Features and Best Practices
  3. iOS 源代码分析 --- Alamofire
  4. http://www.cnblogs.com/ludashi/p/5588044.html

你可能感兴趣的:(Alamofire 源码解析)