运行 一些代码使用不同的变量多次异步
Running Some Code Multiple Times Asynchronously With Different Variables
所以,我正在尝试让我的应用程序读取 HealthKit
数据。我有一个从主应用程序视图控制器调用的函数,它会在另一个 class 中查询该月的所有健康数据。在数据数组从计算 class 中的单独函数 return 到视图控制器中的单独函数之前,需要进行一些计算。
由于数据量大,每次查询大约需要 2 秒。我希望能够异步关闭它们,当它们全部 returned 时,我可以更新 UI.
问题是,我每个月都调用该函数,它会启动 HKSampleQueries
,但它们没有按顺序 return,而且它们需要花费时间return 各不相同。这意味着我最终在一组数据计算的中途更改了变量,因为下一组数据刚刚开始。
我只知道两种解决方法:
像这样调用每个计算之前设置延迟:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{}
但这会浪费应用时间
或者我可以多次复制代码并为每个月调用不同的 class。但这似乎愚蠢且效率低下。
所以问题是。我如何有效地共享每次将 运行 与不同变量多次的代码。
干杯
函数示例:
在视图控制器中:
HeartRateCalculator *commonClassTwo =[[HeartRateCalculator alloc] init];
[commonClassTwo calculateData:0];
[commonClassTwo calculateData:-1];
[commonClassTwo calculateData:-2];
在心率计算器中
-(void)calculateData:(NSInteger)monthsBack{
//Some other stuff
//Generate monthPeriodPredicate based on monthsBack integer
HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:heartRate predicate:monthPeriodPredicate limit:200000 sortDescriptors:@[timeSortDescriptor] resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {
//Finish Calculations, call other functions (ie. [self doThis];) and then return
//When calculations return, monthPeriodPredicate is always value of the last predicate to be called, not the one that the HKSampleQuery was made with.
}
[healthStoreFive executeQuery:query];
完整代码:
-(void)calculateData:(NSInteger)monthsBack withCompletionBlock:(void(^)())completionBlock {//0 Means only current month, 2 means this month and last month and month before
//for(NSInteger i=0; i>=monthsBack; i--){
//monthForCalculation = monthsBack;
NSDateComponents *components = [[NSCalendar currentCalendar] components: NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond fromDate:[NSDate date]];
NSDateComponents *adjustableComponent = [[NSDateComponents alloc] init];
[adjustableComponent setMonth:monthsBack];
[adjustableComponent setDay:-[components day]+1];
[adjustableComponent setHour:-[components hour]];
[adjustableComponent setMinute:-[components minute]];
[adjustableComponent setSecond:-[components second]];
startOfMonth = [[NSCalendar currentCalendar] dateByAddingComponents:adjustableComponent toDate:[NSDate date] options:0];
adjustableComponent = [[NSDateComponents alloc] init];
[adjustableComponent setMonth:1];
NSDate *endOfMonth = [[NSCalendar currentCalendar] dateByAddingComponents:adjustableComponent toDate:startOfMonth options:0];
NSDate *secondEarlier = [endOfMonth dateByAddingTimeInterval:-1];
components = [[NSCalendar currentCalendar] components: NSCalendarUnitDay fromDate:secondEarlier];
daysInMonth = [components day];
NSPredicate *monthPeriodPredicate = [HKQuery predicateForSamplesWithStartDate:startOfMonth endDate:endOfMonth options:HKQueryOptionStrictStartDate];
healthStoreFive = [[HKHealthStore alloc] init];
HKQuantityType *heartRate = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate];
NSSortDescriptor *timeSortDescriptor = [[NSSortDescriptor alloc] initWithKey:HKSampleSortIdentifierEndDate ascending:NO];
HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:heartRate predicate:monthPeriodPredicate limit:200000 sortDescriptors:@[timeSortDescriptor] resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {
NSMutableArray *dataValues = [[NSMutableArray alloc] init];
NSMutableArray *dataDates = [[NSMutableArray alloc] init];
for (HKQuantitySample *sample in results) {
[dataValues addObject:[NSNumber numberWithFloat:[sample.quantity doubleValueForUnit:[[HKUnit countUnit] unitDividedByUnit:[HKUnit minuteUnit]]]]];
[dataDates addObject:sample.startDate];
}
monthForCalculation = monthsBack;
chronologicalDataValues = [[NSMutableArray alloc] init];
chronologicalDataDates = [[NSMutableArray alloc] init];
chronologicalDataValues = [[[dataValues reverseObjectEnumerator] allObjects] mutableCopy];
chronologicalDataDates = [[[dataDates reverseObjectEnumerator] allObjects] mutableCopy];
//dispatch_async(dispatch_get_main_queue(), ^{
if(dataDates.count == 0){
ViewController *commonClass =[[ViewController alloc] init];
[commonClass receiveCalculationData:[[NSMutableArray alloc] init] array:[[NSMutableArray alloc] init] daysToDisplay:[[NSMutableArray alloc] init] chosenMonth:monthForCalculation];
}
else{
NSLog(@"%@", [dataDates objectAtIndex:dataDates.count-1]);
NSLog(@"%@", [dataDates objectAtIndex:0]);
[self calculateDayStringsFromData];
}
completionBlock();
//});
}];
NSLog(@"HKSampleQuery Requested For Heart Rate Data");
[healthStoreFive executeQuery:query];
// }
}
使用block
[commonClassTwo calculateData:0 withCompletionBlock:^{
[commonClassTwo calculateData:-1 withCompletionBlock:^{
[commonClassTwo calculateData:-2 withCompletionBlock:^{
NSLog(@"All 3 calculation done");
}];
}];
}];
- (void)calculateData:(NSInteger)monthsBack withCompletionBlock:(void(^)())completionBlock {
//Some other stuff
HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:heartRate predicate:monthPeriodPredicate limit:200000 sortDescriptors:@[timeSortDescriptor] resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {
//Finish Calculations, call other functions (ie. [self doThis];) and then return
completionBlock();
}
[healthStoreFive executeQuery:query];
}
更新
对于异步调用,有一个计数器 integer
。初始值将是您想要的异步调用的数量。 (此处 3)
在每次调用的 completionBlock 中,递减计数器并检查所有调用是否完成。
如果完成更新 UI
。
[commonClassTwo calculateData:0 withCompletionBlock:^{
// counter--;
// [self updateUI];
}];
[commonClassTwo calculateData:-1 withCompletionBlock:^{
// counter--;
// [self updateUI];
}];
[commonClassTwo calculateData:-2 withCompletionBlock:^{
// counter--;
// [self updateUI];
}];
- (void)updateUI {
// if counter == 0 {
// dispatch_async(dispatch_get_main_queue(), ^{
// update UI
// });
// }
}
您可以制作单独的 class 并同时 运行 它们。
HeartRateCalculator *commonClassOne =[[HeartRateCalculator alloc] init];
HeartRateCalculator *commonClassTwo =[[HeartRateCalculator alloc] init];
HeartRateCalculator *commonClassThree =[[HeartRateCalculator alloc] init];
[commonClassOne calculateData:0];
[commonClassTwo calculateData:-1];
[commonClassThree calculateData:-2];
这样您就不必复制代码,但它们不会覆盖相同 class 中的值。
您可以使用 dispatch_group
来安排一个块在您完成所有任务后触发。
您只需修改 calculateData:
方法以接受 dispatch_group_t
参数(如果需要,您也可以随时添加完成块):
- (void)calculateData:(NSInteger)monthsBack group:(dispatch_group_t)group {
dispatch_group_enter(group); // increment group task count
//Some other stuff
HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:heartRate predicate:monthPeriodPredicate limit:200000 sortDescriptors:@[timeSortDescriptor] resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {
//Finish Calculations, call other functions (ie. [self doThis];) and then return
dispatch_group_leave(group); // decrement task count
}];
[healthStoreFive executeQuery:query];
}
那你就可以这样称呼它了:
HeartRateCalculator *commonClassTwo =[[HeartRateCalculator alloc] init];
dispatch_group_t group = dispatch_group_create();
[commonClassTwo calculateData:0 group:group];
[commonClassTwo calculateData:-1 group:group];
[commonClassTwo calculateData:-2 group:group];
dispatch_group_notify(group, dispatch_get_main_queue(), ^{ // called when all tasks are finished.
// update UI
});
与的原理相同,但dispatch_groups
比使用您自己的变量来跟踪执行的任务数更优雅。
虽然我不太清楚你说的要让任务异步执行是什么意思。来自 Apple docs on HKSampleQuery
:
Queries run on an anonymous background queue.
因此您的任务已经异步。
所以,我正在尝试让我的应用程序读取 HealthKit
数据。我有一个从主应用程序视图控制器调用的函数,它会在另一个 class 中查询该月的所有健康数据。在数据数组从计算 class 中的单独函数 return 到视图控制器中的单独函数之前,需要进行一些计算。
由于数据量大,每次查询大约需要 2 秒。我希望能够异步关闭它们,当它们全部 returned 时,我可以更新 UI.
问题是,我每个月都调用该函数,它会启动 HKSampleQueries
,但它们没有按顺序 return,而且它们需要花费时间return 各不相同。这意味着我最终在一组数据计算的中途更改了变量,因为下一组数据刚刚开始。
我只知道两种解决方法:
像这样调用每个计算之前设置延迟:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{}
但这会浪费应用时间
或者我可以多次复制代码并为每个月调用不同的 class。但这似乎愚蠢且效率低下。
所以问题是。我如何有效地共享每次将 运行 与不同变量多次的代码。 干杯
函数示例:
在视图控制器中:
HeartRateCalculator *commonClassTwo =[[HeartRateCalculator alloc] init];
[commonClassTwo calculateData:0];
[commonClassTwo calculateData:-1];
[commonClassTwo calculateData:-2];
在心率计算器中
-(void)calculateData:(NSInteger)monthsBack{
//Some other stuff
//Generate monthPeriodPredicate based on monthsBack integer
HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:heartRate predicate:monthPeriodPredicate limit:200000 sortDescriptors:@[timeSortDescriptor] resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {
//Finish Calculations, call other functions (ie. [self doThis];) and then return
//When calculations return, monthPeriodPredicate is always value of the last predicate to be called, not the one that the HKSampleQuery was made with.
}
[healthStoreFive executeQuery:query];
完整代码:
-(void)calculateData:(NSInteger)monthsBack withCompletionBlock:(void(^)())completionBlock {//0 Means only current month, 2 means this month and last month and month before
//for(NSInteger i=0; i>=monthsBack; i--){
//monthForCalculation = monthsBack;
NSDateComponents *components = [[NSCalendar currentCalendar] components: NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond fromDate:[NSDate date]];
NSDateComponents *adjustableComponent = [[NSDateComponents alloc] init];
[adjustableComponent setMonth:monthsBack];
[adjustableComponent setDay:-[components day]+1];
[adjustableComponent setHour:-[components hour]];
[adjustableComponent setMinute:-[components minute]];
[adjustableComponent setSecond:-[components second]];
startOfMonth = [[NSCalendar currentCalendar] dateByAddingComponents:adjustableComponent toDate:[NSDate date] options:0];
adjustableComponent = [[NSDateComponents alloc] init];
[adjustableComponent setMonth:1];
NSDate *endOfMonth = [[NSCalendar currentCalendar] dateByAddingComponents:adjustableComponent toDate:startOfMonth options:0];
NSDate *secondEarlier = [endOfMonth dateByAddingTimeInterval:-1];
components = [[NSCalendar currentCalendar] components: NSCalendarUnitDay fromDate:secondEarlier];
daysInMonth = [components day];
NSPredicate *monthPeriodPredicate = [HKQuery predicateForSamplesWithStartDate:startOfMonth endDate:endOfMonth options:HKQueryOptionStrictStartDate];
healthStoreFive = [[HKHealthStore alloc] init];
HKQuantityType *heartRate = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate];
NSSortDescriptor *timeSortDescriptor = [[NSSortDescriptor alloc] initWithKey:HKSampleSortIdentifierEndDate ascending:NO];
HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:heartRate predicate:monthPeriodPredicate limit:200000 sortDescriptors:@[timeSortDescriptor] resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {
NSMutableArray *dataValues = [[NSMutableArray alloc] init];
NSMutableArray *dataDates = [[NSMutableArray alloc] init];
for (HKQuantitySample *sample in results) {
[dataValues addObject:[NSNumber numberWithFloat:[sample.quantity doubleValueForUnit:[[HKUnit countUnit] unitDividedByUnit:[HKUnit minuteUnit]]]]];
[dataDates addObject:sample.startDate];
}
monthForCalculation = monthsBack;
chronologicalDataValues = [[NSMutableArray alloc] init];
chronologicalDataDates = [[NSMutableArray alloc] init];
chronologicalDataValues = [[[dataValues reverseObjectEnumerator] allObjects] mutableCopy];
chronologicalDataDates = [[[dataDates reverseObjectEnumerator] allObjects] mutableCopy];
//dispatch_async(dispatch_get_main_queue(), ^{
if(dataDates.count == 0){
ViewController *commonClass =[[ViewController alloc] init];
[commonClass receiveCalculationData:[[NSMutableArray alloc] init] array:[[NSMutableArray alloc] init] daysToDisplay:[[NSMutableArray alloc] init] chosenMonth:monthForCalculation];
}
else{
NSLog(@"%@", [dataDates objectAtIndex:dataDates.count-1]);
NSLog(@"%@", [dataDates objectAtIndex:0]);
[self calculateDayStringsFromData];
}
completionBlock();
//});
}];
NSLog(@"HKSampleQuery Requested For Heart Rate Data");
[healthStoreFive executeQuery:query];
// } }
使用block
[commonClassTwo calculateData:0 withCompletionBlock:^{
[commonClassTwo calculateData:-1 withCompletionBlock:^{
[commonClassTwo calculateData:-2 withCompletionBlock:^{
NSLog(@"All 3 calculation done");
}];
}];
}];
- (void)calculateData:(NSInteger)monthsBack withCompletionBlock:(void(^)())completionBlock {
//Some other stuff
HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:heartRate predicate:monthPeriodPredicate limit:200000 sortDescriptors:@[timeSortDescriptor] resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {
//Finish Calculations, call other functions (ie. [self doThis];) and then return
completionBlock();
}
[healthStoreFive executeQuery:query];
}
更新
对于异步调用,有一个计数器 integer
。初始值将是您想要的异步调用的数量。 (此处 3)
在每次调用的 completionBlock 中,递减计数器并检查所有调用是否完成。
如果完成更新 UI
。
[commonClassTwo calculateData:0 withCompletionBlock:^{
// counter--;
// [self updateUI];
}];
[commonClassTwo calculateData:-1 withCompletionBlock:^{
// counter--;
// [self updateUI];
}];
[commonClassTwo calculateData:-2 withCompletionBlock:^{
// counter--;
// [self updateUI];
}];
- (void)updateUI {
// if counter == 0 {
// dispatch_async(dispatch_get_main_queue(), ^{
// update UI
// });
// }
}
您可以制作单独的 class 并同时 运行 它们。
HeartRateCalculator *commonClassOne =[[HeartRateCalculator alloc] init];
HeartRateCalculator *commonClassTwo =[[HeartRateCalculator alloc] init];
HeartRateCalculator *commonClassThree =[[HeartRateCalculator alloc] init];
[commonClassOne calculateData:0];
[commonClassTwo calculateData:-1];
[commonClassThree calculateData:-2];
这样您就不必复制代码,但它们不会覆盖相同 class 中的值。
您可以使用 dispatch_group
来安排一个块在您完成所有任务后触发。
您只需修改 calculateData:
方法以接受 dispatch_group_t
参数(如果需要,您也可以随时添加完成块):
- (void)calculateData:(NSInteger)monthsBack group:(dispatch_group_t)group {
dispatch_group_enter(group); // increment group task count
//Some other stuff
HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:heartRate predicate:monthPeriodPredicate limit:200000 sortDescriptors:@[timeSortDescriptor] resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {
//Finish Calculations, call other functions (ie. [self doThis];) and then return
dispatch_group_leave(group); // decrement task count
}];
[healthStoreFive executeQuery:query];
}
那你就可以这样称呼它了:
HeartRateCalculator *commonClassTwo =[[HeartRateCalculator alloc] init];
dispatch_group_t group = dispatch_group_create();
[commonClassTwo calculateData:0 group:group];
[commonClassTwo calculateData:-1 group:group];
[commonClassTwo calculateData:-2 group:group];
dispatch_group_notify(group, dispatch_get_main_queue(), ^{ // called when all tasks are finished.
// update UI
});
与dispatch_groups
比使用您自己的变量来跟踪执行的任务数更优雅。
虽然我不太清楚你说的要让任务异步执行是什么意思。来自 Apple docs on HKSampleQuery
:
Queries run on an anonymous background queue.
因此您的任务已经异步。