使用 `DispatchGroup` 或某些并发构造来加载数据并顺序填充 `UITableViewController` 中的单元格

Using `DispatchGroup` or some concurency construct to load data and populate cells in `UITableViewController` sequentially

平台:

我在 swift 4 和 xcode 11.4

用例和期望的行为

该应用正在加载可能包含 100 或 1000 项的供稿,假设 500 项。使用 AmplifyGraphQL 查询将抓取 500 个项目,然后每个项目将加载 额外的 数据。数据将填充 UITableViewController 中的单元格。理想情况下,此过程将按以下确切顺序发生:

  1. query 500 件商品
  2. cell_1 加载额外的数据。
  3. cell_1 渲染数据并显示在 UITableViewController
  4. cell_2 加载额外的数据。
  5. cell_2 渲染数据并在 UITableViewController
  6. 中显示

...

  1. cell_500加载额外数据
  2. cell_500 渲染数据并在 UITableViewController
  3. 中显示

因此用户将看到在提要中呈现的单元格“瀑布”。

问题

这似乎是一个需要更好地控制执行的用例,需要这样:https://developer.apple.com/documentation/dispatch/dispatchgroup

我是 Swift 的新手,所以这对我来说有点高级。提供的是 GraphQL 查询的存根,加载附加数据的 class 函数,以及顶层 UITableViewController。请指导我如何使用 DispatchGroup.

class Feed: UITableViewController {
    
    var dataSource: [FullItem] = []

    override func viewDidLoad(){
       super.viewDidLoad()
       
       queryItem{ items
           
           for item in items {
              let itemInstanceWithMoreData = FullItem( id: item.id )
              itemInstanceWithMoreData.loadFullData()
           }
         
       }           
    }
}


func queryItems( callBack: @escaping ([Item]) -> Void ){

    _ = Amplify.API.query(from: Item.self, where: predicate) { (event) in
        switch event {
            case .completed(let result):
                switch result {
                    case .success(let xs):
                        callBack(xs)
                    case .failure: 
                        break
                }
            case .failed: 
                break
            default:
                break
        }
    }
}


class FullItem {
    
    id: String
    name: String?
    
    init( id ){ self.id = id; self.name = "" }

    
    func loadData(){

        let _ = Amplify.API.query(from: FullItem.self, byId: self.id) { (event) in
            
            switch event {
                case .completed(let res):
                    switch res{
                        case .success (let musr):
                            if (musr != nil){
                                self.name = musr!.name
                            } else {
                                break
                            }
                        default:
                           break
                    }
                default:
                    print("failed")
            }
        }
    }
}

附录

如果我要求的顺序不可行,我也会选择 query 500 个项目,每个项目 load 附加数据,然后渲染单元格。但无论哪种方式,单元格都不应呈现空数据。

您的示例不完整且无法编译,所以这是简短版本

声明loadData()

func loadData(completion: @escaping () -> Void) {

并确保 completion() 任何 情况下被调用(这很重要!)例如

default:
    completion()
    print("failed")

要正确使用 DispatchGroup 你必须在调用异步任务之前调用 enter inside 循环并在完成时调用 leave任务的处理者。最后 outside 循环实现 notify

override func viewDidLoad(){
    super.viewDidLoad()

    let group = DispatchGroup()

    queryItems { items

        for item in items {
            group.enter()
            let itemInstanceWithMoreData = FullItem( id: item.id )
            itemInstanceWithMoreData.loadData {
                group.leave()
            }
        }

        group.notify(queue: .main) {
            self.tableView.reloadData()
        }

    }
}

要按顺序顺序插入和更新项目,您需要一个异步Operation 和一个串行OperationQueueDispatchGroup 不保留顺序。