ios - Multi-Context Core Data - Duplicate issues when importing from a background context -


i have typical multi-context coredata stack setup - master moc on private queue (attached psc), has child on main queue , used app's main context. finally, bulk import operations (find-or-create) passed off onto third moc using background queue (new context created each operation). once operation complete saves propagated psc.

i'd been suffering issues duplicate objects being created, presumably because these operations executing concurrently. operation 1 start. if object didn't exist, created in operation 1. @ same time operation 2 concurrently running. because operation 1 hadn't completed, operation 2 couldn't possibly know newly created object, proceed create new object, hence duplicates.

to resolve this, funnelled find-or-create operation serial nsoperationqueue ensure operations executed 1 @ time:

- (void) performblock: (void(^)(player *player, nsmanagedobjectcontext *managedobjectcontext)) operation onsuccess: (void(^)()) successcallback onerror:(void(^)(id error)) errorcallback {     //add operation nsoperationqueue ensure      //duplicate records not created in multi-threaded environment     [self.operationqueue addoperationwithblock:^{          nsmanagedobjectcontext *managedobjectcontext = [[nsmanagedobjectcontext alloc] initwithconcurrencytype:nsprivatequeueconcurrencytype];         [managedobjectcontext setundomanager:nil];         [managedobjectcontext setparentcontext:self.mainmanagedobjectcontext];          [managedobjectcontext performblockandwait:^{              //retrive copy of player object attached new context             id player = [managedobjectcontext objectwithid:[self.player objectid]];             //execute block operation             operation(player, managedobjectcontext);              nserror *error = nil;             if (![managedobjectcontext save:&error])             {                 //call error handler                 dispatch_async(dispatch_get_main_queue(), ^{                     nslog(@"%@", error);                     if(errorcallback) return errorcallback(error);                 });                 return;             }              //save parent moc (mainmanagedobjectcontext) - block main thread breifly             [managedobjectcontext.parentcontext performblockandwait:^{                 nserror *error = nil;                 if (![managedobjectcontext.parentcontext save:&error])                 {                     //call error handler                     dispatch_async(dispatch_get_main_queue(), ^{                         nslog(@"%@", error);                         if(errorcallback) return errorcallback(error);                     });                     return;                 }             }];              //attempt clear retain cycles created during operation             [managedobjectcontext reset];              //call success handler             dispatch_async(dispatch_get_main_queue(), ^{                 if (successcallback) return successcallback();             });         }];     }]; } 

my operation queue configured follows:

single.operationqueue = [[nsoperationqueue alloc] init]; [single.operationqueue setmaxconcurrentoperationcount:1]; 

this seem have reduced incidences of duplicate issues quite surprise has not eliminated entirely. occurrences less frequent, still happens.

can enlighten me why still happening?

update: added additional code describe happening in operation block:

- (void) importupdates: (id) methodresult onsuccess: (void (^)()) successcallback onerror: (void (^)(id error)) errorcallback {     [_model performblock:^(player *player, nsmanagedobjectcontext *managedobjectcontext) {         //player , opponents         nsmutabledictionary *opponents = [nsmutabledictionary dictionary]; //store opponents in dict can relationship fix-up later          [player parsedictionary:methodresult[@"player"] inmanagedobjectcontext:managedobjectcontext];          (nsdictionary *opponentdictionary in methodresult[@"opponents"])         {             opponent *opponent = [opponent updateorinsertintomanagedobjectcontext:managedobjectcontext withdictionary:opponentdictionary];             opponents[opponent.playerid] = opponent;         }          //matches         [self parsematches: methodresult[@"matches"] withplayer: player andopponents: opponents usingmanagedobjectcontext: managedobjectcontext];      } onsuccess:successcallback onerror:errorcallback]; } 

parsematches contains implementation of find-or-create:

- (nsarray *) parsematches: (nsarray *) matchdictionaries withplayer: (player *) player andopponents: (nsdictionary *) opponents usingmanagedobjectcontext: (nsmanagedobjectcontext *) managedobjectcontext {     nsmutablearray *parsedmatches = [nsmutablearray array];      [managedobjectcontext performblockandwait:^{         //sorted matchdictionaties         nsarray *sortedmatchdictionaries = [matchdictionaries sortedarrayusingdescriptors:@[[nssortdescriptor sortdescriptorwithkey:@"matchid" ascending:yes]]];         nsarray *sortedmatchidsinresponse = [sortedmatchdictionaries valueforkeypath:@"matchid"];          //fetch existing matches         nsfetchrequest *fetchrequest = [nsfetchrequest fetchrequestwithentityname:@"match"];         fetchrequest.predicate = [nspredicate predicatewithformat:@"(matchid in %@)", sortedmatchidsinresponse];         [fetchrequest setsortdescriptors:@[[nssortdescriptor sortdescriptorwithkey:@"matchid" ascending:yes]]];         [fetchrequest setrelationshipkeypathsforprefetching:@[@"roundsset", @"player", @"opponent"]];          nserror *error;         nsarray *existingmatches = [managedobjectcontext executefetchrequest:fetchrequest error:&error];           //walk through existing match array , matches in response         int i=0,j=0,updated=0,added=0,skipped=0;         while ((i < [sortedmatchdictionaries count]))         {             nsdictionary *matchdictionary = sortedmatchdictionaries[i];             match *match = j < [existingmatches count] ? existingmatches[j] : nil;              nslog(@"%@ %@", matchdictionary[@"matchid"], match.matchid);              nscomparisonresult result = match ? [((nsstring *)matchdictionary[@"matchid"]) compare: match.matchid] : nsordereddescending;              if (result == nsorderedsame)             {                 //match exists in both, update                 nslog(@"updated");                  [match parsedictionary:matchdictionary inmanagedobjectcontext:managedobjectcontext];                  //set match opponent (as may have been initally nil in case of random match)                 if (match.opponentid != nil && [opponents objectforkey:match.opponentid] != nil)                 {                     [match setvalue:opponents[match.opponentid] forkey:@"opponent"];                 }                  [parsedmatches addobject:match];                 i++,j++,updated++;             }             else if (result == nsordereddescending)             {                 nslog(@"added");                  //match doesnt exist on device, add                 match *match = [match insertnewobjectwithdictionary:matchdictionary inmanagedobjectcontext:managedobjectcontext];                  //set match player , opponent                 if (match.opponentid != nil && [opponents objectforkey:match.opponentid] != nil)                 {                     [match setvalue:opponents[match.opponentid] forkey:@"opponent"];                 }                 [match setvalue:player forkey:@"player"];                  [parsedmatches addobject:match];                 i++,added++;              } else {                 nslog(@"match %@ exists on device not in response, skipping", match.matchid);                 j++;skipped++;             }         }          nslog(@"core data import: inserted %u matches. updated %u matches. skipped %u matches", added, updated, skipped);     }];      return [nsarray arraywitharray:parsedmatches]; } 

it's worth noting once duplicate objects same matchid find way store algorithm no longer works , duplicates proliferated. that's not problem i'm concerned about, it's fact duplicates occurring in first place.

second update:

this stack trace crash i'm seeing seems occur before duplicates store. narrow down issue?

sigsegv coredata_pffastqueuerelease   0   wit premium 0x0019e36e  testflight_backtrace 1   wit premium 0x0019da02  tfsignalhandler 2   libsystem_c.dylib   0x3afd7e92  _sigtramp 3   coredata    0x32d06de8  _pffastqueuerelease 4   coredata    0x32ce6eec  -[nsmanagedobject release] 5   corefoundation  0x32e40310  cfrelease 6   corefoundation  0x32f1b433  __cfbasichashdrain 7   corefoundation  0x32e403d0  cfrelease 8   coredata    0x32cb0d0e  -[_nsfaultingmutableset dealloc] 9   coredata    0x32cb51a4  -[_nsnotifyingwrappermutableset dealloc] 10  libobjc.a.dylib 0x3ab56488  _zn12_global__n_119autoreleasepoolpage3popepv 11  corefoundation  0x32e42440  _cfautoreleasepoolpop 12  foundation  0x3378c6da  -[__nsoperationinternal start] 13  foundation  0x33804be2  __block_global_6 14  libdispatch.dylib   0x3af7111e  _dispatch_call_block_and_release 15  libdispatch.dylib   0x3af7f258  _dispatch_root_queue_drain 16  libdispatch.dylib   0x3af7f3b8  _dispatch_worker_thread2 17  libsystem_c.dylib   0x3afa5a10  _pthread_wqthread 18  libsystem_c.dylib   0x3afa58a3  start_wqthread 


Comments

Popular posts from this blog

Unable to remove the www from url on https using .htaccess -