在 NSManagedObjectContextObjectsDidChangeNotification
上执行提取请求时,我得到了过时的结果。
例如,考虑包含可以使用 bool 属性 (softDeleted
) 逻辑删除的文档的目录。提取请求应返回至少有一个未删除文档 (directoriesWithDocuments
) 的所有目录。
初始状态是带有单个已删除文档的单个目录。 directoriesWithDocuments
返回一个空数组。
以下代码通过将 softDeleted
bool 值设置为 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;
}
我怀疑上下文有某种缓存,用于获取请求,直到通知被处理后才会被清除。这是 Core Data 的行为方式吗?还是我做错了什么?
解决方法
我目前正在延迟执行这样的获取请求:
- (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 的建议,我检查了 NSManagedObjectContextWillSaveNotification
和 NSManagedObjectContextDidSaveNotification
中 directoriesWithDocuments
的返回值。结果是(按顺序):
NSManagedObjectContextObjectsDidChangeNotification
:空(错误)NSManagedObjectContextWillSaveNotification
:空(错误)NSManagedObjectContextDidSaveNotification
:1 个目录(正确)
最佳答案
首先,看起来您在模型中定义了一个名为“deleted”的 bool 属性。
我记得这样做可能是一个严重的问题,因为它与 NSManagedObject isDeleted
冲突(在 KVC 级别)。您可能想要更改它以确保它不是罪魁祸首。
编辑
Thanks for replying. I used deleted as a simple example; it's not the actual attribute I'm using. Will change it to softDeleted to avoid confusion
我已经更新了下面的建议以匹配您示例中的 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];
您使用伪造的 will/did changeValueForKey 所做的是“弄脏”关联的 Directory 对象。我的猜测是,它将被视为“已更改”,并因此包含获取结果。
关于ios - 获取请求返回旧数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22229617/