Alamofire-Request啟動

一. 首先看一下request用法

    SessionManager.default.request(urlString, method: .get, parameters: nil, encoding: URLEncoding.default).response { (response) in
       debugPrint(response)
    }

二. 詳細參數(shù)解析

 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. url : URLConvertible,針對入?yún)⒂腥N處理方式:

String,轉(zhuǎn)換成URL后返回
URL,直接使用
URLComponents,直接返回

//傳入`String `,轉(zhuǎn)為`URL`
extension String: URLConvertible {
    public func asURL() throws -> URL {
        guard let url = URL(string: self) else { throw AFError.invalidURL(url: self) }
        return url
    }
}
//如果傳入的是`URL `,直接返回
extension URL: URLConvertible {
    public func asURL() throws -> URL { return self }
}

extension URLComponents: URLConvertible {
    public func asURL() throws -> URL {
        guard let url = url else { throw AFError.invalidURL(url: self) }
        return url
    }
}
  1. method默認是get,支持以下幾種
public enum HTTPMethod: String {
    case options = "OPTIONS"
    case get     = "GET"
    case head    = "HEAD"
    case post    = "POST"
    case put     = "PUT"
    case patch   = "PATCH"
    case delete  = "DELETE"
    case trace   = "TRACE"
    case connect = "CONNECT"
}
  1. parameters傳進來的參數(shù)
public typealias Parameters = [String: Any]
  1. encoding編碼格式,默認URLEncoding.default,有以下幾種格式:

URLEncoding
JSONEncoding
PropertyListEncoding

  1. headers請求頭信息,默認nil
public typealias HTTPHeaders = [String: String]
  1. 返回DataRequest

三. 源碼分析

1. 編碼
  • 首先初始化一個originalRequest
originalRequest = try URLRequest(url: url, method: method, headers: headers)
  • 編碼后返回request(encodedURLRequest)
 let encodedURLRequest = try encoding.encode(originalRequest!, with: parameters)
return request(encodedURLRequest)
  • encode編碼,通過下面源碼可以看到,最后都會處理參數(shù)query,主要分兩種情況
  • encodesParametersInURL如果是.get, .head, .delete三種方式,進行百分號編碼,放入到percentEncodedQuery
  • 其他的請求方式,設(shè)置header,然后將參數(shù)拼接到請求體httpbody
    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
    }
  • query 遍歷參數(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: "&")
    }
  • 通過ASCII排序
  • queryComponents對參數(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
    }
  • components.map { "\($0)=\($1)" }.joined(separator: "&") 將參數(shù)之間插入&符號
2. request內(nèi)部邏輯解剖:
    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)
        }
    }
2.1 創(chuàng)建Task

let originalTask = DataRequest.Requestable(urlRequest: originalRequest!)
借助DataRequest內(nèi)部的結(jié)構(gòu)體Requestable創(chuàng)建Task

通過urlRequest初始化Requestable
然后再用originalTask創(chuàng)建Task
返回queue.sync { session.dataTask(with: urlRequest) }

    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)
                return queue.sync { session.dataTask(with: urlRequest) }
            } catch {
                throw AdaptError(error: error)
            }
        }
    }
2.2 創(chuàng)建request:

let request = DataRequest(session: session, requestTask: .data(originalTask, task))

  • 調(diào)用DataRequest的父類Request的初始化方法.通過傳入枚舉的方式,初始化參數(shù)同時保存信息,此時傳入的是.data(let originalTask, let task)
 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() }
  • 初始化taskDelegate,調(diào)用super.init(task: task)
    override init(task: URLSessionTask?) {
        mutableData = Data()
        progress = Progress(totalUnitCount: 0)
        super.init(task: task)
    }
  • 調(diào)用父類初始化方法init(task: URLSessionTask?)保存_task,初始化隊列
    init(task: URLSessionTask?) {
        _task = task
        self.queue = {
            let operationQueue = OperationQueue()
            operationQueue.maxConcurrentOperationCount = 1
            operationQueue.isSuspended = true
            operationQueue.qualityOfService = .utility
            return operationQueue
        }()
    }
  • 保存self.originalTask = originalTask
2.3 保存request:

delegate[task] = request,將request保存到SessionDelegate中,便于SessionDelegate管理

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
        }
    }
2.4 啟動request.resume()
if startRequestsImmediately { request.resume() }

以上就是request啟動流程,通過上面流程分析,可知:

SessionDelegate是總的任務(wù)管理者,具體執(zhí)行的時候,通過不同的request如:DataRequest,DownloadRequest,UploadRequest等去處理,實現(xiàn)解耦的目的。

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

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