先上代码,再来看我们需要了解的是啥.
- vc:
import UIKit
import Alamofire
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
request("https://www.baidu.com", method: .get, parameters: ["username":"lb", "password":"123456"]).response { (response) in
print("response === \(response)")
}
}
}
- 这里直接写
request
还是写SessionManager.default.request
或是Alamofire.request
都是一样的, 都来到SessionManager.default
的request
方法里.- 该方法除了
url
是必传,其他都有默认值,可以不传.
@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)
}
}
该方法我们一步一步来分析.
一 : 参数encode准备工作
① : 先创建了一个原生URLRequest. 这里没啥好说的.
② : 对参数 encoding
进行 encode
.
public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
var urlRequest = try urlRequest.asURLRequest()
guard let parameters = parameters else { return urlRequest }
if let method = HTTPMethod(rawValue: urlRequest.httpMethod ?? "GET"), encodesParametersInURL(with: method) {
guard let url = urlRequest.url else {
throw AFError.parameterEncodingFailed(reason: .missingURL)
}
if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false), !parameters.isEmpty {
let percentEncodedQuery = (urlComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters)
urlComponents.percentEncodedQuery = percentEncodedQuery
urlRequest.url = urlComponents.url
}
} else {
if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
urlRequest.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
}
urlRequest.httpBody = query(parameters).data(using: .utf8, allowLossyConversion: false)
}
return urlRequest
}
- 先判断 method 是不是
.get, .head, .delete
(具体实现可以点击encodesParametersInURL
进去查看)- 是
.get, .head, .delete
则把参数直接拼接到 URL 后面.- 其他方式则把参数放到
httpBody
中编码.
具体的参数拼接方式不具体讲述了, 总结一下:
1 : 由于请求是通过 ASCII编码的,所以要进行百分号编码,第一步就是对当前请求的所有路由百分号编码.
2 : query
方法
private func query(_ parameters: [String: Any]) -> String {
var components: [(String, String)] = []
for key in parameters.keys.sorted(by: <) {
let value = parameters[key]!
components += queryComponents(fromKey: key, value: value)
}
return components.map { "\($0)=\($1)" }.joined(separator: "&")
}
- 通过 ASCII 有小到大进行排序.
queryComponents
把key
和value
取出 , 进行参数递归,直到基础数据类型,如bool
,string
,然后进行了百分号编码, 之后添加到元祖返回。- 拿到
queryComponents
返回的元祖数据,添加到数组中.- 将保存了元祖的数组映射 ,元祖中数据变成
$1 = $2
形式 , 然后新生成的数组按&
拼接成字符串.
二 : 开始请求 request(encodedURLRequest)
先点进去查看该方法:
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)
}
}
1. 首先 DataRequest.Requestable 方法
DataRequest.Requestable(urlRequest: originalRequest!)
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)
}
}
}
- 创建了一个结构体.并且初始化时就持有了
urlRequest
2. originalTask.task
调用该结构体的 task
方法,同步创建了一个 URLSessionTask
. ( 源码在上面结构体源码中.)
3. DataRequest(session: , requestTask: )
创建一个 DataRequest
, 自己没有 init
方法.查找其父类 Request
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
case .download(let originalTask, let task):
taskDelegate = DownloadTaskDelegate(task: task)
self.originalTask = originalTask
case .upload(let originalTask, let task):
taskDelegate = UploadTaskDelegate(task: task)
self.originalTask = originalTask
case .stream(let originalTask, let task):
taskDelegate = TaskDelegate(task: task)
self.originalTask = originalTask
}
delegate.error = error
delegate.queue.addOperation { self.endTime = CFAbsoluteTimeGetCurrent() }
}
这里根据不同的
requestTask
生成了不同的子类TaskDelegate
.
DataTaskDelegate
/DownloadTaskDelegate
等等. 其实就是实际去干活的人. 它们与SessionDelegate
也就是整体与局部的关系.
4. delegate[task] = request
这个
delegate
是我们的SessionDelegate
.
绑定task
和request
, 方便在SessionDelegate
下发任务,task
直接检索, 方便直接获取request
.
实现方式点进原码找到如下:
var requests: [Int: Request] = [:]
private let lock = NSLock()
/// Access the task delegate for the specified task in a thread-safe manner.
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
}
}
通过一个 requests: [Int: Request]
属性和开放出去的 subscript
下标查找方法以此实现 task
和 request
的绑定 以及 delegate[task]
的 set
和 get
的实现.
5. request.resume()
直接 request.resume()
启动任务.
下篇博客会继续探讨 request
层级结构和具体流程.之后会统一进行总结.