问题是如何在允许执行继续之前等待HealthKit上的异步查询返回结果。返回的数据对于进一步执行至关重要。
我知道这个问题已经被问到/解决了很多次,我已经阅读了很多帖子,但是我尝试了完成处理程序,Dispatch同步和Dispatch组,并且无法提出可行的实现方案。
使用完成处理程序 每个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()
完成执行OK,但是从不调用.notify方法,因此block变量从不更新,程序挂起,也不会从while语句退出。
在目标函数中放入调度组,但在调用级别保留.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帖子说:“如果要等待块完成,请使用sync()方法。
但是“完整”是什么意思?似乎并不意味着完成该功能并获得以后的异步完成。例如,在完成返回之前,以下内容不保留执行:
func readHK() {
DispatchQueue.global(qos: .background).sync {
hk.findLastBloodGlucoseInHealthKit()
}
print ("Back from readHK")
}
谢谢您的帮助。
答案 0 :(得分:0)
是的,请不要与事物的异步性质作斗争。通过制作效率低下的应用(计时器和其他延迟),或者通过实现自己的阻止功能为难以诊断的错误创造机会,您几乎总是会迷失。
我离Swift / iOS专家还很远,但是看来最好的选择是使用Grand Central Dispatch或第三方库之一来管理异步工作。例如,看看PromiseKit,尽管我还没有看到像JavaScript的bluebird这样的Swift Promises / Futures库那么好。
您可以使用DispatchGroup跟踪查询的完成处理程序。设置查询时,调用“ enter”方法,并在结果处理程序的末尾(而不是在设置或执行查询之后)调用“ leave”。即使查询完成并出现错误,也请确保退出。我不确定您为什么会遇到麻烦,因为这在我的应用程序中效果很好。我认为,诀窍是确保无论出什么问题,您都始终“离开”调度组。
如果愿意,可以在DispatchQueue中设置屏障任务-仅在队列中所有较早的任务完成后才执行-而不使用DispatchGroup。为此,您可以在DispatchWorkItem中添加正确的options。