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

本文通過一個get請求脆淹,追蹤代碼流程凹联,來進入到整個框架的學(xué)習(xí)滤淳。

1.創(chuàng)建請求

Alamofire.request("https://httpbin.org/get")
  • 通過Alamofire來創(chuàng)建
@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
    )
}
  • 關(guān)鍵字@discardableResult爪飘,在方法沒有返回值時不會報出警告
  • swift參數(shù)是支持默認值的义起,調(diào)用時值傳了一個url參數(shù),繼承URLConvertible協(xié)議师崎,用來生成URL類型
  • HTTPMethod是一個枚舉類型默终,這里默認值使用get
  • Parameters定義為[String: Any],默認是nil犁罩。它的定義使用typealias關(guān)鍵字齐蔽,用來為已經(jīng)存在的類型重新定義名字的,通過命名床估,可以使代碼變得更加清晰含滴。HTTPHeaders亦是如此
  • Alamofire只是一個入口,內(nèi)部調(diào)用SessionManager來完成創(chuàng)建
@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類型缎脾,也就是后面跟一個"祝闻?"。
  • 試著創(chuàng)建URLRequest遗菠,這里加try是因為asURL可能會拋出異常
  • 然后調(diào)用ParameterEncoding.swift的方法對參數(shù)進行編碼
  • 最后進入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值是一個結(jié)構(gòu)體联喘,這個結(jié)構(gòu)體繼承TaskConvertible協(xié)議华蜒。該協(xié)議定義了task方法,讓每種不同類型的request實現(xiàn)自己創(chuàng)建task的邏輯豁遭。這里創(chuàng)建NSURLSessionDataTask類型task的實現(xiàn)代碼是session.dataTask(with: urlRequest)
  • 用session和.data(originalTask, task)創(chuàng)建DataRequest叭喜。URLRequest(DataRequest的父類)初始化方法會根據(jù)task的不同生成對應(yīng)的taskDelegate
  • 把task和request的關(guān)系存到delegate中,調(diào)用request的resume方法發(fā)出請求堤框,內(nèi)部會觸發(fā)task.resume()方法發(fā)請求域滥,最后并返回request

整個過程大致是,調(diào)用SessionManager.request()方法蜈抓,獲取URLRerequest启绰,獲取對應(yīng)的task,創(chuàng)建DataRequest沟使,發(fā)起請求委可。

2.接收請求

if let request = request as? DataRequest {
            request.responseString { response in
                requestComplete(response.response, response.result)
            }
        }
  • 這里剛開始可能看不太懂,這是尾隨閉包的寫法腊嗡。這個閉包是request.responseString方法的最后一個參數(shù)着倾。
  • 這個閉包會被加入到DataRequest的delegate.queue中。TaskDelegate的queue是一個串行隊列燕少,存放task結(jié)束之后需要執(zhí)行的任務(wù)
@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隊列中卡者,并且這個隊列初始化時設(shè)置了isSuspend=true,所以它會在改為false才會執(zhí)行
  • 這個opration里會生成result和response客们,供回調(diào)使用崇决,也就是requestComplete(response.response, response.result)的參數(shù)
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,實現(xiàn)請求的各種回調(diào)
  • 下面的判斷會走到else底挫,前面在delegate存儲過task和delegate恒傻,所以這里self[dataTask]會得到之前發(fā)請求的request,并且是DataTaskDelegate類型
  • 于是執(zhí)行delegate(DataTaskDelegate)的對應(yīng)方法
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計算接收數(shù)據(jù)的大小
  • totalBytesExpected將可選類型dataTask.response解包官边,然后獲取expectedContentLength.“??"運算符可以用于判斷變量或常量的數(shù)值是否是nil沸手,不為nil則取變量或者常量本身的值,如果是nil則使用后面的值替代
  • 判斷是否有progressHandler,這不會調(diào)用注簿。因為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內(nèi)實現(xiàn)的回調(diào)罐氨,表示task已經(jīng)完成數(shù)據(jù)傳輸
  • completeTask,如果請求不需要重試滩援,就會執(zhí)行這個閉包
  • sessionManager沒有被賦值,所以不會走到第一個判斷里
  • validations是[() -> Void] = []一種閉包類型的數(shù)組塔嬉,放一些檢查的方法玩徊。$0()表示第一個參數(shù)租悄,也就是數(shù)組里的每一個值
  • 用request.delegate.error取到錯誤,如果設(shè)置了retrier并有error恩袱,就執(zhí)行重試的方法泣棋。否則執(zhí)行completeTask
  • 在completeTask中,首選判斷taskDidComplete有無值然后執(zhí)行畔塔,然后通過strongSelf[task]得到request并調(diào)用其urlSession方法
  • 最后發(fā)通知并把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) 的方法實現(xiàn)
  • 這里在有錯誤時潭辈,對DownloadTaskDelegate做了一些處理。
  • queue.isSuspended這里是關(guān)鍵澈吨,isSuspended設(shè)置為false后把敢,回調(diào)傳進來operation將會開始執(zhí)行
  • @objc 作用:1,fileprivate 或者 private 保證方法私有 能在同一個類 或者 同一個文件(extension)中訪問這個方法 如果定義為private 那么只能在一個類中訪問 不能在類擴展中訪問谅辣;2修赞,允許這個函數(shù)在“運行時”通過oc的消息機制調(diào)用
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的實現(xiàn)
  • 使用lock,保證賦值是線程安全的
  • open關(guān)鍵字桑阶,訪問控制在權(quán)限柏副。在swift3中,fileprivate來顯式的表明蚣录,這個元素的訪問權(quán)限為文件內(nèi)私有割择,即extension也可以訪問到。private則是真正的私有萎河,離開了這個類或者結(jié)構(gòu)體的作用域外面就無法訪問荔泳。open在module內(nèi)可以被override,在被import到其他地方后其他用戶使用的時候不能被override公壤。通過open和public標記區(qū)別一個元素在其他module中是只能被訪問還是可以被override
  • defer關(guān)鍵字换可,推遲執(zhí)行,會在return之前執(zhí)行厦幅。記得react cocoa里面oc有寫過一個黑魔法來實現(xiàn)這個方法沾鳄,swift可以直接用關(guān)鍵字解決了

整理一下整個過程,把完成的閉包傳入确憨,然后被加到queue中译荞,請求返回后出發(fā)queue執(zhí)行,最終調(diào)用最初傳入的閉包

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市塔猾,隨后出現(xiàn)的幾起案子篙骡,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件糯俗,死亡現(xiàn)場離奇詭異尿褪,居然都是意外死亡,警方通過查閱死者的電腦和手機得湘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進店門杖玲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人淘正,你說我怎么就攤上這事摆马。” “怎么了鸿吆?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵囤采,是天一觀的道長。 經(jīng)常有香客問我伞剑,道長斑唬,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任黎泣,我火速辦了婚禮恕刘,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘抒倚。我一直安慰自己褐着,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布托呕。 她就那樣靜靜地躺著含蓉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪项郊。 梳的紋絲不亂的頭發(fā)上馅扣,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天,我揣著相機與錄音着降,去河邊找鬼差油。 笑死,一個胖子當(dāng)著我的面吹牛任洞,可吹牛的內(nèi)容都是我干的蓄喇。 我是一名探鬼主播,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼交掏,長吁一口氣:“原來是場噩夢啊……” “哼妆偏!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起盅弛,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤钱骂,失蹤者是張志新(化名)和其女友劉穎叔锐,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體罐柳,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡掌腰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了张吉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡催植,死狀恐怖肮蛹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情创南,我是刑警寧澤伦忠,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站稿辙,受9級特大地震影響昆码,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜邻储,卻給世界環(huán)境...
    茶點故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一赋咽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧吨娜,春花似錦脓匿、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至勾扭,卻和暖如春毡琉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背妙色。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工桅滋, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人燎斩。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓虱歪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親栅表。 傳聞我的和親對象是個殘疾皇子笋鄙,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,884評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)怪瓶,斷路器萧落,智...
    卡卡羅2017閱讀 134,656評論 18 139
  • 1. Alamofire結(jié)構(gòu) Alamofire全部實現(xiàn)共有17個文件組成践美,如下: Alamofire有五模塊組成...
    繁華落盡丶lee閱讀 1,915評論 0 3
  • 本篇是Alamofire中的請求抽象層的講解 前言 在Alamofire中,圍繞著Request找岖,設(shè)計了很多額外的...
    老馬的春天閱讀 2,226評論 0 11
  • 如果许布,有一天兴革,我們的距離越來越遠 不是你變了,而是蜜唾,我真的愛上你了…… 如果杂曲,有一天,我們只是相擁而沒有言語 不是...
    靜曉懶閱讀 258評論 0 1
  • 如果我們在人生中體驗的每一次轉(zhuǎn)變都讓我們在生活中走得更遠棚饵,那么,我們就真正體驗到了生活想讓我們體驗的東西掩完。 ——《...
    張書畫閱讀 441評論 1 2