前言
通過上一篇URlSession后臺下載,我們已經(jīng)知道了SessionManager的作為整個Alamofire框架的核心樞紐的重要性??,還有一個比較重要的是SessionManager的代理SessionDelegate,封裝了URLSessionDelegate。負責對Task的回調(diào)事件提供默認實現(xiàn)(轉(zhuǎn)發(fā)給TaskDelegate進行實際處理),并且以閉包的方式暴露給外部,讓外部可以自定義實現(xiàn)。TaskDelegate 對URLSessionTask的回調(diào)進行實際的處理,并且執(zhí)行task的完成回調(diào)用。Request就 是對URLSessionTask的封裝,是暴露給上層的請求任務(wù)。
Alamofire Request
廢話不多說直接來用URLSession創(chuàng)建一個request和利用Alamofire創(chuàng)建一個request的例子??看看:
首先來看一個URLSession的request的用法:
guard let url = URL(string: "https://www.douban.com/j/app/radio/channels") else {
return;
}
let dataTask = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else{
return;
}
do{
let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
print(json)
}catch let error{
print(error)
}
};
dataTask.resume()
再來看一個基礎(chǔ)的Alamofire.request用法:
Alamofire.request("https://www.douban.com/j/app/radio/channels").responseJSON { (response) in
if let JSONString = response.result.value {
print("JSONString: \(JSONString)")
}
}
有沒有覺得,如果使用系統(tǒng)的API,我們不得不先創(chuàng)建URL,然后建立dataTask,并且resume,接著在callback里去解析JSON。由于Swift是一種強類型的語言,我們不得不進行大量的邏輯判斷和try-catch???♂?。而Alamofire把這些步驟簡化成了一個靜態(tài)的方法調(diào)用,并且用鏈式的方式來處理異步的Response解析。由于是鏈式的,我們也可以用鏈式的方式實現(xiàn)更多的邏輯。
那么在Alamofire.request的內(nèi)部具體做了什么“騷”操作呢???沒辦法???♀??,想要看到內(nèi)部實現(xiàn),就要看到這里的源碼,那么就來吧...
根據(jù)Alamofire.request的這個方法,我們來到Alamofire.swift看到這個request方法:
可以看到這個方法里的幾個參數(shù)都有默認值,所有我們可以直接傳入一個
Url參數(shù),這個方法返回一個DataRequest對象,是利用SessionManager的request方法,這說明了真正的request應(yīng)該是由SessionManager發(fā)起的,下面再來看一下SessionManager.default.request方法內(nèi)部實現(xiàn)了什么?
在這個方法中創(chuàng)建了一個
URLRequest對象,并把傳入的參數(shù)傳入到這個URLRequest對象中,并調(diào)用了request方法,并且把上面創(chuàng)建的URLRequest對象進行編碼之后作為參數(shù)傳入到這個request方法,
這里是插入一個小插曲,這個參數(shù)編碼到底是如何操作的,也就是這個encoding.encode()方法具體實現(xiàn)了什么?
這里先取出請求方法的類型,然后根據(jù)不同的類型對參數(shù)進行不同的編碼,由前面的代碼我們知道在
encode()這個方法傳入了參數(shù)parameters,而在這里處理這個parameters的是query(parameters),那么接著來看這個query方法的具體實現(xiàn):
來到這里把傳入的參數(shù)根據(jù)key進行排序,然后遍歷排序的結(jié)果,然后利用queryComponents方法將所有的參數(shù)(key,value)不斷的遞歸, 返回一個元素為元組的數(shù)組,然后將這個數(shù)組進行map映射,將key=value并且拼接&符號,至此就完成了參數(shù)的拼接,
??然后我們在回到encode方法中,根據(jù)請求方法的不同,
- 如果請求方法是
.get,.head,.delete類型,需要urlComponents.percentEncodedQuery進行百分號編碼,并且利用query(parameters)把參數(shù)直接拼接到URL后面。 - 如果是其他類型的請求方法,例如
.post,需要設(shè)置請求頭application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type,然后利用query(parameters)拼接參數(shù),將參數(shù)轉(zhuǎn)換為二進制并賦值到urlRequest.httpBody請求體中。
那么接下來回到request的流程再來看看這個request(encodedURLRequest)方法內(nèi)部實現(xiàn)了什么?
在這里先拿到傳入的已經(jīng)編碼過后的URLRequest,這里的DataRequest是繼承于Request的一個子類,用來管理URLSessionDataTask,而這里DataRequest.Requestable,難道是調(diào)用了一個Requestable方法?我們跟進去這個Requestable看一下:
可以看到這個Requestable是一個結(jié)構(gòu)體,也就是說在當前這個DataRequest類里面嵌套了一個結(jié)構(gòu)體,但是可以看到這個結(jié)構(gòu)體內(nèi)部并沒有urlRequest這個方法,那它怎么會調(diào)用urlRequest方法呢?
我們來看這句代碼let urlRequest: URLRequest,可以看到在這個結(jié)構(gòu)體的內(nèi)部有個urlRequest屬性,而且默認初始化。??那么這句DataRequest.Requestable(urlRequest: )實際上就是創(chuàng)建了一個類型為Requestable的而且包含創(chuàng)建task方法的結(jié)構(gòu)體對象,在這個task方法的內(nèi)部返回的是同步創(chuàng)建的session,return queue.sync { session.dataTask(with: urlRequest) }.
??????那么這里為什么不使用DataRequest直接創(chuàng)建一個task,而非要task由Requestable這個結(jié)構(gòu)體來創(chuàng)建,其實是Requestable結(jié)構(gòu)體就相當于是DataRequest的秘書,其實這里DataRequest和task任務(wù)應(yīng)該是一一對應(yīng)的,平級關(guān)系,如果DataRequest直接創(chuàng)建task,更像是歸屬關(guān)系,而由Requestable來創(chuàng)建task也就是降低了DataRequest與task的耦合性,使DataRequest與task保持平級關(guān)系。
繼續(xù)回到源代碼,可以看到在
Requestable創(chuàng)建完task之后,緊接著DataRequest就創(chuàng)建了一個request,那么接下來來到DataRequest的init方法,然而DataRequest并沒有init方法,那么就看其父類Request的init方法:
可以看到這里
requestTask很明顯是個枚舉類型,
而這個
requestTask調(diào)用了一個關(guān)聯(lián)方法.data(let originalTask, let task),而這個.data 即是DataRequest初始化方法中傳入的.data(originalTask, task),這里會根據(jù)傳入的方法的不同的類型來把task交給不同的TaskDelegate.
再一次回到request流程的源碼???♂????♂????♂? ,這里一句很重要的代碼 delegate[task] = request,這里是做了一個綁定操作,(有點類似于字典和數(shù)組),當前這個delegate由源碼可知public let delegate: SessionDelegate是一個SessionDelegate就是SessionDelegate通過task可以讀取到request。方便在 SessionDelegate 下發(fā)任務(wù)的時候,通過task直接檢索,方便直接獲取到request ,然后調(diào)用request.resume()任務(wù)啟動.
直接通過
SessionDelegate實現(xiàn)代理, 為什么這里還要有一個DataTaskDelegate, 真是費勁呢。
首先,要明白要SessionDelegate是所有 Session 的代理遵循者,任何的代理都會來到這里響應(yīng),而DataTaskDelegate 是具體任務(wù)的實現(xiàn)者,這里面所有方法都是由 SessionDelegate 下發(fā)響應(yīng)的。(一個下發(fā)任務(wù),一個具體干活)
下面給我一首歌的時間??????來看一下SessionDelegate.swift中URLSessionDownloadDelegate的源碼??:
從標紅部分可以看出,
self[downloadTask]就等同于DownloadRequest,DownloadRequest.delegate 就等同于DownloadTaskDelegate.這里把downloadTask交給了DownloadTaskDelegate.urlSession去執(zhí)行,如果再跟進去源碼DownloadTaskDelegate.urlSession,就會發(fā)現(xiàn)執(zhí)行的就是具體的Download操作。
總結(jié)
在SessionManager 的 Request 方法中,表面上看是由SessionManager直接處理了request,然而實際上,是通過SessionManager的代理響應(yīng)SessionDelegate,然后SessionDelegate把這個具體Request分發(fā)給了具體的DataTaskDelegate,然后DataTaskDelegate來執(zhí)行具體的任務(wù)內(nèi)容。之所以這么做,是因為SessionDelegate 通過的任務(wù)分發(fā),讓處理具體任務(wù)的taskdelegate 去處理對應(yīng)的事務(wù),避免了其內(nèi)部邏輯混亂和代碼冗余,實現(xiàn)任務(wù)邏輯下沉。關(guān)于Request還有很多內(nèi)容的哦,后面再做補充,如有錯誤,還望大佬們指正。
