我正在使用以下代码运行一个解析我的xml文件的函数......
dispatch_queue_t queue = dispatch_queue_create("updateQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue,^ { [self updateFromXMLFile:@"http://path/to/file.xml"]; } );
dispatch_async(queue,^ { [self updateFromXMLFile:@"http://path/to/file1.xml"]; } );
dispatch_async(queue,^ { [self updateFromXMLFile:@"http://path/to/file2.xml"]; } );
dispatch_async(queue,^ { [self updateFromXMLFile:@"http://path/to/file3.xml"]; } );
dispatch_barrier_async(queue,^ {
dispatch_async(dispatch_get_main_queue(),^ {
[self setBottomBarToUpdated];
});
});
以下是函数updateFromXMLFile
:
- (BOOL) updateFromXMLFile:(NSString *)pathToFile {
NSURL *url = [[NSURL alloc] initWithString:pathToFile];
NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
XMLParser *parser = [[XMLParser alloc] initXMLParser];
parser.managedObjectContext = self.managedObjectContext;
[xmlParser setDelegate: parser];
BOOL success = [xmlParser parse];
if(success)
return TRUE;
else
return FALSE;
}
我遇到的问题是此错误消息:***Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSCFSet: 0xc675e10> was mutated while being enumerated.'
我猜它与所有与我的ManagedObjectContext同时搞乱的进程有关。我不知道如何处理它。有任何想法吗?谢谢!
答案 0 :(得分:1)
问题是你不能使用并发队列来连接MOC,你必须使用串行队列。将队列创建更改为
dispatch_queue_t queue = dispatch_queue_create("updateQueue", DISPATCH_QUEUE_SERIAL);
一切都会好的。同样,这样做的原因是最终的XMLParser对象同时尝试与moc进行交互。
您的选择取决于XMLParser(或任何类)正在做什么。如果一个类在与MOC交互之前完成其繁重的工作,或者它从Web下载数据,那么你将通过并发获得一些东西。
我建议你做的是从URLS下载你需要的数据,然后连续使用MOC。您必须在单个线程(在iOS中)与MOC交互,但不必是mainThread - 它可以是任何线程,只要它只是一个。这意味着如果您创建自己的串行队列,则必须在该队列上完成所有操作,包括创建MOC!
返回您的代码。让我们假设您在mainQueue上执行所有Core Data工作(目前)。解决方案是将代码更改为:
dispatch_async(dispatch_get_global_queue(0,0) {
NSURL *url = [[NSURL alloc] initWithString:pathToFile];
NSData *data = [NSData dataWithContentsOfURL:url];
NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithData:data];
XMLParser *parser = [[XMLParser alloc] initXMLParser];
parser.managedObjectContext = self.managedObjectContext;
[xmlParser setDelegate: parser];
dispatch_async(dispatch_get_main_queue(), {
BOOL success = [xmlParser parse];
// need some means to associate success/failure with the URL
} );
你可以同时做到,在击中MOC时进行序列化。对MOC使用串行队列时唯一的区别是你会发送该队列而不是mainQueue。
答案 1 :(得分:1)
您需要显示XMLParser如何处理其委托职责的代码,因为这就是所谓的内容。
现在,您正在从单独的线程中同时解析多个文件。它们都调用不同的XMLParser对象,但每个对象都使用相同的托管对象上下文(MOC)。
如果您使用默认的MOC设置,则需要重新召集所有那些对主线程的MOC调用(如果是,确实是您首次创建上下文的位置)。如果您使用限制,并且您的MOC未在主线程上创建,那么您将给自己带来更多麻烦。
然而,这是一个非常简单的修复。在该解析器委托方法中,每当您使用托管对象上下文时,将其包含在调用中以在主线程上分派该部分。
dispatch_async(dispatch_get_main_queue(), ^{
// Put your code that accesses the MOC in here.
});
现在,要进行批量下载到Core Data,最好使用两种方法之一。
创建一个新的MOC,直接附加到持久性商店协调员,并在那里进行所有保存。您的主要MOC需要观察保存通知并合并这些更改。
或者,让新的MOC成为您的主要MOC的孩子,并直接保存到它,然后它可以保存。
但是,如果您的托管对象上下文是NSMainQueueConcurrencyType或NSPrivateQueueConcurrency类型(只能在上面做第二个选项),那么您可以使用它的perfromBlock方法......
[managedObjectContext performBlock:^{
// Do your MOC stuff in here
}];
底线是可以使用三种并发类型之一创建MOC。如果是NSConfinementConcurrencyType,则不得在创建它的线程外触摸它。如果是NSMainQueueConcurrencyType,则必须使用performBlock *或仅在主线程中触摸它。如果是NSPrivateConcurrencyType,则必须使用performBlock *。