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