先上代碼,再來看我們需要了解的是啥.
- vc:
import UIKit
import Alamofire
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
request("https://www.baidu.com", method: .get, parameters: ["username":"lb", "password":"123456"]).response { (response) in
print("response === \(response)")
}
}
}
- 這里直接寫
request
還是寫SessionManager.default.request
或是Alamofire.request
都是一樣的, 都來到SessionManager.default
的request
方法里.- 該方法除了
url
是必傳,其他都有默認值,可以不傳.
@discardableResult
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)
}
}
該方法我們一步一步來分析.
一 : 參數(shù)encode準備工作
① : 先創(chuàng)建了一個原生URLRequest. 這里沒啥好說的.
② : 對參數(shù) encoding
進行 encode
.
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
}
- 先判斷 method 是不是
.get, .head, .delete
(具體實現(xiàn)可以點擊encodesParametersInURL
進去查看)- 是
.get, .head, .delete
則把參數(shù)直接拼接到 URL 后面.- 其他方式則把參數(shù)放到
httpBody
中編碼.
具體的參數(shù)拼接方式不具體講述了, 總結(jié)一下:
1 : 由于請求是通過 ASCII編碼的,所以要進行百分號編碼植旧,第一步就是對當前請求的所有路由百分號編碼.
2 : 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
把key
和value
取出 , 進行參數(shù)遞歸,直到基礎(chǔ)數(shù)據(jù)類型,如bool
,string
热康,然后進行了百分號編碼, 之后添加到元祖返回胁后。- 拿到
queryComponents
返回的元祖數(shù)據(jù),添加到數(shù)組中.- 將保存了元祖的數(shù)組映射 ,元祖中數(shù)據(jù)變成
$1 = $2
形式 , 然后新生成的數(shù)組按&
拼接成字符串.
二 : 開始請求 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)
}
}
1. 首先 DataRequest.Requestable 方法
DataRequest.Requestable(urlRequest: originalRequest!)
Requestable
點進去
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)
}
}
}
- 創(chuàng)建了一個結(jié)構(gòu)體.并且初始化時就持有了
urlRequest
2. originalTask.task
調(diào)用該結(jié)構(gòu)體的 task
方法,同步創(chuàng)建了一個 URLSessionTask
. ( 源碼在上面結(jié)構(gòu)體源碼中.)
3. DataRequest(session: , requestTask: )
創(chuàng)建一個 DataRequest
, 自己沒有 init
方法.查找其父類 Request
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() }
}
這里根據(jù)不同的
requestTask
生成了不同的子類TaskDelegate
.
DataTaskDelegate
/DownloadTaskDelegate
等等. 其實就是實際去干活的人. 它們與SessionDelegate
也就是整體與局部的關(guān)系.
4. delegate[task] = request
這個
delegate
是我們的SessionDelegate
.
綁定task
和request
, 方便在SessionDelegate
下發(fā)任務(wù)挎袜,task
直接檢索骑素, 方便直接獲取request
.
實現(xiàn)方式點進原碼找到如下:
var requests: [Int: Request] = [:]
private let lock = NSLock()
/// Access the task delegate for the specified task in a thread-safe manner.
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
}
}
通過一個 requests: [Int: Request]
屬性和開放出去的 subscript
下標查找方法以此實現(xiàn) task
和 request
的綁定 以及 delegate[task]
的 set
和 get
的實現(xiàn).
5. request.resume()
直接 request.resume()
啟動任務(wù).
下篇博客會繼續(xù)探討 request
層級結(jié)構(gòu)和具體流程.之后會統(tǒng)一進行總結(jié).