Ios Alamofire请求未在NSO操作内运行完成块
Ios Alamofire请求未在NSO操作内运行完成块,ios,swift,concurrency,alamofire,nsoperation,Ios,Swift,Concurrency,Alamofire,Nsoperation,注意:虽然在SO中还有其他类似的问题,但在这些问题中,作者似乎都没有自己控制操作的生命周期。在提到另一个问题之前,请通读一遍 我在Swift 3.0中创建了一个[NS]操作来下载、解析和缓存核心数据中的一些数据 首先,我在操作中使用了main()方法来执行手头的任务,效果很好。现在,我需要运行几个单独的任务来检索我在这一步中获得的每个设备的信息。为此,在尝试获取其他信息之前,我需要确保设备实际上位于核心数据中。出于这个原因,我想确保在触发依赖请求之前,我决定了任务何时完成——也就是当缓存中的所有
注意
:虽然在SO中还有其他类似的问题,但在这些问题中,作者似乎都没有自己控制操作的生命周期。在提到另一个问题之前,请通读一遍
我在Swift 3.0中创建了一个[NS]操作来下载、解析和缓存核心数据中的一些数据
首先,我在操作中使用了main()
方法来执行手头的任务,效果很好。现在,我需要运行几个单独的任务来检索我在这一步中获得的每个设备的信息。为此,在尝试获取其他信息之前,我需要确保设备实际上位于核心数据中。出于这个原因,我想确保在触发依赖请求之前,我决定了任务何时完成——也就是当缓存中的所有设备都安全可靠时
问题是,即使我检查了Alamofire是否执行了请求,服务器是否发送了数据,但标有注释[THIS worn execute!
]的完成块永远不会执行。这导致队列暂停,因为操作在所述完成块内标记为已完成
,这是所需的行为
有人知道这里发生了什么吗
class FetchDevices: Operation {
var container: NSPersistentContainer!
var alamofireManager: Alamofire.SessionManager!
var host: String!
var port: Int!
private var _executing = false
private var _finished = false
override internal(set) var isExecuting: Bool {
get {
return _executing
}
set {
willChangeValue(forKey: "isExecuting")
_executing = newValue
didChangeValue(forKey: "isExecuting")
}
}
override internal(set) var isFinished: Bool {
get {
return _finished
}
set {
willChangeValue(forKey: "isFinished")
_finished = newValue
didChangeValue(forKey: "isFinished")
}
}
override var isAsynchronous: Bool {
return true
}
init(usingContainer container: NSPersistentContainer, usingHost host: String, usingPort port: Int) {
super.init()
self.container = container
self.host = host
self.port = port
let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForResource = 10 // in seconds
self.alamofireManager = Alamofire.SessionManager(configuration: configuration)
}
override func start() {
if self.isCancelled {
self.isFinished = true
return
}
self.isExecuting = true
alamofireManager!.request("http://apiurlfor.devices")
.validate()
.responseJSON { response in
// THIS WONT EXECUTE!
if self.isCancelled {
self.isExecuting = false
self.isFinished = true
return
}
switch response.result {
case .success(let value):
let jsonData = JSON(value)
self.container.performBackgroundTask { context in
for (_, rawDevice):(String, JSON) in jsonData {
let _ = Device(fromJSON: rawDevice, usingContext: context)
}
do {
try context.save()
} catch {
let saveError = error as NSError
print("\(saveError), \(saveError.userInfo)")
}
self.isExecuting = false
self.isFinished = true
}
case .failure(let error):
print("May Day! May Day! \(error)")
self.isExecuting = false
self.isFinished = true
}
}
}
}
一条可能有用的信息是,在我对所有操作进行排队的方法中,我使用queue.waitUntilallOperationsRefinished()
在所有操作完成后执行一个完成处理程序。问题是,您有其他东西阻塞了主线程,而responseJSON
使用它来关闭,导致僵局。您可以通过将responseJSON
快速替换为.responseJSON(queue:.global())
来确认这一点,以使Alamofire使用主队列以外的队列,您将看到此行为更改。但是,如果您这样做(仅用于诊断目的),您应该将其更改回去,然后将注意力转向识别并消除阻塞主线程的内容(即不要等待主线程),因为您永远不应该阻塞主线程
您提到您正在调用waituntlalloperations已完成
。虽然这是一个等待一系列操作完成的令人陶醉的简单解决方案,但您永远不应该从主线程执行该操作。主线程永远不应该被阻塞(或者至少不应该被阻塞超过几毫秒)。它可能会导致用户体验不合格(应用程序冻结),并且您的应用程序很容易被“看门狗”流程立即终止。我怀疑Alamofire作者在默认情况下将其完成处理程序分派到主队列时感到如此舒适的原因之一是,它不仅通常有用且方便,而且他们知道永远不会阻塞主线程
使用操作队列时,避免等待的典型模式是使用完成操作:
let completionOperation = BlockOperation {
// something that we'll do when all the operations are done
}
for object in arrayOfObjects {
let networkOperation = ...
completionOperation.addDependency(networkOperation)
queue.addOperation(networkOperation)
}
OperationQueue.main.addOperation(completionOperation)
如果您使用调度组和调度组“notify”,则可以实现类似的效果(不过,通常,如果您使用的是操作队列,出于一致性考虑,您通常会停留在操作队列范例中)
如果您想调用WaitUntilalAllOperationsRefiefined
,从技术上讲,您可以这样做,但只有当您将“等待”分派到某个后台队列(例如,全局队列,但显然不能分派到您自己添加了所有这些操作的操作队列)时,您才应该这样做。不过,我认为这是一种浪费的模式(如果您有一个非常好的机制来指定完成操作,为什么还要占用一些全局工作线程等待操作完成)。感谢您花时间回答@Rob。我照你的建议做了,没有改变。我将看一看阻塞主线程的问题,但我不知道是什么原因造成的。你看到的代码就是整个操作。没有更多。好吧@Rob,我认为你指出了正确的方向,但还有更多的工作要做。我在负责对任务进行排队的方法中使用了queue.waitUntillalOperationsDefished()
。如果我对此进行注释,队列将不会暂停,但实际上我需要等待所有操作完成,然后才能调用完成处理程序。有办法吗?你的回答最终让我找到了正确的方向。我只是编辑了它,添加了一个链接,指向解决原始问题的问题,这是我在分析问题并考虑到您的指针后才发现的。该链接向您展示了如何使用主队列以外的队列,这就是我试图在上面描述的内容。但我的观点是,这并不是一个好的解决方案,而是一个有用的诊断来确认这个更深层次的问题的存在,即你正在用“等待”阻塞主线程。但是,您永远不应该等待主队列中的所有操作完成。如果要在所有操作完成后执行某些操作,请创建一个“完成”操作,并在所有其他操作和此依赖项操作之间添加依赖项。但永远不要“等待”。我认为你的答案现在更完整了,它仍然是正确的答案。我按照建议做了,效果很好。谢谢你的努力。