Alamofire-从一个简单的请求深入源代码(3)

2017-10-12  本文已影响48人  yww

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!)

RequestableDataRequest 中的的一个内部类型, 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 参数之外, 还有一个 RequestAdapterDispatchQueue
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 能够接受到事件. 我们需要做以下几件事

  1. 创建一个 SessionDelegate 用于接收所有事件
  2. 将请求的 URlSessionTaskTaskDelegate 以某种方式绑定起来
  3. 产生事件后, 通过与事件关联的 URlSessionTask 找到 TaskDelegate并执行对应的函数

第一步我们已经在最开始创建 URLSession 的时候做了.
第二步, 由于我们这里的 TaskDelegate 都是与 Request类一一对应, 所以, 我们在 SessionDelegate 中通过下标写入的方式就可以把 URlSessionTaskTaskDelegate 绑定起来. 如以下代码

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, 记录开始时间, 发起请求, 发送通知.

上一篇下一篇

猜你喜欢

热点阅读