Alamofire中SessionManager之DataRequest詳解

你好,我是Emma,今天研究的課題是Alamofire中SessionManager和DataRequest以及task之間的關(guān)系。本篇文檔以SessionManager.default.request(urlString)
.response { (response) in debugPrint(response) }為切入點(diǎn)。

1. SessionManager類(lèi)

open class SessionManager {
    public static let `default`: SessionManager = {
        let configuration = URLSessionConfiguration.default
        configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders

        return SessionManager(configuration: configuration)
    }()
}

其中的SessionManager.defaultHTTPHeaders指的是什么旧困?

public static let defaultHTTPHeaders: HTTPHeaders = {
        let acceptEncoding: String = "gzip;q=1.0, compress;q=0.5"
        
        let acceptLanguage = Locale.preferredLanguages.prefix(6).enumerated().map { index, languageCode in
            let quality = 1.0 - (Double(index) * 0.1)
            return "\(languageCode);q=\(quality)"
        }.joined(separator: ", ")

        let userAgent: String = {...}()
        return [
            "Accept-Encoding": acceptEncoding,
            "Accept-Language": acceptLanguage,
            "User-Agent": userAgent
        ]
    }()

初始化方法:

open class SessionManager {
    public init(
        configuration: URLSessionConfiguration = URLSessionConfiguration.default,
        //原生和Alomofire之間的代理移交
        delegate: SessionDelegate = SessionDelegate(),
        serverTrustPolicyManager: ServerTrustPolicyManager? = nil)
    {
        //設(shè)置代理這個(gè)類(lèi)的delegate 就是 SessionDelegate = SessionDelegate()橡娄,做的是移交工作笋婿。
        //這里是如何實(shí)現(xiàn)將UIApplicationDelegate后臺(tái)的方法橋接到URLSessionDelegate中的初始化方法呻澜。這點(diǎn)體現(xiàn)了SessionDelegate這個(gè)類(lèi)的強(qiáng)大之處嫌术。
        self.delegate = delegate
        //初始化這個(gè)類(lèi)的URLSession
        self.session = URLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)
        //提供給SessionDelegate的一些變量初始化
        commonInit(serverTrustPolicyManager: serverTrustPolicyManager)
    }
}

commonInit方法的具體實(shí)現(xiàn):

//好吧梢睛,被SessionDelegate 的set方法附身之感
private func commonInit(serverTrustPolicyManager: ServerTrustPolicyManager?) {
    session.serverTrustPolicyManager = serverTrustPolicyManager
//初始化中有self.delegate = delegate,這里有delegate.sessionManager持有self不會(huì)造成循環(huán)引用嗎咖摹?不會(huì)狮含。原因 `weak var sessionManager: SessionManager?`
    delegate.sessionManager = self

//這個(gè)主要是給這個(gè)類(lèi)提供backgroundCompletionHandler?()屬性的作用洋满。
delegate.sessionDidFinishEventsForBackgroundURLSession = { [weak self] session in
        guard let strongSelf = self else { return }
        DispatchQueue.main.async { strongSelf.backgroundCompletionHandler?() }
    }
}

SessionManager與SessionDelegate的關(guān)聯(lián)小節(jié):

open class SessionManager {
    public let delegate: SessionDelegate 
    public init?(delegate: SessionDelegate)
    {
        self.delegate = delegate
        commonInit()
    }
    private func commonInit() {
        delegate.sessionManager = self
    }
}
open class SessionDelegate: NSObject {
    weak var sessionManager: SessionManager?
}
//這樣雙方互相持有但是是打破循環(huán)鏈的持有剩晴÷嘀洌可以在SessionDelegate中做一些關(guān)于SessionManager的容錯(cuò)處理。

2.Request之DataRequest

下面進(jìn)入今天的正題Request之DataRequest赞弥,首先看request方法:

@discardableResult
open func request(
    _ url: URLConvertible,
    method: HTTPMethod = .get,//指定是請(qǐng)求的方法類(lèi)型
    parameters: Parameters? = nil,//參數(shù)
    encoding: ParameterEncoding = URLEncoding.default,//編碼毅整,此處默認(rèn)的是URLEncoding.default
    headers: HTTPHeaders? = nil)
    -> DataRequest
{
    var originalRequest: URLRequest?

    do {
        originalRequest = try URLRequest(url: url, method: method, headers: headers)
        //重點(diǎn)重點(diǎn),編碼重點(diǎn)
        let encodedURLRequest = try encoding.encode(originalRequest!, with: parameters)
        return request(encodedURLRequest)
    } catch {
        return request(originalRequest, failedWith: error)
    }
}

方法的百分號(hào)編碼和區(qū)分方法的具體實(shí)現(xiàn):

public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
    var urlRequest = try urlRequest.asURLRequest()
//判斷是否有參數(shù)
    guard let parameters = parameters else { return urlRequest }
//根據(jù)HTTPMethod這個(gè)枚舉進(jìn)行派分
    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 {
        //進(jìn)行百分號(hào)編碼和添加&符號(hào)绽左。
            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
}

private func query(_ parameters: [String: Any]) -> String {
    //創(chuàng)建元祖
    var components: [(String, String)] = []
    //遍歷
    for key in parameters.keys.sorted(by: <) {
        let value = parameters[key]!
        //進(jìn)行遞歸操作去除key悼嫉,value
        components += queryComponents(fromKey: key, value: value)
    }
    //joined添加&分隔符,實(shí)例:A&B&C
    return components.map { "\($0)=\($1)" }.joined(separator: "&")
    
}

從request方法閉包中的return request(encodedURLRequest)方法可以看出走的是下面這步

@discardableResult
open func request(_ urlRequest: URLRequestConvertible) -> DataRequest {
    var originalRequest: URLRequest?
    do {
    //保存的請(qǐng)求
        originalRequest = try urlRequest.asURLRequest()
    //保存的任務(wù)
        let originalTask = DataRequest.Requestable(urlRequest: originalRequest!)

        let task = try originalTask.task(session: session, adapter: adapter, queue: queue)
        //重點(diǎn)點(diǎn)擊進(jìn)入下面代碼拼窥,RequestTask是枚舉戏蔑,區(qū)分了.data,.download,.stream,.upload
        let request = DataRequest(session: session, requestTask: .data(originalTask, task))
        //task和Request是意義對(duì)應(yīng)的關(guān)聯(lián)關(guān)系。
        delegate[task] = request
        if startRequestsImmediately { request.resume() }
        return request
    } catch {
        return request(originalRequest, failedWith: error)
    }
}
//保存了DataRequest的task適配器請(qǐng)求序列
struct Requestable: TaskConvertible {
    let urlRequest: URLRequest
    func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
        do {
            let urlRequest = try self.urlRequest.adapt(using: adapter)
            //重點(diǎn)是通過(guò)請(qǐng)求找到dataTask
            return queue.sync { session.dataTask(with: urlRequest) }
        } catch {
            throw AdaptError(error: error)
        }
    }
}

這個(gè)Request的初始化主要是一些與task的代理之間的持有關(guān)系和保存一些值鲁纠。

open class Request{
    init(session: URLSession, requestTask: RequestTask, error: Error? = nil) {
    //session這是是用來(lái)進(jìn)行保存session的总棵。
        self.session = session
    //  requestTask是一個(gè)枚舉,進(jìn)行任務(wù)和代理的分發(fā)
        switch requestTask {
        case .data(let originalTask, let task):
            taskDelegate = DataTaskDelegate(task: task)
            self.originalTask = originalTask
        case .download(let originalTask, let task):
            taskDelegate = DownloadTaskDelegate(task: task)
            self.originalTask = originalTask
        case .upload(let originalTask, let task):
            taskDelegate = UploadTaskDelegate(task: task)
            self.originalTask = originalTask
        case .stream(let originalTask, let task):
            taskDelegate = TaskDelegate(task: task)
            self.originalTask = originalTask
        }
    
        delegate.error = error
        //此處這個(gè)線程添加了addOperation 改含,其初始化見(jiàn)下面代碼情龄。
        delegate.queue.addOperation { self.endTime = CFAbsoluteTimeGetCurrent() }
    }
}
open class TaskDelegate: NSObject {
    init(task: URLSessionTask?) {
    //關(guān)于task代理中線程的初始化
        self.queue = {
            let operationQueue = OperationQueue() 
            operationQueue.maxConcurrentOperationCount = 1
            operationQueue.isSuspended = true
            operationQueue.qualityOfService = .utility
            return operationQueue
        }()
    }
}

總結(jié):

1)delegate[task] = request這樣設(shè)計(jì)的原因是什么?

原因是實(shí)現(xiàn)SessionManager和Request之間的解耦捍壤。

2)那么SessionManager骤视,Request,Task之間各自負(fù)責(zé)什么職能又是如何聯(lián)系的鹃觉?

SessionManager通過(guò)SessionDelegate來(lái)做請(qǐng)求的主體序列专酗,通過(guò)這個(gè)來(lái)塞入一些頭信息。請(qǐng)求體就有DataRequest等Request來(lái)負(fù)責(zé)帜慢,他負(fù)責(zé)的是一些URL笼裳,params,Method粱玲,Ecode躬柬,header等的打包,請(qǐng)求得開(kāi)線程啊抽减,線程這部分就分離出來(lái)由task負(fù)責(zé)允青,并且是Request和task是一對(duì)一的對(duì)練關(guān)系。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末卵沉,一起剝皮案震驚了整個(gè)濱河市颠锉,隨后出現(xiàn)的幾起案子法牲,更是在濱河造成了極大的恐慌,老刑警劉巖琼掠,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拒垃,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡瓷蛙,警方通過(guò)查閱死者的電腦和手機(jī)悼瓮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)艰猬,“玉大人横堡,你說(shuō)我怎么就攤上這事」谔遥” “怎么了命贴?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)食听。 經(jīng)常有香客問(wèn)我胸蛛,道長(zhǎng),這世上最難降的妖魔是什么碳蛋? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任胚泌,我火速辦了婚禮,結(jié)果婚禮上肃弟,老公的妹妹穿的比我還像新娘玷室。我一直安慰自己,他們只是感情好笤受,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布穷缤。 她就那樣靜靜地躺著,像睡著了一般箩兽。 火紅的嫁衣襯著肌膚如雪津肛。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,268評(píng)論 1 309
  • 那天汗贫,我揣著相機(jī)與錄音身坐,去河邊找鬼。 笑死落包,一個(gè)胖子當(dāng)著我的面吹牛部蛇,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播咐蝇,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼涯鲁,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起抹腿,我...
    開(kāi)封第一講書(shū)人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤岛请,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后警绩,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體崇败,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年肩祥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了僚匆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡搭幻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出逞盆,到底是詐尸還是另有隱情檀蹋,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布云芦,位于F島的核電站俯逾,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏舅逸。R本人自食惡果不足惜桌肴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望琉历。 院中可真熱鬧坠七,春花似錦、人聲如沸旗笔。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蝇恶。三九已至拳魁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間撮弧,已是汗流浹背潘懊。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留贿衍,地道東北人授舟。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像舌厨,于是被迫代替她去往敵國(guó)和親岂却。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

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