objective c - Race conditions in iOS using GCD for UICollectionView -


i hoping can me out code , try figure out best way optimize don't have many race conditions.

some things know happening:

  • sometimes collection view not show up
  • only items show in collection view i.e 3 out of 8 items
  • the refresh graph function gets called anywhere 1-3 times randomly

i think overall code can made more efficient. if has advice, appreciate it.

i going post bulk of code here, have removed lot of processing save lines isn't hard read think left in enough can see gcd being utilized , if i'm doing wrong. link full code in gist if see whole thing.

gist

#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];     // additional setup after loading 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 available on 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 want read , write     nsset *datatypestowrite = [self datatypestowrite];     nsset *datattypestoread = [self datatypestoread];      alldashboarditems = [self loaddashboarditems];     unitpreference = [self loadunitpreferences];     dashboarditems = [nsmutablearray new];     itemdata = [nsmutabledictionary new];      (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]];      (int i=0; < [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 not given 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];      (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 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();      (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 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) {          (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;  } 

it seems code complex 1 @ , guess goes on. nice if separated proper model or service layer, such code doing presentation , code fetching data healthkit separate.

i see threading issue here, first , foremost usage of hkhealthstore method,

- (void)requestauthorizationtosharetypes:(nsset *)typestoshare                                readtypes:(nsset *)typestoread                               completion:(void (^)(bool success,                                                    nserror *error))completion  

it quite dictated in documentation method run , callback called on arbitrary queue,

https://developer.apple.com/library/prerelease/ios/documentation/healthkit/reference/hkhealthstore_class/index.html#//apple_ref/occ/instm/hkhealthstore/requestauthorizationtosharetypes:readtypes:completion:

healthkit performs these requests asynchronously. if call method new data type (a type of data user has not granted or denied permission in app), system automatically displays permission form, listing requested permissions. after user has finished responding, method calls completion block on background queue. if user has chosen grant or prohibit access of types specified, completion called without prompting user.

now, sure doing ui call in main thread. think there multiple asynchronous queries executing. so, doesnot dispatch_semaphore complex in case. make more sense use dispatch_group_enter , dispatch_group_leave manage concurrent tasks. dont see reloading collectionview in main thread. main issues, see code above. save tonns of time refactoring , figuring out bugs later if pick service code different objects.


Comments

Popular posts from this blog

Java 3D LWJGL collision -

spring - SubProtocolWebSocketHandler - No handlers -

methods - python can't use function in submodule -