1. 程式人生 > IOS開發 >Alamofire-Request啟動

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,直接返回

//傳入`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 PropertyListEncoding

  1. headers請求頭資訊,預設nil
public typealias HTTPHeaders = [String: String]
複製程式碼
  1. 返回DataRequest

三. 原始碼分析

1. 編碼

  • 首先初始化一個originalRequest
originalRequest = try URLRequest(url: url,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建立Task

通過urlRequest初始化Requestable 然後再用originalTask建立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 建立request:

let request = DataRequest(session: session,task))

  • 呼叫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啟動流程,通過上面流程分析,可知:

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