Alamofire.request
request 函數(shù)簽名
request 函數(shù)實(shí)現(xiàn)
SessionManager.default
SessionManager.default.request
之前我們寫的 Alamofire.request 最終就是調(diào)用到這里來(lái)了, 現(xiàn)在終于可以看看這里到底做了些什么了
open func request(
_ url: URLConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoding: ParameterEncoding = URLEncoding.default,
headers: HTTPHeaders? = nil)
-> DataRequest
{
var originalRequest: URLRequest?
do {
// 根據(jù) url , header 生成請(qǐng)求
originalRequest = try URLRequest(url: url, method: method, headers: headers)
// 編碼進(jìn)去參數(shù)
let encodedURLRequest = try encoding.encode(originalRequest!, with: parameters)
return request(encodedURLRequest)
} catch {
return request(originalRequest, failedWith: error)
}
}
可以看到, 這里調(diào)用了我們之前介紹過(guò)的 ParameterEncoding 中的 encoding 方法編碼參數(shù).
這里利用 url 等相關(guān)信息, 生成了一個(gè) URLRequest 對(duì)象, 再利用它去請(qǐng)求數(shù)據(jù)
另一個(gè) request 函數(shù)
open func request(_ urlRequest: URLRequestConvertible) -> DataRequest {
...
}
拋開函數(shù)體先不談, 這個(gè)函數(shù)的參數(shù)跟 URLConvertible 也是一樣, 是一個(gè)協(xié)議. 想必你已經(jīng)猜出來(lái)了. 這個(gè)協(xié)議一定是為了將對(duì)象轉(zhuǎn)換成URLRequest
接下來(lái), 看看函數(shù)體
敲黑板, 重點(diǎn)來(lá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)
}
}
這里出現(xiàn)了很多新的類型, 先一步一步分析
originalRequest = try urlRequest.asURLRequest()
這一步, 只是為了獲取 URLRequest 對(duì)象而已, 當(dāng)然, 有可能會(huì)出錯(cuò), 所以有 try 語(yǔ)句.
接下來(lái)這一句
let originalTask = DataRequest.Requestable(urlRequest: originalRequest!)
Requestable 是 DataRequest 中的的一個(gè)內(nèi)部類型, DataRequest 我們先放一放, 先看看Requestable
Requestable
這個(gè)類型是一個(gè)結(jié)構(gòu)體, 代碼很少, 也很簡(jiǎn)單
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)
}
}
}
這個(gè)結(jié)構(gòu)體實(shí)現(xiàn)了一個(gè) TaskConvertible 協(xié)議, 想必你也猜出來(lái)了, 這個(gè)協(xié)議有一個(gè)生成 URLSessionTask 的方法.
結(jié)構(gòu)體中, 除了那個(gè)方法之外, 還有一個(gè) urlRequest 屬性, 用于保存對(duì)應(yīng)的 URLRequest, 之前的調(diào)用的構(gòu)造函數(shù)也是為了初始化這個(gè)屬性.
task 函數(shù)中, 除了必要的 session 參數(shù)之外, 還有一個(gè) RequestAdapter 和 DispatchQueue
RequestAdapter 可以在創(chuàng)建URLSessionTask 之前, 修改URLRequest 對(duì)象, 這是一個(gè)很有用的東西, 你可以拿這個(gè)做很多事情, 比如, 加上一個(gè) token, 或是一個(gè)授權(quán)碼等等.
RequestAdapter 本身是一個(gè)協(xié)議, 只有一個(gè)方法, 實(shí)現(xiàn)起來(lái)也超級(jí)容易
public protocol RequestAdapter {
func adapt(_ urlRequest: URLRequest) throws -> URLRequest
}
如果你自定義了一個(gè) Adapter, 要如何使用呢?
很簡(jiǎn)單, 賦值到 SessionManager 里的 adapter 屬性就好了, 例如
class TokenAdapter: RequestAdapter{
func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
var urlRequest = urlRequest
urlRequest.addValue("some token", forHTTPHeaderField: "ACCESS-TOKEN")
return urlRequest
}
}
...
// 由于要修改 sessionManager, 所以這里就不能直接使用 request 方法了, 我們需要自己創(chuàng)建一個(gè) sessionManager
let sessionManager = Alamofire.SessionManager.default
// 設(shè)置 adapter
sessionManager.adapter = TokenAdapter()
// 使用這個(gè) sessionManager 發(fā)起請(qǐng)求
sessionManager.request("https://httpbin.org/get").responseString { (response) in
if let string = response.result.value {
print("alamofire with adapter", string)
}
}
而另一個(gè)參數(shù), queue, 這里調(diào)用方式是, queue.sync 同步執(zhí)行. 在這里主要作用是保證同一時(shí)間只會(huì)同時(shí)創(chuàng)建一個(gè) URLSessionTask, queue 本身也是一個(gè)串行的隊(duì)列
可以在 SessionManager 里面看到定義
open class SessionManager {
...
let queue = DispatchQueue(label: "org.alamofire.session-manager." + UUID().uuidString)
...
}
繼續(xù)回到 reqeust 函數(shù)中, 下一句
let task = try originalTask.task(session: session, adapter: adapter, queue: queue)
這一句, 也就是生成 URLSessionTask, 跟我們寫原聲代碼的這一句是一樣的.
let dataTask = session.dataTask(with: URL(string: "https://httpbin.org/get")!)
接下來(lái)繼續(xù)看下一句代碼
let request = DataRequest(session: session, requestTask: .data(originalTask, task))
這里使用了一個(gè) DataRequest 類型.
DataRequest
DataRequest 負(fù)責(zé)發(fā)送請(qǐng)求, 接收響應(yīng), 并在內(nèi)部管理著 URLSessionTask
DataRequest 繼承自 Request, 除了這一個(gè)子類之外, 常用的還有下載用的請(qǐng)求 DownloadRequest, 上傳請(qǐng)求 UploadRequest.
父類中有一個(gè)內(nèi)部類型RequestTask 用于區(qū)別這幾種不同的請(qǐng)求
enum RequestTask {
case data(TaskConvertible?, URLSessionTask?)
case download(TaskConvertible?, URLSessionTask?)
case upload(TaskConvertible?, URLSessionTask?)
case stream(TaskConvertible?, URLSessionTask?)
}
而上面調(diào)用的構(gòu)造函數(shù)中, 第二個(gè)參數(shù)就是這個(gè)枚舉
.data(originalTask, task)則表示我們要初始化的是一個(gè)數(shù)據(jù)類型的請(qǐng)求, 來(lái)看看這個(gè)構(gòu)造函數(shù)
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
... 其他類型請(qǐng)求
}
delegate.error = error
delegate.queue.addOperation { self.endTime = CFAbsoluteTimeGetCurrent() }
}
可以大致看出來(lái), 首先是根據(jù)請(qǐng)求的類型以及 task 參數(shù)(URLSessionTask 類型) , 生成了一個(gè) DataTaskDelegate,
并將originalTask(TaskConvertible 類型) 保存起來(lái)了, 這個(gè)是為了后面如果需要, 例如網(wǎng)絡(luò)錯(cuò)誤, 需要重試, 可以重新生成一個(gè) URLSessionTask
接下來(lái), 如果有錯(cuò)誤, 將錯(cuò)誤保存在其中, 并添加了一個(gè)操作.
看起來(lái)有點(diǎn)復(fù)雜, 我們一點(diǎn)一點(diǎn)分解.
兩個(gè)代理對(duì)象taskDelegate 與 delegate
其實(shí)這兩個(gè)都是 SessionManager 里面的屬性, 都是指向的同一個(gè)對(duì)象, 不過(guò) delegate 使用起來(lái)是線程安全的
定義如下
private var taskDelegate: TaskDelegate
private var taskDelegateLock = NSLock()
open internal(set) var delegate: TaskDelegate {
get {
taskDelegateLock.lock() ; defer { taskDelegateLock.unlock() }
return taskDelegate
}
set {
taskDelegateLock.lock() ; defer { taskDelegateLock.unlock() }
taskDelegate = newValue
}
}
DataTask設(shè)置Delegate
TaskDelegate 類型主要作用之一就是管理與 URLSessionTask 關(guān)聯(lián)的回調(diào).
看到這里, 你可能會(huì)感到疑惑, URLSessionTask 并不能設(shè)置回調(diào), 唯一獲取事件回調(diào)的地方只有一個(gè), 就是我們最初設(shè)置的 SessionDelegate(). 但是 SessionDelegate 內(nèi)部將與 URLSessionTask 關(guān)聯(lián)的任務(wù)又重新分發(fā)出來(lái)了, 所以, 這里的 TaskDelegate 才能接收到事件
SessionDelegate
SessionDelegate 中實(shí)現(xiàn)了所有URLSessionDelegate及子協(xié)議的方法, 如URLSessionTaskDelegate, URLSessionDataDelegate, 并且以閉包的形式暴露出來(lái), 我們這里截取部分代碼略微說(shuō)明一下
open class SessionDelegate: NSObject {
open var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)?
weak var sessionManager: SessionManager?
private var requests: [Int: Request] = [:]
private let lock = NSLock()
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
}
}
}
extension SessionDelegate: URLSessionDataDelegate {
open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
if let dataTaskDidReceiveData = dataTaskDidReceiveData {
dataTaskDidReceiveData(session, dataTask, data)
} else if let delegate = self[dataTask]?.delegate as? DataTaskDelegate {
delegate.urlSession(session, dataTask: dataTask, didReceive: data)
}
}
}
我們以dataTaskDidReceiveData 這個(gè)回調(diào)為例.
為了能夠讓 TaskDelegate 能夠接受到事件. 我們需要做以下幾件事
- 創(chuàng)建一個(gè)
SessionDelegate用于接收所有事件 - 將請(qǐng)求的
URlSessionTask與TaskDelegate以某種方式綁定起來(lái) - 產(chǎn)生事件后, 通過(guò)與事件關(guān)聯(lián)的
URlSessionTask找到TaskDelegate并執(zhí)行對(duì)應(yīng)的函數(shù)
第一步我們已經(jīng)在最開始創(chuàng)建 URLSession 的時(shí)候做了.
第二步, 由于我們這里的 TaskDelegate 都是與 Request類一一對(duì)應(yīng), 所以, 我們?cè)?SessionDelegate 中通過(guò)下標(biāo)寫入的方式就可以把 URlSessionTask 與 TaskDelegate 綁定起來(lái). 如以下代碼
someSessionDelegate[someUrlSessionTask] = SomeRequest
第三步, 我們可以在 SessionDelegate 中擴(kuò)展URLSessionDataDelegate部分看到.
如果用戶沒(méi)有手動(dòng)的去實(shí)現(xiàn)SessionDelegate 的對(duì)應(yīng)屬性, 那么就會(huì)自動(dòng)去找對(duì)應(yīng)的Request, 然后獲取內(nèi)部的 TaskDelegate, 最后, 調(diào)用 TaskDelegate 中對(duì)應(yīng)的方法.
TaskDelegate 除了接收事件外, 還有一個(gè)很大的功能. 可以在任務(wù)完成之后, 完成一些任務(wù). 我們繼續(xù)回到Request 類的構(gòu)造函數(shù)中, 有一句這樣一句代碼
delegate.queue.addOperation { self.endTime = CFAbsoluteTimeGetCurrent() }
這句代碼就會(huì)在任務(wù)請(qǐng)求結(jié)束之后執(zhí)行, 并將請(qǐng)求結(jié)束的實(shí)際記錄下來(lái).
我們來(lái)看看 TaskDelegate
TaskDelegate
open class TaskDelegate: NSObject {
open let queue: OperationQueue
private var _task: URLSessionTask? {
didSet { reset() }
}
init(task: URLSessionTask?) {
_task = task
self.queue = {
let operationQueue = OperationQueue()
operationQueue.maxConcurrentOperationCount = 1
operationQueue.isSuspended = true
operationQueue.qualityOfService = .utility
return operationQueue
}()
}
...
}
可以看到, TaskDelegate 在構(gòu)造的時(shí)候, 內(nèi)部除了管理
URLSessionTask 之外 還維護(hù)了一個(gè)任務(wù)隊(duì)列, 一開始是暫停的, 當(dāng)請(qǐng)求結(jié)束時(shí), 就會(huì)恢復(fù). 這樣也就可以在請(qǐng)求執(zhí)行完畢時(shí), 執(zhí)行某些邏輯. 除這里的記錄時(shí)間外, 還可以做很多事情, 例如將返回結(jié)果轉(zhuǎn)換為一個(gè)字符串.
現(xiàn)在我們對(duì) DataRequest 的構(gòu)造過(guò)程(其實(shí)是父類Request) 有一個(gè)理解.
接下來(lái)我們繼續(xù)回到 之前的 request 函數(shù)中, 創(chuàng)建好 DataRequest 后, 我們就需要將其與 SessionDelegate 綁定起來(lái)
// 有點(diǎn)暈了?這里的 delegate 是 SessionDelegte 類型的
delegate[task] = request
接下來(lái), 我們就開始發(fā)起請(qǐng)求了
if startRequestsImmediately { request.resume() }
由于 startRequestsImmediately 的默認(rèn)值是 true
open class SessionManager {
...
open var startRequestsImmediately: Bool = true
...
}
所以我們這里就立即開始發(fā)起請(qǐng)求了.
發(fā)起請(qǐng)求 request.resume()
終于, 我們將請(qǐng)求發(fā)送出去了, 這里調(diào)用的依然是 Request 類中的方法, 我們看一下實(shí)現(xiàn)
open func resume() {
guard let task = task else {
delegate.queue.isSuspended = false
return
}
if startTime == nil {
startTime = CFAbsoluteTimeGetCurrent()
}
task.resume()
NotificationCenter.default.post(
name: Notification.Name.Task.DidResume,
object: self,
userInfo: [Notification.Key.Task: task]
)
}
其實(shí)也就是簡(jiǎn)單的幾步, 首先, 獲取 URLSessionTask, 記錄開始時(shí)間, 發(fā)起請(qǐng)求, 發(fā)送通知.