【问题标题】:Dispatch group won't block after JSON callJSON调用后调度组不会阻塞
【发布时间】:2016-12-10 20:07:25
【问题描述】:

我试图在填充 TableCell 之前让我的 JSON 调用返回,但是作为 swift 的新手,我无法使用调度队列来阻止调用....谁能告诉我如何让它停止在下面发布的代码中的group.wait() 行。

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let queue:DispatchQueue = DispatchQueue.global()
    let group:DispatchGroup = DispatchGroup()

    Alamofire.request("http://localhost:3000/locations.json").responseJSON { response in
        group.enter()
        //print(response.request)  // original URL request
        //print(response.response) // HTTP URL response
        //print(response.data)     // server data
        //print(response.result)   // result of response serialization
        guard response.result.error == nil else{
            print("Error: unable to get a list of locations.")
            return
        }
        if let JSON = response.result.value {
            //  print("JSON: \(JSON)")
            if let result = response.result.value as? [[String: Any]] {
                nameArray = result.map { ($0["name"] as? String)! }
                print(nameArray[0])

            }
        }
        group.leave()
    }

    group.notify(queue: queue) {
        // This closure will be executed when all tasks are complete
        print("All tasks complete")
    }

    // synchronize on task completion by waiting on the group to block the current thread.
    print("block while waiting on task completion")
    let _ = group.wait()
    print("continue")

    let cell = tableView.dequeueReusableCell(withIdentifier: "Picture", for: indexPath)
    cell.textLabel?.text = LocationData[indexPath.row].name
    print (LocationData[indexPath.row].name)
    print(indexPath.row)
    // cell.textLabel?.text = nameArray[indexPath.row]
    return cell
}

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    if let vc = storyboard?.instantiateViewController(withIdentifier: "Detail") as? DetailViewController {
        vc.selectedImage = pictures[indexPath.row]
        navigationController?.pushViewController(vc, animated: true)
    }
}

【问题讨论】:

  • 强烈建议您不要屏蔽cellForRowAt,因为您将获得糟糕的使用体验。 异步在 viewDidLoad 中加载 JSON,然后重新加载表格视图。

标签: swift uitableview grand-central-dispatch alamofire


【解决方案1】:

您必须在执行请求之前调用group.enter(),而不是在闭包内。


但是因为 Alamofire 使用主队列作为其完成处理程序,如果你在主队列上 wait 直到完成处理程序调用 leave(),你就会死锁。如果您真的想使用这种“等待”模式,则必须指定 request 应该使用该后台队列作为其完成处理程序。


话虽如此,虽然它对wait 具有直观的吸引力,但它的用法并不合适。 (使用信号量的其他逻辑等效解决方案也不会。)

您应该发起网络请求,然后异步更新单元格。但cellForRowAtIndexPath 不应等待网络请求。


我本来打算建议替代实现,但尚不清楚您要做什么(现在,您正在为表格的每一行执行另一个网络请求,这似乎不正确)。 LocationData 来自哪里?假设您确实想要执行一个请求来获取位置名称,您可以将该请求移至 viewDidLoad,然后在完成处理程序中调用 tableView.reload()

var locations: [String]?

override func viewDidLoad() {
    super.viewDidLoad()

    Alamofire.request("http://localhost:3000/locations.json").responseJSON { response in
        guard response.result.error == nil else{
            print("Error: unable to get a list of locations.")
            return
        }
        if let result = response.result.value as? [[String: Any]] {
            self.locations = result.map { $0["name"] as! String }
            self.tableView.reloadData()
        }
    }
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return locations?.count ?? 0
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Picture", for: indexPath)
    cell.textLabel?.text = locations?[indexPath.row]
    return cell
}

【讨论】:

  • Rob,你真是个天才——非常感谢,我从周五开始就一直在研究这个问题(我可能已经花了 15 个小时试图弄清楚)。关于 Swift 我还有很多东西要学,老实说,这有点像从 Ruby on Rails 的背景走出来的悬崖......
  • 没问题。顺便说一句,如果我回答了您的问题,请考虑通过单击旁边的复选标记来接受答案。见What should I do when someone answers my question?
  • 另外,花一点时间来适应 Swift 的异步模式也不要太难过。一旦你做了几次并习惯了这些模式,它可能会感觉像是第二天性。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-19
  • 2015-05-11
  • 1970-01-01
  • 1970-01-01
  • 2013-11-16
  • 1970-01-01
相关资源
最近更新 更多