上一篇我们主要说了一下
Alamofire
中对一个普通的DataRequest
的响应序列化的步骤。这次我们针对Requset
的一个过程简要分析下
首先我们看下一个普通的请求的流程经过了哪些步骤
之前分析过Alamofire
中的静态方法都是调用SessionManager
里面的方法,SessionManager
里的default
存放着默认的session
,而SessionDelegate
则实现了session
的代理。
private func commonInit(serverTrustPolicyManager: ServerTrustPolicyManager?) {
session.serverTrustPolicyManager = serverTrustPolicyManager
///这里设置了代理
delegate.sessionManager = self
delegate.sessionDidFinishEventsForBackgroundURLSession = { [weak self] session in
guard let strongSelf = self else { return }
DispatchQueue.main.async { strongSelf.backgroundCompletionHandler?() }
}
}
再来看看SessionManager
里面
首先声明了许多闭包,如果你想自己定义你的接收响应的逻辑你可以实现这些闭包
// MARK: URLSessionDelegate Overrides
/// Overrides default behavior for URLSessionDelegate method `urlSession(_:didBecomeInvalidWithError:)`.
open var sessionDidBecomeInvalidWithError: ((URLSession, Error?) -> Void)?
/// Overrides default behavior for URLSessionDelegate method `urlSession(_:didReceive:completionHandler:)`.
open var sessionDidReceiveChallenge: ((URLSession, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?
再来看看里面代理方法的实现
/// Tells the delegate that the data task has received some of the expected data.
///
/// - parameter session: The session containing the data task that provided data.
/// - parameter dataTask: The data task that provided data.
/// - parameter data: A data object containing the transferred data.
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 {
///否则通过保存的[task:request]来获取到request,调用其taskDelegate实例对象的方法,将数据传递给了TaskDelegate
delegate.urlSession(session, dataTask: dataTask, didReceive: data)
}
}
可以看到将数据传递到了TaskDelegate
里,再来看下TaskDelegate
是怎么处理的
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 {
///如果实现了request的stream方法,则这里将数据通过闭包传递
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) }
}
}
}
我们可以看到,针对一个普通的Data
请求的话,这里的data
就保存了返回的响应数据。
override var data: Data? {
if dataStream != nil {
return nil
} else {
return mutableData
}
}
所以后面在序列化返回数据的时候我们可以看到
这里的data就是获取的request.delegate.data
var dataResponse = DataResponse<T.SerializedObject>(
request: self.request,
response: self.response,
data: self.delegate.data,
result: result,
timeline: self.timeline
)
总的来说,一次普通的请求流程是
- 获取
URLRequest
- 获取
URLSessionTask
-
SessionDelegate
接受到代理回调传递给TaskDelegate,TaskDelegate
进行处理并保存在自己的属性里
分步骤详细解析
我们还是从最基础的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)
}
}
这里通过传递一个URLRequestConvertible
类型的参数来开始一个请求
- 通过
URLRequestConvertible
获取URLRequest
URLRequestConvertible
是一个协议,只有一个asURLRequest
的方法,所以我们知道,你可以传任意类型哪怕是一个Pig
的Class
类,只要实现这个协议方法能够返回URLRequest
即可,从这里我们可以看到但凡是后缀是Convertible
的协议都是类似于这样的便利方法,就像我们前面分析过的URLConvertible
一样
-
通过
TaskConvertible
获取到URLSessionTask
且对urlRequest
进行自定义加工-
通过
TaskConvertible
获取到URLSessionTask
let originalTask = DataRequest.Requestable(urlRequest: originalRequest!)
这里的
originalTask
类型其实是DataRequest
的结构体Requestable
的一个实例初始化需要传入urlRequest
,而这个结构体遵守了协议TaskConvertible
,所以我们知道这里可以获取到URLSessionTask
-
对
urlRequest
进行自定义加工在协议
TaskConvertible
的方法声明我们看到传递了一个RequestAdapter
的参数,且在结构体Requestable
的协议实现内我们可以看到这行let urlRequest = try self.urlRequest.adapt(using: adapter)
点击这里
self.urlRequest
的adapt
方法func adapt(using adapter: RequestAdapter?) throws -> URLRequest { guard let adapter = adapter else { return self } return try adapter.adapt(self) }
是不是绕晕了???哈哈,只能说作者封装的太仔细了
其实这里的
RequestAdapter
协议就是专门对URLRequest
加工的一个协议,从它的协议声明方法可以看出。 -
返回
URLSessionTask
许多人可能对最后的返回觉得很奇怪
return queue.sync { session.dataTask(with: urlRequest) }
这里返回的是
URLSessionTask
么??点击这个sync方法你就可以看到了public func sync<T>(execute work: () throws -> T) rethrows -> T
最后返回的是一个泛型函数,而这个泛型函数返回的结果就是函数参数
() throws -> T
返回的值,而session.dataTask(with: urlRequest)
返回的是URLSessionTask
-
-
对
request
进行包装let request = DataRequest(session: session, requestTask: .data(originalTask, task))
这里调用的是
DataRequest
父类Request
的init
方法,其第三个参数requestTask
是一个枚举类型,用来对request
进行分类并关联值的,在这个init
方法里面我们可以看到case .data(let originalTask, let task): taskDelegate = DataTaskDelegate(task: task) self.originalTask = originalTask
这里用我们之前获取到的
sessionTask
来初始化DataTaskDelegate
,而之前我们说了TaskDelegate
以及其子类是我们最终接收Session
响应的地方。 -
一个
request
请求的开始、暂停、结束通过上面我们能看到初始化了一个
DataTaskDelegate
对象,我们看初始化方法init(task: URLSessionTask?) { _task = task self.queue = { let operationQueue = OperationQueue() operationQueue.maxConcurrentOperationCount = 1 operationQueue.isSuspended = true operationQueue.qualityOfService = .utility return operationQueue }() }
一个
request
对应着一个TaskDelegate
对象,我们再来看Request
中的resume()
、suspend()
、cancel()
方法本质上都是控制taskDelegate
的task
对象。 -
TaskDelegate
的queue
前面我们可以看到通过
Request
获得URLSessionTask
,然后执行任务。而这些任务是在SessionManager
的一个queue
这个队列里同步进行的。这个queue
是初始化SessionManager
时候用uuid
生成的一个不会重复的队列let queue = DispatchQueue(label: "org.alamofire.session-manager." + UUID().uuidString)
所以所有的网络请求,如果我们用的是同一个
session
的话,那么这些请求虽然是在后台执行的,但是是同步的。所以有时同时处理多个请求时,我们可以采用多个session
。而
TaskDelegate
里也有一个queue
,/// The serial operation queue used to execute all operations after the task completes. open let queue: OperationQueue
这是OperationQueue类型(不熟悉的可以先了解下),从注释来看这是当任务执行完成后才会开始执行任务的一条队列。
在此文件的
didCompleteWithError
方法里我们可以看到queue.isSuspended = false
,所以这是当URLSessionTask
完成之后,此queue
才开始执行,且此队列添加的第一个任务就是在Request的初始化方法里的delegate.queue.addOperation { self.endTime = CFAbsoluteTimeGetCurrent() }
结合之前在
didCompleteWithError
启动队列,所以第一个任务就是获取该请求的结束时间,后面的响应处理在前面的一篇文章中可以看到。
单纯的技术分析,也没啥图。如果这都能从头看到尾的话,我佩服你。哈哈。
这次简单通过一个
DataRequest
来分析了下请求的一个过程,后面还会写一篇文章来分析下别的类型的Request
的设计思路和我对于Alamofire
的一篇最终总结吧。希望能学习到作者的代码设计风格。
最终
太轻易得到的往往都不会去珍惜,真正失去了却又后悔莫及。
联系方式
- GitHub: 大猫传说中的gitHud地址
- 邮箱: 1030472953@qq.com