Alamofire
是一個網絡請求的框架,使用起來很是簡單,幾行代碼就能夠實現網絡請求的功能。那麼它內部到底作了些什麼呢?讓咱們不用再寫一些繁瑣的代碼就可以實現一樣的功能。這邊文章就來分析下Request
模塊的具體實現。數組
SessionManager.default.request(urlStr, method: .get, parameters: ["name":"xxx"])
.response { (response) in
print(response)
}
複製代碼
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)
}
}
複製代碼
URLRequest
對象,而後對請求參數進行編碼。Alamofire
支持的編碼格式有三種:
URLEncoding
: URL相關的編碼,有兩種編碼方式:1.直接拼接到URL中,2.經過request的httpBody傳值JSONEncoding
把參數字典編碼成JSONData後賦值給request的httpBodyPropertyListEncoding
把參數字典編碼成PlistData後賦值給request的httpBodyencode
方法查看參數編碼的具體實現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
}
複製代碼
urlRequest
對象,而後經過請求方法判斷參數怎麼傳遞。若是是.get, .head, .delete
就把參數拼接到URL後面,不然放到請求體裏面。ASCII
編碼的,因此須要對參數進行百分號編碼。進入到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
方法。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
}
複製代碼
key
和value
取出,而後進行了百分號編碼。並把編碼後的結果放進元組保存,造成參數對。而後把元組加入到數組中。components.map { "\($0)=\($1)" }.joined(separator: "&")
把數據中的數據經過map
映射成($0)=\($1)
,而後在映射以後的元素之間加入一個分隔符號&
進行拼接。get,head,delete
方法就直接拼接到URL的後面,若是是POST
等方法就是把這些編碼好的參數對放入請求體中。其中還要加入Content-Type
的請求頭。request
方法裏面,調用了request(encodedURLRequest)
這個方法,跟蹤進入。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)
}
}
複製代碼
urlRequest
是URLRequestConvertible
類型的。URLRequestConvertible
協議的目的是對URLRequest
進行自定義的轉換。在得到轉換後的URLRequest
後,須要用URLRequest
生成task
,這樣才能發起網絡請求。DataRequest.Requestable
,Requestable
其實一個結構體,他實現了TaskConvertible
協議,所以,它可以用URLRequest
生成與之相對應的task
。在這裏是經過內部的Requestable
結構體來幫助 DataRequest
建立 Task
,任務分層,架構思路更清晰。task
和 request
, 方便在 SessionDelegate
下發任務,task
方便獲取request
,request
也方便獲取task
。request.resume()
啓動DataRequest
的初始化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() }
}
複製代碼
Request
初始化的時候利用了枚舉的便利性,構造建立,相關信息保存。taskDelegate
屬性,那麼這個taskDelegate
是幹嗎用的呢?前面不是已經經過SessionDelegate
這個類來專門實現了相關代理嗎?爲何這裏還要有一個 DataTaskDelegate
?
SessionDelegate
是全部 Session
的代理遵循者,任何的代理都會來到這裏響應。可是裏面不會實現具體的繁瑣的任務,它會把任務下發到對應任務執行者。DataTaskDelegate
就是咱們具體繁瑣任務執行者,這裏面全部方法都是由 SessionDelegate
下發響應的。SessionDelegate
是事件總響應者,它會把具體的事務交給具體的人去執行。不能由於在 SessionDelegate
可以拿到全部的響應,就把全部的代碼都羅列在這裏,那樣會顯得很臃腫很亂。因此咱們根據不一樣的需求,響應總代理會根據需求的不一樣交給專業的人去作專業的事。耦合性大大下降,架構的分層更加明顯。open func urlSession(
_ session: URLSession,
downloadTask: URLSessionDownloadTask,
didFinishDownloadingTo location: URL)
{
if let downloadTaskDidFinishDownloadingToURL = downloadTaskDidFinishDownloadingToURL {
downloadTaskDidFinishDownloadingToURL(session, downloadTask, location)
} else if let delegate = self[downloadTask]?.delegate as? DownloadTaskDelegate {
delegate.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location)
}
}
複製代碼
task
的一個代理方法,當代理回調時,會先經過 task
找到相應的 Request
,而後直接拿出 Request
的屬性delegate
來處理相關事務,SessionDelegate
只是作一箇中轉的做用,是一個管理者,負責分發任務。從這裏也就知道前面爲何要讓Request
和task
創建一個綁定關係。調用request
發起請求後:bash
Requestable
建立task
。request
並保存了DataTaskDelegate
。request
和task
進行綁定,方便使用。resume
啓動任務。整個流程下來很是的清晰,容易理解,代碼閱讀性高。SessionDelegate
負責全部Session
的響應回調,而後把任務分發給DataTaskDelegate
去具體實現。實現解耦,業務下沉,提升代碼的能夠性。網絡
有問題或者建議和意見,歡迎你們評論或者私信。 喜歡的朋友能夠點下關注和喜歡,後續會持續更新文章。session