Alamofire Response源碼解讀

前言

Alamofire設計了2種與Request相對應的Response類型,他們分別是:json

  • DefaultDataResponse / DataResponse -- >DataRequest,UploadRequest
  • DefaultDownloadResponse / DataResponse --> DownloadRequest
  • 若是使用了沒有序列化的response方法,返回的就是帶有Default開頭的響應者,好比DefaultDataResponseDefaultDownloadResponse
  • 若是使用了序列化的response方法,返回的就是DataResponse或者DataResponse
  • DefaultDataResponse / DataResponse來舉例,DataResponse基本上只比DefaultDataResponse多了一個系列化後的數據屬性。

Response流程分析

先看一段簡單代碼swift

SessionManager.default
    .request(urlString)
    .response { (response) in
        print(response)
    }
    .responseJSON { (jsonResponse) in
        print(jsonResponse)
}
複製代碼
  • 由於Alamofire是採用的鏈式調用設計,因此能夠在調用response後還能繼續調用responseJSON。能實現鏈式訪問的原理就是每一個函數的返回值都是Self
  • 在上面的代碼中,先調用了request,再調用了response。那麼Alamofire是怎麼保證response的的執行時機是在request發起請求並在數據回調以後再執行的呢?這裏必定有貓膩,要否則只要調用了response方法就會裏面執行裏面的代碼。進入到response方法裏看看:
public func response(queue: DispatchQueue? = nil, completionHandler: @escaping (DefaultDataResponse) -> Void) -> Self {
    delegate.queue.addOperation {
        (queue ?? DispatchQueue.main).async {
            var dataResponse = DefaultDataResponse(
                request: self.request,
                response: self.response,
                data: self.delegate.data,
                error: self.delegate.error,
                timeline: self.timeline
            )

            dataResponse.add(self.delegate.metrics)

            completionHandler(dataResponse)
        }
    }

    return self
}
複製代碼
  • response加到了一個隊列queue
self.queue = {
    let operationQueue = OperationQueue()

    operationQueue.maxConcurrentOperationCount = 1
    operationQueue.isSuspended = true
    operationQueue.qualityOfService = .utility

    return operationQueue
}()
複製代碼
  • operationQueue.maxConcurrentOperationCount = 1說明是一個串行隊列
  • operationQueue.isSuspended = true隊列默認是掛起狀態
  • 相信看到這裏就可以猜到具體是怎麼實現的了。
    1. 調用response方法時把任務加到隊列queue
    2. queue是一個串行隊列,而且默認是掛起狀態,因此先不執行任務
    3. 在請求完成的代理回調方法中把queue.isSuspended = false,開始執行隊列中的任務。
  • 找到請求完成的代理回調發現確實如上所說。
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
    if let taskDidCompleteWithError = taskDidCompleteWithError {
        taskDidCompleteWithError(session, task, error)
    } else {
        // 省略無關代碼......
        queue.isSuspended = false
    }
}
複製代碼

DefaultDataResponse

把目光移到response方法,建立了一個DefaultDataResponse對象並做爲completionHandler的參數回調出去。api

public struct DefaultDataResponse {
    /// 表示該響應來源於那個請求
    public let request: URLRequest?

    /// 服務器返回的響應
    public let response: HTTPURLResponse?

    /// 響應數據
    public let data: Data?

    /// 在請求中可能發生的錯誤
    public let error: Error?

    /// 請求的時間線封裝
    public let timeline: Timeline
    
    /// 包含了請求和響應的統計信息
    var _metrics: AnyObject?
}
複製代碼
  • DefaultDataResponse是一個結構體類型,用來保存數據。通常來講,在Swift中,若是隻是爲了保存數據,那麼應該把這個類設計成structstruct是值傳遞,所以對數據的操做更安全。
  • 把全部關於Response的數據所有保存在DefaultDataResponse結構體中,化零爲整,很好的一個面向對象的設計原則,把這個完整的數據返回給用戶,用戶想用什麼就取什麼。
  • 咱們來重點看下保存在DefaultDataResponse中的data是如何來的。繼續來到response方法中:
var dataResponse = DefaultDataResponse(
    request: self.request,
    response: self.response,
    data: self.delegate.data,
    error: self.delegate.error,
    timeline: self.timeline
)
複製代碼
  • 點進去找到DataTaskDelegate裏的data,發現其實返回的是mutableData,那麼mutableData又是什麼呢?
override var data: Data? {
    if dataStream != nil {
        return nil
    } else {
        return mutableData
    }
}
複製代碼
  • 在當前文件中搜索mutableData發現:
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
    if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() }

    if let dataTaskDidReceiveData = dataTaskDidReceiveData {
        dataTaskDidReceiveData(session, dataTask, data)
    } else {
        if let dataStream = dataStream {
            dataStream(data)
        } else {
            mutableData.append(data)
        }

        // 省略無關代碼......
    }
}
複製代碼
  • 在接收數據的代理方法中,把接收到的數據拼接在mutableData中。

DataResponse

DataResponse<Value>比上邊的DefaultDataResponse多了一個public let result: Result<Value>屬性,該屬性存儲了序列化後的數據。接着看看在Alamofire中是如何使用Result的,來到responseJSON方法中:安全

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
    )
}
複製代碼
  • 上邊的這個函數的主要目的是把請求成功後的結果序列化爲JSON再返回,completionHandler函數的參數類型爲DataResponse<Any>,其中的Any就會傳遞給Result,也就是Result<Any>

DataResponseSerializer

通常來講,咱們須要對response.data作序列化處理以後才方便使用。Alamofire已經提供了一些經常使用的序列化器,能夠直接調用api使用。一樣也能夠自定義序列化器來實現本身功能。下面來看看responseJSON方法時怎麼實現的。服務器

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
    )
}
複製代碼
  • 其實內部也是調用的response方法,只是傳了一個json序列化器做爲參數
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)
    }
}
複製代碼
  • 初始化了一個DataResponseSerializer結構體,而且保存了一個尾隨閉包serializeResponse
  • 經過前面對Response流程分析能夠知道,當請求完成回調以後代碼會執行到response方法中加入到隊列的任務。也就會調用上面的尾隨閉包。
  • 點擊進入到Request.serializeResponseJSON方法
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)))
    }
}
複製代碼
  • 返回的是一個Result<Any>類型的枚舉,若是有錯誤信息,就返回return .failure(error!)並把錯誤信息作完枚舉的關聯值返回出去。
  • 若是成功就把結果序列化以後返回出去return .success(json)
  • 因此能夠在處理請求結果時,能夠直接經過dataResponse.result來判斷請求結果成功仍是失敗。

總結

  • response方法的響應結果回調completionHandler(dataResponse)加入到一個串聯隊列,而且這個隊列是默認掛起的,當請求完成後隊列開始執行,這樣才能保證是在請求完成以後再回調結果。
  • 能夠自定義系列化器,只須要自定義的序列化器實現DataResponseSerializerProtocol這個協議就能夠。
  • DataResponse幫助咱們統一管理請求過程當中的數據,請求成功、失敗、時間軸等等,便於業務層處理。

有問題或者建議和意見,歡迎你們評論或者私信。 喜歡的朋友能夠點下關注和喜歡,後續會持續更新文章。session

相關文章
相關標籤/搜索