NSFetchedResultsController不总是调用didChangeObject:atIndexPath:forChangeType:newIndexPath:for NSFetchedResultsChangeMove

我正在使用NSFetchedResultsControllersortDescriptors的请求来填充一个表中有很多的结果。 我注意到当发生一个从表底部附近移动一行到顶部的变化时, didChangeObject:atIndexPath:forChangeType:newIndexPath:根本不会被调用。

奇怪的是,我可以通过遍历所有提取的对象并在调用performFetch之后访问它们的任何属性来解决这个问题。

有关问题的任何提示,或者这只是一个晦涩的苹果错误?

这是我的代码:

 NSManagedObjectContext *context = [self managedObjectContext]; NSFetchRequest *request = [[NSFetchRequest alloc] init]; request.entity = [NSEntityDescription entityForName:@"MyObject" inManagedObjectContext:context]; request.sortDescriptors = @[NSSortDescriptor sortDescriptorWithKey:@"order" ascending:NO]]; request.fetchBatchSize = 20; NSFetchedResultsController *fetched = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:context sectionNameKeyPath:nil cacheName:nil]; fetched.delegate = self; NSError *error = nil; if (![fetched performFetch:&error]) { NSLog(@"Unresolved error fetching objects: %@", error); } // Should not be necessary, but objects near the bottom won't move to the top without it. for (MyObject *o in fetched.fetchedObjects) { o.someAttribute; } 

2014年9月12日更新:

我将所有数据保存在后台托pipe对象上下文中,似乎与我所看到的问题有关。 这是我的代码合并到主要的对象上下文的变化:

 +(void)initSaveListener { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mergeChanges:) name:NSManagedObjectContextDidSaveNotification object:[self privateContext]]; } +(void)mergeChanges:(NSNotification*)notification { NSManagedObjectContext *context = [self mainContext]; [context performBlock:^{ [context mergeChangesFromContextDidSaveNotification:notification]; NSError *error = nil; if (![context save:&error]) { NSLog(@"error merging changes %@, %@", error, [error userInfo]); } }]; } 

事实certificate,这个问题是由于使用NSManagedObjectContextDidSaveNotification从另一个上下文传播了对managedObjectContext的更改。 这个博客文章详细解释了为什么这会导致NSFetchedResultsController出现问题,以及如何解决这个问题:

http://www.mlsite.net/blog/?p=518

这里是我上面的代码中的具体修复:

 +(void)mergeChanges:(NSNotification*)notification { NSManagedObjectContext *context = [self mainContext]; // Fault all objects that have changed so that NSFetchedResultsController will see the changes. NSArray *objects = [[notification userInfo] objectForKey:NSUpdatedObjectsKey]; for (NSManagedObject *object in objects) { [[context objectWithID:[object objectID]] willAccessValueForKey:nil]; } [context performBlock:^{ [context mergeChangesFromContextDidSaveNotification:notification]; NSError *error = nil; if (![context save:&error]) { NSLog(@"error merging changes %@, %@", error, [error userInfo]); } }]; } 

您正在创build一个全新的获取结果控制器。 所以没有改变(像插入,删除,更新),所以委托不被调用。 有两种方法来处理这个问题。

首先,您可以使用现有的FRC,并更改其获取请求的谓词(获取请求本身是readonly )。 然后你只需调用performFetch 。 根据您的需要,这可能就足够了。

其次,如果您需要擦除FRC并创build一个新的,则需要在表视图上调用reloadData 。 我通常通过改变FRC创build的逻辑来实现这一点,通过一些ivars(在苹果模板之后,FRC被懒惰地创build),然后将FRC设置nil然后调用[self.tableView reloadData];