Swift 3 - 异步请求表为空,刷新后填充

标签 swift uitableview asynchronous request alamofire

我是 Swift 的新手,目前正在构建一个应用程序,它在第一页上向我显示一个带有图像和文本字段的表格 View 。 我有两个请求,一个是获取所有图像,一个是获取所有名称。 我的问题是,当我同时调用这两个请求时,表格是“空的”——当我下拉并刷新表格时,数据就会出现。奇怪的是,当我只执行一个请求并“禁用”另一个请求时。它运行流畅。 (针对两个单独的请求对此进行了测试)。

第一个请求:

func getAllToDo(){
        let url:String = "XXXXXXXXXXX"
        let getAllToDosRequest = Alamofire.request(url)
        getAllToDosRequest.responseJSON{ response in
            switch response.result{
            case .success(let JSON):
                let response = JSON as! NSArray
                self.listOfTodos.removeAll()
                for item in response {
                    let obj = item as! NSDictionary
                    let toDo = ToDo(id: obj["id_ToDo"] as! Int, name: obj["name"] as! String, todo_description: obj["description"] as! String)
                    self.listOfTodos.append(toDo)
                }
            case .failure(let error):
                print("Request failed with error: \(error)")
            }
        }
    }

第二个请求:

func getAllImages(){
        let url:String = "XXXXXXXXXXXXXXX"
        let getAllImagesRequest = Alamofire.request(url)
        getAllImagesRequest.responseJSON{ response in
            switch response.result{
            case .success(let JSON):
                let response = JSON as! NSArray
                self.listOfImages.removeAll()
                for item in response {
                    let obj = item as! NSDictionary
                    let image = Image(id: obj["id"] as! Int, binary: obj["binary"] as! String, name: obj["name"] as! String)
                    self.listOfImages.append(image)
                }
            case .failure(let error):
                print("Request failed with error: \(error)")
            }
        }
    }

线程处理:

func refreshData(){
        let myGroup = DispatchGroup()
        for i in 0 ..< 2 {
            myGroup.enter()
            if(i == 0){
                getAllImages()
                print("Finished request \(i)")
                myGroup.leave()
            }
            if(i == 1){
                getAllToDo()
                print("Finished request \(i)")
                myGroup.leave()
            }
        }
        myGroup.notify(queue: .main){
            print("All finished")
            DispatchQueue.main.async {
                self.tableView.reloadData()
                self.refreshControl.endRefreshing()
            }
        }
    }

表格 View :

override func viewDidLoad() {
        super.viewDidLoad()
        refreshData()
        if #available(iOS 10.0, *){
            tableView.refreshControl = refreshControl
        } else{
            tableView.addSubview(refreshControl)
        }
    }

    public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    {
        return listOfTodos.count
    }

    public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
    {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ViewControllerTableViewCell
        let todo = listOfTodos[indexPath.row]
        let image = listOfImages[indexPath.row]
        cell.myLabel.text = todo.name
        cell.myImage.image = convert64BaseToImage(binary: image.binary)
        return(cell)
    }

如果我按照代码所示进行操作,tableView 在启动后是空的,但是当我下拉时它有数据。我究竟做错了什么?如果我只执行一个单独的请求,tableView 会被填满...似乎在数据完全加载之前调用了 tableView。

最佳答案

您的 refreshData() 函数中存在严重的设计错误

函数 getAllToDo()getAllImages() 都是异步。换句话说,即使在函数返回后,它们也会继续处理。您当前的 refreshData 错误地假设此类处理在每个函数返回时结束 - 事实并非如此!因此,您离开 DispatchGroup 的时间过早。最后,这完美地解释了您的经验观察:

It seems like that the tableView is called before the data is completely loaded.

处理异步通信的标准模式是在调用异步函数时将完成处理程序传递给异步函数。例如:

func getAllToDo(completion: () -> Void) {
    ...
    getAllToDosRequest.responseJSON { response in
        switch response.result { 
            ...
        }
        completion() // Processing is done!
    }
 }

然后,在您的 refreshData() 函数中,定义一个适当的完成处理程序:

getAllToDo {
    print("Finished request \(i)")
    myGroup.leave()
}

当然,您也必须对 getAllImages() 函数进行相同的重构。


顺便说一下,你可以简化这个:

myGroup.notify(queue: .main){
    print("All finished")
    DispatchQueue.main.async {
        self.tableView.reloadData()
        self.refreshControl.endRefreshing()
    }
}

作为:

myGroup.notify(queue: .main){
    print("All finished")
    self.tableView.reloadData()
    self.refreshControl.endRefreshing()
}

您正在推迟您所在的相同队列的工作——这不会带来任何性能提升。事实上,相反可能会发生,因为:队列处理开销;锁定成本;上下文切换;等

关于Swift 3 - 异步请求表为空,刷新后填充,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43814697/

相关文章:

ios - 方法 selectRow(at :animated:scrollPosition:) doesn't call tableView(_:didSelectRowAt:)

ios - 在 TableView 中显示结构数组

ios - Swift - 只允许在 UITableViewCell 中选中一个复选框

iphone - 为什么我的委托(delegate)方法没有被调用?

c# - 是否可以同步调用异步调用?

javascript - 使用回调参数获取匿名函数中的值

c++ - 在相应的 promise 离开范围后有效使用 future

ios - tableview RowInSection fatal error swift

swift - Realm 结果值未分配

swift - 协议(protocol)中的方法