Swift 4. 在继续执行之前等待 HealthKit HKQuery 的异步结果

标签 swift asynchronous grand-central-dispatch healthkit completionhandler

问题是如何在允许执行继续之前等待 HealthKit 上的异步查询返回结果。返回的数据对于进一步执行至关重要。

我知道这个问题已经被问过/解决了很多次,并且我已经阅读了很多帖子,但是我尝试过完成处理程序、调度同步和调度组,但无法提出有效的实现。

使用完成处理程序Wait for completion handler to finish - Swift

这会调用一个方法来运行 HealthKit 查询:

func readHK() {

    var block: Bool = false

    hk.findLastBloodGlucoseInHealthKit(completion: { (result) -> Void in
        block = true

        if !(result) {
            print("Problem with HK data")
        }

        else {
            print ("Got HK data OK")
        }

    })

    while !(block) {
    }

    // now move on to the next thing ...

}

这确实有效。从概念上讲,使用“ block ”变量来保持等待回调的执行似乎与阻塞信号量没有什么不同,但如果由于任何原因完成没有返回,它真的很难看并且会带来麻烦。有更好的办法吗?

使用调度组

如果我将调度组放在调用函数级别:

调用函数:

func readHK() {

    var block: Bool = false
    dispatchGroup.enter()

    hk.findLastBloodGlucoseInHealthKit(dg: dispatchGroup)
    print ("Back from readHK")

    dispatchGroup.notify(queue: .main) {
        print("Function complete")
        block = true
    }

    while !(block){   
    }
}

接收函数:

func findLastBloodGlucoseInHealthKit(dg: DispatchGroup) {

        print ("Read last HK glucose")

        let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
        let query = HKSampleQuery(sampleType: glucoseQuantity!, predicate: nil, limit: 10, sortDescriptors: [sortDescriptor]) { (query, results, error) in
            // .... other stuff
            dg.leave()

完成执行正常,但 .notify 方法从未被调用,因此 block 变量从未更新,程序挂起并且永远不会从 while 语句退出。

将 Dispatch Group 放入目标函数中,但将 .notify 保留在调用级别:

func readHK() {

    var done: Bool = false

    hk.findLastBloodGlucoseInHealthKit()
    print ("Back from readHK")

    hk.dispatchGroup.notify(queue: .main) {
        print("done function")
        done = true
    }

    while !(done) {
    }

}

同样的问题。

使用调度

文档和其他 S.O 帖子说:“如果您想等待 block 完成,请改用sync()方法。”

但是“完整”是什么意思呢?似乎这并不意味着完成该功能并获得稍后的异步完成。例如,下面的代码在完成返回之前不会保留执行:

func readHK() {

    DispatchQueue.global(qos: .background).sync {
        hk.findLastBloodGlucoseInHealthKit()
    }

    print ("Back from readHK")

}

感谢您的帮助。

最佳答案

是的,请不要对抗事物的异步本质。你几乎总是会失败,要么是因为制作了一个低效的应用程序(计时器和其他延迟),要么是通过实现自己的阻止功能来为难以诊断的错误创造机会。

我远不是 Swift/iOS 专家,但看来您最好的选择是使用 Grand Central Dispatch 或用于管理异步工作的第三方库之一。看PromiseKit ,例如,尽管我还没有看到像 JavaScript 的 bluebird 那样好的 Swift Promises/Futures 库。 .

您可以使用 DispatchGroup 来跟踪查询的完成处理程序。在设置查询时调用“enter”方法,并在结果处理程序末尾调用“leave”方法,而不是在设置或执行查询之后调用。即使查询完成但出现错误,也请确保退出。我不确定您为什么遇到麻烦,因为这在我的应用程序中运行良好。我认为,诀窍是确保无论出现什么问题,您始终“leave()”调度组。

如果您愿意,可以在 DispatchQueue 中设置屏障任务——这只会在队列中所有较早的任务完成时执行——而不是使用 DispatchGroup。您可以通过添加正确的 options 来完成此操作。到 DispatchWorkItem。

关于Swift 4. 在继续执行之前等待 HealthKit HKQuery 的异步结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53091348/

相关文章:

c# - 为什么修改数据库数据的查询是可等待的,而只读取数据的查询不是?

swift - requestAccessForMediaType 不请求许可

swift - UIView 阴影不可见

ios - AudioKit iOS : how can an input node be dynamically connected to mixer in active chain?

c# - 如何在异步多线程爬虫中锁定回调类?

c# - 我的 azure Http 触发器遇到错误,因为它不是异步的。为什么我的方法不是异步的?

ios - NSURL 实现的 UITableView 上的 GCD

ios - 使用调度组时如何使公共(public)资源线程安全?

ios - 当动画添加到 subview 时立即调用 CATransaction 完成 block

swift - 如何在 View Controller 中以编程方式切换 View ? (Xcode,iPhone)