iOS网络协议栈原理(二) — URLSessionTask层
上一篇文章总结到, URLSession
是一个出产URLSessionDataTask
的工厂, 其中有几个要害内二进制的运算规则容, 会在这一节中解释:
- 在创立
task
时, 会初始化失败是怎么解决将URLSession
实例传入, 也二进制便是说task
可能会在内部持有URLSession
- 真实建议恳求时, 需求调用
task.resume()
办法 — 那么Ta状态机是什么sk
究竟是如何作业的
1. Task
的初始化创立与HTTP B容器是什么ody
包装
简略概括一下Task的初始化的要害信息:
- Task的回调办法: 相关回调办法, 是经过
Behavappearanceiour + taskIdentifier
存状态机的概念储在URLSession
中 -
_Body枚举
: 根据URLReqapproachuest
的httpBody or httpBodyStream
的成员, 在内部一致运用_Bo状态机模式dy枚举
收口, 具会分成4类. - 参阅以下代码注释
open class URLSessionTask : NSObject, NSCopying {
...
// 结构 SessionTask 的 Body 可能有4种情况
enum _Body {
case none
case data(DispatchData) // 为啥用 DispatchData 是一个内部二进制容器类型, 实际会避免copy, 和传入的 request.httpBody 共用同一份内存
/// Body data is read from the given file URL
case file(URL) // 其他的 Task会运用 URL形式的 body
case stream(InputStream)
}
// 中心办法!
internal convenience init(session: URLSession, request: URLRequest, taskIdentifier: Int) {
if let bodyData = request.httpBody, !bodyData.isEmpty {
self.init(session: session, request: request, taskIdentifier: taskIdentifier, body: _Body.data(createDispatchData(bodyData)))
} else if let bodyStream = request.httpBodyStream {
self.init(session: session, request: request, taskIdentifier: taskIdentifier, body: _Body.stream(bodyStream))
} else {
// 其他场景, 没有body -> 例如运用 GET 恳求时
self.init(session: session, request: request, taskIdentifier: taskIdentifier, body: _Body.none)
}
}
// 更加重要的办法
internal init(session: URLSession, request: URLRequest, taskIdentifier: Int, body: _Body?) {
// 1. task 会强引证创立它的 session, 而且在 task 完毕的时候 self.session = nil
self.session = session
// 2. 将 workQueue的 targetQueue 设置成 session.workQueue
// 3. 后续有很多关于 task.state 的操作都在这个 serial queue 中完结!!!
self.workQueue = DispatchQueue.init(label: "org.swift.URLSessionTask.WorkQueue", target: session.workQueue)
// 4. 仅有 task Identifier, 运用这个仅有标记, 可以经过 URLSession 相关 CompletionHandler
self.taskIdentifier = taskIdentifier
// 5. 持有一份外部传入的 Request!!! 因为整个恳求进程中, 可能遇到 HTTP Status Code = 302的情况, 需求重定向, 可能一次Task进程会建议多个恳求
self.originalRequest = request // 原始的恳求
// 6. 收口今后的 http body
self.knownBody = body
super.init()
// 7. 同 5, 初始化时, 当前的恳求便是初始化的request (后边如果有302重定向场景, Task会创立一个新的指向重定向路径的request, 那时会修改 currentRequest)
self.currentRequest = request
// 8. 疏忽...
self.progress.cancellationHandler = { [weak self] in
self?.cancel()
}
}
}
2. Task
的内部状态机
前面我们知道, 在运用Session
工厂, 创立Task
实例今后,二进制的运算规则 需求主动调用task.resume()
办法发动这个使命
!!!初始化是什么意思 why??初始化sdk什么意思?
实际上在task内部维护了一个state
!!! 而且经过一个成员变量s容器英文uspend容器的容积一定比它的体积小Coun初始化磁盘t
来记录这个Tappearask使命
是否在挂起appstore状态!!!
extension URLSessionTask {
/// How many times the task has been suspended, 0 indicating a running task.
internal var suspendCount = 1
public enum State : Int {
/// The task is currently being serviced by the session
case running
case suspended // 初始是 suspended
/// The task has been told to cancel. The session will receive a URLSession:task:didCompleteWithError: message.
case canceling // 退出 ing!!!!!! 只是标记 canceling -> 可是没 completed
/// The task has completed and the session will receive no more delegate notifications
case completed
}
/// Updates the (public) state based on private / internal state.
///
/// - Note: This must be called on the `workQueue`.
internal func updateTaskState() {
func calculateState() -> URLSessionTask.State {
if suspendCount == 0 {
return .running
} else {
return .suspended
}
}
state = calculateState()
}
// 中心办法
open func resume() {
workQueue.sync {
// 疏忽 canceling 和 completed
guard self.state != .canceling, self.state != .completed else {
return
}
if self.suspendCount > 0 {
self.suspendCount -= 1
}
self.updateTaskState()
if self.suspendCount == 0 {
...
// 真实的发动使命
}
}
}
// 中心办法
open func cancel() {
workQueue.sync {
let canceled = self.syncQ.sync { () -> Bool in
guard self._state == .running || self._state == .suspended else {
return true
}
self._state = .canceling
return false
}
guard !canceled else {
return
}
...
}
}
}
经过以上代码能看出task
的两个中心办法, 首要便是切换task.state
, 而且关于state
的操作都是在workQueue
中完结的:
- 初始化时,
task.state => .suspe初始化失败是怎么解决nded
- 当外部调用
resume()
,task.state => .running
, 然后真实发动耗时使命, 详细的耗时使命是交给别的一个URLP容器是什么rotocl状态机编程的实例
完结的, 详细发动使命的逻辑我们后边分析!!! - 当外部调用
cance()
,task.st容器苗ate => .canceling
, 表明task
现已撤销, 然后去处理底层的资源清理作业 - 在
task
相关的URLProtocl的实例
的清理作业完结今后, 会调用task.state => .completed
!!!
因此, 简略来说, Task
并不是自己去履行网络恳求approve, 而是又笼统了一层, 真实的苦力活都是交给URLProtoco状态机l
去做的!!!
别的Task
功能性不同, Apple 创立状态机编程多个子类来进行差异化使命的实现, 别的, task progress
以及HTTP 302
重定向等逻辑都是在task层
实现的:
open class URLSessionDataTask: URLSessionTask {}
/*
* An URLSessionUploadTask does not currently provide any additional
* functionality over an URLSessionDataTask. All delegate messages
* that may be sent referencing an URLSessionDataTask equally apply
* to URLSessionUploadTasks.
*/
open class URLSessionUploadTask: URLSessionDataTask {}
/*
* URLSessionDownloadTask is a task that represents a download to
* local storage.
*/
open class URLSessionDownloadTask: URLSessionTask {
...
}