Swift 调度组未通知?

Swift 调度组未通知?,swift,grand-central-dispatch,Swift,Grand Central Dispatch,我在另一个应用程序中有这个相同的代码,它可以正常工作,现在在这个应用程序中,调度组没有发出通知,我的处理程序也没有被调用?我无法找出这两个应用程序之间的区别,因为代码是相同的 func getHeartRateMaxFromWorkouts(workouts: [HKWorkout], handler: @escaping (careerMaxHeartRatePerWorkoutAsCustomHistoricalSample, careerAverageHeartRatePerWorkou

我在另一个应用程序中有这个相同的代码,它可以正常工作,现在在这个应用程序中,调度组没有发出通知,我的处理程序也没有被调用?我无法找出这两个应用程序之间的区别,因为代码是相同的

 func getHeartRateMaxFromWorkouts(workouts: [HKWorkout], handler: @escaping (careerMaxHeartRatePerWorkoutAsCustomHistoricalSample, careerAverageHeartRatePerWorkoutAsCustomHistoricalSample) -> Void) {

        let workoutsReversed = workouts.reversed()
        guard let heartRateType:HKQuantityType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate) else { return }
        let heartRateUnit:HKUnit = HKUnit(from: "count/min")
        var heartRateMaxArrayAsCustomHistoricalSample = [CustomHistoricalSample]()
        var heartRateAvgArrayAsCustomHistoricalSample = [CustomHistoricalSample]()

        //DispatchGroup needed since making async call per workout and need notified when all calls are done
        let dispatchGroup = DispatchGroup()

        for workout in workoutsReversed {

            //predicate
            let startDate = workout.startDate
            let endDate = workout.endDate

            let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: .strictStartDate)

            //descriptor
            let sortDescriptors = [
                NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: true) //Changed this to false so that HRR and MCS would calculate, always check this if not getting these values
            ]
            dispatchGroup.enter()
            let heartRateQuery = HKSampleQuery(sampleType: heartRateType,
                                               predicate: predicate,
                                               limit: (HKObjectQueryNoLimit),
                                               sortDescriptors: sortDescriptors)
                { (query:HKSampleQuery, results:[HKSample]?, error:Error?) -> Void in

                    guard error == nil else { print("get heart rate error"); return }

                    guard let unwrappedResults = results as? [HKQuantitySample] else { print("get heart rate error"); return}

                    let heartRatesAsDouble = unwrappedResults.map {$0.quantity.doubleValue(for: heartRateUnit)}

                    guard let max = heartRatesAsDouble.max() else { return }

                    let maxAsCustomHistoricalSample = CustomHistoricalSample(value: max, date: workout.startDate)
                    heartRateMaxArrayAsCustomHistoricalSample.append(maxAsCustomHistoricalSample)

                    let average = heartRatesAsDouble.average
                    let averageAsCustomHistoricalSample = CustomHistoricalSample(value: average, date: workout.startDate)
                    heartRateAvgArrayAsCustomHistoricalSample.append(averageAsCustomHistoricalSample)

                    dispatchGroup.leave()

                }
            healthStore.execute(heartRateQuery)

        } //End of for workout loop

        dispatchGroup.notify(queue: .main) {

            //Need to sort by date since the dates come back jumbled 
            let sortedReversedHeartRateMaxArrayAsCustomHistoricalSampple = heartRateMaxArrayAsCustomHistoricalSample.sorted { $0.date > $1.date }.reversed() as [CustomHistoricalSample]

             let sortedReversedHeartRateAverageArrayAsCustomHistoricalSampple = heartRateAvgArrayAsCustomHistoricalSample.sorted { $0.date > $1.date }.reversed() as [CustomHistoricalSample]
            print("handler called = \(sortedReversedHeartRateMaxArrayAsCustomHistoricalSampple.count)")

            handler(sortedReversedHeartRateMaxArrayAsCustomHistoricalSampple, sortedReversedHeartRateAverageArrayAsCustomHistoricalSampple)
        }



    } //End getHeartRateMaxFromWorkouts

将leave置于callback的顶部是一个很好的做法

guard error == nil else { print("get heart rate error");  dispatchGroup.leave() return ; }

guard let unwrappedResults = results as? [HKQuantitySample] else { print("get heart rate error"); dispatchGroup.leave(); return}

let heartRatesAsDouble = unwrappedResults.map {$0.quantity.doubleValue(for: heartRateUnit)}

guard let max = heartRatesAsDouble.max() else { dispatchGroup.leave();  return }

let maxAsCustomHistoricalSample = CustomHistoricalSample(value: max, date: workout.startDate)

heartRateMaxArrayAsCustomHistoricalSample.append(maxAsCustomHistoricalSample)

let average = heartRatesAsDouble.average

let averageAsCustomHistoricalSample = CustomHistoricalSample(value: average, date: workout.startDate)

dispatchGroup.leave()

heartRateAvgArrayAsCustomHistoricalSample.append(averageAsCustomHistoricalSample)

当您在完成块中出错时,我认为您没有离开调度组。在每个错误情况下,只需离开组。当你无法通过防护时,你应该将组留在else块中。我不认为这是因为1没有错误打印在控制台中,2我在dispatchgroup的正上方放置了一个断点。离开并点击它,此防护让max=heartRatesAsDouble.max else{return}是的,你们都是对的,绝对不会在错误区离开,或者,正如@Sh_Khan所说,将其放在顶部是不好的,因为可能会在该块的其余部分完成之前调用notify,并且notify块可能依赖于此数据。同意将其放在所有错误块和底部是更好的做法path@rmaddy我认为这可能会损害最后的反应,但是,如果发生错误,则不会有任何区别,因为结果已经不完整。请记住,副本中的答案比这要好得多,因为它使用了“延迟”,避免了在每个卫兵发出离开通知时将其弄得一团糟。