核心数据和多线程

时间:2011-01-15 20:44:39

标签: 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];
 }

基本上,解析NSOperation的main()会定期检查ivar'insoveToMerge'。如果确实如此,则在本地MOC上调用-mergeChangesFromContextDidSaveNotification:缓存的NSNotifications。然后needToMerge被重置。如果通知是在本地发起的,那么主线程被告知在其MOC上执行-mergeChangesFromContextDidSaveNotification:

我确信有一个很好的理由说明为什么这不起作用以及为什么我会这样做:

  

警告:取消调用 - objc代码   在当前线程的堆栈上   这不安全。

我还尝试使用NSPeristentStoreCoordinator的锁来控制访问 - 但是如果在调用NSManagedObjectContext的-save:方法期间保持这是有问题的,因为-save:将通知感兴趣的观察者save事件和-mergeChangesFromContextDidSaveNotification:似乎阻止试图获得PSC的锁定。

看起来这应该更容易了。

2 个答案:

答案 0 :(得分:2)

我认为我讨厌同样的问题,这就是我如何解决它:

创建您定义的自定义NSOperation类:

NSMutableArray * changeNotifications;
NSLock  * changeNotificationsLock;
NSManagedObjectContext  * localManagedObjectContext;

在保存上下文之前的NSOperation主方法中,首先应用所有请求的更改:

[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%坚如磐石,可能在某些情况下,更改通知可能来得太晚。在这种情况下,context save方法将返回错误,您必须重新加载NSManagedObject并再次保存。 如果需要更多细节,请告诉我。

答案 1 :(得分:0)

这两个代码的和平现在正在我的应用程序中正常工作:

- (void)mergeChanges:(NSNotification *)notification;
{
//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];  
}

-(void) main {
// Register context with the notification center
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; 
[nc addObserver:self
       selector:@selector(mergeChanges:) 
           name:NSManagedObjectContextDidSaveNotification
         object:managedObjectContext];

当然,managedObjectContext意味着:

managedObjectContext = [[NSManagedObjectContext alloc] init];
[managedObjectContext setPersistentStoreCoordinator:[appDelegate persistentStoreCoordinator]];
[managedObjectContext setUndoManager:nil];

如果你需要在从主moc进行更新之前删除某些内容,请小心。我有疯狂的时间和很多难以调试的错误,而我明白我不能在主线程中使用moc,而在任何地方处理其他相同内容的更改。