首页 > 解决方案 > 使用 `DispatchGroup` 或一些并发结构来加载数据并按顺序填充 `UITableViewController` 中的单元格

问题描述

平台:

我在 swift 4 和 xcode 11.4

用例和期望的行为

该应用程序正在加载可能包含 100 或 1000 件商品的提要,比如说 500 件商品。Amplify使用's查询将抓取 500 个项目GraphQL,然后每个项目将加载附加数据。数据将填充UITableViewController. 理想情况下,此过程将按以下确切顺序发生:

  1. query500 项
  2. cell_1 加载附加数据。
  3. cell_1 渲染数据并显示在UITableViewController
  4. cell_2 加载附加数据。
  5. cell_2 渲染数据并显示在UITableViewController

...

  1. cell_500 加载附加数据
  2. cell_500 渲染数据并显示在UITableViewController

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

问题

这似乎是一个需要对执行进行更精细控制的用例,这需要:https ://developer.apple.com/documentation/dispatch/dispatchgroup

我是 Swift 新手,所以这对我来说有点先进。提供了用于GraphQL查询的存根、加载附加数据的类函数以及顶层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")
            }
        }
    }
}

附录

如果我要求的顺序是不可能的,我也会满足于query500 个项目,load每个项目都有额外的数据,然后渲染单元格。但无论哪种方式,单元格都不应使用空数据呈现。

标签: iosswiftdispatchgroup

解决方案


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

宣布loadData()

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

并确保在任何completion()情况下都调用它(这是至关重要的!)例如

default:
    completion()
    print("failed")

DispatchGroup正确使用,您必须在调用异步任务之前在循环enter 内部leave调用,并在任务的完成处理程序中调用。最后在循环外实现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()
        }

    }
}

要按顺序插入和更新项目,您需要一个 asynchronousOperation和一个 serial OperationQueueDispatchGroup不保留订单。


推荐阅读