NSFetchedResultsController刷新表视图太慢

时间:2014-11-14 06:10:43

标签: ios core-data nsfetchedresultscontroller

我正在通过将对话列表存储在核心数据中来编写IM应用程序,并通过NSFetchedResultsController显示数据源。核心数据堆栈是从Apple的示例代码中复制的。

NSFetchedResultsController初始化如下:

- (NSFetchedResultsController *)fetchedResultsController {
    if (!_fetchedResultsController) {
        NSManagedObjectContext *context = [[IMWrapper sharedInstance].chatManager managedObjectContext];
        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
        // Edit the entity name as appropriate.
        NSEntityDescription *entity = [Conversation entityInManagedObjectContext:context];
        [fetchRequest setEntity:entity];

        NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:ConversationAttributes.lastTime ascending:NO];
        NSArray *sortDescriptors = @[sortDescriptor];
        [fetchRequest setSortDescriptors:sortDescriptors];

        // Edit the section name key path and cache name if appropriate.
        // nil for section name key path means "no sections".
        NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:context sectionNameKeyPath:nil cacheName:nil];
        aFetchedResultsController.delegate = self;
        _fetchedResultsController = aFetchedResultsController;

        NSError *error = nil;
        if (![_fetchedResultsController performFetch:&error]) {
            // Replace this implementation with code to handle the error appropriately.
            // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
    }

    return _fetchedResultsController;
}

收到消息后,我将更新存储在Core Data中的相应会话的未读计数,并刷新会话和相应标签栏项目上的徽章编号。但是,标签栏项目的徽章编号会立即更新,而会话的徽章编号会在几秒钟后更新。我担心NSFetchedResultsController无法及时获取新的未读数。以下代码显示了NSFetchedResultsControllerDelegate

的方法
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
    [self.tableView beginUpdates];
}

- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
           atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
    switch(type) {
        case NSFetchedResultsChangeInsert:
            [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeDelete:
            [self.tableView 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.tableView;

    switch(type) {
        case NSFetchedResultsChangeInsert:
            [tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeDelete:
            [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeUpdate:
            [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeMove:
            [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
            [tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    [self.tableView endUpdates];
}

我希望有人能帮助我。非常感谢你!

P.S。我在堆栈溢出中搜索,有人说它可能是线程问题。我在didChangeObject方法中打印线程信息,并发现它不在主线程中。一些答案告诉我们将moc中的更新合并到主线程中。但我不知道怎么做。

以下代码显示未读邮件计数的更新。问题可能出在那里。

dispatch_async(self.messageQueue, ^{
    // Update unreadMessageCount here
    if (aMessage.isRead == NO) {
        // mogenerator provides this convenient method.
        self.unreadMessageCountValue += 1;
    }

    NSError *updateError = nil;
    if (![self.managedObjectContext save:&updateError]) {
        DLog(@"Unable to update managed object context.");
        DLog(@"%@, %@", updateError, updateError.localizedDescription);
    }

    // Add aMessage to message array....

    // Update tab tar badges via chat manager....
});

1 个答案:

答案 0 :(得分:0)

你错过了一行代码,告诉表视图在相应的托管对象发生变化时刷新一行。对于以下方法:

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
   atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
  newIndexPath:(NSIndexPath *)newIndexPath 

添加缺失的行,如下所示:

       case NSFetchedResultsChangeUpdate:
            [self.savedLocsTable reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

这可能就是所需要的。因此,当Conversation的unreadCount更改并保存到Core Data时,将使用NSFetchedResultsChangeUpdate调用此方法,现在将重新加载相应的表行。


更新

在看到您的代码更新邮件计数后,我认为您确实存在线程问题。

如果您确实从Apple的示例中复制了Core Data堆栈,那么您只有1 moc并且它位于主线程上。因此,您需要做的就是返回主线程来修改任何托管对象或对moc执行任何操作。 (请注意,moc和托管对象都不是线程安全的。如果在主线程上创建它们,则只能在主线程上修改它们。)

另外,我注意到您正在更新后台线程中的标签栏。您可能还想将其移动到主线程。 (AFAIK,大多数与UIKit的交互必须在主线程上完成,除了UIImage,UIColor,UIFont和Core Graphics,它们都是线程安全的。但是,在新版本的iOS中修改标签栏可能是线程安全的,并且你的程序似乎在该领域运作良好。所以,请谨慎行事。)

以下是返回主线程的代码:

dispatch_async(self.messageQueue, ^{
    if (aMessage.isRead == NO) {

        dispatch_async(dispatch_get_main_queue(), ^{
            // Update unread message count here
            // mogenerator provides this convenient method.
            self.unreadMessageCountValue += 1;

            NSError *updateError = nil;
            if (![self.managedObjectContext save:&updateError]) {
                DLog(@"Unable to update managed object context.");
                DLog(@"%@, %@", updateError, updateError.localizedDescription);
            }
        });

        // Add aMessage to message array....

        dispatch_async(dispatch_get_main_queue(), ^{
            // Update tab bar badges (unless chat manager stuff must be done in messageQueue and cannot be separated from tab bar stuff)
        });

    }
});

因此,需要进行两项更改:(1)如上所示处理NSFetchedResultsChangeUpdate,以及(2)仅在主线程上对NSManagedObject或moc进行任何更改。