[英]Execute Completion Handler in DispatchGroup
我想在UITableView
顯示服務器延遲,唯一的問題是ping結果處理程序不會在DispatchQueue
下運行
let dispatch_queue = DispatchQueue(label: "PingQueue", qos: .background)
dispatch_queue.async {
let client = SimplePingClient()
client.pingHostname(hostname: self.Servers[indexPath.row].Address, andResultCallback: { result in
guard let r = result as? String else {
DispatchQueue.main.async {
cell.lblLatency.text = "Timeout"
}
return
}
DispatchQueue.main.async{
cell.lblLatency.text = r + " ms"
}
})
}
andResultCallback
不會調用(我不知道為什么!?)
首先,您應該注意一些有關現有代碼的事情。
您對dispatch_queue.async
的調用正在執行client
初始化,並且對client.pingHostname
的調用在dispatch_queue.async
隊列上運行。 請注意,因為pingHostname
是異步API,所以它幾乎立即返回,並不意味着競爭處理程序將在dispatch_queue
運行。
dispatch_queue.async
可能沒有任何意義。 假設客戶端初始化是一個輕量級的,不依賴IO的任務(即,它不執行任何自身的網絡調用),並假設pingHostname是輕量級的(鑒於它是其自身的異步功能,則幾乎可以肯定是這樣),那么就沒有理由為什么不能使用dispatch_queue.sync
調用,甚至不能直接從當前線程進行這些調用。 它可能幾乎一樣快,甚至可能更快(因為異步調用使轉義分析變得更加困難,並限制了編譯器的優化)。 這樣做還有一個好處,那就是消除了“如果在下一個事情之前完成此操作?或者如果相反則要做什么?”的擔心。 您提供了一個異步pingHostname
API的閉包,它可以在所需的任何線程/隊列上自由運行,我們稱其為queue / thread X
API文檔可能會對此有所啟發。
從隊列/線程X
的上下文中,您對DispatchQueue.main
進行異步調用。 這是對的; 可可的目的是使所有UI更新必須始終在主線程中進行。
如果您聲明的意圖是在dispatch_queue
上運行競爭處理程序代碼,則您必須:
提供pingHostname
API的dispatch_queue
作為參數,以便它可以在您的隊列上運行競爭處理程序,而不是采用其他默認值。 當然,這是API必須具有的功能。
在競爭處理程序中,從線程/隊列X
調用自己的dispatch_queue.sync
。
在沒有DispatchQueue
參數的情況下,這是我的寫法:
pingQueue = DispatchQueue(label: "PingQueue", qos: .background)
let client = SimplePingClient()
client.pingHostname(
hostname: self.Servers[indexPath.row].Address, // This is jank, but I'm ignoring it for now
andResultCallback: { _latency in
pingQueue.sync {
print("Do some stuff on pingQueue")
DispatchQueue.main.sync { // enter main queue for UI updates
cell.lblLatency.text = (_latency as? String).map { $0 + " ms" } ?? "Timeout"
}
}
}
)
在cellForRowAt
內部調用此方法是不正確的。 您應該在ViewController結果數組中的某處
var results = [String]()
現在,在別的地方獲取結果,而不是在cellForRowAt
。 為此創建自定義方法,或使用自定義模型。
更好的方法是,如果您的pingHostname
方法返回所有結果,然后您只分配results
數組,然后重新加載TableView的數據
client.pingHostname { results in
if let r = result as? [String] {
self.results = r
self.tableView.reloadData()
}
}
也將此results
數組用作TableView數據源方法的源
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return results.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
...
cell.lblLatency.text = results[indexPath.row]
...
}
不會調用結果回調,因為在完成操作之前,您的ping客戶端已被釋放。
使client
成為實例變量而不是局部變量。 完成后將其設置為nil
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.