經過上一篇內容學習了關於Request的基本內容,SessionManager
管理Request
和SessionDelegate
的建立,並經過task
綁定Request
;Request管理請求的參數的配置編碼,建立task
和TaskDelegate
方法,而後SessionDelegate
經過task
將任務分發給 TaskDelegate
,TaskDelegate
代理執行任務的具體內容。下面對於不夠完善的地方再來作一丟丟補充🧠。json
讓咱們把視線再拉回到上一篇中的SessionManager.swift
的request
方法: swift
來看👀,這裏在建立task
的時候傳入了一個adapter
參數,那麼這個adapter
是幹嗎的?🤔api
adapt
方法,並且若是繼續跟進去
adapt
方法,徹底看不到
adapt
方法的具體實現,(偷個懶,就不截圖了😌😌😌)那麼既然這是一個協議,是否是須要用戶去實現呢?而且這個方法會放回一個
URLRequest
,從上面的
request
方法方法中已經知道存在了
URLRequest
,那麼這裏爲甚麼還會返回呢?
其實也不難猜,既然是協議,並且adapt
方法,傳入一個urlRequest
,最後又返回URLRequest
,那麼必然能夠在URLRequest
設置參數,好比:Token
,那麼下面就重寫這個adapt
方法;bash
class ZHAdapter: RequestAdapter{
func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
var request = urlRequest
request.setValue("XZXQWYEHNSDXXSCJHSJDSDSJD=", forHTTPHeaderField: "Token")
request.setValue("iPhone", forHTTPHeaderField: "DeviceModel")
return request
}
}
複製代碼
寫個例子🌰試一下:服務器
let urlStr = "https://www.douban.com/j/app/radio/channels"
let url = URL.init(string: urlStr)!
Alamofire.SessionManager.default.adapter = ZHAdapter()
Alamofire.request(url,method: .post,parameters: ["Username":"Henry","Age":"18"]).responseJSON {
(response) in
switch response.result{
case .success(let json):
print("json:\(json)")
break
case .failure(let error):
print("error:\(error)")
break
}
}
複製代碼
OK🙆♂️,搞定了。網絡
其實RequestAdapter
這個協議還有另一個用法:重定向,直接返回一個新地址。session
class ZHAdapter: RequestAdapter{
func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
let newURLRequest = URLRequest.init(url: URL.init(string: "https://www.douban.com/j/app/radio/channels")!)
return newURLRequest
}
}
複製代碼
總結🗣🗣🗣:閉包
首先實現 RequestAdapter
協議的 adapt
方法 並對傳入的 urlRequest
進行處理,好比配置 token
等參數, 或者對urlRequest
重定向,換一個新的 request
請求. 可是最重要的是必定要配置: Alamofire.SessionManager.default.adapter = ZHAdapter()app
在進行網絡請求時,通常狀況下,服務器會返回不一樣的狀態碼,而後拿到狀態碼來進行相應的任務,好比須要將某一結果404
定義爲錯誤請求,那麼就要在error
中來作處理,此時咱們可使用validate
來從新驗證,並定義請求結果。dom
let urlStr = "https://www.douban.com/j/app/radio/channels"
let url = URL.init(string: urlStr)!
Alamofire.request(url,method: .post,parameters: ["Username":"Henry","Age":"18"]).responseJSON {
(response) in
switch response.result{
case .success(let json):
print("json:\(json)")
break
case .failure(let error):
print("error:\(error)")
break
}
}.validate{ (request, response, data) -> Request.ValidationResult in
print(response)
guard let _ = data else {
return .failure(NSError(domain: "你總說,是個人錯", code: 10000, userInfo: nil))
}
let code = response.statusCode
if (code == 404 ){
return .failure(NSError(domain: "錯錯錯,說個人錯,", code: 10010, userInfo: nil))
}
return .success
}
複製代碼
ok🙆♂️,再次搞定在這裏經過鏈式方法調用validate
驗證方法,而後在閉包內部自定義驗證方式,而後根據不一樣的狀態碼來作相應的自定義處理。
當SessionDelegate
完成請求的時候,可是請求失敗的時候,會調用urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?)
方法,來看一下在這個方法裏retrier
作了什麼處理
if let retrier = retrier, let error = error {
retrier.should(sessionManager, retry: request, with: error) { [weak self] shouldRetry, timeDelay in
guard shouldRetry else { completeTask(session, task, error) ; return }
DispatchQueue.utility.after(timeDelay) { [weak self] in
guard let strongSelf = self else { return }
let retrySucceeded = strongSelf.sessionManager?.retry(request) ?? false
if retrySucceeded, let task = request.task {
strongSelf[task] = request
return
} else {
completeTask(session, task, error)
}
}
}
}
複製代碼
這裏會先判斷有沒有retrier
,若是有就調用should
方法,若是沒有就直接調用完成回調。經過源碼會發現retrier
是繼承於RequestRetrier
協議的類對象(與RequestAdapter
相似)一樣須要本身來實現:
extension ZHRetrier: RequestRetrier{
func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion) {
completion(true,1)
//這裏不能讓它一直從新請求,須要有結束方法.
completion(false,0)
}
}
複製代碼
這裏should
方法傳入四個參數,前三個參數很簡單,重點介紹⚔一下completion
,completion
有兩個參數shouldRetry
爲是否請求,timeDelay
爲延時請求的延時時間,因此在上面的代碼中寫告終束再次請求的方法completion(false,0)
.
let urlStr = "https://www.douban.com/j/app/radio/channels"
let url = URL.init(string: urlStr)!
Alamofire.SessionManager.default.retrier = ZHRetrier()
Alamofire.request(url,method: .post,parameters: ["Username":"Henry","Age":"18"]).responseJSON {
(response) in
switch response.result{
case .success(let json):
print("json:\(json)")
break
case .failure(let error):
print("error:\(error)")
break
}
}.validate{ (request, response, data) -> Request.ValidationResult in
print(response)
guard let _ = data else {
return .failure(NSError(domain: "你總說,是個人錯", code: 10000, userInfo: nil))
}
let code = response.statusCode
if (code == 404 ){
return .failure(NSError(domain: "錯錯錯,說個人錯,", code: 10010, userInfo: nil))
}
return .success
}
複製代碼
一樣最重要的是:Alamofire.SessionManager.default.retrier = ZHRetrier();
再次把視線拉回到文章的最開始的那副圖,是否是有這句代碼if startRequestsImmediately { request.resume() }
你會發現這裏是request.resume()
,然而正常狀況下不該該是task.resume()
嗎🙅♀️,由此可知,在這裏的request.resume()
方法內部必然保存了task.resume()
方法。跟進去看下:
resume()
方法並無傳入參數,那麼必然會走到
else
中去,
delegate.queue.isSuspended = false ;
若是沒有任務,隊列暫停掛起?
你這怕不是在逗我,搞得我好像不太聰明的亞子??????
有源碼可知當前這個delegate
是TaskDelegate
,進入到TaskDelegate.swift
源碼能夠發現queue
是OperationQueue
,而且在TaskDelegate
的init
方法中實現了初始化。
queue
做爲
TaskDelegate
的一個屬性,在初始化時成爲一個同步隊列,而且隊列是掛起的。 也就是說在發起
request
以後,建立的
TaskDelegate
會默認初始化一個隊列,而且把隊列掛起。
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if let taskDidCompleteWithError = taskDidCompleteWithError {
taskDidCompleteWithError(session, task, error)
} else {
//省略部分代碼
queue.isSuspended = false
}
}
複製代碼
在這裏隊列就取消掛起了,這也就說明了加入到這個隊列中的任務都是在請求完成以後的。
OK🙆,下面帶着這個queue
來看一下Timeline
的具體實現:
resume
方法中,會給
startTime
賦值當前時間戳,這就是網絡請求的發起時間
DataRequest
的
init
方法中,會向
queue
隊列中添加一個任務,獲取當前的時間戳賦值給
endTime
,這就是網絡請求結束時間。
可是由於此時當前隊列默認爲掛起狀態,因此不會執行裏面的任務。在網絡請求完成回調 didCompleteWithError
方法時會恢復 queue
隊列queue.isSuspended = false
,而後緊接着完成endTime
賦值。
initialResponseTime
爲當前時間戳,這個時間就是初始化響應的時間。
ResponseSerialization.swift
的
Response
方法中,會向
queue
隊列中添加一個任務,由於當前未使用自定義的序列化方法,因此直接返回請求回來的數據,而返回的數據中保存着
self.timeline
.
因此在賦值
self.timeline
時,會初始化
Timeline
對象,對前面的時間作個記錄,並將當前時間戳做爲參數
serializationCompletedTime
的值傳遞給
Timeline
對象。 然而這個
serializationCompletedTime
就是序列化結束的時間,同時這個任務也是在隊列恢復時執行。
TimeLine
的初始化方法中,記錄了請求過程當中的操做時間點,並計算了每一個操做的時間間隔,在請求結束後返回至
ResponseSerialization
的
response
方法中。能夠看到整個時間軸
TimeLine
上的操做都是經過同步隊列來保證的,同時也確保了操做時間的準確性。
借用Bo_Bo大佬的總結圖😀😺😁:
關於Request
的Adapter
(適配器),validate
(自定義驗證),retrier
(從新請求),Timeline
(時間軸)內容就學習到這裏了,我的感受仍是比較重要的,爲用戶的封裝使用提供了必定的便利性。