ios - 在 OperationQueue 上运行 DataTask 时忽略 NSURLSession .waitsForConnectivity 标志
问题描述
我有一个手写类 MyURLRequest,它实现了 Operation。在它里面创建 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 有关。感谢您的帮助!
更新: 问题的根源似乎是操作发布得太早(当请求尚未完成时)。我尝试使用 DispatchGroup() 同步它们:
override open func start() {
if isCancelled {
isFinished = true
return
}
startDate = Date()
dispatchGroup.enter()
sessionTask?.resume()
dispatchGroup.wait()
localURLSession.finishTasksAndInvalidate()
}
其中 .leave() 在 URLSessionDelegate 的方法中被调用。没有任何改变,仍然没有等待连接。
更新: 这是我在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
方法返回,an 就被认为是完整的。这绝对不是您需要的,因为任务必须异步完成。无法开箱即用地支持这种行为Operation
。
您start
立即返回,早在会话有任何时间完成您已开始的任务之前。然后,随着操作完成,队列将其移除。这通常最终成为该实例的唯一所有者。如果这是真的,它会启动操作 deinit 进程,最终释放URLSession
. 此时,会话看起来可能会进行一些清理并终止任何未完成的任务,将一些调用转发给它的委托。我不确定 URLSession 是否这样做,但根据您所看到的,听起来可能。
为了实现你想要的,我认为你需要将你的NSOperation
子类重组为完全异步的,并且只在启动任务完成时完成。
构建一个完全线程安全的异步NSOperation
子类是一件非常痛苦的事情。如果这不是您之前解决过的问题,您可以在此处查看实现:https ://github.com/ChimeHQ/OperationPlus
推荐阅读
- html - 使用 Handlebars 隐藏过滤项目的空列表
- typescript - TypeScript:如何创建一个类型或接口,它是一个具有 n 个键之一的对象还是一个字符串?
- mongodb - 如何在ubuntu启动后启动mongo db
- node.js - NodeJS-Electron-msnodesqlv8 - 在 msnodesqlv8 中加载二进制文件时出错
- pointers - 队列实现抛出不兼容的指针类型错误
- c# - 在给定时间内执行任务
- php - 当页面不存在时不要创建错误页面
- delphi - 如何使用 TRttiProperty.setValue() 设置“记录”值?
- javascript - Google 代码美化代码突出显示不适用于 Polymer 3
- sql - SQL 计数显示不同的名称,但不正确的匹配数