[英]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.