这里是上下文:
在我的iPhone项目中,我需要在后台导入数据并将其插入到受管对象上下文中。根据这里找到的意见,这里是我在做什么:
>保存主moc
>使用主moc使用的持久存储协调器实例化后台moc
>注册我的控制器作为背景moc的NSManagedobjectContextDIDSaveNotification通知的观察者
>在后台线程中调用导入方法
>每次收到数据时,将其插入背景moc
>所有数据导入后,保存后台moc
>将更改合并到主moc中,在主线程上
>取消注册我的控制器作为通知的观察者
>重置并释放背景moc
有时(和随机),异常…
*** Terminating app due to uncaught exception 'NSGenericException',reason: '*** Collection <__NSCFSet: 0x5e0b930> was mutated while being enumerated...
…当我在后台moc上调用executeFetchRequest时抛出,以检查导入的数据是否已经存在于数据库中。我不知道是什么是突变的集合,因为没有什么运行导入方法之外。
我已经包括我的控制器和我的测试实体的整个代码(我的项目包括这两个类和应用程序委托,它是未经修改):
//// RootVIEwController.h// FK1//// Created by Eric on 09/08/10.// copyright (c) 2010 __MyCompanyname__. All rights reserved.//#import <CoreData/CoreData.h>@interface RootVIEwController : UItableVIEwController <NSFetchedResultsControllerDelegate> { NSManagedobjectContext *managedobjectContext; NSManagedobjectContext *backgroundMOC;}@property (nonatomic,retain) NSManagedobjectContext *managedobjectContext;@property (nonatomic,retain) NSManagedobjectContext *backgroundMOC;@end//// RootVIEwController.m// FK1//// Created by Eric on 09/08/10.// copyright (c) 2010 __MyCompanyname__. All rights reserved.//#import "RootVIEwController.h"#import "FK1Message.h"@implementation RootVIEwController@synthesize managedobjectContext;@synthesize backgroundMOC;- (voID)vIEwDIDLoad { [super vIEwDIDLoad]; self.navigationController.toolbarHIDden = NO; UIbarbuttonItem *refreshbutton = [[UIbarbuttonItem alloc] initWithbarbuttonSystemItem:UIbarbuttonSystemItemRefresh target:self action:@selector(refreshAction:)]; self.toolbaritems = [NSArray arrayWithObject:refreshbutton];}#pragma mark -#pragma mark ACTIONS- (voID)refreshAction:(ID)sender { // If there already is an import running,we do nothing if (self.backgroundMOC != nil) { return; } // We save the main moc NSError *error = nil; if (![self.managedobjectContext save:&error]) { NSLog(@"error = %@",error); abort(); } // We instantiate the background moc self.backgroundMOC = [[[NSManagedobjectContext alloc] init] autorelease]; [self.backgroundMOC setPersistentStoreCoordinator:[self.managedobjectContext persistentStoreCoordinator]]; // We call the fetch method in the background thread [self performSelectorInBackground:@selector(_importData) withObject:nil];}- (voID)_importData { NSautoreleasePool *pool = [[NSautoreleasePool alloc] init]; [[NSNotificationCenter defaultCenter] addobserver:self selector:@selector(backgroundMOCDIDSave:) name:NSManagedobjectContextDIDSaveNotification object:self.backgroundMOC]; FK1Message *message = nil; NSFetchRequest *fetchRequest = nil; NSEntityDescription *entity = [NSEntityDescription entityForname:@"FK1Message" inManagedobjectContext:self.backgroundMOC]; nspredicate *predicate = nil; NSArray *results = nil; // fake import to keep this sample simple for (NSInteger index = 0; index < 20; index++) { predicate = [nspredicate predicateWithFormat:@"msgid == %@",[Nsstring stringWithFormat:@"%d",index]]; fetchRequest = [[[NSFetchRequest alloc] init] autorelease]; [fetchRequest setEntity:entity]; [fetchRequest setPredicate:predicate]; // The following line sometimes randomly throw the exception : // *** Terminating app due to uncaught exception 'NSGenericException',reason: '*** Collection <__NSCFSet: 0x5b71a00> was mutated while being enumerated. results = [self.backgroundMOC executeFetchRequest:fetchRequest error:NulL]; // If the message already exist,we retrIEve it from the database // If it doesn't,we insert a new message in the database if ([results count] > 0) { message = [results objectAtIndex:0]; } else { message = [NSEntityDescription insertNewObjectForEntityForname:@"FK1Message" inManagedobjectContext:self.backgroundMOC]; message.msgid = [Nsstring stringWithFormat:@"%d",index]; } // We update the message message.updateDate = [NSDate date]; } // We save the background moc which trigger the backgroundMOCDIDSave: method [self.backgroundMOC save:NulL]; [[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedobjectContextDIDSaveNotification object:self.backgroundMOC]; [self.backgroundMOC reset]; self.backgroundMOC = nil; [pool drain];}- (voID)backgroundMOCDIDSave:(NSNotification*)notification { if (![NSThread isMainThread]) { [self performSelectorOnMainThread:@selector(backgroundMOCDIDSave:) withObject:notification waitUntilDone:YES]; return; } // We merge the background moc changes in the main moc [self.managedobjectContext mergeChangesFromContextDIDSaveNotification:notification];}@end//// FK1Message.h// FK1//// Created by Eric on 09/08/10.// copyright 2010 __MyCompanyname__. All rights reserved.//#import <CoreData/CoreData.h>@interface FK1Message : NSManagedobject {}@property (nonatomic,retain) Nsstring * msgid;@property (nonatomic,retain) NSDate * updateDate;@end// // FK1Message.m// FK1//// Created by Eric on 09/08/10.// copyright 2010 __MyCompanyname__. All rights reserved.//#import "FK1Message.h"@implementation FK1Message #pragma mark -#pragma mark PROPERTIES@dynamic msgid;@dynamic updateDate;@end
这就是全部 !整个项目在这里。没有表视图,没有NSFetchedResultsController,没有别的背景线程在背景moc上导入数据。
在这种情况下,什么可以改变集合?
我很肯定我错过了一些明显的东西,它驱使我疯了。
编辑:
这里是完整的堆栈跟踪:
2010-08-10 10:29:11.258 FK1[51419:1b6b] *** Terminating app due to uncaught exception 'NSGenericException',reason: '*** Collection <__NSCFSet: 0x5d075b0> was mutated while being enumerated.<CFBasicHash 0x5d075b0 [0x25c6380]>{type = mutable set,count = 0,entrIEs =>}'*** Call stack at first throw:( 0 CoreFoundation 0x0255d919 __exceptionPreprocess + 185 1 libobjc.A.dylib 0x026ab5de objc_exception_throw + 47 2 CoreFoundation 0x0255d3d9 __NSFastEnumerationMutationHandler + 377 3 CoreData 0x02287702 -[NSManagedobjectContext executeFetchRequest:error:] + 4706 4 FK1 0x00002b1b -[RootVIEwController _fetchData] + 593 5 Foundation 0x01d662a8 -[NSThread main] + 81 6 Foundation 0x01d66234 __NSThread__main__ + 1387 7 libSystem.B.dylib 0x9587681d _pthread_start + 345 8 libSystem.B.dylib 0x958766a2 thread_start + 34)terminate called after throwing an instance of 'NSException'解决方法 OK,我想我已经解决了我的问题,我必须感谢这个博客文章从Fred McCann的:
http://www.duckrowing.com/2010/03/11/using-core-data-on-multiple-threads/
问题似乎来自于我在主线程而不是后台线程实例化我的后台moc。当苹果告诉每个线程需要有自己的moc,你必须认真对待:每个moc必须在线程中实例化,将使用它!
移动以下行…
// We instantiate the background mocself.backgroundMOC = [[[NSManagedobjectContext alloc] init] autorelease];[self.backgroundMOC setPersistentStoreCoordinator:[self.managedobjectContext persistentStoreCoordinator]];
…在_importData方法(刚才注册控制器作为通知的观察者)解决问题。
感谢您的帮助,Peter。感谢Fred McCann的宝贵博客!
@H_502_76@ 总结以上是内存溢出为你收集整理的多线程 – “在枚举期间变异的集合”on executeFetchRequest全部内容,希望文章能够帮你解决多线程 – “在枚举期间变异的集合”on executeFetchRequest所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)