核心数据更改不合并

我已经build立了一个非层次的双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]); }]; 

保存成功(validation)并触发执行合并的保存通知(已validation)。

但是MOC仍然不一致:当我从主MOC中获取对象A并logging其关系R中的对象数时,发现对象数与我从我的私有MOC中获取对象A的次数不同,关系R中的对象

在这一点的下游,在相关的一连串事件中,我去保存我的私人MOC。 应用程序暂停(只有启用了全部exception或全部目标-c-exception断点),才能恢复执行而没有任何明显的伤害。 这里描述了这个问题: 核心数据MOC保存暂停执行,并保存失败(没有错误或崩溃) 。 我有一种感觉是相关的,但是我认为这种错误的合并是一个更基本的问题。

其他说明:

  • 我已经尝试过每一个合并政策
  • 我发现,如果我尝试使用performBlock AndWait执行合并,应用程序将无限期地挂起; 我不知道这是否是预期的行为。
  • 我已经阅读了所有关于这个问题的问题,并尝试了我所能想到的一切。

这段代码有什么问题吗? 我还有什么可以尝试? 谢谢!

你的合并代码是什么样的? 如果在合并期间使用-performBlockAndWait获取了一个无限块,那么这意味着当收到通知时,您已经在该上下文的线程中。 这将有助于看到你的代码。

也看到你的通知的观察员build设代码将有所帮助。

当你正在做一个主线程和私有线程上下文时,你为什么不在这里做一个父/子结构呢?

至于多个NSPersistentStoreCoordinator实例被初始化的风险,这不是一个风险。 即使你构build了100个PSC实例,它仍然可以很好地对付SQLite文件。 SQLite是为多用户访问而devise的。 我不明白这可能是你的问题。

更新

好吧,我不build议将主要更改合并到私人。 即使没有父母/小孩的devise,你的私人排队也应该被使用一次并被扔掉。 在两个方向上合并可能会非常迅速地变得混乱,我怀疑你得到了来回合并的相同数据。 在一些日志logging将确认。

我也build议使用父母/孩子。 这种types的情况是它的devise目的,并会显着提高您的合并性能。 这是一个相当小的代码更改(在私人设置PSC,closures观察员,很好去)来testing和validation性能的变化。

不是一个直接的答案,但…

看看这个示例项目。

(希望所有的断点都留在原地)

通过暂停和恢复线程,可能的输出可能是:

 2014-04-11 20:50:16.199 RaceCondition[13787:60b] setting timestamp: 2014-04-11 17:50:16 +0000 2014-04-11 20:50:16.202 RaceCondition[13787:1303] thread 0x8f81f00 2014-04-11 20:50:16.202 RaceCondition[13787:60b] main context: <NSManagedObjectContext: 0x8d5c170> created 2014-04-11 20:50:16.202 RaceCondition[13787:1303] no private context found 2014-04-11 20:50:16.203 RaceCondition[13787:60b] main context: <NSManagedObjectContext: 0x8d5c170> already exists 2014-04-11 20:50:28.122 RaceCondition[13787:60b] thread 0x8e48cd0 2014-04-11 20:50:28.122 RaceCondition[13787:60b] no private context found ** 2014-04-11 20:50:46.866 RaceCondition[13787:1303] private context: <NSManagedObjectContext: 0x8c59b60> set 2014-04-11 20:50:46.867 RaceCondition[13787:1303] working with context: <NSManagedObjectContext: 0x8c59b60> 2014-04-11 20:50:46.868 RaceCondition[13787:1303] event timestamp in private context: 2014-04-11 17:50:16 +0000 ** 2014-04-11 20:51:22.923 RaceCondition[13787:60b] private context: <NSManagedObjectContext: 0x8d5c6b0> set 2014-04-11 20:51:22.924 RaceCondition[13787:60b] setting new timestamp: 2014-04-11 17:52:16 +0000 2014-04-11 20:51:22.924 RaceCondition[13787:3503] thread 0x8d4d290 2014-04-11 20:51:30.123 RaceCondition[13787:60b] main context: <NSManagedObjectContext: 0x8d5c170> already exists 2014-04-11 20:51:22.924 RaceCondition[13787:3503] private context: <NSManagedObjectContext: 0x8d5c6b0> already exists 2014-04-11 20:51:30.123 RaceCondition[13787:3503] merging to private context: <NSManagedObjectContext: 0x8d5c6b0> 2014-04-11 20:51:30.124 RaceCondition[13787:60b] thread 0x8e48cd0 2014-04-11 20:51:30.124 RaceCondition[13787:3503] thread 0x8d4d290 2014-04-11 20:51:30.124 RaceCondition[13787:60b] private context: <NSManagedObjectContext: 0x8d5c6b0> already exists 2014-04-11 20:51:30.125 RaceCondition[13787:3503] private context: <NSManagedObjectContext: 0x8d5c6b0> already exists 2014-04-11 20:51:30.125 RaceCondition[13787:60b] main context: <NSManagedObjectContext: 0x8d5c170> already exists 2014-04-11 20:51:30.126 RaceCondition[13787:3503] thread 0x8d4d290 2014-04-11 20:51:30.126 RaceCondition[13787:3503] private context: <NSManagedObjectContext: 0x8d5c6b0> already exists ** 2014-04-11 20:51:30.126 RaceCondition[13787:3503] merging to private context: <NSManagedObjectContext: 0x8d5c6b0> 2014-04-11 20:51:30.127 RaceCondition[13787:3503] thread 0x8d4d290 2014-04-11 20:51:30.127 RaceCondition[13787:3503] private context: <NSManagedObjectContext: 0x8d5c6b0> already exists 2014-04-11 20:51:36.086 RaceCondition[13787:1303] event timestamp in private context: 2014-04-11 17:50:16 +0000 

星号显示可能的竞争条件,导致BG操作不显示主要上下文提交的更改。

编辑:
可以看出,创build了2个私有上下文:
– 由BG操作使用
– 由主线程的并行保存创build

当主要上下文仅保存上下文时:正在合并更改。

由于devise的对称性,这可以用来重现主要上下文设置两次导致更新没有传播到UI的情况。

多位协调员的风险被作为POdevise和实施中涉及的问题和竞争条件的另一个例子

正如所显示的,一个可能的原因可能是多个“私有上下文”初始化导致错误的私有上下文被初始化。