ios - Thread safe and "Collection was mutated while being enumerated" -
i have class "ad" set static variable inside ad.m :
static nsdictionary *citiesdict = nil; // class variable
and in same file have implemented class method load plist file of name of cities index numbers if it's not loaded , convert number value passed paramater name of city :
+(nsstring *) cityfromnumbervalue:(nsstring *)citynumbervalue { // load cities plist file named "cities" if it's not loaded if (!citiesdict) { nsstring *path = [[nsbundle mainbundle] pathforresource:@"cities" oftype:@"plist"]; citiesdict = [[nsdictionary alloc ]initwithcontentsoffile:path]; nslog(@"loading plist"); } nslog(@"will return value"); nsarray *temp = [ citiesdict allkeysforobject:citynumbervalue]; nsstring *key = [temp lastobject]; return key ; }
and in same file have implement init method convert dictionary ad object uses class method +cityfromnumbervalue:citynumbervalue :
-(id) initwithdictionary: (nsdictionary *) dictionay { self = [super init]; if (self) { // convert number name of city self.departurecity= [ad cityfromnumbervalue:[dictionay objectforkey:@"ad_villedepart"]]; self.arrivalcity= [ad cityfromnumbervalue:[dictionay objectforkey:@"ad_villearrivee"]]; } return self; }
and have inside same file method fetch ads web service calls method +cityfromnumbervalue:citynumbervalue: inside loop :
+(nsdictionary *) fetchadofpage : (nsinteger) page perpage : (nsinteger) perpage { nsmutablearray * adsarray = [[nsmutablearray alloc] init]; ...... nsmutablearray *array = [nsarray arraywithcontentsofurl:url]; // convert new fetchted dictionnaries of ads ad objects (nsmutabledictionary *dictad in array) { // convertion using designated initializer ad *ad = [[ad alloc]initwithdictionary:dictad]; [adsarray addobject:ad]; } .... return dictfinal ; }
and somewhere else mu controller call fetch methode :
// request on async thread dispatch_queue_t fetchq = dispatch_get_global_queue(dispatch_queue_priority_default, 0); dispatch_async(fetchq, ^{ nsdictionary * dict = [ad fetchadofpage:currentpage perpage:kadsperpage]; dispatch_sync(dispatch_get_main_queue(), ^{ ..... }); }); dispatch_release(fetchq);
now problem last time when run app inside simulator got error "collection mutated while being enumerated" , points line :
nsarray *temp = [ citiesdict allkeysforobject:citynumbervalue];
but got error first time , didn't before , use same code more 3 months , ok , , can't reproduce same error again ! after putting nslog see happen :
2013-05-21 19:39:20.141 myapp[744:3b03] loading plist 2013-05-21 19:39:20.151 myapp[744:6303] return value 2013-05-21 19:39:20.151 myapp[744:3b03] return value 2013-05-21 19:39:20.152 myapp[744:6303] return value 2013-05-21 19:39:20.153 myapp[744:6303] return value 2013-05-21 19:39:20.154 myapp[744:6303] return value 2013-05-21 19:39:20.153 myapp[744:3b03] return value 2013-05-21 19:39:20.155 myapp[744:6303] return value
but 1 time got :
2013-05-21 19:39:20.141 myapp[744:3b03] loading plist 2013-05-21 19:39:20.142 myapp[744:6303] loading plist 2013-05-21 19:39:20.151 myapp[744:6303] return value 2013-05-21 19:39:20.151 myapp[744:3b03] return value 2013-05-21 19:39:20.152 myapp[744:6303] return value 2013-05-21 19:39:20.153 myapp[744:6303] return value 2013-05-21 19:39:20.154 myapp[744:6303] return value 2013-05-21 19:39:20.153 myapp[744:3b03] return value 2013-05-21 19:39:20.155 myapp[744:6303] return value
the app didn't crash there strange "loading plist" twice since have checked using if statement ! guess there 2 threads have entered :
if (!citiesdict)
at same time , , both of them hav set citiesdict dictionary , since dictionary can used in
[ citiesdict allkeysforobject:citynumbervalue];
just after if statement can cause crash "collection mutated while being enumerated" , can real senario ?
since can't reproduce error again wonder if adding :
@synchronized(citiesdict) { citiesdict = [[nsdictionary alloc ]initwithcontentsoffile:path]; nslog(@"loading plist"); }
can fix issue ? have suggestion better implement safer implementation , how avoid in general "collection mutated while being enumerated" error when obliged work same array different threads , , can reading content of array different threads causes problem or problem when writing @ same time ? thank in advance
when working static objects, best practice use dispatch_once, ensure code executed once, giving complete thread safety
it's practice have getter method static objects
+ (nsdictionary *)getcitiesdict { static dispatch_once_t pred; static nsdictionary *citiesdict = nil; dispatch_once(&pred, ^{ nsstring *path = [[nsbundle mainbundle] pathforresource:@"cities" oftype:@"plist"]; citiesdict = [[nsdictionary alloc ]initwithcontentsoffile:path]; }); return citiesdict; }
to access it, go this
[[ownerclass getcitiesdict] valueforkey:@"key"]; // ownerclass name of class defined
Comments
Post a Comment