获取请求返回旧数据

时间:2014-03-06 16:00:30

标签: ios macos core-data nsfetchrequest nsnotification

NSManagedObjectContextObjectsDidChangeNotification上执行获取请求时,我的结果已经过时了。

作为示例,请考虑具有可以使用布尔属性(softDeleted)逻辑删除的文档的目录。获取请求应返回至少具有一个未删除文档(directoriesWithDocuments)的所有目录。

初始状态是具有单个已删除文档的单个目录。 directoriesWithDocuments返回一个空数组。

以下代码通过将softDeleted布尔值设置为NO来恢复文档。

[_context.undoManager beginUndoGrouping];
[_context.undoManager setActionName:@"undelete"];
document.softDeleted = @(NO);
NSError *error;
BOOL success = [_context save:&error]; // This triggers the notification
[_context.undoManager endUndoGrouping];

保存会触发NSManagedObjectContextObjectsDidChangeNotification。我希望directoriesWithDocuments返回目录,但它仍然返回一个空数组。

- (void)objectsDidChangeNotification:(NSNotification*)notification
{
    NSArray *objects = [self directoriesWithDocuments]; // Still empty!
}

然而,如果我在保存上下文之后执行directoriesWithDocuments ,无论是立即还是在下一个运行循环中,都会按预期返回目录。

这是获取请求的代码:

- (NSArray*)directoriesWithDocuments
{
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ANY documents.softDeleted == NO"];
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Directory"];
    fetchRequest.predicate = predicate;
    fetchRequest.includesPendingChanges = YES; // Just in case
    NSError *error = nil;
    NSArray *objects = [_context executeFetchRequest:fetchRequest error:&error];
    return directories;
}

我怀疑上下文对于获取请求有某种缓存,直到处理通知才会清除。这是核心数据的预期表现吗?或者我做错了什么?

解决方法

我目前正在推迟执行获取请求,如下所示:

- (void)objectsDidChangeNotification:(NSNotification*)notification
{
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        // HACK: Give time to Core Data to process pending changes or invalidate caches
        NSArray *objects = [self directoriesWithDocuments]; // Returns the directory as expected
    }];

}

实验

根据@ MikePollard的建议,我检查了directoriesWithDocumentsNSManagedObjectContextWillSaveNotificationNSManagedObjectContextDidSaveNotification的返回值。结果是(按顺序):

  1. NSManagedObjectContextObjectsDidChangeNotification:空(错误)
  2. NSManagedObjectContextWillSaveNotification:空(错误)
  3. NSManagedObjectContextDidSaveNotification:1个目录(正确)

2 个答案:

答案 0 :(得分:1)

首先,看起来您的模型中定义了一个名为“deleted”的布尔属性。

我记得这样做可能是一个重大问题,因为它与NSManagedObject isDeleted冲突(在KVC级别)。你可能想要改变它只是为了确保它不是罪魁祸首。

修改

  

感谢您的回复。我用删除作为一个简单的例子;它不是   我正在使用的实际属性。将其更改为softDeleted以避免   混乱

我已在下面更新了我的建议,以匹配您示例中的softDeleted


那就是说,我认为这里的工作原理归结为includesPendingChanges = YES的“变化”的构成问题。

在上下文完成保存之前,唯一的“更改”是文档实体,而不是目录实体。

因此,当获取请求包含挂起的更改时,没有任何具有任何挂起更改的目录实体,因此您最终得到了以前的结果。

为了测试这个理论,请试一试:

[_context.undoManager beginUndoGrouping];
[_context.undoManager setActionName:@"delete"];
document.softDeleted = @(NO);
[document.directory willChangeValueForKey:@"documents"] // any attribute will do really
[document.directory didChangeValueForKey:@"documents"]
NSError *error;
BOOL success = [_context save:&error];
[_context.undoManager endUndoGrouping];

你正在做什么假意/做的changeValueForKey是“脏”关联的目录对象。我的猜测是,它将被视为“已更改”,因此包括获取结果。

答案 1 :(得分:0)

让我觉得谓词将被翻译成一个SQL语句,所以在保存到达DB之前你不会得到你想要的结果......(不是你想要的那个只需阅读NSFetchRequest文档。)

因此,请尝试执行NSManagedObjectContextDidSaveNotification

的结果

我打赌如果你打开-com.apple.CoreData.SQLDebug 1,你会看到SQL语句。