Core data iOS5 CoreData在主MOC和子MOC上同时触发故障,导致崩溃

Core data iOS5 CoreData在主MOC和子MOC上同时触发故障,导致崩溃,core-data,nested,Core Data,Nested,假设我有4个MOC(托管对象上下文),如下所示: rootMOC(后台保存)->fetchMOC(后台抓取)->mainMOC(与用户界面相关) rootMOC->网络moc(网络相关) 因此,工作流程如下所示: 在networkMOC中创建对象,[确定] 保存到rootMOC,[确定] 将objectid传递给fetchMOC和mainMOC[确定] 调用[mocobjectWithID:objectID][OK] [mocrefreshObject:对象合并更改:YES]在fetchMOC

假设我有4个MOC(托管对象上下文),如下所示:

rootMOC(后台保存)->fetchMOC(后台抓取)->mainMOC(与用户界面相关)

rootMOC->网络moc(网络相关)

因此,工作流程如下所示:

  • 在networkMOC中创建对象,[确定]
  • 保存到rootMOC,[确定]
  • 将objectid传递给fetchMOC和mainMOC[确定]
  • 调用[mocobjectWithID:objectID][OK]
  • [mocrefreshObject:对象合并更改:YES]在fetchMOCmainMOC中,拉入更改[OK]

  • 稍后,我将访问导致在fetchMOC和mainMOC中同时触发故障的对象的属性[Boom,crash!]

我确信的事情:

  • 使用NSPrivateQueueConcurrencyTypeNSMainQueueConcurrencyType正确设置所有MOC
  • 所有与MOC相关的API调用都包装在performBlock中,甚至只是访问托管对象的属性(这可能会导致引发错误)
下面是线程4中的代码崩溃(fetchMOC


同时,在mainMOC发生了什么:

[self.fetchMOC performBlock:^{
                    [self.fetchMOC refreshObject:self.currentUserFetchMOC mergeChanges:YES];
                    for (Stream* stream in self.currentUserFetchMOC.streams) {
                        [self.fetchMOC refreshObject:stream mergeChanges:YES];
                    }
                    [self.mainMOC performBlock:^{
                        LogMessage(@"MainMOC", 0, @"%@", @"Refresh steams Begin");
                        [self.mainMOC refreshObject:self.currentUserMainMOC mergeChanges:YES];
                        for (Stream* stream in self.currentUserMainMOC.streams) {
                            [self.mainMOC refreshObject:stream mergeChanges:YES];
                            if ([stream.uri isEqualToString:self.currentUserMainMOC.currentStreamURI]) {//Thread 1, here
                                self.currentStreamMainMOC = stream;
                            }
                        }
                        LogMessage(@"MainMOC", 0, @"%@", @"Refresh steams End");
                    }];
                }];

编辑

为什么失败

在检查了我的每一行代码之后,我找到了原因。 我通过用existingObjectWithID替换objectWithID进行检查,以确保对象已经存在于fetchMOC和mainMOC中。结果是existingObjectWithID返回零,这让人大吃一惊。因此,我认为原因是[networkMOC save]没有同步保存所有内容,而且我不应该在保存返回后立即调用objectWithID

解决方法

我只是在调用[fetchMOC MergeChanges FromContextDidSaveNotification]之后将所有对象合并代码移动到NSManagedObjectContextDidSaveNotification处理程序中,这次existingObjectWithID返回有效对象,不会再次崩溃

仍在疑惑

会话303-iOS上核心数据的新功能向我展示了通过使用嵌套MOC,我可以在兄弟MOC之间共享未保存的更改,这就是我试图实现的(但我失败了)。据我所知,在调用[sourceMOC save]之后,没有任何示例代码/博客/视频向我显示要做什么,苹果的家伙说只要访问托管对象的属性就可以“拉动”更改。很明显,仅通过访问属性,它就不起作用了! 当使用mergeChangesFromContextDidSaveNotification时,它只是太慢了。 我在Dev论坛上创建了一个线程,到目前为止还没有得到任何答案

维护主运行中心之间的一致性可能很棘手。MOC易于创建和销毁。持久性存储及其协调器旨在保持一致性。行缓存非常快。因此,我建议您根据需要创建和销毁MOC。我怀疑,当您开始为每个后台操作使用新的MOC时,您的问题就会消失。(在我的应用程序中,情况确实如此。)


安德鲁

谢谢你的回答,很抱歉我这么晚才发表评论。可能是通过使用实时MOC(根据需要创建,之后立即发布),崩溃可能会消失,但它实际上隐藏了问题,并且不适合我的应用程序的工作流。无论如何,我在编辑->解决方案部分找到了一个解决方案。赵,请允许我恭敬地建议,我的解决方案并没有隐藏问题,而是避免了问题。我的主要观点是,维护后台MOC有助于创建一致性问题。持久存储中的行和对象缓存是保持一致性的核心数据方式。它工作得很好。安德鲁,对不起,如果我糟糕的英语听起来很粗鲁,你知道我不是故意的。我从苹果WWDC2011视频303中了解到MOC设计模式,可能我的理解是错误的,因为视频中很少提到这种模式(11:34)“1.shared parent context 2.push to parent 3.pull in peer child。”在开发论坛中没有示例代码,也没有响应。所以我创建了这些MOC:networkMOC用于接收网络数据,并不断创建核心数据对象,通过rootMOC->fetchMOC->mainMOC共享它们。fetchMOC在这里用于异步抓取。正如您所看到的,我的问题存在于这个共享模式的拉部分而不是推部分。如果我创建临时MOC,我必须重新蚀刻这些MOC中的所有托管对象,因为KVO会通知我的UI这些对象,这是相当多的工作。通过使用永久的MOC,我可以只设置一次KVO,然后永远使用它们。Jay,很明显,你已经为自己的道路做出了承诺。那么,我建议您制作一个非常简单的CD项目,使用一个非常简单的模型,将嵌套的MOC作为实验框架。这样你就能看到发生了什么。当我无法从一个复杂的框架中获得预期的结果时,这种策略总是对我有效。祝你好运找到答案。Andrew P.S.如果我用错了你的名字,我道歉。原来赵是你的名字。
comparator = [^NSComparisonResult(id obj1, id obj2) {
                ArticleInfo* info1 = obj1;
                ArticleInfo* info2 = obj2;
                if (info2.timeStamp > info1.timeStamp) { //[Thread4 fetchMOC:Crash]
                    return NSOrderedAscending;
                } 
                else if (info2.timeStamp == info1.timeStamp) {
                    return NSOrderedSame;
                }
                else {
                    return NSOrderedDescending;
                }
            } copy];
[self.fetchMOC performBlock:^{
                    [self.fetchMOC refreshObject:self.currentUserFetchMOC mergeChanges:YES];
                    for (Stream* stream in self.currentUserFetchMOC.streams) {
                        [self.fetchMOC refreshObject:stream mergeChanges:YES];
                    }
                    [self.mainMOC performBlock:^{
                        LogMessage(@"MainMOC", 0, @"%@", @"Refresh steams Begin");
                        [self.mainMOC refreshObject:self.currentUserMainMOC mergeChanges:YES];
                        for (Stream* stream in self.currentUserMainMOC.streams) {
                            [self.mainMOC refreshObject:stream mergeChanges:YES];
                            if ([stream.uri isEqualToString:self.currentUserMainMOC.currentStreamURI]) {//Thread 1, here
                                self.currentStreamMainMOC = stream;
                            }
                        }
                        LogMessage(@"MainMOC", 0, @"%@", @"Refresh steams End");
                    }];
                }];