核心数据和多线程

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的锁.

看起来这应该会轻松得多.

Eli*_*lme 2

我想我也遇到了同样的问题,这是我解决的方法:

创建一个自定义 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 并再次保存。如果需要更多详细信息,请告诉我。