2015-08-20 4 views
0

В моем проекте я создаю HKStatisticsCollectionQueries для серии HKQuantityTypes. Затем результатыHandler добавляет эти данные в упорядоченный по дате массив объектов. Я хочу выполнить другую операцию только после обработки всей серии HKStatisticsCollectionQueries и результатов, добавленных в мой массив.HKStatisticsCollectionQuery resultsHandler and NSOperation

Я попытался сделать это, поставив задачу внутри подкласса NSOperation, но зависимый блок запускается до того, как любой из образцов будет добавлен в массив. Согласно документации HKStatisticsCollectionQuery «Этот метод выполняет запрос на анонимную очереди фона. Когда запрос завершен, он выполняет обработчик результатов на ту же фон очередь»

Есть ли способ использовать initialResultsHandler и statisticsUpdateHandler HKStatisticsCollectionQuery с NSOperation?

при запуске этого я получаю следующий вывод:

cycleOperation начать
cycleOperation CompletionBlock
dependentOperation начать
dependentOperation CompletionBlock
SumStatistics addSamplesToArray: цикл: 96 пробы добавляли
SumStatistics Основной полное: 96 образцы добавлены

func getCycleKm(){ 
    let sampleType = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDistanceCycling) 
    let hkUnit = HKUnit.meterUnitWithMetricPrefix(.Kilo) 
    println("cycleOperation start") 

    let cycleOperation = SumStatistics(quantityType: sampleType, startDate: startingDate, heathDataArray: self.healthDataArray) 
    cycleOperation.completionBlock = {println("cycleOperation CompletionBlock ")} 
    let dependentOperation = NSBlockOperation{()->Void in println("dependentOperation start")} 
    dependentOperation.completionBlock = {println("dependentOperation CompletionBlock")} 
    dependentOperation.addDependency(cycleOperation) 

    self.operationQueue.addOperation(cycleOperation) 
    self.operationQueue.addOperation(dependentOperation) 
} 


class SumStatistics:NSOperation{ 

let healthKitStore:HKHealthStore = HKHealthStore() 

private let quantityType:HKQuantityType 
private let startDate:NSDate 
private let endDate: NSDate 
private let statsOption: HKStatisticsOptions 

var healthDataArray:[HealthData] 

required init(quantityType:HKQuantityType, startDate:NSDate, heathDataArray:[HealthData]){ 
    self.quantityType = quantityType 
    self.startDate = startDate 
    let startOfToday = NSDate().getStartOfDate() 
    self.endDate = NSCalendar.currentCalendar().dateByAddingUnit(.CalendarUnitDay, value: 1, toDate: startOfToday, options: nil)! 
    self.statsOption = HKStatisticsOptions.CumulativeSum 
    self.healthDataArray = heathDataArray 
    super.init() 
} 


override func main() { 
    getSumStatistics { (hkSamples, statsError) -> Void in 
     self.addSamplesToArray(hkSamples) 
     println("SumStatistics main complete: \(hkSamples.count) samples added") 
    } 
} 

func addSamplesToArray(newSamples:[HKQuantitySample]){ 
    var samples = newSamples 
    samples.sort({$0.startDate.timeIntervalSinceNow > $1.startDate.timeIntervalSinceNow}) 
    if samples.count == 0{ 
     println("SumStatistics addSamplesToArray: no samples!") 
     return 
    } 
    var ctr = 0 
    var typeString = "" 
    for healthDataDate in self.healthDataArray{ 

     while healthDataDate.date.isSameDate(samples[ctr].startDate) && ctr < samples.count - 1{ 
      switch samples[ctr].quantityType.identifier { 
      case HKQuantityTypeIdentifierBodyMass: 
       healthDataDate.weight = samples[ctr].quantity 
       typeString = "weight" 
      case HKQuantityTypeIdentifierDietaryEnergyConsumed: 
       healthDataDate.dietCalories = samples[ctr].quantity 
       typeString = "diet" 
      case HKQuantityTypeIdentifierActiveEnergyBurned: 
       healthDataDate.activeCalories = samples[ctr].quantity 
       typeString = "active" 
      case HKQuantityTypeIdentifierBasalEnergyBurned: 
       healthDataDate.basalCalories = samples[ctr].quantity 
       typeString = "basal" 
      case HKQuantityTypeIdentifierStepCount: 
       healthDataDate.steps = samples[ctr].quantity 
       typeString = "steps" 
      case HKQuantityTypeIdentifierDistanceCycling: 
       healthDataDate.cycleKM = samples[ctr].quantity 
       typeString = "cycle" 
      case HKQuantityTypeIdentifierDistanceWalkingRunning: 
       healthDataDate.runWalkKM = samples[ctr].quantity 
       typeString = "runWalk" 
      default: 
       println("SumStatistics addSamplesToArray type not found -> \(samples[ctr].quantityType)") 
      } 
      if ctr < samples.count - 1{ 
       ctr += 1 
      }else{ 
       break 
      } 
     } 
    } 
    println("SumStatistics addSamplesToArray: \(typeString): \(newSamples.count) samples added") 
} 




func getSumStatistics(completionHandler:([HKQuantitySample], NSError!)->Void){ 
    let dayStart = NSCalendar.currentCalendar().startOfDayForDate(startDate) 
    let addDay = NSCalendar.currentCalendar().dateByAddingUnit(.CalendarUnitDay, value: 1, toDate: endDate, options:nil) 
    let dayEnd = NSCalendar.currentCalendar().startOfDayForDate(addDay!) //add one day 
    let interval = NSDateComponents() 
    interval.day = 1 

    let predicate = HKQuery.predicateForSamplesWithStartDate(startDate, endDate: endDate, options: HKQueryOptions.None) 
    let newQuery = HKStatisticsCollectionQuery(quantityType: quantityType, 
     quantitySamplePredicate: predicate, 
     options: statsOption, 
     anchorDate: dayStart, 
     intervalComponents: interval) 

    newQuery.initialResultsHandler = { 
     query, statisticsCollection, error in 
     var resultsArray = [HKQuantitySample]() 
     if error != nil { 
      println("*** An error occurred while calculating the statistics: \(error.localizedDescription) ***") 
     }else{ 
      statisticsCollection.enumerateStatisticsFromDate(self.startDate, toDate: self.endDate, withBlock: { (statistics, stop) -> Void in 
       if let statisticsQuantity = statistics.sumQuantity() { 
        let startD = NSCalendar.currentCalendar().startOfDayForDate(statistics.startDate) 
        let endD = NSCalendar.currentCalendar().dateByAddingUnit(.CalendarUnitDay, value: 1, toDate: startD, options: nil) 

        let qSample = HKQuantitySample(type: self.quantityType, quantity: statisticsQuantity, startDate: startD, endDate: endD) 
        resultsArray.append(qSample) 
       } 
      }) 
     } 
     completionHandler(resultsArray,error) 
    } 

    newQuery.statisticsUpdateHandler = { 
     query, statistics, statisticsCollection, error in 
     println("*** updateHandler fired") 
     var resultsArray = [HKQuantitySample]() 
     if error != nil { 
      println("*** An error occurred while calculating the statistics: \(error.localizedDescription) ***") 
     }else{ 
      statisticsCollection.enumerateStatisticsFromDate(self.startDate, toDate: self.endDate, withBlock: { (statistics, stop) -> Void in 
       if let statisticsQuantity = statistics.sumQuantity() { 
        let startD = NSCalendar.currentCalendar().startOfDayForDate(statistics.startDate) 
        let endD = NSCalendar.currentCalendar().dateByAddingUnit(.CalendarUnitDay, value: 1, toDate: startD, options: nil) 

        let qSample = HKQuantitySample(type: self.quantityType, quantity: statisticsQuantity, startDate: startD, endDate: endD) 
        resultsArray.append(qSample) 
       } 
      }) 
     } 
     completionHandler(resultsArray,error) 
    } 
    self.healthKitStore.executeQuery(newQuery) 
} 

}

ответ

0

Короткий ответ был RTFM! (более тщательно). Я оставляю свой исходный вопрос и код так, как есть, и добавляю решение здесь.

Благодаря этой должности за помощь мне понять это: http://szulctomasz.com/ios-second-try-to-nsoperation-and-long-running-tasks/ И конечно же, как я обнаружил, что нет никакой замены для внимательного прочтения задания: https://developer.apple.com/library/ios/documentation/Cocoa/Reference/NSOperation_class/

Проблема, что я не был подклассов NSOperation правильно работает одновременный операции. Нужно добавить параллельные операции для start(), а не main(), а затем использовать KVO для обновления готовых и исполняемых свойств, которые сигнализируют о завершении операции.

мне нужно изменить класс выше, включают:

private var _executing = false 
private var _finished = false 

override var executing:Bool{return _executing} 
override var finished:Bool{return _finished} 
override func cancel() { 
super.cancel() 
finish() 
} 

override func start() { 
if cancelled{ 
    finish() 
    return 
} 
willChangeValueForKey("isExecuting") 
_executing = true 
didChangeValueForKey("isExecuting") 

getSumStatistics { (hkSamples, statsError) -> Void in 
    println("LoadStatistics getStatistics completion: \(hkSamples.count) samples") 
    self.addSamplesToArray(hkSamples, completionHandler: { (success) -> Void in 
     println("LoadStatistics addSamplesToArray completion") 
     self.finish() 

    }) 
    self.completion(true) 
} 
main() 

} 

func finish(){ 
willChangeValueForKey("isExecuting") 
willChangeValueForKey("isFinished") 
_executing = false 
_finished = true 
didChangeValueForKey("isExecuting") 
didChangeValueForKey("isFinished") 

} 


override func main() { 
if cancelled == true && _finished != false{ 
    finish() 
    return 
} 
} 

Теперь я получаю!

cycleOperation начать
LoadStatistics getStatistics завершение: 97 образцов
LoadStatistics addSamplesToArray завершение
cycleOperation CompletionBlock
dependentOperation начать
dependentOperation CompletionBlock