我是初学者,在使我的代码正常工作方面遇到困难。
我有一个在类的初始化中调用的函数:
doLongTask(forPast: 6)
这是我与调度小组一起创建的游乐场:
//MAKE THE STRUCT
struct Calorie: Identifiable {
private static var idSequence = sequence(first: 1, next: {$0 + 1})
var id: Int
var day: Date
var activeCal: CGFloat
var restingCal: CGFloat
var dietaryCal: CGFloat
init?(id: Int, day: Date, activeCal:CGFloat, restingCal:CGFloat, dietaryCal:CGFloat) {
guard let id = Calorie.idSequence.next() else { return nil}
self.id = id
self.day = day
self.activeCal = activeCal
self.restingCal = restingCal
self.dietaryCal = dietaryCal
}
}
//CREATE HEALTHSTORE
let healthStore = HKHealthStore()
//MAKE TEST ARRAY
var testCalorieArray = [Calorie]()
func doLongTask(forPast days: Int) {
print("Enter the function!")
print("---")
func getTempEnergy (for type:HKQuantityType!, unit u:HKUnit!, start fromDate:Date, end endDate:Date, completion: @escaping (Double) -> Void) {
let countQuantityType = type
let predicate = HKQuery.predicateForSamples(withStart: fromDate, end: endDate, options: .strictStartDate)
let query = HKStatisticsQuery(quantityType: countQuantityType!, quantitySamplePredicate: predicate, options: .cumulativeSum) { (_, result, error) in
var resultCount = 0.0
guard let result = result else {
return
}
if let sum = result.sumQuantity() {
resultCount = sum.doubleValue(for: u)
}
DispatchQueue.main.async {
print(resultCount)
completion(resultCount)
}
}
healthStore.execute(query)
}
let queue = DispatchQueue(label: "com.WILDFANGmedia.queues.serial")
let group = DispatchGroup()
let now = Calendar.current.startOfDay(for: Date())
//Initialize to test values to see if they get overwritten
var _activeEnergyBurned:CGFloat = 99.9
var _restingEnergyBurned:CGFloat = 99.9
var _dietaryEnergyConsumed:CGFloat = 99.9
//EACH DAY
for day in 0...days {
group.enter()
queue.async(group: group) {
// Start und Enddatum
let fromDate = Calendar.current.date(byAdding: .day, value: -day-1, to: now)!
let endDate = Calendar.current.date(byAdding: .day, value: -day, to: now)!
getTempEnergy(for: HKQuantityType.quantityType(forIdentifier: .activeEnergyBurned), unit: HKUnit.kilocalorie(), start: fromDate, end: endDate, completion: { (activeEnergyBurned) in
print(activeEnergyBurned)
_activeEnergyBurned = CGFloat(activeEnergyBurned)
})
print("End Datum: \(endDate)")
print("Active Cal: \(_activeEnergyBurned)")
print("Day \(day) done")
print("---")
testCalorieArray.append(Calorie(id: 1, day: endDate, activeCal: CGFloat(_activeEnergyBurned), restingCal: CGFloat(_restingEnergyBurned), dietaryCal: CGFloat(_dietaryEnergyConsumed))!)
group.leave()
}
}
//SHOW WHAT'S IN THE ARRAY
group.notify(queue: queue) {
print("All tasks done")
print(testCalorieArray)
print("---")
}
//AFTER LOOP, GO ON WITH BUSINESS
print("Continue execution immediately")
}
doLongTask(forPast: 6)
print("AFTER THE FUNCTION")
//TEST LOOP TO RUN LONGER
for i in 1...7 {
sleep(arc4random() % 2)
print("Outter Row Task \(i) done")
}
print(testCalorieArray)
它应该做的是进行HKStatisticsQuery调用(以后将进行3次调用)并将结果写回到我的数组中。但是,它将在函数完成之前写入数组,因此不会返回正确的值。我已经尝试了几天的调度小组,但被困了。
测试循环结束后,在最后一刻打印出getEnergyTemp-function的完成处理程序中的print(value),并打印出正确的值。
我要去哪里错了?我以为我已经理解了这个原理,但是我无法使它生效。
提前感谢!
主要问题是您在错误的位置拨打了leave
。因此,而不是:
for day in 0...days {
group.enter()
queue.async(group: group) {
...
getTempEnergy(for: HKQuantityType.quantityType(forIdentifier: .activeEnergyBurned), unit: HKUnit.kilocalorie(), start: fromDate, end: endDate, completion: { (activeEnergyBurned) in
print(activeEnergyBurned)
_activeEnergyBurned = CGFloat(activeEnergyBurned)
})
...
testCalorieArray.append(Calorie(id: 1, day: endDate, activeCal: CGFloat(_activeEnergyBurned), restingCal: CGFloat(_restingEnergyBurned), dietaryCal: CGFloat(_dietaryEnergyConsumed))!)
group.leave()
}
}
[请记住,getTempEnergy
闭包是异步调用的(即稍后)。您需要移动leave
调用并附加结果inside该闭包:
for day in 0...days {
group.enter()
queue.async {
...
getTempEnergy(for: .quantityType(forIdentifier: .activeEnergyBurned), unit: .kilocalorie(), start: fromDate, end: endDate) { (activeEnergyBurned) in
testCalorieArray.append(Calorie(id: 1, day: endDate, activeCal: CGFloat(activeEnergyBurned), restingCal: CGFloat(_restingEnergyBurned), dietaryCal: CGFloat(_dietaryEnergyConsumed))!)
group.leave()
}
}
}
请注意,这使该旧变量_activeEnergyBurned
已过时,您现在可以将其删除。
无关,但:
注意,在queue.async(group: group)
通话中,我已经删除了该组。当您手动调用enter
和leave
时,group
的async
参数现在是多余的。
您通常在处理本身是异步的调用时使用enter
/ leave
模式(在这种情况下),或者在分派的代码是同步的时对group
使用async
参数。但不是两个。
请特别注意getTempEnergy
中的所有路径都必须调用完成处理程序(否则,您的DispatchGroup
可能永远无法解析)。因此,在guard
内的getTempEnergy
语句中还必须调用completion
。
这引出了为completion
闭包提供什么值的问题。一种方法是使Double
参数为可选,并在出错时返回nil
。或者,更健壮的方法是使用Result
类型:
func getTempEnergy (for type: HKQuantityType?, unit u: HKUnit, start fromDate: Date, end endDate: Date, completion: @escaping (Result<Double, Error>) -> Void) {
guard let countQuantityType = type else {
completion(.failure(HKProjectError.invalidType))
return
}
let predicate = HKQuery.predicateForSamples(withStart: fromDate, end: endDate, options: .strictStartDate)
let query = HKStatisticsQuery(quantityType: countQuantityType, quantitySamplePredicate: predicate, options: .cumulativeSum) { _, result, error in
DispatchQueue.main.async {
guard error == nil else {
completion(.failure(error!))
return
}
guard let resultCount = result?.sumQuantity()?.doubleValue(for: u) else {
completion(.failure(HKProjectError.noValue))
return
}
completion(.success(resultCount))
}
}
healthStore.execute(query)
}
然后
for day in 0...days {
group.enter()
queue.async {
let fromDate = Calendar.current.date(byAdding: .day, value: -day-1, to: now)!
let endDate = Calendar.current.date(byAdding: .day, value: -day, to: now)!
getTempEnergy(for: .quantityType(forIdentifier: .activeEnergyBurned), unit: .kilocalorie(), start: fromDate, end: endDate) { result in
switch result {
case .failure(let error):
print(error)
case .success(let activeEnergyBurned):
testCalorieArray.append(Calorie(id: 1, day: endDate, activeCal: CGFloat(activeEnergyBurned), restingCal: CGFloat(_restingEnergyBurned), dietaryCal: CGFloat(_dietaryEnergyConsumed))!)
}
group.leave()
}
}
}
您的自定义错误在哪里:
enum HKProjectError: Error {
case noValue
case invalidType
}
避免使用sleep
。那阻塞了当前线程。如果您要定期检查运行情况,请使用计时器。