当 DataTask 在 OperationQueue 上 运行 时,NSURLSession .waitsForConnectivity 标志被忽略
NSURLSession .waitsForConnectivity flag ignored when DataTask being run on OperationQueue
我有一个手写的 class MyURLRequest,它实现了操作。在里面创建 URLSession,配置它
public init(shouldWaitForConnectivity: Bool, timeoutForResource: Double?) {
baseUrl = URL(string: Self.relevantServerUrl + "api/")
self.shouldWaitForConnectivity = shouldWaitForConnectivity
self.timeoutForResource = timeoutForResource
super.init()
localURLSession = URLSession(configuration: localConfig, delegate: self, delegateQueue: nil)
}
public var localConfig: URLSessionConfiguration {
let res = URLSessionConfiguration.default
res.allowsCellularAccess = true
if let shouldWaitForConnectivity = shouldWaitForConnectivity {
res.waitsForConnectivity = shouldWaitForConnectivity
if let timeoutForResource = timeoutForResource {
res.timeoutIntervalForResource = timeoutForResource
}
}
return res
}
创建 URLRequest、dataTask,然后在 OperationQueue 上 运行。操作方法如下所示
override open func start() {
if isCancelled {
isFinished = true
return
}
startDate = Date()
sessionTask?.resume()
localURLSession.finishTasksAndInvalidate()
}
override open func cancel() {
super.cancel()
sessionTask?.cancel()
}
MyURLRequest 还实现了 URLSessionDataDelegate
和 URLSessionTaskDelegate
以及它自己的 URLSession 的代理。
waitsForConnectivity
NSURLSessionConfiguration 的标志有问题。在构造函数中我将它设置为 true,但是这个标志被忽略了。在 运行 时间,当网络关闭时,请求立即完成并出现错误 -1009。 URLSessionTaskDelegate 的方法 urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) 立即被触发。 func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) 根本没有被调用。
绝对不是的原因是标志 waitsForConnectivity 没有正确设置:我已经检查了 urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) ,waitsForConnectivity == true。
我也尝试在没有操作队列的情况下发出请求,结果很顺利 - 表现如预期。也许与 OperationQueue 有关。非常感谢您的帮助!
更新:
问题的根源似乎是 Operation 发布得太早(当请求尚未完成时)。我尝试使用 DispatchGroup():
来同步它们
override open func start() {
if isCancelled {
isFinished = true
return
}
startDate = Date()
dispatchGroup.enter()
sessionTask?.resume()
dispatchGroup.wait()
localURLSession.finishTasksAndInvalidate()
}
在 URLSessionDelegate 的方法中调用了 .leave()。什么都没有改变,仍然没有等待连接。
更新:
这是我在 didCompleteWithError:
中遇到的错误
Error Domain=NSURLErrorDomain Code=-1009 "" UserInfo={_kCFStreamErrorCodeKey=50, NSUnderlyingError=0x7fc319112de0 {Error Domain=kCFErrorDomainCFNetwork Code=-1009 "(null)" UserInfo={_kCFStreamErrorCodeKey=50, _kCFStreamErrorDomainKey=1}}, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <6388AD46-8497-40DF-8768-44FEBB84A8EC>.<1>, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <6388AD46-8497-40DF-8768-44FEBB84A8EC>.<1>",
"LocalDataTask <26BCBD73-FC8B-4A48-8EA2-1172ABB8093C>.<1>"
), NSLocalizedDescription=., NSErrorFailingURLStringKey=}
我认为问题的根源在于您对 finishTasksAndInvalidate
的使用。看起来您依赖于同步等待所有未决任务完成的方法,但根据文档,它不是这样工作的。
这是对我认为正在发生的事情的更深入的解释。默认情况下,Operation
与 start
方法 returns 一样被认为是完整的。这绝对不是您需要的,因为任务必须异步完成。 Operation
不 能够立即支持这种行为。
您的 start
returns 立即,远在会话有任何时间完成您已启动的任务之前。然后,随着操作完成,队列将其删除。这通常最终成为该实例的唯一所有者。如果是这样,它将启动操作 deinit 进程,最终释放 URLSession
。在 那个 点,会话看起来可能会做一些清理并终止任何未完成的任务,将一些调用转发给它的委托。我不确定 URLSession 是否会执行此操作,但根据您所看到的情况,听起来可能会执行此操作。
为了实现你想要的,我认为你需要重构你的 NSOperation
子类,使其完全异步,并且仅在启动的任务完成时才完成。
构建一个完全线程安全的异步 NSOperation
子类真的很痛苦。如果这不是您之前处理过的问题,您可以在此处查看实施:https://github.com/ChimeHQ/OperationPlus
我有一个手写的 class MyURLRequest,它实现了操作。在里面创建 URLSession,配置它
public init(shouldWaitForConnectivity: Bool, timeoutForResource: Double?) {
baseUrl = URL(string: Self.relevantServerUrl + "api/")
self.shouldWaitForConnectivity = shouldWaitForConnectivity
self.timeoutForResource = timeoutForResource
super.init()
localURLSession = URLSession(configuration: localConfig, delegate: self, delegateQueue: nil)
}
public var localConfig: URLSessionConfiguration {
let res = URLSessionConfiguration.default
res.allowsCellularAccess = true
if let shouldWaitForConnectivity = shouldWaitForConnectivity {
res.waitsForConnectivity = shouldWaitForConnectivity
if let timeoutForResource = timeoutForResource {
res.timeoutIntervalForResource = timeoutForResource
}
}
return res
}
创建 URLRequest、dataTask,然后在 OperationQueue 上 运行。操作方法如下所示
override open func start() {
if isCancelled {
isFinished = true
return
}
startDate = Date()
sessionTask?.resume()
localURLSession.finishTasksAndInvalidate()
}
override open func cancel() {
super.cancel()
sessionTask?.cancel()
}
MyURLRequest 还实现了 URLSessionDataDelegate
和 URLSessionTaskDelegate
以及它自己的 URLSession 的代理。
waitsForConnectivity
NSURLSessionConfiguration 的标志有问题。在构造函数中我将它设置为 true,但是这个标志被忽略了。在 运行 时间,当网络关闭时,请求立即完成并出现错误 -1009。 URLSessionTaskDelegate 的方法 urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) 立即被触发。 func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) 根本没有被调用。
绝对不是的原因是标志 waitsForConnectivity 没有正确设置:我已经检查了 urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) ,waitsForConnectivity == true。
我也尝试在没有操作队列的情况下发出请求,结果很顺利 - 表现如预期。也许与 OperationQueue 有关。非常感谢您的帮助!
更新: 问题的根源似乎是 Operation 发布得太早(当请求尚未完成时)。我尝试使用 DispatchGroup():
来同步它们override open func start() {
if isCancelled {
isFinished = true
return
}
startDate = Date()
dispatchGroup.enter()
sessionTask?.resume()
dispatchGroup.wait()
localURLSession.finishTasksAndInvalidate()
}
在 URLSessionDelegate 的方法中调用了 .leave()。什么都没有改变,仍然没有等待连接。
更新: 这是我在 didCompleteWithError:
中遇到的错误Error Domain=NSURLErrorDomain Code=-1009 "" UserInfo={_kCFStreamErrorCodeKey=50, NSUnderlyingError=0x7fc319112de0 {Error Domain=kCFErrorDomainCFNetwork Code=-1009 "(null)" UserInfo={_kCFStreamErrorCodeKey=50, _kCFStreamErrorDomainKey=1}}, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <6388AD46-8497-40DF-8768-44FEBB84A8EC>.<1>, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <6388AD46-8497-40DF-8768-44FEBB84A8EC>.<1>",
"LocalDataTask <26BCBD73-FC8B-4A48-8EA2-1172ABB8093C>.<1>"
), NSLocalizedDescription=., NSErrorFailingURLStringKey=}
我认为问题的根源在于您对 finishTasksAndInvalidate
的使用。看起来您依赖于同步等待所有未决任务完成的方法,但根据文档,它不是这样工作的。
这是对我认为正在发生的事情的更深入的解释。默认情况下,Operation
与 start
方法 returns 一样被认为是完整的。这绝对不是您需要的,因为任务必须异步完成。 Operation
不 能够立即支持这种行为。
您的 start
returns 立即,远在会话有任何时间完成您已启动的任务之前。然后,随着操作完成,队列将其删除。这通常最终成为该实例的唯一所有者。如果是这样,它将启动操作 deinit 进程,最终释放 URLSession
。在 那个 点,会话看起来可能会做一些清理并终止任何未完成的任务,将一些调用转发给它的委托。我不确定 URLSession 是否会执行此操作,但根据您所看到的情况,听起来可能会执行此操作。
为了实现你想要的,我认为你需要重构你的 NSOperation
子类,使其完全异步,并且仅在启动的任务完成时才完成。
构建一个完全线程安全的异步 NSOperation
子类真的很痛苦。如果这不是您之前处理过的问题,您可以在此处查看实施:https://github.com/ChimeHQ/OperationPlus