5

我已经阅读了 Marcus Zarra 在他的 Core Data 书中关于多线程的一章,并且相当仔细地查看了他的示例代码。但是他的代码和我在其他地方找到的其他代码似乎都集中在不需要相互了解的后台进程。这些示例适用于导入树结构 - 但它们没有解决更一般(复杂)结构的导入,例如有向无环图。

就我而言,我正在尝试解析 C++ 类层次结构,并希望使用尽可能多的 NSOperations。我想为每个遇到的类创建一个 NSManagedObject 实例,并且我想在保存一个时合并不同的 NSManagedObjectContexts。

顺便说一句:我能够使用单个 NSOperation 来迭代文件并一次解析一个。在此实现中,在主线程的 MOC 上调用 -mergeChangesFromContextDidSaveNotification: 的 -mergeChanges: 方法运行良好。

但理想情况下,我会让一个 NSOperation 遍历源文件并生成 NSOperation 来解析每个文件。我尝试了几种方法 - 但似乎无法做到正确。最有希望的是让每个 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 上被调用并缓存了 NSNotification。然后 needsToMerge 被重置。如果通知来自本地,则主线程被告知在其 MOC 上执行 -mergeChangesFromContextDidSaveNotification:。

我确信这有一个很好的理由为什么这不起作用以及为什么我得到这个:

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

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

似乎这应该容易得多。

4

2 回答 2

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% 坚如磐石,在某些情况下更改通知可能会为时已晚。在这种情况下,上下文保存方法将返回错误,您必须重新加载 NSManagedObject 并再次保存。如果需要更多详细信息,请告诉我。

于 2011-02-15T13:55:15.540 回答
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 而在任何地方处理具有相同内容的其他更改。

于 2011-02-03T10:13:14.303 回答