??????Alamofire專(zhuān)題目錄踢星,歡迎及時(shí)反饋交流 ??????
Alamofire 目錄直通車(chē) --- 和諧學(xué)習(xí),不急不躁港庄!
上一篇 Alamofire-后臺(tái)下載 其中就介紹了關(guān)于
SesssionManager
到SessionDelegate
的分層!下面我在總結(jié)一下很泊,然后開(kāi)始一個(gè)Alamofire
非常重要的模塊Request
!
一囱嫩、SesssionManager的總結(jié)
-
SesssionManager
就是對(duì)外提供的管理者,這個(gè)管理者具備整個(gè)Alamofire
的所有功能儿捧。-
request
請(qǐng)求荚坞、download、upload菲盾、stream
- 請(qǐng)求頭信息設(shè)置以及多表單頭信息設(shè)置
-
session
直接對(duì)外提供颓影,方便自由處理 - 初始化暴露,方便自由工廠(chǎng)構(gòu)造懒鉴,比如
URLSessionConfiguration
的配置模式 - 重試以及適配請(qǐng)求
- 閉包對(duì)外提供:
backgroundCompletionHandler
后臺(tái)下載回來(lái)監(jiān)聽(tīng)閉包 - 其中一個(gè)非常重要的點(diǎn)就是
SesssionManager
把Session
的代理移交給了一個(gè)專(zhuān)門(mén)的類(lèi) :SessionDelegate
-
SessionDelegate
實(shí)現(xiàn)了URLSessionDelegate
诡挂、URLSessionTaskDelegate
、URLSessionDataDelegate
临谱、URLSessionDownloadDelegate
璃俗、URLSessionStreamDelegate
等代理-
更多有意思的是:
SessionDelegate
還提供了對(duì)外的閉包,意味著所有的內(nèi)部實(shí)現(xiàn)的代理情況悉默,再外界都可以進(jìn)行監(jiān)聽(tīng)城豁。當(dāng)然這個(gè)所有的對(duì)外閉包分為兩種情況:- 在原來(lái)代理回調(diào)的內(nèi)部添加閉包執(zhí)行。
- 另一種是二選一麦牺,如果代理回來(lái)就不執(zhí)行下層代理下發(fā)钮蛛,執(zhí)行對(duì)外閉包回調(diào)
總結(jié):
SesssionManager
負(fù)責(zé)創(chuàng)建和管理Request
對(duì)象及其底層NSURLSession
首先給大家貼出一張非常熟悉的圖鞭缭,看懂了這張圖對(duì)下面理解 Request
的幫助大大的
- 從上面這張圖可以看出,我們的對(duì)外模塊是
SesssionManager
魏颓,他給外界的用戶(hù)提供了很多的功能 - 但是這些工作的真正實(shí)現(xiàn)者是由
iOS岭辣、Android、前端甸饱、后臺(tái)沦童、測(cè)試
實(shí)現(xiàn)的! - 其中單拿
iOS
模塊的任務(wù)來(lái)說(shuō)叹话,有首頁(yè)偷遗、發(fā)現(xiàn)、我的驼壶、SDK氏豌、視頻....
模塊要實(shí)現(xiàn),但是我們的項(xiàng)目經(jīng)理有可能都不知道這些到底是什么热凹,怎么實(shí)現(xiàn)泵喘!所有來(lái)說(shuō)如果全部交給SesssionManager
來(lái)實(shí)現(xiàn),顯然耦合性過(guò)強(qiáng)般妙,還有任務(wù)亂七八糟纪铺,沒(méi)有體現(xiàn)一個(gè)牛逼項(xiàng)目分層架構(gòu)的效果。所以在iOS
任務(wù)細(xì)化和SesssionManager
之間就缺了一個(gè)小管理者碟渺,對(duì)下:他知道具體事務(wù)和調(diào)度鲜锚。對(duì)上:他能和SesssionManager
協(xié)調(diào)配合。那就是Request
二苫拍、Request
1. Request參數(shù)編碼
-
首先說(shuō)明一下
Alamofire
支持編碼格式,具體格式差異根據(jù)名字很容易理解芜繁,實(shí)在不能理解的可以自行百度查漏補(bǔ)缺- URLEncoding
- JSONEncoding
- PropertyListEncoding
let encodedURLRequest = try encoding.encode(originalRequest!, with: parameters)
通過(guò)這句代碼還編碼請(qǐng)求
public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
var urlRequest = try urlRequest.asURLRequest()
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
}
代碼自行查閱,這里總結(jié)一下
-
首先取出請(qǐng)求方法绒极,根據(jù)不同的請(qǐng)求方法浆洗,參數(shù)編碼是不同的
-
.get, .head, .delete
這三個(gè)方法是把參數(shù)直接拼接到URL
后面 - 其他通過(guò)請(qǐng)求體 (
httpBody
) 的形式編碼
-
因?yàn)槲覀兊恼?qǐng)求是通過(guò)
ASCII編碼
的,所以要進(jìn)行百分號(hào)編碼,第一步就是對(duì)當(dāng)前請(qǐng)求的所有路由百分號(hào)編碼參數(shù)便利編碼, 拼接拿出
private func query(_ parameters: [String: Any]) -> String {
var components: [(String, String)] = []
for key in parameters.keys.sorted(by: <) {
let value = parameters[key]!
components += queryComponents(fromKey: key, value: value)
}
return components.map { "\($0)=\($1)" }.joined(separator: "&")
}
- 通過(guò)
ASCII
有小到大進(jìn)行排序 -
queryComponents
這個(gè)方法代碼過(guò)度省略集峦。- 里面進(jìn)行遞歸參數(shù),
- 把
key和value
取出抠刺,然后進(jìn)行了百分號(hào)編碼塔淤。 - 放進(jìn)元組保存,形成參數(shù)對(duì)
- 外面講元組加入數(shù)組中
- map 映射
($0)=\($1)
- 元素之間鍵入一個(gè)分隔符號(hào)
&
普通方法就直接拼接到URL的后面速妖,例如POST方法就是把這些編碼好的參數(shù)對(duì)放入請(qǐng)求體中高蜂。其中還要加入Content-Type
的請(qǐng)求頭
2. Request內(nèi)部關(guān)系梳理
探索完 request
繁瑣事務(wù)之一的參數(shù)編碼之后,開(kāi)始分析內(nèi)部:url -> request -> task
的過(guò)程罕容。這個(gè)過(guò)程中還建立 task以及request
之間的綁定關(guān)系
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)
}
}
- 內(nèi)部創(chuàng)建
Requestable
來(lái)幫助下層的DataRequest
創(chuàng)建Task
备恤,也是常規(guī)說(shuō)法稿饰,任務(wù)分層,架構(gòu)思路更清晰 - 綁定
task 和 request
, 方便在SessionDelegate
下發(fā)任務(wù)露泊,task
直接檢索喉镰,request
方便直接獲取 - 直接任務(wù)
request.resume()
啟動(dòng)
-
Request
初始化的時(shí)候利用了枚舉的便利性,構(gòu)造創(chuàng)建惭笑,相關(guān)信息保存
可能很多小伙伴這個(gè)時(shí)候就會(huì)有疑慮侣姆,你不就是通過(guò)SessionDelegate
實(shí)現(xiàn)代理, 為什么這里還要有一個(gè) DataTaskDelegate
首先一定要明白
SessionDelegate
是所有Session
的代理遵循者,任何的代理都會(huì)來(lái)到這里響應(yīng)沉噩。DataTaskDelegate
是我們具體繁瑣任務(wù)實(shí)現(xiàn)者捺宗,這里面所有方法都是由SessionDelegate
下發(fā)響應(yīng)的。說(shuō)白了就是整體與局部的關(guān)系
SessionDelegate
是事件總響應(yīng)者川蒙,但是具體的事務(wù)應(yīng)該交給具體的人去執(zhí)行蚜厉,不能因?yàn)樵?SessionDelegate
能夠拿到所有的響應(yīng),那么所有的代碼都羅列在這里畜眨,很顯然如果那樣做必然會(huì)導(dǎo)致混亂昼牛,我們根據(jù)不同的需求,響應(yīng)總代理然后根據(jù)需求的不同交給專(zhuān)業(yè)的人去做專(zhuān)業(yè)的事胶果。耦合性大大降低匾嘱,架構(gòu)的分層更加明顯。其中還有異步非常重要的點(diǎn):
delegate[task] = request
給我們的SessionDelegate
提供一個(gè)方便及時(shí)獲得能力
open func urlSession(
_ session: URLSession,
downloadTask: URLSessionDownloadTask,
didFinishDownloadingTo location: URL)
{
// 省略屬性閉包回調(diào)的情況
if let delegate = self[downloadTask]?.delegate as? DownloadTaskDelegate {
delegate.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location)
}
}
- 這段代碼典型的通過(guò)我們前面建立的綁定關(guān)系早抠,通過(guò)每次代理回調(diào)的
task
找到相應(yīng)的Request
然后因?yàn)閷傩躁P(guān)系霎烙,直接拿出Request
的屬性delegate 來(lái)處理相關(guān)事務(wù)
func urlSession(
_ session: URLSession,
downloadTask: URLSessionDownloadTask,
didFinishDownloadingTo location: URL)
{
temporaryURL = location
guard
let destination = destination,
let response = downloadTask.response as? HTTPURLResponse
else { return }
let result = destination(location, response)
let destinationURL = result.destinationURL
let options = result.options
self.destinationURL = destinationURL
do {
if options.contains(.removePreviousFile), FileManager.default.fileExists(atPath: destinationURL.path) {
try FileManager.default.removeItem(at: destinationURL)
}
if options.contains(.createIntermediateDirectories) {
let directory = destinationURL.deletingLastPathComponent()
try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
}
try FileManager.default.moveItem(at: location, to: destinationURL)
} catch {
self.error = error
}
}
- 文件下載完畢轉(zhuǎn)移存儲(chǔ)地址,文件處理相關(guān) 以及error情況
- 上面就是最典型的繁重惡心操作蕊连,必然是不需要被
SessionDelegate
所需要知道的悬垃!
我們從
SessionManager
->Request
這一過(guò)程明面上就是直接交付,但是背地都是通過(guò)代理響應(yīng)聯(lián)通的:SessionDelegate
->具體的Request的delegate
甘苍〕⑷洌看到這里,是不是感覺(jué)非常的爽载庭!優(yōu)秀的框架總是能夠給你在思想方面帶來(lái)很大的提升看彼,我們平時(shí)開(kāi)發(fā)的時(shí)候也會(huì)有很惡心的耦合度,通訊問(wèn)題囚聚。那么想必Alamofire
的設(shè)計(jì)思路肯定能夠給你帶來(lái)一定的啟示靖榕!就問(wèn)此時(shí)此刻還有誰(shuí)?45度仰望天空顽铸,該死茁计!我這無(wú)處安放的魅力!