Alamofire.request
request 函数签名
request 函数实现
SessionManager.default
SessionManager.default.request
之前我们写的 Alamofire.request
最终就是调用到这里来了, 现在终于可以看看这里到底做了些什么了
open func request(
_ url: URLConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoding: ParameterEncoding = URLEncoding.default,
headers: HTTPHeaders? = nil)
-> DataRequest
{
var originalRequest: URLRequest?
do {
// 根据 url , header 生成请求
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)
}
}
可以看到, 这里调用了我们之前介绍过的 ParameterEncoding
中的 encoding
方法编码参数.
这里利用 url 等相关信息, 生成了一个 URLRequest 对象, 再利用它去请求数据
另一个 request 函数
open func request(_ urlRequest: URLRequestConvertible) -> DataRequest {
...
}
抛开函数体先不谈, 这个函数的参数跟 URLConvertible
也是一样, 是一个协议. 想必你已经猜出来了. 这个协议一定是为了将对象转换成URLRequest
接下来, 看看函数体
敲黑板, 重点来了
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)
}
}
这里出现了很多新的类型, 先一步一步分析
originalRequest = try urlRequest.asURLRequest()
这一步, 只是为了获取 URLRequest
对象而已, 当然, 有可能会出错, 所以有 try 语句.
接下来这一句
let originalTask = DataRequest.Requestable(urlRequest: originalRequest!)
Requestable
是 DataRequest
中的的一个内部类型, DataRequest
我们先放一放, 先看看Requestable
Requestable
这个类型是一个结构体, 代码很少, 也很简单
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)
}
}
}
这个结构体实现了一个 TaskConvertible
协议, 想必你也猜出来了, 这个协议有一个生成 URLSessionTask
的方法.
结构体中, 除了那个方法之外, 还有一个 urlRequest
属性, 用于保存对应的 URLRequest
, 之前的调用的构造函数也是为了初始化这个属性.
task
函数中, 除了必要的 session
参数之外, 还有一个 RequestAdapter
和 DispatchQueue
RequestAdapter
可以在创建URLSessionTask 之前, 修改URLRequest
对象, 这是一个很有用的东西, 你可以拿这个做很多事情, 比如, 加上一个 token, 或是一个授权码等等.
RequestAdapter
本身是一个协议, 只有一个方法, 实现起来也超级容易
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 方法了, 我们需要自己创建一个 sessionManager
let sessionManager = Alamofire.SessionManager.default
// 设置 adapter
sessionManager.adapter = TokenAdapter()
// 使用这个 sessionManager 发起请求
sessionManager.request("https://httpbin.org/get").responseString { (response) in
if let string = response.result.value {
print("alamofire with adapter", string)
}
}
而另一个参数, queue, 这里调用方式是, queue.sync
同步执行. 在这里主要作用是保证同一时间只会同时创建一个 URLSessionTask
, queue 本身也是一个串行的队列
可以在 SessionManager 里面看到定义
open class SessionManager {
...
let queue = DispatchQueue(label: "org.alamofire.session-manager." + UUID().uuidString)
...
}
继续回到 reqeust 函数中, 下一句
let task = try originalTask.task(session: session, adapter: adapter, queue: queue)
这一句, 也就是生成 URLSessionTask, 跟我们写原声代码的这一句是一样的.
let dataTask = session.dataTask(with: URL(string: "https://httpbin.org/get")!)
接下来继续看下一句代码
let request = DataRequest(session: session, requestTask: .data(originalTask, task))
这里使用了一个 DataRequest
类型.
DataRequest
DataRequest
负责发送请求, 接收响应, 并在内部管理着 URLSessionTask
DataRequest
继承自 Request
, 除了这一个子类之外, 常用的还有下载用的请求 DownloadRequest
, 上传请求 UploadRequest
.
父类中有一个内部类型RequestTask
用于区别这几种不同的请求
enum RequestTask {
case data(TaskConvertible?, URLSessionTask?)
case download(TaskConvertible?, URLSessionTask?)
case upload(TaskConvertible?, URLSessionTask?)
case stream(TaskConvertible?, URLSessionTask?)
}
而上面调用的构造函数中, 第二个参数就是这个枚举
.data(originalTask, task)
则表示我们要初始化的是一个数据类型的请求, 来看看这个构造函数
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() }
}
可以大致看出来, 首先是根据请求的类型以及 task
参数(URLSessionTask
类型) , 生成了一个 DataTaskDelegate
,
并将originalTask
(TaskConvertible
类型) 保存起来了, 这个是为了后面如果需要, 例如网络错误, 需要重试, 可以重新生成一个 URLSessionTask
接下来, 如果有错误, 将错误保存在其中, 并添加了一个操作.
看起来有点复杂, 我们一点一点分解.
两个代理对象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设置Delegate
TaskDelegate
类型主要作用之一就是管理与 URLSessionTask
关联的回调.
看到这里, 你可能会感到疑惑, URLSessionTask
并不能设置回调, 唯一获取事件回调的地方只有一个, 就是我们最初设置的 SessionDelegate()
. 但是 SessionDelegate
内部将与 URLSessionTask
关联的任务又重新分发出来了, 所以, 这里的 TaskDelegate
才能接收到事件
SessionDelegate
SessionDelegate
中实现了所有URLSessionDelegate
及子协议的方法, 如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
这个回调为例.
为了能够让 TaskDelegate 能够接受到事件. 我们需要做以下几件事
- 创建一个
SessionDelegate
用于接收所有事件 - 将请求的
URlSessionTask
与TaskDelegate
以某种方式绑定起来 - 产生事件后, 通过与事件关联的
URlSessionTask
找到TaskDelegate
并执行对应的函数
第一步我们已经在最开始创建 URLSession
的时候做了.
第二步, 由于我们这里的 TaskDelegate
都是与 Request
类一一对应, 所以, 我们在 SessionDelegate
中通过下标写入的方式就可以把 URlSessionTask
与 TaskDelegate
绑定起来. 如以下代码
someSessionDelegate[someUrlSessionTask] = SomeRequest
第三步, 我们可以在 SessionDelegate
中扩展URLSessionDataDelegate
部分看到.
如果用户没有手动的去实现SessionDelegate
的对应属性, 那么就会自动去找对应的Request
, 然后获取内部的 TaskDelegate
, 最后, 调用 TaskDelegate
中对应的方法.
TaskDelegate
除了接收事件外, 还有一个很大的功能. 可以在任务完成之后, 完成一些任务. 我们继续回到Request
类的构造函数中, 有一句这样一句代码
delegate.queue.addOperation { self.endTime = CFAbsoluteTimeGetCurrent() }
这句代码就会在任务请求结束之后执行, 并将请求结束的实际记录下来.
我们来看看 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
在构造的时候, 内部除了管理
URLSessionTask
之外 还维护了一个任务队列, 一开始是暂停的, 当请求结束时, 就会恢复. 这样也就可以在请求执行完毕时, 执行某些逻辑. 除这里的记录时间外, 还可以做很多事情, 例如将返回结果转换为一个字符串.
现在我们对 DataRequest
的构造过程(其实是父类Request
) 有一个理解.
接下来我们继续回到 之前的 request
函数中, 创建好 DataRequest
后, 我们就需要将其与 SessionDelegate
绑定起来
// 有点晕了?这里的 delegate 是 SessionDelegte 类型的
delegate[task] = request
接下来, 我们就开始发起请求了
if startRequestsImmediately { request.resume() }
由于 startRequestsImmediately
的默认值是 true
open class SessionManager {
...
open var startRequestsImmediately: Bool = true
...
}
所以我们这里就立即开始发起请求了.
发起请求 request.resume()
终于, 我们将请求发送出去了, 这里调用的依然是 Request
类中的方法, 我们看一下实现
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
, 记录开始时间, 发起请求, 发送通知.
网友评论