问题是如何在允许执行继续之前等待 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/