我已经设置了一个非分层的双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)。我有一种感觉它是相关的,但我怀疑这种合并失败是一个更基本的问题。
其他说明:
这段代码有问题吗?我还能尝试什么?谢谢!
答案 0 :(得分:1)
您的合并代码是什么样的?如果在合并期间获得带有-performBlockAndWait
的无限期块,则表示您在收到通知时已经在该上下文的线程上。不过,看看你的代码会有所帮助。
同时查看通知的观察者构造的代码会有所帮助。
在执行主线程和私有线程上下文时,为什么不在这里执行父/子构造?
至于初始化多个NSPersistentStoreCoordinator
个实例的风险,这不是风险。即使您构建了100个PSC实例,它仍然可以正常对抗SQLite文件。 SQLite专为多用户访问而设计。我不明白这是怎么回事。
好的,我不建议将更改从主要版本更改为私有版本。即使没有父/子设计,您的私人队列也应该被使用一次并被丢弃。两个方向的合并可能会很快变得混乱,我怀疑你是来回合并相同的数据。进行一些记录将确认。
我还建议使用父母/孩子。这种情况是它的设计目标,并将显着提高您的合并性能。这是一个相当小的代码更改(在私有中设置PSC,关闭观察者,很好去)来测试和验证性能变化。
答案 1 :(得分:1)
不是直接答案,而是......
查看THIS示例项目。
(希望所有断点都留在原地)
通过暂停和恢复线程,可能的输出可能是:
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操作不显示主要上下文所做出的更改。
修改强>
可以看出,创建了2个私有上下文:
- 由BG操作使用
- 由主线程的并行保存创建
当主上下文仅保存上下文时:正在合并更改。
由于设计的对称性,这可以用于重现主要上下文设置两次导致更新不传播到UI的情况。
多重协调员的风险是PO设计和实施中涉及的问题和竞争条件的另一个例子
如图所示,可能的原因可能是多个私人环境&#34;初始化导致错误的私人环境被初始化。