繁体   English   中英

在DispatchGroup中执行完成处理程序

[英]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不会调用(我不知道为什么!?)

首先,您应该注意一些有关现有代码的事情。

  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的闭包,它可以在所需的任何线程/队列上自由运行,我们称其为queue / thread X API文档可能会对此有所启发。

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

如果您声明的意图是在dispatch_queue上运行竞争处理程序代码,则您必须:

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

  2. 在竞争处理程序中,从线程/队列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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM