本文通過一個get請求脆淹,追蹤代碼流程凹联,來進入到整個框架的學(xué)習(xí)滤淳。
1.創(chuàng)建請求
Alamofire.request("https://httpbin.org/get")
- 通過Alamofire來創(chuàng)建
@discardableResult
public func request(
_ url: URLConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoding: ParameterEncoding = URLEncoding.default,
headers: HTTPHeaders? = nil)
-> DataRequest
{
return SessionManager.default.request(
url,
method: method,
parameters: parameters,
encoding: encoding,
headers: headers
)
}
- 關(guān)鍵字@discardableResult爪飘,在方法沒有返回值時不會報出警告
- swift參數(shù)是支持默認值的义起,調(diào)用時值傳了一個url參數(shù),繼承URLConvertible協(xié)議师崎,用來生成URL類型
- HTTPMethod是一個枚舉類型默终,這里默認值使用get
- Parameters定義為[String: Any],默認是nil犁罩。它的定義使用typealias關(guān)鍵字齐蔽,用來為已經(jīng)存在的類型重新定義名字的,通過命名床估,可以使代碼變得更加清晰含滴。HTTPHeaders亦是如此
- Alamofire只是一個入口,內(nèi)部調(diào)用SessionManager來完成創(chuàng)建
@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)
}
}
- 聲明originalRequest變量丐巫,類型是URLRequest谈况。Swift里不會自動給變量賦初始值,也就是說變量不會有默認值递胧,所以要求使用變量之前必須要對其初始化碑韵,如果沒有初始化就會報錯。這時候可以使用optional類型缎脾,也就是后面跟一個"祝闻?"。
- 試著創(chuàng)建URLRequest遗菠,這里加try是因為asURL可能會拋出異常
- 然后調(diào)用ParameterEncoding.swift的方法對參數(shù)進行編碼
- 最后進入request方法來生成DataRequest
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)
}
}
- originalTask值是一個結(jié)構(gòu)體联喘,這個結(jié)構(gòu)體繼承TaskConvertible協(xié)議华蜒。該協(xié)議定義了task方法,讓每種不同類型的request實現(xiàn)自己創(chuàng)建task的邏輯豁遭。這里創(chuàng)建NSURLSessionDataTask類型task的實現(xiàn)代碼是session.dataTask(with: urlRequest)
- 用session和.data(originalTask, task)創(chuàng)建DataRequest叭喜。URLRequest(DataRequest的父類)初始化方法會根據(jù)task的不同生成對應(yīng)的taskDelegate
- 把task和request的關(guān)系存到delegate中,調(diào)用request的resume方法發(fā)出請求堤框,內(nèi)部會觸發(fā)task.resume()方法發(fā)請求域滥,最后并返回request
整個過程大致是,調(diào)用SessionManager.request()方法蜈抓,獲取URLRerequest启绰,獲取對應(yīng)的task,創(chuàng)建DataRequest沟使,發(fā)起請求委可。
2.接收請求
if let request = request as? DataRequest {
request.responseString { response in
requestComplete(response.response, response.result)
}
}
- 這里剛開始可能看不太懂,這是尾隨閉包的寫法腊嗡。這個閉包是request.responseString方法的最后一個參數(shù)着倾。
- 這個閉包會被加入到DataRequest的delegate.queue中。TaskDelegate的queue是一個串行隊列燕少,存放task結(jié)束之后需要執(zhí)行的任務(wù)
@discardableResult
public func response<T: DataResponseSerializerProtocol>(
queue: DispatchQueue? = nil,
responseSerializer: T,
completionHandler: @escaping (DataResponse<T.SerializedObject>) -> Void)
-> Self
{
delegate.queue.addOperation {
let result = responseSerializer.serializeResponse(
self.request,
self.response,
self.delegate.data,
self.delegate.error
)
var dataResponse = DataResponse<T.SerializedObject>(
request: self.request,
response: self.response,
data: self.delegate.data,
result: result,
timeline: self.timeline
)
dataResponse.add(self.delegate.metrics)
(queue ?? DispatchQueue.main).async {
completionHandler(dataResponse)
}
}
return self
}
- 最終傳進來的閉包會加入到delegate的queue隊列中卡者,并且這個隊列初始化時設(shè)置了isSuspend=true,所以它會在改為false才會執(zhí)行
- 這個opration里會生成result和response客们,供回調(diào)使用崇决,也就是requestComplete(response.response, response.result)的參數(shù)
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)
}
}
- SessionDelegate繼承URLSessionDataDelegate,實現(xiàn)請求的各種回調(diào)
- 下面的判斷會走到else底挫,前面在delegate存儲過task和delegate恒傻,所以這里self[dataTask]會得到之前發(fā)請求的request,并且是DataTaskDelegate類型
- 于是執(zhí)行delegate(DataTaskDelegate)的對應(yīng)方法
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() }
if let dataTaskDidReceiveData = dataTaskDidReceiveData {
dataTaskDidReceiveData(session, dataTask, data)
} else {
if let dataStream = dataStream {
dataStream(data)
} else {
mutableData.append(data)
}
let bytesReceived = Int64(data.count)
totalBytesReceived += bytesReceived
let totalBytesExpected = dataTask.response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown
progress.totalUnitCount = totalBytesExpected
progress.completedUnitCount = totalBytesReceived
if let progressHandler = progressHandler {
progressHandler.queue.async { progressHandler.closure(self.progress) }
}
}
}
- dataStream為((_ data: Data) -> Void)?類型建邓,mutableData被初始化為Data()盈厘,它們都是是DataTaskDelegate的實例變量。這里會走到mutableData.append(data)
- bytesReceived計算接收數(shù)據(jù)的大小
- totalBytesExpected將可選類型dataTask.response解包官边,然后獲取expectedContentLength.“??"運算符可以用于判斷變量或常量的數(shù)值是否是nil沸手,不為nil則取變量或者常量本身的值,如果是nil則使用后面的值替代
- 判斷是否有progressHandler,這不會調(diào)用注簿。因為demo中并沒有傳
open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
let completeTask: (URLSession, URLSessionTask, Error?) -> Void = { [weak self] session, task, error in
guard let strongSelf = self else { return }
strongSelf.taskDidComplete?(session, task, error)
strongSelf[task]?.delegate.urlSession(session, task: task, didCompleteWithError: error)
NotificationCenter.default.post(
name: Notification.Name.Task.DidComplete,
object: strongSelf,
userInfo: [Notification.Key.Task: task]
)
strongSelf[task] = nil
}
guard let request = self[task], let sessionManager = sessionManager else {
completeTask(session, task, error)
return
}
request.validations.forEach { $0() }
var error: Error? = error
if request.delegate.error != nil {
error = request.delegate.error
}
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)
}
}
}
} else {
completeTask(session, task, error)
}
}
- SessionDelegate內(nèi)實現(xiàn)的回調(diào)罐氨,表示task已經(jīng)完成數(shù)據(jù)傳輸
- completeTask,如果請求不需要重試滩援,就會執(zhí)行這個閉包
- sessionManager沒有被賦值,所以不會走到第一個判斷里
- validations是[() -> Void] = []一種閉包類型的數(shù)組塔嬉,放一些檢查的方法玩徊。$0()表示第一個參數(shù)租悄,也就是數(shù)組里的每一個值
- 用request.delegate.error取到錯誤,如果設(shè)置了retrier并有error恩袱,就執(zhí)行重試的方法泣棋。否則執(zhí)行completeTask
- 在completeTask中,首選判斷taskDidComplete有無值然后執(zhí)行畔塔,然后通過strongSelf[task]得到request并調(diào)用其urlSession方法
- 最后發(fā)通知并把task置為nil
@objc(URLSession:task:didCompleteWithError:)
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if let taskDidCompleteWithError = taskDidCompleteWithError {
taskDidCompleteWithError(session, task, error)
} else {
if let error = error {
if self.error == nil { self.error = error }
if
let downloadDelegate = self as? DownloadTaskDelegate,
let resumeData = (error as NSError).userInfo[NSURLSessionDownloadTaskResumeData] as? Data
{
downloadDelegate.resumeData = resumeData
}
}
queue.isSuspended = false
}
}
- 上述代碼 strongSelf[task]?.delegate.urlSession(session, task: task, didCompleteWithError: error) 的方法實現(xiàn)
- 這里在有錯誤時潭辈,對DownloadTaskDelegate做了一些處理。
- queue.isSuspended這里是關(guān)鍵澈吨,isSuspended設(shè)置為false后把敢,回調(diào)傳進來operation將會開始執(zhí)行
- @objc 作用:1,fileprivate 或者 private 保證方法私有 能在同一個類 或者 同一個文件(extension)中訪問這個方法 如果定義為private 那么只能在一個類中訪問 不能在類擴展中訪問谅辣;2修赞,允許這個函數(shù)在“運行時”通過oc的消息機制調(diào)用
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
}
}
- 上述代碼strongSelf[task] = nil的實現(xiàn)
- 使用lock,保證賦值是線程安全的
- open關(guān)鍵字桑阶,訪問控制在權(quán)限柏副。在swift3中,fileprivate來顯式的表明蚣录,這個元素的訪問權(quán)限為文件內(nèi)私有割择,即extension也可以訪問到。private則是真正的私有萎河,離開了這個類或者結(jié)構(gòu)體的作用域外面就無法訪問荔泳。open在module內(nèi)可以被override,在被import到其他地方后其他用戶使用的時候不能被override公壤。通過open和public標記區(qū)別一個元素在其他module中是只能被訪問還是可以被override
- defer關(guān)鍵字换可,推遲執(zhí)行,會在return之前執(zhí)行厦幅。記得react cocoa里面oc有寫過一個黑魔法來實現(xiàn)這個方法沾鳄,swift可以直接用關(guān)鍵字解決了
整理一下整個過程,把完成的閉包傳入确憨,然后被加到queue中译荞,請求返回后出發(fā)queue執(zhí)行,最終調(diào)用最初傳入的閉包
這篇文章從demo里的Alamofire.request("https://httpbin.org/get")開始分析了整個發(fā)送和接收請求的過程休弃,有一個大概流程的概念吞歼。后面再去逐個分析每個類