问题
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