我的iOS应用有一个视图控制器,可显示收件箱项目列表。这些收件箱项是核心数据数据库中的实体,列表由NSFetchedResultsController
使用主线程托管上下文(NSMainQueueConcurrencyType
)在视图控制器中管理。
除NSFetchedResultsController
之外,对实体的所有访问仅限于在具有私有上下文的专用GCD调度队列上运行的代码。因此,在实体中设置属性值以及读取这些实体属性值会在此专用GCD队列和上下文中发生。
具有NSFetchedResultsController
的视图控制器监视主线程上下文中的更改。变更发生在专用队列和上下文的后台。代码监视NSManagedObjectContextDidSaveNotification
通知,当后台线程更新属性值时,这些更改将被推送到主线程上下文中。
NSFetchedResultsController设置为对两个排序描述符进行排序,即“sortScore”和“date”(其中只有一个正在更新,“date”,因此每个托管对象实例都具有相同的“sortScore”做它不会影响排序。)
当“日期”更改发生时,NSFetchedResultsController
会通知它并发布更改,但会将其发布为“更新”而不是“移动”,因此排序不会发生变化基于排序描述符和更新的“日期”,新值应该落在排序的其他地方。
我不知疲倦,经过几个小时的工作并跟踪NSManagedObjectContextDidSaveNotification
和其他可能出现问题的点,以及为什么取出的结果控制器发送“更新”更改而不是“移动” “改变。
此应用程序使用较旧的分叉版Robbie Hanson XMPPFramework来管理核心数据,以备不熟悉。
(为了解决这个问题,昨晚一旦DID正常工作,并且一次又一次地进行了移动。我已经删除了应用程序并从全新安装开始,它开始工作。但后续运行无效并删除了应用程序开始新鲜没有解决它,以便开始新鲜的效果可能是巧合。)
我已经使用了日志记录和断点来显示获取的结果控制器正在发送“更新”而不是“移动”。
这是代码。
在视图控制器的init内部调用此创建方法,设置并执行NSFetchedResultsController
的初始设置。
@property (nonatomic, strong) NSFetchedResultsController *fetchedResultsController;
- (NSFetchedResultsController *)createFetchedResultsControllerUsingStorage:(XMPPCoreDataStorage *)storage entityName:(NSString *)entityName
{
NSManagedObjectContext *context = [storage mainThreadManagedObjectContext];
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:entityName];
NSSortDescriptor *sortDescriptorScore = [[NSSortDescriptor alloc] initWithKey:@"sortScore" ascending:NO];
NSSortDescriptor *sortDescriptorDate = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptorScore, sortDescriptorDate, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *controller = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:context sectionNameKeyPath:nil cacheName:nil];
[controller setDelegate:self];
[self updateResultsDataSetForController:controller];
return controller;
}
- (BOOL)updateResultsDataSetForController:(NSFetchedResultsController *)controller
{
NSError *error;
BOOL success = [controller performFetch:&error];
if (nil != error) {
NSLog(@"Error fetching inbox items for the inbox view controller. Error = %@", error);
}
return success;
}
处理NSFetchedResultsControllerDelegate
中的更改的代码基本上是从Apple文档中复制和粘贴的
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
[self.conversationsTable beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
switch(type) {
case NSFetchedResultsChangeInsert:
[self.conversationsTable insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]
withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.conversationsTable deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]
withRowAnimation:UITableViewRowAnimationFade];
break;
default:
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
{
UITableView *tableView = self.conversationsTable;
switch(type) {
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate: {
MyAppInboxCell *cell = [tableView cellForRowAtIndexPath:indexPath];
MyAppXMPPInboxBaseMember *inboxItem = [self.fetchedResultsController objectAtIndexPath:indexPath];
MyAppConversation *conversation = [inboxItem conversation];
[cell configureCell:conversation];
}
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
}
}