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

回答:

一些观察:

  1. 它总是执行2之前1处的值。获得描述的行为的唯一方法是,如果要在for循环内执行其他操作,而该操作本身就是异步的。如果是这种情况,您将使用一个调度组来解决该问题(或重构代码以处理异步模式)。但是,如果看不到for循环中的内容,则很难进一步评论。仅问题中的代码不应显示您所描述的问题。一定是别的东西。

  2. 无关的,您应该注意,在异步执行的for循环中更新模型对象(假设它正在后台线程上运行)是有点危险的。更新局部变量,然后通过完成处理程序将其传递回去,让调用者负责将模型更新和UI更新都分派到主队列,这要安全得多。

  3. 在注释中,您提到在for循环中您正在执行异步操作,并且必须在调用completionHandler之前完成某些操作。因此,您将使用调度组来确保仅在完成所有异步任务之后才发生这种情况。

  4. 请注意,由于您在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

回到顶部