这是一个非常奇怪的问题,我认为我理解Core Data。
我使用没有父级的背景上下文。直接进入持久性商店协调员。我在此背景上下文中更新对象,然后保存它。我听ContextDidSaveNotification
并将这些更改合并到我的主线程上下文中。这些更新的对象不是主线程上的错误,因为它们已经用于填充表视图单元格。所以我希望这些更改能够实际合并。但他们不是。
如果不了解我的数据模型的细节,就可以说对象具有属性“downloadState
”。解析工作在后台线程完成后,downloadStateValue
(枚举)设置为“3”,对应于“已完成”。
我现在订阅了ContentWillSave
通知来检查发生了什么。我在解析工作结束时得到了这个:
2016-06-13 10:19:21.055 MyApp[29162:52855206] Going to save background context.
updated:{(
<QLUserPinnedCourse: 0x7fe195403c10> (entity: QLUserPinnedCourse; id: 0xd0000000002c0002 <x-coredata://95821ADC-8A1F-4DAC-B20C-EDD8F8F413EA/QLUserPinnedCourse/p11> ; data: {
course = "0xd000000000dc0008 <x-coredata://95821ADC-8A1F-4DAC-B20C-EDD8F8F413EA/QLCourse/p55>";
courseId = 2794;
/* other fields redacted */
}),
<QLCourse: 0x7fe1954cded0> (entity: QLCourse; id: 0xd000000000dc0008 <x-coredata://95821ADC-8A1F-4DAC-B20C-EDD8F8F413EA/QLCourse/p55> ; data: {
/* other fields redacted*/
contentDownloadState = 3;
courseId = 2794;
pinnedUserData = "0xd0000000002c0002 <x-coredata://95821ADC-8A1F-4DAC-B20C-EDD8F8F413EA/QLUserPinnedCourse/p11>";
})
持有NSFetchedResultsController
个对象的QLUserPinnedCourse
获得delegate
次调用,这会在我的表中触发单元格重新加载。
谓词是:
// Specify criteria for filtering which objects to fetch
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"pinned == %@ && course.contentDownloadState IN %@",
@YES,
@[@(QLDownloadStateSucceeded), @(QLDownloadStateNotYetAttempted), @(QLDownloadStateFailed), @(QLDownloadStateIncomplete)]
];
现在,当我到达单元代码时,我有一个QLUserPinnedCourse
对象可以使用。我在调试器中设置了一个断点并得到:
(lldb) po userCourse.course
<QLCourse: 0x7fe19568f740> (entity: QLCourse; id: 0xd000000000dc0008 <x-coredata://95821ADC-8A1F-4DAC-B20C-EDD8F8F413EA/QLCourse/p55> ; data: {
contentDownloadState = 1;
courseId = 2794;
pinnedUserData = "0xd0000000002c0002 <x-coredata://95821ADC-8A1F-4DAC-B20C-EDD8F8F413EA/QLUserPinnedCourse/p11>";
})
问题是,为什么contentDownloadState
不是3,但仍然是1?我不明白。
这些变化不应该合并吗?
关于我的堆栈的详细信息:
PSC -> Private Concurrent (saving context) -> Main Thread context
PSC -> Private Concurrent (import context)
ContextDidSave: 如果上下文是导入上下文,则将更改合并到上面的两个上下文中:
_contextSaveObserver = [[NSNotificationCenter defaultCenter] addObserverForName:NSManagedObjectContextDidSaveNotification
object:nil
queue:nil
usingBlock:^(NSNotification* note)
{
NSManagedObjectContext *contextSaved = note.object;
NSManagedObjectContext *moc = weakself.mainQueueContext;
// basically, if this was a background worker thread
DDLogDebug(@"updatedObjects:%@", note.userInfo[NSUpdatedObjectsKey]);
if ([contextSaved.userInfo[CoreDataUserInfoKeyIsWorkerContext] boolValue])
{
[weakself.privateSavingContext performBlock:^(){
for (NSManagedObject *object in note.userInfo[NSUpdatedObjectsKey]) {
[[weakself.privateSavingContext objectWithID:[object objectID]] willAccessValueForKey:nil];
}
[weakself.privateSavingContext mergeChangesFromContextDidSaveNotification:note];
[moc performBlock:^(){
for (NSManagedObject *object in note.userInfo[NSUpdatedObjectsKey]) {
[[moc objectWithID:[object objectID]] willAccessValueForKey:nil];
}
[moc mergeChangesFromContextDidSaveNotification:note];
}];
}];
}
}];
请注意,我问userCourse
。course对象的属性,尽管我的FRC对QLUserPinnedCourse
个对象感兴趣。我想因为我在谓词中指定了与QLCourse
对象相关的键路径,所以这些更改会被刷新。
答案 0 :(得分:1)
这是核心数据的一个怪癖。实际上,您需要在主上下文中重新设置由保存操作更新的对象。
这是Swift中的一个例子:
mainContext.performBlock {
let updatedObjects : Set<NSManagedObject> = notification.userInfo![NSUpdatedObjectsKey] as! Set<NSManagedObject>
for obj in updatedObjects {
self.mainContext.objectWithID(obj.objectID).willAccessValueForKey(nil)
}
self.mainContext.mergeChangesFromContextDidSaveNotification(notification)
}
主要部分是对willAccessValueForKey:nil
的调用,导致该对象被标记为错误。这将导致主要上下文中的NSFetchedResultsController
被触发。
答案 1 :(得分:1)
所以我找到了一个解决方案,但我无法告诉你为什么会有效。
我想问的是我会有一个方法'开始下载内容',我会将主线程上下文中的属性contentDownloadState
更新为'不完整/下载',然后继续获取所有内容。
所有其余的工作都是在后台线程上下文中完成的。完成后,我用'succeeded'更新了这个值。它并没有合并这种变化。我不明白为什么。
一旦我决定在工作环境中做所有事情。即更改其值然后将上下文保存到磁盘,更改,所有更改都在传播。
所以最后我解决了它,但实际上并不理解这个问题。