核心数据并发 - 收集在枚举期间发生了变化 [英] Core Data Concurrency - Collection was mutated while being enumerated
问题描述
我在Core Data应用程序中添加了一些名为 Transaction
的对象。这些交易与一个帐户相关联。
I'm adding some objects called Transaction
s 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"
在 NSArray * matches = [managedObjectContext executeFetchRequest:request error:& error];
中的行accountmanagedObjectWithId
方法。
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
@Valentin Radu
我试过了,但问题仍然存在。我必须为每次添加设置一个 managedObjectContext
。这是工作,但它减慢了应用程序。这是正确的方法吗?
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;
}
推荐答案
一个上下文,如果您对核心数据对象执行并行操作,那么这是错误的。你应该每个线程有一个上下文,并在每次保存时合并上下文。 Apple的核心数据指南更详细地讨论了这一点。
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.
这篇关于核心数据并发 - 收集在枚举期间发生了变化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!