OperationsQueue 完成所有操作后如何重新加载 UITableView
How to reload UITableView when OperationsQueue is done with all operations
我正在尝试通过调用 .reloadData()
来更新 UITableView
中的单元格,但它会在进程完成之前重新加载数据,因此并没有真正更新任何内容,因为它从中获取数据的数组仍然存在空的。我构建代码的方式是它进行了一堆 API 调用,每个调用都是 Swift Operation
。 A OperationQueue
执行这些操作。现在需要做的是当所有任务都完成后调用 self.tableView.reloadData()
。我试过将它放在操作的完成块中,但它不是主线程,因此不会执行。
如有任何帮助,我们将不胜感激。
编辑:我尝试使用 OperationQueue.main
将 OperationQueue
的线程更改为主线程,并在所有其他操作完成后添加最终操作 运行包含 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()
}
}
有两种可能的方法:
创建一个您希望在所有其他操作完成后执行的新操作,我将其称为 "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。
另一种方法是使用调度组。每次您创建一个单独的操作时,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(留在操作队列范例中),但这两种方法都可行。
我正在尝试通过调用 .reloadData()
来更新 UITableView
中的单元格,但它会在进程完成之前重新加载数据,因此并没有真正更新任何内容,因为它从中获取数据的数组仍然存在空的。我构建代码的方式是它进行了一堆 API 调用,每个调用都是 Swift Operation
。 A OperationQueue
执行这些操作。现在需要做的是当所有任务都完成后调用 self.tableView.reloadData()
。我试过将它放在操作的完成块中,但它不是主线程,因此不会执行。
如有任何帮助,我们将不胜感激。
编辑:我尝试使用 OperationQueue.main
将 OperationQueue
的线程更改为主线程,并在所有其他操作完成后添加最终操作 运行包含 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()
}
}
有两种可能的方法:
创建一个您希望在所有其他操作完成后执行的新操作,我将其称为 "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
returnstrue
) 并确保它在完成后正确执行isFinished
/isExecuting
KVO。另一种方法是使用调度组。每次您创建一个单独的操作时,
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(留在操作队列范例中),但这两种方法都可行。