Alamofire 源码解析

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

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 来为 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

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) <NSURLRequest: 0x60000001b770> { URL: https://api.github.com/users/devinshine/repos }
(Response) <NSHTTPURLResponse: 0x6000000398a0> { 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) <NSURLRequest: 0x60000001bbf0> { URL: https://api.github.com/users/devinshine/repos }
(Response) <NSHTTPURLResponse: 0x6000000398e0> { 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<Any>
    {
        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)来管理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<T: DataResponseSerializerProtocol>(
   queue: DispatchQueue? = nil,
   responseSerializer: T,
   completionHandler: @escaping (DataResponse<T.SerializedObject>) -> 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<T.SerializedObject>(
           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<T>(_ 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<Any>
    {
        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<Any>) -> Void)
        -> Self
    {
        return response(
            queue: queue,
            responseSerializer: DataRequest.jsonResponseSerializer(options: options),
            completionHandler: completionHandler
        )
    }
}

通过阅读源码我们能够知道 responseJSON 是对 DataRequest 的 extension 方法。那么这个方法里面的 response 方法是在哪定义的呢,我们先回过头看一下这个文件的结构划分。

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

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

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

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

4.3.15 ServerTrustPolicy.swift

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

4.3.16 Timeline.swift

Timeline 这个类就是负责去计算一个 Request 从开始到完成的各个时间点。下面再祭出WWDC上的这张图。
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
2016-09-26 01:36126