Swift关闭异步执行顺序
在我的模型中,具有获取数据的功能,该数据需要完成处理程序作为参数:
func fetchMostRecent(completion: (sortedSections: [TableItem]) -> ()) { self.addressBook.loadContacts({
(contacts: [APContact]?, error: NSError?) in
// 1
if let unwrappedContacts = contacts {
for contact in unwrappedContacts {
// handle constacts
...
self.mostRecent.append(...)
}
}
// 2
completion(sortedSections: self.mostRecent)
})
}
它正在调用另一个函数,该函数执行联系人的异步加载,我将完成情况转发到该函数
fetchMostRecent
具有完成的调用如下所示:
model.fetchMostRecent({(sortedSections: [TableItem]) in dispatch_async(dispatch_get_main_queue()) {
// update some UI
self.state = State.Loaded(sortedSections)
self.tableView.reloadData()
}
})
有时这是可行的,但是执行的顺序常常不是我期望的那样。问题是,有时completion()
下// 2
的范围之前执行if
下// 1
结束了。
这是为什么?如何确保// 2
在之后开始执行// 1
?
回答:
一些观察:
它总是执行2之前1处的值。获得描述的行为的唯一方法是,如果要在for循环内执行其他操作,而该操作本身就是异步的。如果是这种情况,您将使用一个调度组来解决该问题(或重构代码以处理异步模式)。但是,如果看不到for循环中的内容,则很难进一步评论。仅问题中的代码不应显示您所描述的问题。一定是别的东西。
无关的,您应该注意,在异步执行的for循环中更新模型对象(假设它正在后台线程上运行)是有点危险的。更新局部变量,然后通过完成处理程序将其传递回去,让调用者负责将模型更新和UI更新都分派到主队列,这要安全得多。
在注释中,您提到在
for
循环中您正在执行异步操作,并且必须在调用completionHandler之前完成某些操作。因此,您将使用调度组来确保仅在完成所有异步任务之后才发生这种情况。请注意,由于您在
for
循环内执行异步操作,因此不仅需要使用调度组来触发这些异步任务的完成,而且可能还需要创建自己的同步队列(您不应该进行突变来自多个线程的数组)。因此,您可以为此创建一个队列。
综合所有这些,您最终会得到以下结果:
func fetchMostRecent(completionHandler: ([TableItem]?) -> ()) { addressBook.loadContacts { contacts, error in
var sections = [TableItem]()
let group = dispatch_group_create()
let syncQueue = dispatch_queue_create("com.domain.app.sections", nil)
if let unwrappedContacts = contacts {
for contact in unwrappedContacts {
dispatch_group_enter(group)
self.someAsynchronousMethod {
// handle contacts
dispatch_async(syncQueue) {
let something = ...
sections.append(something)
dispatch_group_leave(group)
}
}
}
dispatch_group_notify(group, dispatch_get_main_queue()) {
self.mostRecent = sections
completionHandler(sections)
}
} else {
completionHandler(nil)
}
}
}
和
model.fetchMostRecent { sortedSections in guard let sortedSections = sortedSections else {
// handle failure however appropriate for your app
return
}
// update some UI
self.state = State.Loaded(sortedSections)
self.tableView.reloadData()
}
或者,在Swift 3中:
func fetchMostRecent(completionHandler: @escaping ([TableItem]?) -> ()) { addressBook.loadContacts { contacts, error in
var sections = [TableItem]()
let group = DispatchGroup()
let syncQueue = DispatchQueue(label: "com.domain.app.sections")
if let unwrappedContacts = contacts {
for contact in unwrappedContacts {
group.enter()
self.someAsynchronousMethod {
// handle contacts
syncQueue.async {
let something = ...
sections.append(something)
group.leave()
}
}
}
group.notify(queue: .main) {
self.mostRecent = sections
completionHandler(sections)
}
} else {
completionHandler(nil)
}
}
}
以上是 Swift关闭异步执行顺序 的全部内容, 来源链接: utcz.com/qa/402672.html