我在尝试在公共父级的子上下文中创建或更新托管对象时遇到了竞争条件问题。
假设以下设置:
NSPrivateQueueConcurrencyType
的保存上下文。这也是持久性存储协调器指向支持SQL的唯一上下文。NSMainQueueConcurrencyType
User
。假设User
有2个属性:id
和name
。此外,User
还有多对多关系friends
。检索用户及其属性和关系的流程将是这样的(我正在简化和使用一些伪代码,因为实际的实现是冗长而复杂的,但这应该得到重点):
负责处理属性的上下文执行此操作:
[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];
}];
}];
}};
负责处理这种关系的背景会这样做:
[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];
}];
}];
}};
上述方法的问题在于,为了“创建或更新”,我需要弄清楚相关对象是否已经存在(我试图通过id
进行提取来实现这一点。这是我基于等价的属性。)
就目前而言,有时其中一个子上下文将完成它的事情,当其他上下文尝试获取具有相关id的对象时,它将获得之前由其他上下文创建的对象,更新它一切都会好起来的。但有时,无论哪个子上下文执行第二个,都找不到先前执行的子项先前创建的对象。因此它创建了另一个具有相同id
的新对象。当整个事情完成并且两个孩子将其对象传播到主上下文并且主要上下文又传播到保存上下文时,我最终得到两个具有不同objectID
但相同id
的对象:一个将具有属性,另一个将具有关系。我的猜测是因为当第二个上下文尝试获取对象时,要么:
基本上我需要的是一种方法来同步每个子上下文的提取,这样就不会发生这种情况,直到任何一个子项首先执行并创建对象已保存,反过来该对象已传播到 main 上下文。或者,或许更好的是,主要上下文的一种方式是知道同一实体类和具有相同id
属性值的任意数量的对象是等价的(尽管它们的objectID
是不同的,因为它们是在不同的环境中并行创建的,所以应该合并为一个。
有什么想法吗?
答案 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];
}];
}];
}};
这就是诀窍