3

我的多线程 Core Data 应用程序一直有问题,我想我应该认真看看我在做什么以及如何做。请让我知道以下是否可行。

我有一个DataManager处理核心数据的单例类。它具有managedObjectContext为每个线程返回不同 MOC 的属性。因此,给定NSMutableDictionary *_threadContextDict(字符串线程名称到上下文)和NSMutableDictionary *_threadDict(字符串线程名称到线程),它看起来像这样:

-(NSManagedObjectContext *)managedObjectContext
{
  if ([NSThread currentThread] == [NSThread mainThread])
  {
    MyAppDelegate *delegate = [[UIApplication sharedApplication] delegate];
    return delegate.managedObjectContext; //MOC created in delegate code on main thread
  }
  else
  {
    NSString *thisThread = [[NSThread currentThread] description];
    {
      if ([_threadContextDict objectForKey:thisThread] != nil)
      {
        return [_threadContextDict objectForKey:thisThread];
      }
      else
      {
        NSManagedObjectContext *context = [[NSManagedObjectContext alloc]init];
        MyAppDelegate *delegate = [[UIApplication sharedApplication] delegate];
        [context setPersistentStoreCoordinator:delegate.persistentStoreCoordinator];
        [_threadContextDict setObject:context forKey:thisThread];
        [_threadDict setObject:[NSThread currentThread] forKey:thisThread];

        //merge changes notifications
        NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
        [center addObserver:self selector:@selector(mergeChanges:)
            name:NSManagedObjectContextDidSaveNotification object:context];

        return context;
      }
    }
  }
}

在该mergeChanges方法中,我将来自传入通知的更改合并到除生成通知的上下文之外的所有上下文。它看起来像这样:

-(void)mergeChanges:(NSNotification *)notification
{
  MyAppDelegate *delegate = [[UIApplication sharedApplication] delegate];
  NSManagedObjectContext *context = delegate.managedObjectContext;
  [context performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification)
     withObject:notification waitUntilDone:YES];

  for (NSString *element in [_threadContextDict allKeys])
  {
    if (![element isEqualToString:[[NSThread currentThread] description]])
    {
      NSThread *thread = [_threadDict objectForKey:element];
      NSManagedObjectContext *threadContext = [_threadContextDict objectForKey:element];
      [threadContext performSelector:@selector(mergeChangesFromContextDidSaveNotification)
         onThread:thread withObject:notification waitUntilDone:YES];
    }
  }
}

每当我在 MOC 上保存更改时,都是通过调用saveContext此 shared 上的方法来完成的,该方法DataManager调用save从上述属性获得的上下文:

-(void)saveContext
{
  NSManagedObjectContext *context = self.managedObjectContext;
  NSError *err = nil;
  [context save:&err];
  //report error if necessary, etc.
}

鉴于我对 Core Data 多线程规则的理解,我觉得这应该可行。我为每个线程使用单独的上下文,但对所有线程使用相同的持久存储。但是当我使用它时,我会遇到很多合并冲突,即使我的线程没有处理相同的对象(NSManagedObject子类)。我只是从网络上下载数据,解析结果,然后将它们保存到 Core Data。

难道我做错了什么?我试过使用NSLock实例来锁定一些东西,但后来我就挂了。

更新/解决方案:我可以通过添加一个简单的东西来完成这项工作:一种在我完成后从我的字典中删除线程/MOC 对的方法。在每次调用dispatch_async我做核心数据的地方的每个块的末尾,我调用[self removeThread],它从字典中删除当前线程及其 MOC。我也只合并对主线程 MOC 的更改。实际上,这意味着每次我在后台线程上工作时,我都会得到一个全新的 MOC。

我还通过添加一个数字来区分线程userInfoDict,而不是调用description. 该数字是通过我的类上的只读属性获得的,该属性每次调用时都会返回一个更高的数字。

4

1 回答 1

7

恕我直言,您的方法是一场噩梦,如果出现问题,调试它以解决任何问题应该更糟糕。第一个问题是这样的:

我有一个单身 DataManager

没有一个单例对象来管理不同线程上不同实体的核心数据操作。单例处理起来很棘手,尤其是在多线程环境中,并且将其与核心数据一起使用是一种更糟糕的方法。

第二件事,不要使用 NSThread 处理多线程。还有更现代的 API。使用 Grand Central 调度或 NSOperation/NSOperationQueue。自从引入块(iOS 4)以来,Apple 一直鼓励人们从 NSThread 迁移。并且为了将来参考,不要以您使用对象的方式使用对象的描述。描述通常/主要用于调试目的。那里的信息不应该用来比较。甚至没有指针值(这就是为什么你应该使用 isEqual 而不是 ==)。

这是您需要了解的有关核心数据和多线程的知识:

  1. 每个线程创建一个上下文。核心数据模板已经为您创建了一个主线程上下文。在后台线程执行开始时(在块内,或在您的 NSOperation 子类的 main 方法上),初始化您的上下文。
  2. 一旦你的上下文被初始化,并且有正确的 persistentStoreCoordinator,监听 NSManagedObjectContextObjectsDidChangeNotification。监听通知的对象将在保存上下文的同一线程中接收通知。由于这与主线程不同,因此在使用接收上下文的线程上使用合并上下文进行合并调用。假设您在与主线程不同的线程内使用上下文,并且您想与主线程合并,您需要在主线程内调用 merge 方法。你可以用 dispatch_async(dispatch_get_main_queue(), ^{//code here});
  3. 不要在其 managedObjectContext 所在的线程之外使用 NSManagedObject。

有了这些和其他简单的规则,在多线程环境下管理核心数据就更容易了。你的方法更难实现,更难调试。对您的架构进行一些更改。根据您正在使用的线程(而不是集中式)管理上下文。不要将对上下文的引用保留在其范围之外。创建第一个上下文后,在线程上创建上下文并不昂贵。您可以重用相同的上下文,只要它在同一个块/NSOperation 执行中。

于 2012-09-17T21:20:32.270 回答