OperationsQueue 完成所有操作后如何重新加载 UITableView

How to reload UITableView when OperationsQueue is done with all operations

我正在尝试通过调用 .reloadData() 来更新 UITableView 中的单元格,但它会在进程完成之前重新加载数据,因此并没有真正更新任何内容,因为它从中获取数据的数组仍然存在空的。我构建代码的方式是它进行了一堆 API 调用,每个调用都是 Swift Operation。 A OperationQueue 执行这些操作。现在需要做的是当所有任务都完成后调用 self.tableView.reloadData() 。我试过将它放在操作的完成块中,但它不是主线程,因此不会执行。

如有任何帮助,我们将不胜感激。

编辑:我尝试使用 OperationQueue.mainOperationQueue 的线程更改为主线程,并在所有其他操作完成后添加最终操作 运行包含 self.tableView.reloadData()。虽然这允许我重新加载 UITable,但它会导致 UI 滞后。 (我在 UITableView 中有一个刷新,这使得主线程的使用很明显。)

示例代码:

let operation1 = BlockOperation {
//Gets Data from server & waits until task is complete before ending
}

let operation2 = BlockOperation {
//Parse JSON
}

let updateOperation = BlockOperation {
    self.tableView.reloadData()
    //Throws a "UITableView.reloadData() must be used from main thread only"
}

operation2.addDependency(operation1)
updateOperation.addDependency(operation2)

let queue = OperationQueue()
queue.addOperation(operation1)
queue.addOperation(operation2)
queue.addOperation(completionOperation)

从完成块中,您可以使用 Grand Central Dispatch 在主线程上执行另一个块。使用

DispatchQueue.main.async {
    self.tableView.reloadData()
    // your other code here
}

使用 DispatchGroup。

let dispatchGroup = DispatchGroup()

dispatchGroup.enter()

asyncTask1() { dispatchGroup.leave() }

asyncTask2() { dispatchGroup.leave() }

asyncTask3() { dispatchGroup.leave() }

dispatchGroup.notify(队列:.main){<br> //重新加载table }

你能使用完成处理程序示例吗:

func reloadData(completion: @scaping (completed: Bool)->Void) {
     //your code
     completion(true)
}

调用函数

reloadData() {(completed : Bool) in
    if completed {
      tableView.reloadData()
    }    
}

有两种可能的方法:

  1. 创建一个您希望在所有其他操作完成后执行的新操作,我将其称为 "completion operation"。然后,每次您创建一个现有的单独操作时,将该操作作为依赖项添加到您的 "completion operation"。然后,当您完成添加所有单独的操作(并将每个操作作为依赖项添加到您的“完成操作”)时,然后将您的完成操作添加到主队列。它不会触发,直到所有其他单独操作完成了。

    例如:

    let queue = OperationQueue()
    queue.maxConcurrentOperationCount = 4
    
    let completionOperation = BlockOperation {
        print("done")
    }
    
    for _ in 0 ..< 10 {
        let operation = ...
        completionOperation.addDependency(operation)
        queue.addOperation(operation)
    }
    
    OperationQueue.main.addOperation(completionOperation)
    

    请注意,这假定您已仔细创建操作,以便在完成各个操作中的任务之前操作不会完成。值得注意的是,如果您的操作正在启动一个任务,该任务本身就是一个异步任务(例如网络请求),请确保将 Operation 子类定义为 "asynchronous" 操作(isAsynchronous returns true) 并确保它在完成后正确执行 isFinished/isExecuting KVO。

  2. 另一种方法是使用调度组。每次您创建一个单独的操作时,enter 组(不是在操作本身中,而是在您创建这些操作并将它们添加到您的操作队列中时)。然后,作为您个人操作中的最后一个任务,leave 小组。然后,当您完成添加所有个人操作后,您可以创建一个调度组 notify,您可以在主队列上调度它。然后,当所有单独操作时,您的调度组通知块将触发。

    例如:

    let queue = OperationQueue()
    queue.maxConcurrentOperationCount = 4
    
    let group = DispatchGroup()
    
    for _ in 0 ..< 10 {
        group.enter()
        let operation = BlockOperation {
            ...
            group.leave()
        }
        queue.addOperation(operation)
    }
    
    group.notify(queue: .main) {
        print("done")
    }
    

我倾向于选项 1(留在操作队列范例中),但这两种方法都可行。