[英]Execute Completion Handler in DispatchGroup
I want to show server(s) latency in a UITableView
the only problem is ping result handler wont run under DispatchQueue
我想在
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"
}
})
}
the andResultCallback
wont called at all (i dont know why !?) andResultCallback
不会调用(我不知道为什么!?)
First, there's a few things you should note about your existing code. 首先,您应该注意一些有关现有代码的事情。
Your call to dispatch_queue.async
is doing the client
initialization and the invocation of client.pingHostname
run on the dispatch_queue.async
queue. 您对
dispatch_queue.async
的调用正在执行client
初始化,并且对client.pingHostname
的调用在dispatch_queue.async
队列上运行。 Note that because pingHostname
is an asynchronous API, this returns almost immediately, and doesn't mean that the competition handler will run on dispatch_queue
. 请注意,因为
pingHostname
是异步API,所以它几乎立即返回,并不意味着竞争处理程序将在dispatch_queue
运行。
dispatch_queue.async
probably doesn't make sense. dispatch_queue.async
可能没有任何意义。 Assuming client initialization is a lightweight, non-IO dependant task (ie it doesn't do any network calls of its own), and assuming pingHostname is lightweight (which it almost certainly is, given that it's an async function of its own), then there's no reason why you can't use a dispatch_queue.sync
call, or even directly make these calls off your current thread. dispatch_queue.sync
调用,甚至不能直接从当前线程进行这些调用。 It'll probably be pretty much just as fast, or perhaps even faster (because async calls make escape analysis much harder, and limit compiler optimization). You provide a closure to the asynchronous pingHostname
API, which it has the freedom to run on any thread/queue it wishes, let's call it queue/thread X
. 您提供了一个异步
pingHostname
API的闭包,它可以在所需的任何线程/队列上自由运行,我们称其为queue / thread X
The API document will probably shed some light on this. API文档可能会对此有所启发。
From the context of queue/thread X
, you make async calls into DispatchQueue.main
. 从队列/线程
X
的上下文中,您对DispatchQueue.main
进行异步调用。 This is correct; 这是对的; Cocoa is designed to such that all UI updates must always happen from the main thread.
可可的目的是使所有UI更新必须始终在主线程中进行。
If your stated intention is to run competition handler code on dispatch_queue
, then you have to either: 如果您声明的意图是在
dispatch_queue
上运行竞争处理程序代码,则您必须:
Provide dispatch_queue
as a parameter to the pingHostname
API, so that it can run the competition handler on your queue, instead of whatever default it resorts to otherwise. 提供
pingHostname
API的dispatch_queue
作为参数,以便它可以在您的队列上运行竞争处理程序,而不是采用其他默认值。 Of course, this is something the API must have been designed to have. 当然,这是API必须具有的功能。
Make your own call to dispatch_queue.sync
from thread/queue X
from within the competition handler. 在竞争处理程序中,从线程/队列
X
调用自己的dispatch_queue.sync
。
In the absence of a DispatchQueue
parameter, here is how I would write this: 在没有
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"
}
}
}
)
It's not right to call this inside cellForRowAt
. 在
cellForRowAt
内部调用此方法是不正确的。 You should have somewhere in your ViewController array of results 您应该在ViewController结果数组中的某处
var results = [String]()
Now, get results somewhere else, not in cellForRowAt
. 现在,在别的地方获取结果,而不是在
cellForRowAt
。 Create custom method for this, or use custom model. 为此创建自定义方法,或使用自定义模型。
Also better would be if your pingHostname
method returned all results and then you just assign your results
array and then reload data of your TableView 更好的方法是,如果您的
pingHostname
方法返回所有结果,然后您只分配results
数组,然后重新加载TableView的数据
client.pingHostname { results in
if let r = result as? [String] {
self.results = r
self.tableView.reloadData()
}
}
also use this results
array as source for TableView's data source methods 也将此
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]
...
}
The result callback isn't being called because your ping client is being deallocated before it's finished. 不会调用结果回调,因为在完成操作之前,您的ping客户端已被释放。
Make client
an instance variable instead of a local variable. 使
client
成为实例变量而不是局部变量。 Set it to nil
when you're done with it. 完成后将其设置为
nil
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.