Alamofire-Request解析

請(qǐng)求過(guò)程

Alamofire中的靜態(tài)方法都是調(diào)用SessionManager里面的方法,SessionManagerdefault里存放著默認(rèn)的session,而SessionDelegate則實(shí)現(xiàn)了session的代理。
SessionDelegate類中:

1.聲明了許多閉包,如果你想自定義接收響應(yīng)的邏輯可以實(shí)現(xiàn)閉包

 open var sessionDidBecomeInvalidWithError: ((URLSession, Error?) -> Void)?

    /// Overrides default behavior for URLSessionDelegate method `urlSession(_:didReceive:completionHandler:)`.
    open var sessionDidReceiveChallenge: ((URLSession, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?

    /// Overrides all behavior for URLSessionDelegate method `urlSession(_:didReceive:completionHandler:)` and requires the caller to call the `completionHandler`.
    open var sessionDidReceiveChallengeWithCompletion: ((URLSession, URLAuthenticationChallenge, @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) -> Void)?

2.看下回調(diào)的代理方法的實(shí)現(xiàn)

   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)
        }
    }

如果外界實(shí)現(xiàn)閉包dataTaskDidReceiveData,就直接回調(diào)閉包dataTaskDidReceiveData,不再下發(fā)到具體的代理DataTaskDelegate執(zhí)行。
否則將任務(wù)下發(fā)到DataTaskDelegate去執(zhí)行。DataTaskDelegate是怎么處理的?

 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) }
            }
        }
    }

Request初探

明白Alamofire中一個(gè)請(qǐng)求的過(guò)程,是非常有必要的。先看下邊的代碼:

SessionManager.default.request(myGetUrlString, method: .get, parameters: ["array":getJsonFromArray(array)])
            .response { (response) in
                debugPrint(response)
        }

上邊的代碼是最簡(jiǎn)單的一個(gè)請(qǐng)求,我們看看Alamofire.request中究竟干了什么?

   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)
        }
    }

1.創(chuàng)建URLRequest
2.encodingURLEncoding類型的.encoding.encode會(huì)調(diào)用到

  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
    }

urlRequestURLRequestConvertible協(xié)議類型,URLRequestConvertible協(xié)議的目的是對(duì)URLRequest進(jìn)行自定義的轉(zhuǎn)換。
判斷method,如果method.get, .head, .delete類型,需要urlComponents.percentEncodedQuery進(jìn)行百分號(hào)編碼,并且query(parameters)參數(shù)拼接。
如果是post,需要設(shè)置請(qǐng)求頭application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type,然后拼接參數(shù)query(parameters),將參數(shù)轉(zhuǎn)換為二進(jìn)制并賦值到urlRequest.httpBody

我們看看query(parameters)的實(shí)現(xiàn):

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: "&")
    }

(1)將所有參數(shù)的keys按照ASCII碼排序
(2)創(chuàng)建components數(shù)組,遍歷parameters,取出每一對(duì)ket-value
(3)調(diào)用queryComponents(key,value),返回一個(gè)元祖,并添加到數(shù)組components

 public func queryComponents(fromKey key: String, value: Any) -> [(String, String)] {
        var components: [(String, String)] = []

        if let dictionary = value as? [String: Any] {
            for (nestedKey, value) in dictionary {
                components += queryComponents(fromKey: "\(key)[\(nestedKey)]", value: value)
            }
        } else if let array = value as? [Any] {
            for value in array {
                components += queryComponents(fromKey: arrayEncoding.encode(key: key), value: value)
            }
        } else if let value = value as? NSNumber {
            if value.isBool {
                components.append((escape(key), escape(boolEncoding.encode(value: value.boolValue))))
            } else {
                components.append((escape(key), escape("\(value)")))
            }
        } else if let bool = value as? Bool {
            components.append((escape(key), escape(boolEncoding.encode(value: bool))))
        } else {
            components.append((escape(key), escape("\(value)")))
        }

        return components
    }

如果是dictionaryarray類型,繼續(xù)遞歸調(diào)用queryComponents,如果是NSNumber,Bool,String類型,將數(shù)據(jù)添加到components。

(4).components.map { "\($0)=\($1)" }轉(zhuǎn)化為key-vaue的字符串?dāng)?shù)組,然后調(diào)用joined(separator: "&"),&作為分隔符,連接成我們需要的數(shù)據(jù)并返回

Request內(nèi)部關(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)
        }
    }

1.首先創(chuàng)建一個(gè)Requestable結(jié)構(gòu)體對(duì)象,使用Requestable創(chuàng)建task
為什么要有Requestable這個(gè)層,不直接把task·放到DataRequest? (1)我們知道taskrequest是一一對(duì)應(yīng)的關(guān)系,關(guān)系是平級(jí)的,如果直接把task放到DataRequest`中,體現(xiàn)的是一種歸屬關(guān)系。
(2)降低耦合性。任務(wù)分層,架構(gòu)思路更清晰

2.delegate[task] = request,綁定taskrequest , 方便SessionDelegate下發(fā)任務(wù),用task直接檢索到request。
delegate[task] = request我們?cè)?code>OC中看到這種寫(xiě)法都是數(shù)組或者字典,而這里的delegate是一個(gè)類。這是Swift的下標(biāo)法。我們看看實(shí)現(xiàn):

  var requests: [Int: Request] = [:]
   private let lock = NSLock()
 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
        }
    }

類可以重寫(xiě)下標(biāo),支持get,get方法。這種方式SessionDelegate不直接持有Request可以降低耦合。

3.if startRequestsImmediately { request.resume() },滿足startRequestsImmediately,就會(huì)發(fā)起請(qǐng)求。 open var startRequestsImmediately: Bool = true,startRequestsImmediately默認(rèn)會(huì)true。為什么要這么設(shè)計(jì)?而不直接調(diào)用request.resume()。這里是為了讓用戶可以通過(guò)在外界對(duì)startRequestsImmediately控制,而達(dá)到控制請(qǐng)求發(fā)不發(fā)起。

4.Request有許多不同的子類實(shí)現(xiàn)。DataRequest,DownloadRequest,UploadRequest,StreamRequest。這里是任務(wù)細(xì)分。

func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
            do {
                let urlRequest = try self.urlRequest.adapt(using: adapter)
                return queue.sync { session.dataTask(with: urlRequest) }
            } catch {
                throw AdaptError(error: error)
            }
        }

DataRequest初始化:調(diào)用父類Requestinit方法

 init(session: URLSession, requestTask: RequestTask, error: Error? = nil) {
        self.session = session

        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
        delegate.queue.addOperation { self.endTime = CFAbsoluteTimeGetCurrent() }
    }

1.使用枚舉requestTask標(biāo)識(shí)不同的類型。是工廠設(shè)計(jì)模式,根據(jù)用戶傳進(jìn)來(lái)的枚舉標(biāo)識(shí),走不同的初始化。
2.保存相關(guān)信息originalTask(Requestable類型)
requestTask有如下幾種:

 enum RequestTask {
        case data(TaskConvertible?, URLSessionTask?)
        case download(TaskConvertible?, URLSessionTask?)
        case upload(TaskConvertible?, URLSessionTask?)
        case stream(TaskConvertible?, URLSessionTask?)
    }

這里的根據(jù)枚舉類型的不同,會(huì)初始化不同的代理,DataTaskDelegate,DownloadTaskDelegate,UploadTaskDelegate,TaskDelegate。疑問(wèn):為什么已經(jīng)有SessionDelegate,還要有這些代理?這些代理與SessionDelegate是什么關(guān)系?

上面的請(qǐng)求流程我們有提到:
1.SessionDelegatesession的代理,那么所有的代理方法都會(huì)首先來(lái)到SessionDelegate
2.SessionDelegate下發(fā)響應(yīng)到具體的任務(wù)實(shí)現(xiàn)者SessionDelegate,其實(shí)就是整體局部關(guān)系.
3.所有的響應(yīng)都會(huì)來(lái)到SessionDelegate,如果SessionDelegate將所有的響應(yīng)都自己處理,那將會(huì)很復(fù)雜,混亂.根據(jù)不同的需求將具體實(shí)現(xiàn)下發(fā)到對(duì)應(yīng)的代理中處理,耦合性大大降低,架構(gòu)的分層更加明顯。

如上述請(qǐng)求過(guò)程中提到的:

   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可以快速方便的通過(guò)dataTask 獲取到request。然后拿到具體的delegate,如DataTaskDelegate。然后下發(fā)具體的任務(wù),至于DataTaskDelegate中做了什么SessionDelegate根本就不需要知道。

總結(jié):


999.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容