在DispatchGroup中执行完成处理程序

时间:2018-12-11 13:00:24

标签: ios swift

我想显示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根本不会打来(我不知道为什么!?)

3 个答案:

答案 0 :(得分:2)

首先,您需要注意一些有关现有代码的信息。

  1. 您对dispatch_queue.async的调用正在进行client的初始化,并且client.pingHostname的调用在dispatch_queue.async队列上运行。请注意,由于pingHostname是异步API,因此它几乎立即返回,并不意味着竞争处理程序将在dispatch_queue上运行。

    • 请注意,使用dispatch_queue.async可能没有意义。假设客户端初始化是一个轻量级的,不依赖IO的任务(即它不进行任何网络调用),并假设pingHostname是轻量级的(鉴于它是其自身的异步功能,则几乎可以肯定是这样),那么就没有理由不能使用dispatch_queue.sync调用,甚至不能直接从当前线程进行这些调用。它可能几乎一样快,甚至可能更快(因为异步调用使转义分析变得更加困难,并限制了编译器的优化)。这样做还有一个好处,那就是消除了“如果在完成下一件事情之前完成此操作?或者如果在另一件事之前又完成了该操作,该怎么办?”的担忧。
  2. 您提供了异步pingHostname API的闭包,它可以自由地在希望的任何线程/队列上运行,我们称它为队列/线程X。 API文档可能会对此有所启发。

  3. 从队列/线程X的上下文中,您对DispatchQueue.main进行异步调用。这是对的;可可的目的是使所有UI更新都必须始终在主线程中进行。

如果您打算在dispatch_queue上运行竞赛处理程序代码,则您必须:

  1. 提供dispatch_queue作为pingHostname API的参数,以便它可以在您的队列上运行竞争处理程序,而不是采用其他默认值。当然,这是API必须具有的功能。

  2. 从竞争处理程序中的线程/队列dispatch_queue.syncX进行自己的调用。

在没有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"
            }
        }
    }
)

答案 1 :(得分:1)

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]
    ...
}

答案 2 :(得分:0)

由于您的ping客户端在完成操作之前已被释放,因此未调用结果回调。

使client成为实例变量而不是局部变量。完成操作后,将其设置为nil