UITableView endUpdates在NSFetchedResultsChangeUpdate上花了一秒多的时间

时间:2014-01-25 12:05:22

标签: ios uitableview core-data

我使用CoreData基于Xcode Master / Detail模板UITableView。当NSManagedObject中的detailViewController的属性发生变化时,对来电的调用  [self.tableView endUpdates]需要一秒钟以上。该表有大约30个条目。

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
    NSLog(@"endUpdates start");
    [self.tableView endUpdates];
    NSLog(@"endUpdates end");
}

这是日志输出,类型4是NSFetchedResultsChangeUpdate。只更新了一行。

2014-01-25 12:40:31.313 Batteries[1511:60b] beginUpdates
2014-01-25 12:40:31.315 Batteries[1511:60b] Type: 4
2014-01-25 12:40:31.317 Batteries[1511:60b] endUpdates start
2014-01-25 12:40:32.344 Batteries[1511:60b] endUpdates end

使用插入和删除,endUpdates方法调用很快:

2014-01-25 12:39:46.732 Batteries[1511:60b] beginUpdates
2014-01-25 12:39:46.733 Batteries[1511:60b] Type: 2
2014-01-25 12:39:46.735 Batteries[1511:60b] endUpdates start
2014-01-25 12:39:46.742 Batteries[1511:60b] endUpdates end

如果我将beginUpdates - endUpdates序列替换为[self.tableView reloadData],则会更快:

2014-01-25 13:02:32.028 Batteries[1522:60b] Type: 4
2014-01-25 13:02:32.032 Batteries[1522:60b] reload start
2014-01-25 13:02:32.034 Batteries[1522:60b] reload end

这里要求的是NSFetchedResultsController代码:     #pragma mark - 获取结果控制器

- (NSFetchedResultsController *)fetchedResultsController
{
    if (_fetchedResultsController != nil) {
        return _fetchedResultsController;
    }

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    // Edit the entity name as appropriate.
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Battery" inManagedObjectContext:self.managedObjectContext];
    [fetchRequest setEntity:entity];

    [fetchRequest setFetchBatchSize:20];

    // sort by identifier ASC
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"identifier" ascending:YES];
    NSArray *sortDescriptors = @[sortDescriptor];

    [fetchRequest setSortDescriptors:sortDescriptors];

    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:@"Master"];
    aFetchedResultsController.delegate = self;
    self.fetchedResultsController = aFetchedResultsController;

    NSError *error = nil;
    if (![self.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;
}    

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
    NSLog(@"beginUpdates");
    [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;
    }
}

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
       atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
      newIndexPath:(NSIndexPath *)newIndexPath
{
    UITableView *tableView = self.tableView;
    NSLog(@"Type: %u", type);
    switch(type) {
        case NSFetchedResultsChangeInsert:
            [tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            self.insertedIndexPath = newIndexPath;
            break;

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

        case NSFetchedResultsChangeUpdate:
            [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            break;

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

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
    NSLog(@"endUpdates start");
    [self.tableView endUpdates];
//    [self.tableView reloadData];
    NSLog(@"endUpdates end");
    if (self.insertedIndexPath) {
        [self.tableView scrollToRowAtIndexPath:self.insertedIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
        self.insertedIndexPath = nil;
    }

}

/*
// Implementing the above methods to update the table view in response to individual changes may have performance implications if a large number of changes are made simultaneously. If this proves to be an issue, you can instead just implement controllerDidChangeContent: which notifies the delegate that all section and object changes have been processed.

 - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
    // In the simplest, most efficient, case, reload the table view.
    [self.tableView reloadData];
}
 */

- (void)configureCell:(BatteryCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
    NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath];
    cell.identifierLabel.text = [object valueForKey:@"identifier"];
    cell.manufacturerLabel.text = [[object valueForKey:@"manufacturedBy"] manufacturerName];

}

有没有人知道它为什么会这样?

0 个答案:

没有答案