前言
通过上一篇URlSession后台下载,我们已经知道了SessionManager
的作为整个Alamofire
框架的核心枢纽的重要性,还有一个比较重要的是SessionManager
的代理SessionDelegate
,封装了URLSessionDelegate
。负责对Task
的回调事件提供默认实现(转发给TaskDelegate
进行实际处理),并且以闭包的方式暴露给外部,让外部可以自定义实现。TaskDelegate
对URLSessionTask
的回调进行实际的处理,并且执行task
的完成回调用。Request
就 是对URLSessionTask
的封装,是暴露给上层的请求任务。
Alamofire Request
废话不多说直接来用URLSession
创建一个request
和利用Alamofire
创建一个request
的例子看看:
首先来看一个URLSession
的request
的用法:
guard let url = URL(string: "https://www.douban.com/j/app/radio/channels") else {
return;
}
let dataTask = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else{
return;
}
do{
let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
print(json)
}catch let error{
print(error)
}
};
dataTask.resume()
再来看一个基础的Alamofire.request
用法:
Alamofire.request("https://www.douban.com/j/app/radio/channels").responseJSON { (response) in
if let JSONString = response.result.value {
print("JSONString: \(JSONString)")
}
}
有没有觉得,如果使用系统的API
,我们不得不先创建URL
,然后建立dataTask
,并且resume
,接着在callback
里去解析JSON
。由于Swift
是一种强类型的语言,我们不得不进行大量的逻辑判断和try-catch
♂️。而Alamofire
把这些步骤简化成了一个静态的方法调用,并且用链式的方式来处理异步的Response
解析。由于是链式的,我们也可以用链式的方式实现更多的逻辑。
那么在Alamofire.request
的内部具体做了什么“骚”操作呢?没办法♀️️,想要看到内部实现,就要看到这里的源码,那么就来吧...
根据Alamofire.request
的这个方法,我们来到Alamofire.swift
看到这个request
方法:
可以看到这个方法里的几个参数都有默认值,所有我们可以直接传入一个
Url
参数,这个方法返回一个DataRequest
对象,是利用SessionManager
的request
方法,这说明了真正的request
应该是由SessionManager
发起的,下面再来看一下SessionManager.default.request
方法内部实现了什么❓
在这个方法中创建了一个
URLRequest
对象,并把传入的参数传入到这个URLRequest
对象中,并调用了request
方法,并且把上面创建的URLRequest
对象进行编码之后作为参数传入到这个request
方法,
这里是插入一个小插曲,这个参数编码到底是如何操作的,也就是这个encoding.encode()
方法具体实现了什么?
这里先取出请求方法的类型,然后根据不同的类型对参数进行不同的编码,由前面的代码我们知道在
encode()
这个方法传入了参数parameters
,而在这里处理这个parameters
的是query(parameters)
,那么接着来看这个query
方法的具体实现:
来到这里把传入的参数根据key
进行排序,然后遍历排序的结果,然后利用queryComponents
方法将所有的参数(key,value)
不断的递归, 返回一个元素为元组的数组,然后将这个数组进行map
映射,将key=value
并且拼接&
符号,至此就完成了参数的拼接,
然后我们在回到encode
方法中,根据请求方法的不同,
- 如果请求方法是
.get
,.head
,.delete
类型,需要urlComponents.percentEncodedQuery
进行百分号编码,并且利用query(parameters)
把参数直接拼接到URL
后面。 - 如果是其他类型的请求方法,例如
.post
,需要设置请求头application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type
,然后利用query(parameters)
拼接参数,将参数转换为二进制并赋值到urlRequest.httpBody
请求体中。
那么接下来回到request
的流程再来看看这个request(encodedURLRequest)
方法内部实现了什么❓
在这里先拿到传入的已经编码过后的URLRequest
,这里的DataRequest
是继承于Request
的一个子类,用来管理URLSessionDataTask
,而这里DataRequest.Requestable
,难道是调用了一个Requestable
方法?我们跟进去这个Requestable
看一下:
可以看到这个Requestable
是一个结构体,也就是说在当前这个DataRequest
类里面嵌套了一个结构体,但是可以看到这个结构体内部并没有urlRequest
这个方法,那它怎么会调用urlRequest
方法呢?
我们来看这句代码let urlRequest: URLRequest
,可以看到在这个结构体的内部有个urlRequest
属性,而且默认初始化。✍️那么这句DataRequest.Requestable(urlRequest: )
实际上就是创建了一个类型为Requestable
的而且包含创建task
方法的结构体对象,在这个task
方法的内部返回的是同步创建的session
,return queue.sync { session.dataTask(with: urlRequest) }
.
那么这里为什么不使用DataRequest
直接创建一个task
,而非要task
由Requestable
这个结构体来创建,其实是Requestable
结构体就相当于是DataRequest
的秘书,其实这里DataRequest
和task
任务应该是一一对应的,平级关系,如果DataRequest
直接创建task
,更像是归属关系,而由Requestable
来创建task
也就是降低了DataRequest
与task
的耦合性,使DataRequest
与task
保持平级关系。
继续回到源代码,可以看到在
Requestable
创建完task
之后,紧接着DataRequest
就创建了一个request
,那么接下来来到DataRequest
的init
方法,然而DataRequest
并没有init
方法,那么就看其父类Request
的init
方法:
可以看到这里
requestTask
很明显是个枚举类型,
而这个
requestTask
调用了一个关联方法.data(let originalTask, let task)
,而这个.data
即是DataRequest
初始化方法中传入的.data(originalTask, task)
,这里会根据传入的方法的不同的类型来把task
交给不同的TaskDelegate
.
再一次回到request
流程的源码♂️♂️♂️ ,这里一句很重要的代码 delegate[task] = request
,这里是做了一个绑定操作,(有点类似于字典和数组),当前这个delegate
由源码可知public let delegate: SessionDelegate
是一个SessionDelegate
就是SessionDelegate
通过task
可以读取到request
。方便在 SessionDelegate
下发任务的时候,通过task
直接检索,方便直接获取到request
,然后调用request.resume()
任务启动.
直接通过
SessionDelegate
实现代理, 为什么这里还要有一个DataTaskDelegate
, 真是费劲呢。
首先,要明白要SessionDelegate
是所有 Session
的代理遵循者,任何的代理都会来到这里响应,而DataTaskDelegate
是具体任务的实现者,这里面所有方法都是由 SessionDelegate
下发响应的。(一个下发任务,一个具体干活)
下面给我一首歌的时间来看一下SessionDelegate.swift
中URLSessionDownloadDelegate
的源码:
从标红部分可以看出,
self[downloadTask]
就等同于DownloadRequest
,DownloadRequest.delegate
就等同于DownloadTaskDelegate
.这里把downloadTask
交给了DownloadTaskDelegate.urlSession
去执行,如果再跟进去源码DownloadTaskDelegate.urlSession
,就会发现执行的就是具体的Download
操作。
总结
在SessionManager
的 Request
方法中,表面上看是由SessionManager
直接处理了request
,然而实际上,是通过SessionManager
的代理响应SessionDelegate
,然后SessionDelegate
把这个具体Request
分发给了具体的DataTaskDelegate
,然后DataTaskDelegate
来执行具体的任务内容。之所以这么做,是因为SessionDelegate
通过的任务分发,让处理具体任务的taskdelegate
去处理对应的事务,避免了其内部逻辑混乱和代码冗余,实现任务逻辑下沉。关于Request
还有很多内容的哦,后面再做补充,如有错误,还望大佬们指正。