如何将子上下文中的NSManagedObjects的两个不同实例合并到父上下文中的单个实例中

时间:2014-01-30 21:55:08

标签: ios core-data nsmanagedobjectcontext

我在尝试在公共父级的子上下文中创建或更新托管对象时遇到了竞争条件问题。

假设以下设置:

  1. 使用NSPrivateQueueConcurrencyType保存上下文。这也是持久性存储协调器指向支持SQL的唯一上下文。
  2. 使用NSMainQueueConcurrencyType
  3. 主要
  4. 根据需要提供上下文的上下文,这是在我从服务器获取数据并使用它来创建或更新图中的对象时动态创建的
  5. 某些模型实体,例如User。假设User有2个属性:idname。此外,User还有多对多关系friends
  6. 检索用户及其属性和关系的流程将是这样的(我正在简化和使用一些伪代码,因为实际的实现是冗长而复杂的,但这应该得到重点):

    1. 您要求ID为
    2. 的用户
    3. 我分为上下文的两个子上下文:一个将获取属性,另一个将获取关系。
    4. 负责处理属性的上下文执行此操作:

      [childContext1 performBlock:^{
      
          // id jsonData = GET http://myserver.com/users/1
      
          NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:user.entity.name];
          fetchRequest.predicate = [NSPredicate predicateWithFormat:@"id = %i", [jsonData[@"id"] intValue]];
          NSArray *results = [childContext1 executeFetchRequest:fetchRequest error:&error];
          NSManagedObject *newOrExistingObject = nil;
          BOOL existsInContext = (BOOL)[results count];
          if (!existsInContext) {
              newOrExistingObject = [NSEntityDescription insertNewObjectForEntityForName:e.name inManagedObjectContext:childContext1];
          }
          else {
              newOrExistingObject = results[0];
          }
      
          // at this point, the id and name of the user would be set.
          [newOrExistingObject setValuesForKeysWithDictionary:jsonData];
          [childContext1 save:nil];
      
          [mainContext performBlock:^{
            [mainContext obtainPermanentIDsForObjects:mainContext.insertedObjects.allObjects error:nil];
            [mainContext save:nil];
            [saveContext performBlock:^{
              [self.saveContext save:nil];
            }];
          }];
      }};
      
    5. 负责处理这种关系的背景会这样做:

      [childContext2 performBlock:^{
      
          // id jsonData = GET http://myserver.com/users/friends/
      
          NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:user.entity.name];
          fetchRequest.predicate = [NSPredicate predicateWithFormat:@"id = %i", [jsonData[@"id"] intValue]];
          NSArray *results = [childContext2 executeFetchRequest:fetchRequest error:&error];
          NSManagedObject *newOrExistingObject = nil;
          BOOL existsInContext = (BOOL)[results count];
          if (!existsInContext) {
              newOrExistingObject = [NSEntityDescription insertNewObjectForEntityForName:e.name inManagedObjectContext:childContext2];
          }
          else {
              newOrExistingObject = results[0];
          }
      
          // at this point, the 'friends' relationship would be set
          [newOrExistingObject setValuesForKeysWithDictionary:jsonData];
          [childContext2 save:nil];
      
          [mainContext performBlock:^{
            [mainContext obtainPermanentIDsForObjects:mainContext.insertedObjects.allObjects error:nil];
            [mainContext save:nil];
            [saveContext performBlock:^{
              [self.saveContext save:nil];
            }];
          }];
      }};
      
    6. 上述方法的问题在于,为了“创建或更新”,我需要弄清楚相关对象是否已经存在(我试图通过id进行提取来实现这一点。这是我基于等价的属性。)

      就目前而言,有时其中一个子上下文将完成它的事情,当其他上下文尝试获取具有相关id的对象时,它将获得之前由其他上下文创建的对象,更新它一切都会好起来的。但有时,无论哪个子上下文执行第二个,都找不到先前执行的子项先前创建的对象。因此它创建了另一个具有相同id的新对象。当整个事情完成并且两个孩子将其对象传播到主上下文并且主要上下文又传播到保存上下文时,我最终得到两个具有不同objectID但相同id的对象:一个将具有属性,另一个将具有关系。我的猜测是因为当第二个上下文尝试获取对象时,要么:

      • A:创建对象的子上下文未在其他子上下文尝试检索它时将其传播回上下文。
      • B: 上下文未完成为刚刚从第一个子上下文获取的对象分配永久ID。

      基本上我需要的是一种方法来同步每个子上下文的提取,这样就不会发生这种情况,直到任何一个子项首先执行并创建对象已保存,反过来该对象已传播到 main 上下文。或者,或许更好的是,主要上下文的一种方式是知道同一实体类和具有相同id属性值的任意数量的对象是等价的(尽管它们的objectID是不同的,因为它们是在不同的环境中并行创建的,所以应该合并为一个。

      有什么想法吗?

1 个答案:

答案 0 :(得分:0)

dispatch_semaphore_t

:P

我不知道我可以在NSManagedObjectContext -performBlock内使用GCD信号量。

我最终创建了一个互斥(二进制信号量),以便子块执行:

[childContext performBlock:^{

    // id jsonData = GET http://myserver.com/<whateverData>

    dispatch_semaphore_wait(_s, DISPATCH_TIME_FOREVER);

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:user.entity.name];
    fetchRequest.predicate = [NSPredicate predicateWithFormat:@"id = %i", [jsonData[@"id"] intValue]];
    NSArray *results = [childContext2 executeFetchRequest:fetchRequest error:&error];
    NSManagedObject *newOrExistingObject = nil;
    BOOL existsInContext = (BOOL)[results count];
    if (!existsInContext) {
        newOrExistingObject = [NSEntityDescription insertNewObjectForEntityForName:e.name inManagedObjectContext:childContext];
    }
    else {
        newOrExistingObject = results[0];
    }

    [newOrExistingObject setValuesForKeysWithDictionary:jsonData];
    [childContext obtainPermanentIDsForObjects:childContext.insertedObjects.allObjects error:nil];
    [childContext save:nil];

    dispatch_semaphore_signal(_s);

    [mainContext performBlock:^{
      [mainContext save:nil];
      [saveContext performBlock:^{
        [self.saveContext save:nil];
      }];
    }];
}};

这就是诀窍