Alamofire源碼閱讀(二)--一個GET請求的流程

本文通過一個get請求,追蹤代碼流程,來進入到整個框架的學習。

1.創建請求

Alamofire.request("https://httpbin.org/get")
  • 通過Alamofire來創建
@discardableResult
public func request(
    _ url: URLConvertible,
    method: HTTPMethod = .get,
    parameters: Parameters? = nil,
    encoding: ParameterEncoding = URLEncoding.default,
    headers: HTTPHeaders? = nil)
    -> DataRequest
{
    return SessionManager.default.request(
        url,
        method: method,
        parameters: parameters,
        encoding: encoding,
        headers: headers
    )
}
  • 關鍵字@discardableResult,在方法沒有返回值時不會報出警告
  • swift參數是支持默認值的,調用時值傳了一個url參數,繼承URLConvertible協議,用來生成URL類型
  • HTTPMethod是一個枚舉類型,這里默認值使用get
  • Parameters定義為[String: Any],默認是nil。它的定義使用typealias關鍵字,用來為已經存在的類型重新定義名字的,通過命名,可以使代碼變得更加清晰。HTTPHeaders亦是如此
  • Alamofire只是一個入口,內部調用SessionManager來完成創建
@discardableResult
    open func request(
        _ url: URLConvertible,
        method: HTTPMethod = .get,
        parameters: Parameters? = nil,
        encoding: ParameterEncoding = URLEncoding.default,
        headers: HTTPHeaders? = nil)
        -> DataRequest
    {
        var originalRequest: URLRequest?

        do {
            originalRequest = try URLRequest(url: url, method: method, headers: headers)
            let encodedURLRequest = try encoding.encode(originalRequest!, with: parameters)
            return request(encodedURLRequest)
        } catch {
            return request(originalRequest, failedWith: error)
        }
    }
  • 聲明originalRequest變量,類型是URLRequest。Swift里不會自動給變量賦初始值,也就是說變量不會有默認值,所以要求使用變量之前必須要對其初始化,如果沒有初始化就會報錯。這時候可以使用optional類型,也就是后面跟一個"?"。
  • 試著創建URLRequest,這里加try是因為asURL可能會拋出異常
  • 然后調用ParameterEncoding.swift的方法對參數進行編碼
  • 最后進入request方法來生成DataRequest
open func request(_ urlRequest: URLRequestConvertible) -> DataRequest {
        var originalRequest: URLRequest?

        do {
            originalRequest = try urlRequest.asURLRequest()
            let originalTask = DataRequest.Requestable(urlRequest: originalRequest!)

            let task = try originalTask.task(session: session, adapter: adapter, queue: queue)
            let request = DataRequest(session: session, requestTask: .data(originalTask, task))

            delegate[task] = request

            if startRequestsImmediately { request.resume() }

            return request
        } catch {
            return request(originalRequest, failedWith: error)
        }
    }
  • originalTask值是一個結構體,這個結構體繼承TaskConvertible協議。該協議定義了task方法,讓每種不同類型的request實現自己創建task的邏輯。這里創建NSURLSessionDataTask類型task的實現代碼是session.dataTask(with: urlRequest)
  • 用session和.data(originalTask, task)創建DataRequest。URLRequest(DataRequest的父類)初始化方法會根據task的不同生成對應的taskDelegate
  • 把task和request的關系存到delegate中,調用request的resume方法發出請求,內部會觸發task.resume()方法發請求,最后并返回request

整個過程大致是,調用SessionManager.request()方法,獲取URLRerequest,獲取對應的task,創建DataRequest,發起請求。

2.接收請求

if let request = request as? DataRequest {
            request.responseString { response in
                requestComplete(response.response, response.result)
            }
        }
  • 這里剛開始可能看不太懂,這是尾隨閉包的寫法。這個閉包是request.responseString方法的最后一個參數。
  • 這個閉包會被加入到DataRequest的delegate.queue中。TaskDelegate的queue是一個串行隊列,存放task結束之后需要執行的任務
@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
            )

            var dataResponse = DataResponse<T.SerializedObject>(
                request: self.request,
                response: self.response,
                data: self.delegate.data,
                result: result,
                timeline: self.timeline
            )

            dataResponse.add(self.delegate.metrics)

            (queue ?? DispatchQueue.main).async {
                completionHandler(dataResponse)
            }
        }

        return self
    }
  • 最終傳進來的閉包會加入到delegate的queue隊列中,并且這個隊列初始化時設置了isSuspend=true,所以它會在改為false才會執行
  • 這個opration里會生成result和response,供回調使用,也就是requestComplete(response.response, response.result)的參數
open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
        if let dataTaskDidReceiveData = dataTaskDidReceiveData {
            dataTaskDidReceiveData(session, dataTask, data)
        } else if let delegate = self[dataTask]?.delegate as? DataTaskDelegate {
            delegate.urlSession(session, dataTask: dataTask, didReceive: data)
        }
    }
  • SessionDelegate繼承URLSessionDataDelegate,實現請求的各種回調
  • 下面的判斷會走到else,前面在delegate存儲過task和delegate,所以這里self[dataTask]會得到之前發請求的request,并且是DataTaskDelegate類型
  • 于是執行delegate(DataTaskDelegate)的對應方法
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)
            }

            let bytesReceived = Int64(data.count)
            totalBytesReceived += bytesReceived
            let totalBytesExpected = dataTask.response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown

            progress.totalUnitCount = totalBytesExpected
            progress.completedUnitCount = totalBytesReceived

            if let progressHandler = progressHandler {
                progressHandler.queue.async { progressHandler.closure(self.progress) }
            }
        }
    }
  • dataStream為((_ data: Data) -> Void)?類型,mutableData被初始化為Data(),它們都是是DataTaskDelegate的實例變量。這里會走到mutableData.append(data)
  • bytesReceived計算接收數據的大小
  • totalBytesExpected將可選類型dataTask.response解包,然后獲取expectedContentLength.“??"運算符可以用于判斷變量或常量的數值是否是nil,不為nil則取變量或者常量本身的值,如果是nil則使用后面的值替代
  • 判斷是否有progressHandler,這不會調用。因為demo中并沒有傳
open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        let completeTask: (URLSession, URLSessionTask, Error?) -> Void = { [weak self] session, task, error in
            guard let strongSelf = self else { return }

            strongSelf.taskDidComplete?(session, task, error)

            strongSelf[task]?.delegate.urlSession(session, task: task, didCompleteWithError: error)

            NotificationCenter.default.post(
                name: Notification.Name.Task.DidComplete,
                object: strongSelf,
                userInfo: [Notification.Key.Task: task]
            )

            strongSelf[task] = nil
        }

        guard let request = self[task], let sessionManager = sessionManager else {
            completeTask(session, task, error)
            return
        }

        request.validations.forEach { $0() }

        var error: Error? = error

        if request.delegate.error != nil {
            error = request.delegate.error
        }

        if let retrier = retrier, let error = error {
            retrier.should(sessionManager, retry: request, with: error) { [weak self] shouldRetry, timeDelay in
                guard shouldRetry else { completeTask(session, task, error) ; return }

                DispatchQueue.utility.after(timeDelay) { [weak self] in
                    guard let strongSelf = self else { return }

                    let retrySucceeded = strongSelf.sessionManager?.retry(request) ?? false

                    if retrySucceeded, let task = request.task {
                        strongSelf[task] = request
                        return
                    } else {
                        completeTask(session, task, error)
                    }
                }
            }
        } else {
            completeTask(session, task, error)
        }
    }
  • SessionDelegate內實現的回調,表示task已經完成數據傳輸
  • completeTask,如果請求不需要重試,就會執行這個閉包
  • sessionManager沒有被賦值,所以不會走到第一個判斷里
  • validations是[() -> Void] = []一種閉包類型的數組,放一些檢查的方法。$0()表示第一個參數,也就是數組里的每一個值
  • 用request.delegate.error取到錯誤,如果設置了retrier并有error,就執行重試的方法。否則執行completeTask
  • 在completeTask中,首選判斷taskDidComplete有無值然后執行,然后通過strongSelf[task]得到request并調用其urlSession方法
  • 最后發通知并把task置為nil
@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
        }
    }
  • 上述代碼 strongSelf[task]?.delegate.urlSession(session, task: task, didCompleteWithError: error) 的方法實現
  • 這里在有錯誤時,對DownloadTaskDelegate做了一些處理。
  • queue.isSuspended這里是關鍵,isSuspended設置為false后,回調傳進來operation將會開始執行
  • @objc 作用:1,fileprivate 或者 private 保證方法私有 能在同一個類 或者 同一個文件(extension)中訪問這個方法 如果定義為private 那么只能在一個類中訪問 不能在類擴展中訪問;2,允許這個函數在“運行時”通過oc的消息機制調用
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
        }
    }
  • 上述代碼strongSelf[task] = nil的實現
  • 使用lock,保證賦值是線程安全的
  • open關鍵字,訪問控制在權限。在swift3中,fileprivate來顯式的表明,這個元素的訪問權限為文件內私有,即extension也可以訪問到。private則是真正的私有,離開了這個類或者結構體的作用域外面就無法訪問。open在module內可以被override,在被import到其他地方后其他用戶使用的時候不能被override。通過open和public標記區別一個元素在其他module中是只能被訪問還是可以被override
  • defer關鍵字,推遲執行,會在return之前執行。記得react cocoa里面oc有寫過一個黑魔法來實現這個方法,swift可以直接用關鍵字解決了

整理一下整個過程,把完成的閉包傳入,然后被加到queue中,請求返回后出發queue執行,最終調用最初傳入的閉包

這篇文章從demo里的Alamofire.request("https://httpbin.org/get")開始分析了整個發送和接收請求的過程,有一個大概流程的概念。后面再去逐個分析每個類

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容