[英]Alamofire request not running completion block inside NSOperation
Note
:雖然在SO中存在與此類似的其他類似問題,但在這些問題中,作者似乎都沒有自己控制Operation
的生命周期。 請在提及另一個問題之前仔細閱讀。
我在Swift 3.0中創建了一個[NS] Operation來下載,解析和緩存Core Data中的一些數據。
首先,我使用了Operation中的main()
方法來執行手頭的任務並且運行良好。 現在我需要運行幾個單獨的任務來檢索有關我在此步驟中獲得的每個/每個設備的信息。 為此,在嘗試獲取其他信息之前,我需要確保設備實際上在Core Data中。 出於這個原因,我想確保在解決相關請求之前,我決定任務何時完成 - 這是所有設備在緩存中是安全的。
問題是,即使我已經檢查過Alamofire確實執行了請求並且服務器確實發送了數據,但標記有注釋的完成塊[ THIS WONT EXECUTE!
]永遠不會被執行。 這導致隊列停止,因為操作在所述完成塊內被標記為finished
,這是期望的行為。
有沒有人對這里可能發生的事情有任何想法?
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.waitUntilAllOperationsAreFinished()
在完成所有操作后執行完成處理程序。
問題是你有其他阻塞主線程的東西, responseJSON
用於閉包,導致死鎖。 您可以使用.responseJSON(queue: .global())
快速確認替換responseJSON
,以使Alamofire使用主隊列以外的隊列,您將看到此行為更改。 但是如果你這樣做(僅用於診斷目的),你應該改回來,然后把注意力轉移到識別並消除阻塞主線程的任何東西(即不要在主線程上等待),因為你永遠不應該阻止主線程。
你提到你正在調用waitUntilAllOperationsAreFinished
。 雖然這是一個令人陶醉的簡單解決方案,等待一系列操作完成,但你絕不應該從主線程中做到這一點。 永遠不應該阻止主線程(或者,至少不超過幾毫秒)。 它可能導致不合標准的用戶體驗(應用程序凍結),並且您的應用程序很容易被“看門狗”進程立即終止。 我懷疑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”,您可以實現類似的功能(但是,通常,如果您使用操作隊列,為了保持一致性,您通常會保持在操作隊列范例內)。
如果你想調用waitUntilAllOperationsAreFinished
,技術上你可以這樣做,但是你應該只在你將“等待”發送到某個后台隊列(例如全局隊列,但顯然不是你自己的操作隊列,你添加了所有的隊列)時才這樣做這些行動)。 我認為這是一種浪費的模式(為什么當你有一個非常好的指定完成操作的機制時,會占用一些全局工作線程等待操作完成)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.