wes*_*der 5 multithreading core-data nsmanagedobjectcontext
我在他的核心数据书中读过Marcus Zarra关于多线程的章节,并且仔细研究了他的示例代码.但他的代码和其他人,我已经找到其他地方似乎那些后台进程重点并不需要知道对方的.这些示例适用于导入树结构 - 但它们不涉及导入更通用(复杂)的结构,如有向非循环图.
在我的例子中,我试图解析一个C++类层次结构,并希望尽可能多地使用NSOperations.我想为每个遇到的类创建一个NSManagedObject实例,并且我想在保存一个时合并不同的NSManagedObjectContexts.
顺便说一句:我能够使用单个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];
}
Run Code Online (Sandbox Code Playgroud)
从本质上讲,解析NSOperation的main()会定期检查ivar"needsToMerge".如果确实如此,则在本地MOC上调用-mergeChangesFromContextDidSaveNotification:缓存的NSNotifications.然后needToMerge被重置.如果通知是在本地发起的,那么主线程被告知在其MOC上执行-mergeChangesFromContextDidSaveNotification :.
我确信这有一个很好的理由,为什么这不起作用,为什么我得到这个:
警告:取消当前线程堆栈上的调用 - objc代码会使其不安全.
我还尝试使用NSPeristentStoreCoordinator的锁来控制访问 - 但是如果在调用NSManagedObjectContext的-save:方法期间保持这是有问题的,因为-save:将通知感兴趣的观察者save事件和-mergeChangesFromContextDidSaveNotification:似乎阻止尝试获取PSC的锁.
看起来这应该会轻松得多.
我想我也遇到了同样的问题,这是我解决的方法:
创建一个自定义 NSOperation 类,在其中定义:
NSMutableArray * changeNotifications;
NSLock * changeNotificationsLock;
NSManagedObjectContext * localManagedObjectContext;
Run Code Online (Sandbox Code Playgroud)
在您的 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]
Run Code Online (Sandbox Code Playgroud)
请注意,我使用了锁,这是因为 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
}
Run Code Online (Sandbox Code Playgroud)
希望这有帮助!此方法并非 100% 坚如磐石,在某些情况下,更改通知可能到达得太晚。在这种情况下,上下文保存方法将返回错误,您必须重新加载 NSManagedObject 并再次保存。如果需要更多详细信息,请告诉我。