NSFetchedResultsController 由 NSManagedObject 的 refreshObject 触发。为啥?
Posted
技术标签:
【中文标题】NSFetchedResultsController 由 NSManagedObject 的 refreshObject 触发。为啥?【英文标题】:NSFetchedResultsController triggered by NSManagedObject's refreshObject. Why?NSFetchedResultsController 由 NSManagedObject 的 refreshObject 触发。为什么? 【发布时间】:2015-01-22 16:28:31 【问题描述】:我有一个NSFetchedResultsController
,它从 CoreData 存储中获取数据。当 frc 初始化时,我调用 performFetch
并检查 frc.fetchedObjects
中获取的对象的数量,结果应该是 0。
在我调用的代码中的另一个地方:
[obj.managedObjectContext refreshObject:obj mergeChanges:NO]
虽然我什至不确定是否需要它,但它的副作用是导致 frc 获取一些最初没有获取的对象,考虑到使用的查询,无论如何都不应该获取这些对象。这些对象与调用 refreshObject:mergeChanges:
的对象完全相同。
为什么会这样?
编辑:
这个查询不会发生这种情况:
query.predicate = [NSPredicate predicateWithFormat:@"(cartEntry != NULL) AND (urlWeb = NULL)", version, dateUrlExpire];
frc = [[NSFetchedResultsController alloc] initWithFetchRequest:query managedObjectContext:context sectionNameKeyPath:nil cacheName:nil];
frc.delegate = self;
[frc performFetch:&error];
但是当我将查询更改为这个版本时,它就会发生(urlWebVersion
和 urlWebDate
是 NULL
对于所有记录):
query.predicate = [NSPredicate predicateWithFormat:@"(cartEntry != NULL) AND ((urlWeb = NULL) OR (urlWebVersion != %@) OR (urlWebDate > %@))", version, dateUrlExpire];
* 编辑 #2 *
这是一个显示奇怪行为的最小代码示例(执行期间没有错误):
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
NSError *error;
// Create an empty entity with the optional fields attr1 (string) and attr2 (date)
Entity *e = [NSEntityDescription insertNewObjectForEntityForName:@"Entity" inManagedObjectContext:[self managedObjectContext]];
// Save entity
[_managedObjectContext save:&error];
// Setup fetched results controller
NSPredicate *pred = [NSPredicate predicateWithFormat:@"(attr1 != %@) AND (attr2 != %@)", @"", [NSDate new], nil];
NSFetchRequest *query = [[NSFetchRequest alloc] initWithEntityName:@"Entity"];
query.predicate = pred;
query.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"attr1" ascending:NO]];
frc = [[NSFetchedResultsController alloc] initWithFetchRequest:query managedObjectContext:_managedObjectContext sectionNameKeyPath:nil cacheName:nil];
frc.delegate = self;
// Load data
[frc performFetch:&error];
// Output #1
NSLog(@"%ld", frc.fetchedObjects.count);
[_managedObjectContext refreshObject:e mergeChanges:NO];
return YES;
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
NSLog(@"%ld", controller.fetchedObjects.count);
输出:
2015-01-22 22:53:44.293 CoreDataBug[10740:1445186] 0
2015-01-22 22:53:44.317 CoreDataBug[10740:1445186] 1
【问题讨论】:
能否请您包括正在执行的提取? NSFRC (NSFetchedResultsController) 是否有委托集?如果设置了委托(并且至少实现了一种方法),NSFRC 将在新数据可用时再次自动执行获取。 “如果您不指定委托,则控制器不会跟踪与其托管对象上下文关联的托管对象的更改。” developer.apple.com/library/ios/documentation/CoreData/…(urlWebVersion != %@)
似乎是罪魁祸首。如果urlWebVersion
是NULL
,那么它肯定不等于version1
,这使得语句为真,因此返回条目。
哦,你可能是对的。但为什么初始结果集大小为 0?紧随performFetch
也许 NSFRC 使用的上下文没有对象,或者在您显式刷新对象之前它们不存在于该上下文中?
相同的 MOC + 更改查询确实会返回结果。所以数据是可用的。
【参考方案1】:
您的问题是 NSPredicate 难以将 !=
与 nil 值匹配。例如,在您的编辑 2 中,您的行:
[NSPredicate predicateWithFormat:@"(attr1 != %@) AND (attr2 != %@)",
@"", [NSDate new], nil];
如您所知,在获取请求后返回零。 (那里有一个额外的 nil,但它不会影响任何东西)。但将其更改为:
NSPredicate *pred = [NSPredicate predicateWithFormat:
@"((attr1 != %@) || (attr1 == nil)) AND ((attr2 != %@) || (attr2 == nil))",
@"", [NSDate new]];
确实返回实体,即使在逻辑上它们是相同的。
这是记录在案的行为,尽管对于如此违反直觉的事情很难理解,请参阅Predicate Programming Guide 中的“使用空值”小节。
不清楚为什么刷新对象会导致谓词突然匹配;这可能是一个错误。但是,鉴于上述文档,!=
不应用于可能为 nil 的值(除非检查 != nil
)而没有配对的 || (x = nil)
。不仅仅是有点烦人和反直觉。
【讨论】:
以上是关于NSFetchedResultsController 由 NSManagedObject 的 refreshObject 触发。为啥?的主要内容,如果未能解决你的问题,请参考以下文章