Core Data Concurrency - Collection was mutated while being enumerated

让人想犯罪 __ 提交于 2019-12-12 01:35:21

问题


I'm adding some objects called Transactions in my Core Data application. Those transactions are linked to an account.

I'd like to update the account amount when all the transactions are saved. Sometimes there is a concurrency exception

"Collection was mutated while being enumerated"

at NSArray *matches = [managedObjectContext executeFetchRequest:request error:&error]; line in accountManagedObjectWithId method.

//TransactionsManager

- (BOOL)addRepeatTransaction:(Transaction *)transaction{

    Account *accountTrx = transaction.account;
    double accountAmount = accountTrx.amount;
    for (int i =0; i<nbRepeat; i++){
         accountAmount +=transactionBiz.amount;
        [[Persister instance] registerTransaction:transaction];
    }
    [[Persister instance]editAmountAccount:transaction.account amount:accountAmount];
    [[Persister instance]saveContext];

    return YES;
}

//Persister

-(id)init {
    if (self = [super init]){
        managedObjectContext = [appDelegate managedObjectContext];
        return self;
    }
    return nil;
}

-(BOOL)registerTransaction:(Transaction *)transaction {
    TransactionManagedObject *transactionsRow = (TransactionsManagedObject *)[NSEntityDescription insertNewObjectForEntityForName:@"Transactions" inManagedObjectContext:managedObjectContext];

    transactionsRow.idTransaction =  [NSNumber numberWithInt:transaction.idTransaction];
    transactionsRow.name = transaction.name;
    transactionsRow.amount = [NSNumber numberWithDouble:transaction.amount];
    [...]
    transactionsRow.account = [[Finder instance] accountManagedObjectWithId:transaction.account.idAccount];

    return YES;
}

-(BOOL)editAmountAccount:(Account *)asset amount:(double)amount {
    AccountManagedObject *accountRow = [[Finder instance] accountManagedObjectWithId:account.idAccount];

    accountRow.amount = [NSNumber numberWithDouble:amount];
    return YES;
}

-(AccountManagedObject *)accountManagedObjectWithId:(NSInteger)idAccount {

    NSFetchRequest *request = [[NSFetchRequest alloc]init];
    [request setEntity:[NSEntityDescription entityForName:@"Accounts" inManagedObjectContext:managedObjectContext]];

    //Predicate
    NSString *recordedIdAccount = @"idAccount";
    NSNumber *numberIdAccount = [NSNumber numberWithInt:idAccount];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%K == %@", recordedIdAccount,numberIdAccount];
    [request setPredicate:predicate];

    //Execute
    NSError *error;
    NSArray *matches = [managedObjectContext executeFetchRequest:request error:&error];
    NSInteger nbResult = [matches count];
    if(nbResult==1){
        return [matches objectAtIndex:0];
    }
    if(nbResult==0){
        [...]
        return nil;
    }
    if(nbResult>0){
        [...]
        return nil;
    }
    return nil;
}

//UI call
[...]
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
     //Save
     [[TransactionsManager instance] addRepeatTransaction:transactionToAdd];

     dispatch_async(dispatch_get_main_queue(), ^{

          //Call the delegate
          [self.delegate theSaveButtonOnTheAddTransactionTVCWasTapped:self];
      });
});

@Valentin Radu

I've tried that but the problem remains. I have to set up a managedObjectContext for each adding. This is working but it slows the application. Is it the right way to do it?

-(BOOL)registerTransaction:(Transaction *)transaction{
    NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init];
   [moc setPersistentStoreCoordinator:[[self managedObjectContext] persistentStoreCoordinator]];


    TransactionsManagedObject *transactionsRow = (TransactionsManagedObject *)[NSEntityDescription insertNewObjectForEntityForName:@"Transactions" inManagedObjectContext:moc];

    transactionsRow.idTransaction =  [NSNumber numberWithInt:transaction.idTransaction];
    transactionsRow.name = transaction.name;
    transactionsRow.amount = [NSNumber numberWithDouble:transaction.amount];
    [...]
    transactionsRow.account = [[Finder instance] accountManagedObjectWithId:transaction.account.idAccount andManagedContext:moc]];

    NSError *error = nil;
    if (![moc save:&error]) {
        [[ErrorManager instance] addError:error];
        return NO;
    }

    return YES;
}

回答1:


You seem to be using only one context, which is wrong if you have parallel operations executing on your core data objects. You should have a context per thread and merge the contexts every time you save. Apple's Core Data Guideline talks about this in more detail.



来源:https://stackoverflow.com/questions/13805013/core-data-concurrency-collection-was-mutated-while-being-enumerated

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!