我已经阅读了 Marcus Zarra 在他的 Core Data 书中关于多线程的一章,并且相当仔细地查看了他的示例代码。但是他的代码和我在别处找到的其他代码似乎都集中在不需要相互了解的后台进程中。这些示例适用于导入树结构 - 但它们没有解决更通用(复杂)结构的导入,如有向无环图。
就我而言,我正在尝试解析 C++ 类层次结构,并希望使用尽可能多的 NSOperations。我想为每个遇到的类创建一个 NSManagedObject 实例,并且我想在保存时合并不同的 NSManagedObjectContexts。
顺便说一句:我能够使用单个 NSOperation 来处理文件,该 NSOperation 迭代文件并一次解析一个。在这个实现中,在主线程的 MOC 上调用 -mergeChangesFromContextDidSaveNotification: 的 -mergeChanges: 方法运行良好。
但理想情况下,我会让一个 NSOperation 迭代源文件并生成 NSOperations 来解析每个文件。我尝试了几种方法 - 但似乎无法正确解决。最有希望的是让每个 NSOperation 观察 NSManagedObjectContextDidSaveNotification。使用 -mergeChanges: 看起来像这样:
- (void) mergeChanges:(NSNotification *)notification
{
// If locally originated, then trigger main thread to merge.
if ([notification object] == [self managedObjectContext])
{
AppDelegate *appDelegate = (AppDelegate*)[[NSApplication sharedApplication] delegate];
NSManagedObjectContext *mainContext = [appDelegate managedObjectContext];
// Merge changes into the main context on the main thread
[mainContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)
withObject:notification
waitUntilDone:YES];
return;
}
// If not locally originated, then flag need to merge with in this NSOperation's thread.
[self setNeedsToMerge:YES];
[self setMergeNotification:notification];
}
本质上,解析 NSOperation 的 main() 会定期检查 ivar 'needsToMerge'。如果为真,则 -mergeChangesFromContextDidSaveNotification: 在本地 MOC 上调用,并带有缓存的 NSNotifications。然后needsToMerge 被重置。如果通知来自本地,则主线程被告知在其 MOC 上执行 -mergeChangesFromContextDidSaveNotification:。
我确信有一个很好的理由为什么这不起作用以及为什么我得到这个:
warning: Cancelling call - objc code on the current thread's stack makes this unsafe.
我还尝试使用 NSPeristentStoreCoordinator 的锁来控制访问 - 但如果在调用 NSManagedObjectContext 的 -save: 方法期间持有它,这是有问题的,因为 -save: 将通知感兴趣的观察者保存事件和 -mergeChangesFromContextDidSaveNotification: 似乎阻止尝试获取PSC 的锁。
似乎这应该容易得多。
最佳答案
我想我遇到了同样的问题,这是我解决它的方法:
创建一个自定义 NSOperation 类,您可以在其中定义:
NSMutableArray * changeNotifications;
NSLock * changeNotificationsLock;
NSManagedObjectContext * localManagedObjectContext;
在保存上下文之前的 NSOperation main 方法中,首先应用所有请求的更改:
[self.changeNotificationsLock lock];
for(NSNotification * change in self.changeNotifications){
[self.localManagedObjectContext mergeChangesFromContextDidSaveNotification:change];
}
if([self.changeNotifications count] >0){
[self.changeNotifications removeAllObjects];
}
[self.changeNotificationsLock unlock];
NSError *error = nil;
[self.localManagedObjectContext save:&error]
请注意,我使用了锁,这是因为 NSMutableArray 不是线程安全的,我想安全地访问 changeNotifications。
changeNotifications 是存储在保存上下文之前需要应用的所有更改的数组。
这是您的合并方法,经过修改,以便使用正确的线程合并需要由您的 NSOperation 合并的所有更改。请注意,此方法由除 NSOperation 之外的其他线程调用,因此您需要锁定对 self.changeNotifications 的访问
- (void) mergeChanges:(NSNotification *)notification
{
// If not locally originated, then add notification into change notification array
// this notification will be treated by the NSOperation thread when needed.
if ([notification object] != self.localManagedObjectContext)
{
[self.changeNotificationsLock lock];
[self.changeNotifications addObject:notification];
[self.changeNotificationsLock unlock];
}
//Here you may want to trigger the main thread to update the main context
}
希望这有帮助!这种方法不是 100% 坚如磐石,在某些情况下,更改通知可能会来得太晚。在这种情况下,上下文保存方法将返回一个错误,您必须重新加载 NSManagedObject 并再次保存它。
如果需要更多详细信息,请告诉我。
关于multithreading - 核心数据和多线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4702041/