Alamofire-Request啓動

一. 首先看一下request用法

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

二. 詳細參數解析

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,針對入參有三種處理方式:

String,轉換成URL後返回 是URL,直接使用 是URLComponents,直接返回swift

//傳入`String `,轉爲`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傳進來的參數
public typealias Parameters = [String: Any]
複製代碼
  1. encoding編碼格式,默認URLEncoding.default,有如下幾種格式:

URLEncoding JSONEncoding PropertyListEncodingmarkdown

  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編碼,經過下面源碼能夠看到,最後都會處理參數query,主要分兩種狀況
  • encodesParametersInURL若是是.get, .head, .delete三種方式,進行百分號編碼,放入到percentEncodedQuery
  • 其餘的請求方式,設置header,而後將參數拼接到請求體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 遍歷參數
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對參數進行遞歸,進行編碼處理後,以元組形式保存在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: "&") 將參數之間插入&符號

2. request內部邏輯解剖:

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 建立Task

let originalTask = DataRequest.Requestable(urlRequest: originalRequest!) 藉助DataRequest內部的結構體Requestable建立Tasksession

經過urlRequest初始化Requestable 而後再用originalTask建立Task 返回queue.sync { session.dataTask(with: urlRequest) }app

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 建立request:

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

  • 調用DataRequest的父類Request的初始化方法.經過傳入枚舉的方式,初始化參數同時保存信息,此時傳入的是.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,調用super.init(task: task)
override init(task: URLSessionTask?) {
    mutableData = Data()
    progress = Progress(totalUnitCount: 0)
    super.init(task: task)
}
複製代碼
  • 調用父類初始化方法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啓動流程,經過上面流程分析,可知:post

SessionDelegate是總的任務管理者,具體執行的時候,經過不一樣的request如:DataRequest,DownloadRequest,UploadRequest等去處理,實現解耦的目的。編碼

相關文章
相關標籤/搜索