Alamofire (3)—— Request

??????Alamofire專(zhuān)題目錄踢星,歡迎及時(shí)反饋交流 ??????


Alamofire 目錄直通車(chē) --- 和諧學(xué)習(xí),不急不躁港庄!


上一篇 Alamofire-后臺(tái)下載 其中就介紹了關(guān)于 SesssionManagerSessionDelegate的分層!下面我在總結(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)就是 SesssionManagerSession 的代理移交給了一個(gè)專(zhuān)門(mén)的類(lèi) : SessionDelegate
  • SessionDelegate實(shí)現(xiàn)了 URLSessionDelegate 诡挂、 URLSessionTaskDelegateURLSessionDataDelegate 临谱、 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ú)處安放的魅力!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末谓松,一起剝皮案震驚了整個(gè)濱河市星压,隨后出現(xiàn)的幾起案子践剂,更是在濱河造成了極大的恐慌,老刑警劉巖娜膘,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件逊脯,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡劲绪,警方通過(guò)查閱死者的電腦和手機(jī)男窟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)贾富,“玉大人歉眷,你說(shuō)我怎么就攤上這事〔梗” “怎么了汗捡?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)畏纲。 經(jīng)常有香客問(wèn)我扇住,道長(zhǎng),這世上最難降的妖魔是什么盗胀? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任艘蹋,我火速辦了婚禮,結(jié)果婚禮上票灰,老公的妹妹穿的比我還像新娘女阀。我一直安慰自己,他們只是感情好屑迂,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布浸策。 她就那樣靜靜地躺著,像睡著了一般惹盼。 火紅的嫁衣襯著肌膚如雪庸汗。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,688評(píng)論 1 305
  • 那天手报,我揣著相機(jī)與錄音蚯舱,去河邊找鬼。 笑死掩蛤,一個(gè)胖子當(dāng)著我的面吹牛晓淀,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播盏档,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼燥爷!你這毒婦竟也來(lái)了蜈亩?” 一聲冷哼從身側(cè)響起懦窘,我...
    開(kāi)封第一講書(shū)人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎稚配,沒(méi)想到半個(gè)月后畅涂,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡道川,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年午衰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片冒萄。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡臊岸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出尊流,到底是詐尸還是另有隱情帅戒,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布崖技,位于F島的核電站逻住,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏迎献。R本人自食惡果不足惜瞎访,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望吁恍。 院中可真熱鬧扒秸,春花似錦、人聲如沸践盼。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)咕幻。三九已至渔伯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間肄程,已是汗流浹背锣吼。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蓝厌,地道東北人玄叠。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像拓提,于是被迫代替她去往敵國(guó)和親读恃。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

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