我希望有人可以帮我解决这个问题并尝试找出优化它的最佳方法,这样我就不会有这么多竞争条件。
我知道的一些事情正在发生:
我只是认为这个代码可以提高效率。如果有人有任何建议,我将非常感激。
我将在这里发布大部分代码,我已经删除了大量处理来保存行,所以它不是很难读,但我想我已经离开了,所以你可以看到GCD在哪里被利用,如果我做错了。如果你想看到整件事,我还会在一个要点中链接完整的代码。
#import "DashboardCollectionViewController.h"
#import "DashboardLayout.h"
#import "HeaderViewCell.h"
#import "DashboardCell.h"
#import "DashboardDetailViewController.h"
#import "PreferencesManager.h"
#import "UIColor+HexColors.h"
#import "PNChart.h"
@interface DashboardCollectionViewController () <UICollectionViewDelegateFlowLayout, UICollectionViewDataSource, UICollectionViewDelegate>
@property (nonatomic, strong) DashboardLayout *listLayout;
@property (nonatomic, strong) DashboardLayout *gridLayout;
@property (strong, nonatomic) HKHealthStore *healthStore;
@property (strong, nonatomic) NSDictionary *dataTypes;
@property (strong, nonatomic) HeaderViewCell *headerCell;
@end
@implementation DashboardCollectionViewController
{
NSString *layoutType;
NSString *unitPreference;
NSMutableDictionary *itemData;
UIRefreshControl *refreshControl;
NSArray *sortedDashboardItems;
__block NSMutableArray *graphData;
}
@synthesize dashboardItems, allDashboardItems, myCollectionView, dataTypes, headerCell;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self setupFlowLayoutA];
[self setupFlowLayoutB];
layoutType = @"listView";
dataTypes = [NSDictionary dictionaryWithObjectsAndKeys:
@1, HKQuantityTypeIdentifierStepCount,
@2, HKQuantityTypeIdentifierFlightsClimbed,
@3, HKQuantityTypeIdentifierDistanceWalkingRunning,
@4, HKQuantityTypeIdentifierActiveEnergyBurned,
@5, HKQuantityTypeIdentifierBodyMass,
@6, HKQuantityTypeIdentifierDistanceCycling,
@7, HKQuantityTypeIdentifierHeartRate,
@8, HKQuantityTypeIdentifierBodyMassIndex, nil];
UINib *headerNib = [UINib nibWithNibName:@"HeaderView" bundle:nil];
UINib *cellNib = [UINib nibWithNibName:@"DashboardCell" bundle:nil];
DashboardLayout *dashboardLayout = [self listLayout];
[myCollectionView registerNib:headerNib forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"HeaderCell"];
[myCollectionView registerNib:cellNib forCellWithReuseIdentifier:@"Cell"];
[myCollectionView setCollectionViewLayout:dashboardLayout animated:YES];
//check if health kit is available on this device
if ([HKHealthStore isHealthDataAvailable]) {
if (!self.healthStore) {
self.healthStore = [HKHealthStore new];
}
}
refreshControl = [UIRefreshControl new];
[refreshControl addTarget:self action:@selector(refreshAllData) forControlEvents:UIControlEventValueChanged];
[myCollectionView addSubview:refreshControl];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
//set data types we want to read and write
NSSet *dataTypesToWrite = [self dataTypesToWrite];
NSSet *datatTypesToRead = [self dataTypesToRead];
allDashboardItems = [self loadDashboardItems];
unitPreference = [self loadUnitPreferences];
dashboardItems = [NSMutableArray new];
itemData = [NSMutableDictionary new];
for (NSDictionary *item in allDashboardItems) {
NSString *enabled = item[@"enabled"];
if ([enabled isEqualToString:@"1"]) {
[dashboardItems addObject:item];
}
}
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:@"order" ascending:YES];
sortedDashboardItems = [dashboardItems sortedArrayUsingDescriptors:@[descriptor]];
graphData = [[NSMutableArray alloc] initWithCapacity:[sortedDashboardItems count]];
for (int i=0; i < [sortedDashboardItems count]; i++) {
[graphData addObject:[NSNull null]];
}
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
#if DEBUG
NSLog(@"%@", [defaults objectForKey:@"unitPreference"]);
#endif
//request authorization
[self.healthStore requestAuthorizationToShareTypes:dataTypesToWrite readTypes:datatTypesToRead completion:^(BOOL success, NSError *error) {
if (!success) {
//user did not authorize healthkit
NSLog(@"Health Kit was not given the correct permissions");
return;
} else {
[self refreshData];
[self refreshGraphs];
// [myCollectionView reloadData];
}
}];
[self refreshData];
[self refreshGraphs];
// [myCollectionView reloadData];
}
- (void)refreshAllData {
[self refreshData];
[self refreshGraphs];
}
- (void)refreshData {
__block NSString *labelString = @"";
__block NSString *unitString = @"";
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *startDate = [calendar startOfDayForDate:[NSDate date]];
NSDate *endDate = [calendar dateByAddingUnit:NSCalendarUnitDay value:1 toDate:startDate options:0];
NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionNone];
for (NSDictionary *item in sortedDashboardItems) {
NSString *type = item[@"type"];
HKSampleType *sampleType = [HKSampleType quantityTypeForIdentifier:type];
HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:sampleType predicate:predicate limit:HKObjectQueryNoLimit sortDescriptors:nil resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {
if (!results) {
NSLog(@"No results were returned form query");
} else if (error) {
NSLog(@"Error: %@ %@", error, [error userInfo]);
} else {
dispatch_async(dispatch_get_main_queue(), ^{
//processing
int order = [item[@"order"] intValue];
NSNumber *orderNumber = [NSNumber numberWithInt:order];
UIFont *arialLarge = [UIFont fontWithName:@"AvenirNext-Bold" size:15.0];
UIFont *arialSmall = [UIFont fontWithName:@"AvenirNext-Bold" size:8.0];
NSDictionary *arialLargeDict = @{NSFontAttributeName : arialLarge};
NSDictionary *arialSmallDict = @{NSFontAttributeName : arialSmall};
NSMutableAttributedString *largeString = [[NSMutableAttributedString alloc] initWithString:labelString attributes:arialLargeDict];
NSMutableAttributedString *smallString = [[NSMutableAttributedString alloc] initWithString:unitString attributes:arialSmallDict];
[largeString appendAttributedString:smallString];
[itemData setObject:largeString forKey:orderNumber];
[myCollectionView reloadData];
[refreshControl endRefreshing];
});
}
}];
[self.healthStore executeQuery:query];
}
}
- (void)refreshGraphs {
#if DEBUG
NSLog(@"graphs");
#endif
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *interval = [NSDateComponents new];
interval.day = 1;
NSDate *anchorDate = [calendar dateByAddingUnit:NSCalendarUnitDay value:-6 toDate:[calendar startOfDayForDate:[NSDate date]] options:0];
dispatch_queue_t queue = dispatch_queue_create([@"graph.queue" UTF8String], DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
for (NSDictionary *item in sortedDashboardItems) {
NSMutableArray *arrayOfValues = [NSMutableArray new];
NSString *type = item[@"type"];
NSUInteger index = [sortedDashboardItems indexOfObject:item];
HKQuantityType *quantityType = [HKObjectType quantityTypeForIdentifier:type];
NSDate *endDate = [NSDate date];
NSDate *startDate = [calendar dateByAddingUnit:NSCalendarUnitDay value:-6 toDate:[calendar startOfDayForDate:endDate] options:0];
dispatch_block_t block = ^{
dispatch_semaphore_t lock = dispatch_semaphore_create(0);
if ([type isEqualToString:HKQuantityTypeIdentifierBodyMass]) {
HKStatisticsCollectionQuery *query = [[HKStatisticsCollectionQuery alloc] initWithQuantityType:quantityType quantitySamplePredicate:nil options:HKStatisticsOptionDiscreteAverage anchorDate:anchorDate intervalComponents:interval];
query.initialResultsHandler = ^(HKStatisticsCollectionQuery *query, HKStatisticsCollection *results, NSError *error) {
//processing
};
[self.healthStore executeQuery:query];
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
} else if ([type isEqualToString:HKQuantityTypeIdentifierBodyMassIndex]) {
HKStatisticsCollectionQuery *query = [[HKStatisticsCollectionQuery alloc] initWithQuantityType:quantityType quantitySamplePredicate:nil options:HKStatisticsOptionDiscreteAverage anchorDate:anchorDate intervalComponents:interval];
query.initialResultsHandler = ^(HKStatisticsCollectionQuery *query, HKStatisticsCollection *results, NSError *error) {
//processing
};
[self.healthStore executeQuery:query];
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
} else if ([type isEqualToString:HKQuantityTypeIdentifierHeartRate]) {
HKStatisticsCollectionQuery *query = [[HKStatisticsCollectionQuery alloc] initWithQuantityType:quantityType quantitySamplePredicate:nil options:HKStatisticsOptionDiscreteAverage anchorDate:anchorDate intervalComponents:interval];
query.initialResultsHandler = ^(HKStatisticsCollectionQuery *query, HKStatisticsCollection *results, NSError *error) {
//processing
};
[self.healthStore executeQuery:query];
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
} else {
HKStatisticsCollectionQuery *query = [[HKStatisticsCollectionQuery alloc] initWithQuantityType:quantityType quantitySamplePredicate:nil options:HKStatisticsOptionCumulativeSum anchorDate:anchorDate intervalComponents:interval];
query.initialResultsHandler = ^(HKStatisticsCollectionQuery *query, HKStatisticsCollection *results, NSError *error) {
if (error) {
NSLog(@"Error: %@ %@", error, [error userInfo]);
} else {
[results enumerateStatisticsFromDate:startDate toDate:endDate withBlock:^(HKStatistics *result, BOOL *stop) {
HKQuantity *quantity = result.sumQuantity;
if (quantity != nil) {
//do some processing
} else {
[arrayOfValues addObject:@0];
}
}];
[self addArrayToGraphData:arrayOfValues atIndex:index];
dispatch_semaphore_signal(lock);
}
};
[self.healthStore executeQuery:query];
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
};
};
dispatch_group_async(group, queue, block);
}
dispatch_group_notify(group, queue, ^{
[myCollectionView reloadData];
});
}
- (void)addArrayToGraphData:(NSArray *)array atIndex:(NSUInteger)index {
[graphData replaceObjectAtIndex:index withObject:array];
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
DashboardCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
if (cell != nil) {
for (UIView *subview in cell.subviews) {
if ([subview isKindOfClass:[PNLineChart class]]) {
[subview removeFromSuperview];
}
}
NSNumber *indexNumber = [NSNumber numberWithInteger:indexPath.row];
NSDictionary *item = [sortedDashboardItems objectAtIndex:indexPath.row];
NSString *imageSafeString = [item[@"title"] stringByReplacingOccurrencesOfString:@" " withString:@""];
NSString *imageString = @"";
if ([layoutType isEqualToString:@"listView"]) {
imageString = [NSString stringWithFormat:@"%@-large.png", imageSafeString];
cell.layoutType = layoutType;
cell.titleLabel.text = item[@"title"];
cell.dataImage.image = [UIImage imageNamed:imageString];
} else if ([layoutType isEqualToString:@"gridView"]) {
imageString = [NSString stringWithFormat:@"%@-small.png", imageSafeString];
cell.layoutType = layoutType;
cell.titleLabel.text = @"";
cell.dataImage.image = [UIImage imageNamed:imageString];
}
PNLineChart *graph = nil;
if ([layoutType isEqualToString:@"listView"]) {
graph = [[PNLineChart alloc] initWithFrame:CGRectMake(80, 50, self.view.frame.size.width - 100, 90)];
} else {
graph = [[PNLineChart alloc] initWithFrame:CGRectMake(10, 0, (self.view.frame.size.width / 2) - 50, 110)];
}
graph.showLabel = NO;
graph.showGenYLabels = NO;
graph.backgroundColor = [UIColor clearColor];
graph.pointColor = [UIColor whiteColor];
graph.userInteractionEnabled = NO;
[cell addSubview:graph];
PNLineChartData *data = [PNLineChartData new];
data.color = [UIColor colorWithHexString:@"49b1d9"];
if (![graphData[indexPath.row] isEqual:[NSNull null]]) {
data.itemCount = [graphData[indexPath.row] count];
} else {
data.itemCount = 0;
}
data.inflexionPointStyle = PNLineChartPointStyleCircle;
data.getData = ^(NSUInteger index) {
CGFloat yValue = [graphData[indexPath.row][index] floatValue];
return [PNLineChartDataItem dataItemWithY:yValue];
};
graph.chartData = @[data];
[graph strokeChart];
NSAttributedString *dataString = [itemData objectForKey:indexNumber];
cell.dataLabel.attributedText = dataString;
}
return cell;
}
答案 0 :(得分:1)
似乎代码太复杂了,任何人都无法查看它并猜测实际发生了什么。如果将它分成适当的模型或服务层,这样做会很好吗,这样进行表示的代码和从healthkit获取数据的代码就会是独立的。
我清楚地看到这里的线程问题,首先是使用HKHealthStore方法,
- (void)requestAuthorizationToShareTypes:(NSSet *)typesToShare
readTypes:(NSSet *)typesToRead
completion:(void (^)(BOOL success,
NSError *error))completion
在文档中明确指出此方法运行并且在某个任意队列上调用回调,
HealthKit异步执行这些请求。如果你这样称呼 具有新数据类型的方法(用户没有的数据类型) 以前在此应用程序中授予或拒绝的权限),系统 自动显示权限表单,列出所有请求 权限。用户完成响应后,此方法调用 它在后台队列上的完成块。如果用户已经 选择授予或禁止访问指定的所有类型, 调用完成时不会提示用户。
现在,您确定要在主线程中执行所有UI调用吗?我认为您正在执行多个异步查询。因此,在这种情况下,dispatch_semaphore看起来并不复杂。使用 dispatch_group_enter 和 dispatch_group_leave 管理并发任务会更有意义吗?我没有看到你正在主线程中重新加载collectionview。这些是我从上面的代码中看到的主要问题。如果您将服务代码提取到某些不同的对象,您可以节省大量的时间重构并计算出错误。