NSFetchedResultsController不总是调用didChangeObject:atIndexPath:forChangeType:newIndexPath:for NSFetchedResultsChangeMove
我正在使用NSFetchedResultsController
与sortDescriptors
的请求来填充一个表中有很多的结果。 我注意到当发生一个从表底部附近移动一行到顶部的变化时, 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];
。