只有在所有循环成功后,才能使用DispatchGroup在for-in循环中处理异步回调

时间:2018-11-22 02:55:20

标签: swift grand-central-dispatch

下面的代码是该任务的粗略草图。查询数据库,它返回结果的集合,对该集合进行循环以搜索特定的属性,如果找到该属性,则立即查询文件存储,并且其异步完成处理程序在循环中返回文件。因为我在for-in循环中处理异步回调,所以我使用DispatchGroup来管理它。仅当集合中的所有文档都具有someIdentifier属性时,此设置才有效。如果集合中的一个文档不具有该属性,则调度组从不调用notify(),而我们陷入了困境。

someDatabaseQuery.retrieveSomeData { (data, error) in

    guard let data = data, error == nil else {
        return
    }

    // database has retrieved data, create dispatch group
    let dispatchGroup = DispatchGroup()

    for document in data { // loop through collection

        // check if document has some identifier
        guard let someIdentifier = document["someIdentifier"] as? String else {
            return
        }

        dispatchGroup.enter() // identifier found, enter dispatch

        // perform async operation inside loop
        Filestorage.getSomeFile(forURL: someIdentifier) { (data, error) in

            guard let file = data, error == nil else {
                return
            }

            // download the file
            dispatchGroup.leave() // leave dispatch

        }

    }

    dispatchGroup.notify(queue: .main) {

        // all data grabbed, load table

    }

}

2 个答案:

答案 0 :(得分:3)

如果您致电leave,则必须致电enter。但是,即使您调用了guardgetSomeFile完成块中的leave仍可以阻止对enter的调用。

一种解决方案是在完成块内使用defer。在leave内调用defer以确保无论您离开该块如何都调用它。

答案 1 :(得分:0)

guard let someIdentifier = document["someIdentifier"] as? String else {
    continue // this is the proper control flow statement
}

问题仅仅是选择了错误的控制流语句。当守卫在循环内失败时,return阻止循环结束,并且从未给调度组提供通知的机会。警卫队中的其他条款应该是continue,它将控制权保持在循环内(通过使其完成),从而使调度组有机会进行通知。