【发布时间】:2014-04-10 16:49:06
【问题描述】:
我已经建立了一个非分层的双 MOC 架构(一个用于主线程,一个用于私有线程),带有用于合并更改的保存通知:
- (NSManagedObjectContext *)managedObjectContext
{
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
NSManagedObjectContext* mainContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[mainContext setPersistentStoreCoordinator:coordinator];
[mainContext setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
_managedObjectContext = mainContext;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(contextDidSaveMainQueueContext:)
name:NSManagedObjectContextDidSaveNotification
object:_managedObjectContext];
}
return _managedObjectContext;
}
- (NSManagedObjectContext *)privateManagedObjectContext
{
if (_privateManagedObjectContext != nil) {
return _privateManagedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
NSManagedObjectContext* privateContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[privateContext setPersistentStoreCoordinator:coordinator];
[privateContext setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
_privateManagedObjectContext = privateContext;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(contextDidSavePrivateQueueContext:)
name:NSManagedObjectContextDidSaveNotification
object:_privateManagedObjectContext];
}
return _privateManagedObjectContext;
}
- (void)contextDidSavePrivateQueueContext:(NSNotification *)notification
{
@synchronized(self) {
[self.managedObjectContext performBlock:^{
[self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
}];
}
}
- (void)contextDidSaveMainQueueContext:(NSNotification *)notification
{
@synchronized(self) {
[self.privateManagedObjectContext performBlock:^{
[self.privateManagedObjectContext mergeChangesFromContextDidSaveNotification:notification];
}];
}
}
现在,我在主 MOC 的主线程上更新了一些对象 A,现在我正在使用某种方法并在我的私有 MOC 队列/线程上的一个块中工作:
[self.privateManagedObjectContext performBlockAndWait:^{
...
我保存了我的主线程 MOC:
[self.managedObjectContext performBlockAndWait:^{
NSError *contextError;
if (![self.managedObjectContext save:&contextError]) NSLog(@"ERROR SAVING MOC : %@ ; %@", contextError, [contextError userInfo]);
}];
保存成功(验证)并触发保存通知(验证),执行合并。
但是 MOC 仍然不一致:当我从我的主 MOC 获取对象 A 并记录其关系 R 中的对象数量时,我发现对象的数量与我从我的私有 MOC 获取对象 A 并记录关系 R 中的对象数。
在这之后,在一个相关的事件链中,我去保存我的私人 MOC。应用程序暂停(仅当我启用了所有异常或所有目标 c 异常断点时),并且可以恢复执行而没有任何明显的伤害。此处描述了此问题:core data MOC save pauses execution and fails to save (w/o error or crash)。我感觉它是相关的,但我怀疑无法正确合并是一个更根本的问题。
其他说明:
- 我已经尝试了所有合并策略
- 我发现如果我尝试使用 performBlockAndWait 执行合并,应用程序会无限期挂起;我不知道这是否是预期的行为。
- 我已经阅读了我能找到的所有关于此的问题,并尝试了我能想到的一切。
这段代码有问题吗?我还能尝试什么?谢谢!
【问题讨论】:
-
您的实现中可能存在竞争条件。如果
privateManagedObjectContext从多个线程并行调用(主MOC 也是如此)。此外,不需要您的“@synchronized(self)”。确保主 MOC 和私有 MOC 没有被初始化两次 -
@DanShelly 感谢您的提示。我删除了
@synchronized。我设置了一个断点并验证 MOC 只初始化了一次。如果您认为比赛条件可能会在这里发挥作用,我很乐意进行一些详细说明,但对我来说似乎不太可能,因为我正在以一种合理隔离的方式进行测试。在我正在排除故障的方法执行期间,不应有其他方法/线程访问核心数据,并且该方法始终使用 performBlockAndWait,所有获取请求也是如此。 -
考虑这种情况:您的视图控制器请求主上下文,并且正在执行 BG 操作,请求私有上下文。在初始化期间,他们都需要一个协调器,假设您实现了延迟加载,
persistentStoreCoordinator是从 2 个不同的线程并行调用的。这将导致每个上下文使用不同的协调器,或者如果上下文没有强烈地保持协调器,甚至可能使您的应用程序崩溃。这可能不会直接导致您的合并问题,但您可能需要考虑这一点。 -
拥有多个
NSPersistentStoreCoordinator实例不会导致合并问题或 OP 描述的问题。 SQLite 和 Core Data 专为NSPersistentStoreCoordinator的多个实例并行运行而设计。 -
@MarcusS.Zarra 多个协调员可能不会导致所描述的问题,但是多个“私有上下文”会(合并直接到当前分配的“私有上下文”)。正如我所提到的,这只是使用 OP 给出的代码不安全的事情之一,但不一定会导致问题。
标签: ios multithreading core-data merge