2

我有一个由 NSFetchedResultsController 驱动的 UITableView。tableview 有一个自定义页脚视图,我正在使用 RestKit 从 api 获取数据并将响应映射到 Core Data。

在 RestKit 请求返回后,我遇到了一个非常奇怪的行为,其中响应被映射到核心数据,我的 FetchedResultsController 发送其委托(我的 tableview 控制器)更新,并且 tableview 部分更新,所有这些都在不到 1 秒的时间内完成。然后,几秒钟后,tableview 完成更新。我说 tableview 是部分更新的,因为起初 tablefooterview 没有重绘,并且在它下面添加了单元格。直到延迟之后,页脚才移动到位。

我尝试使用仪器分析应用程序,这似乎不是基于 CPU 的绘图/加载单元格延迟。我还尝试仅在配置单元格以最小化复杂性时简单地设置单元格的标题标签。我在设备和模拟器上也看到了相同的行为。

我真的很想追踪并消除这种延迟,因为它确实会导致糟糕的用户体验。

日志中的一个例外:

2012-09-18 10:39:43.695 MyApp[28881:470f] -[RDRMarketBooksViewController controllerWillChangeContent:] [Line 236] controller => <NSFetchedResultsController: 0xe657570>
2012-09-18 10:39:43.695 MyApp[28881:470f] -[RDRMarketBooksViewController controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:] [Line 267] controller => <NSFetchedResultsController: 0xe657570>
(...19 similiar lines...)
2012-09-18 10:39:43.703 MyApp[28881:470f] -[RDRMarketBooksViewController controllerDidChangeContent:] [Line 301] controller => <NSFetchedResultsController: 0xe657570>
2012-09-18 10:39:43.705 MyApp[28881:470f] -[RDRMarketBooksViewController configureCell:atIndexPath:] [Line 34] cell => <RDRMarketBooksCell: 0xed5f890; baseClass = UITableViewCell; frame = (0 0; 320 79); autoresize = W+BM; layer = <CALayer: 0xed5f9b0>>, indexPath => <NSIndexPath 0xed5eab0> 2 indexes [0, 0]
2012-09-18 10:39:43.706 MyApp[28881:470f] -[RDRMarketBooksViewController configureCell:atIndexPath:] [Line 34] cell => <RDRMarketBooksCell: 0xe659e50; baseClass = UITableViewCell; frame = (0 0; 320 79); autoresize = W+BM; layer = <CALayer: 0xe659f70>>, indexPath => <NSIndexPath 0xe657b50> 2 indexes [0, 1]
2012-09-18 10:39:43.708 MyApp[28881:470f] -[RDRMarketBooksViewController configureCell:atIndexPath:] [Line 34] cell => <RDRMarketBooksCell: 0xe65aca0; baseClass = UITableViewCell; frame = (0 0; 320 79); autoresize = W+BM; layer = <CALayer: 0xe65adc0>>, indexPath => <NSIndexPath 0xe65a6f0> 2 indexes [0, 2]
2012-09-18 10:39:43.709 MyApp[28881:470f] -[RDRMarketBooksViewController configureCell:atIndexPath:] [Line 34] cell => <RDRMarketBooksCell: 0xe9e1890; baseClass = UITableViewCell; frame = (0 0; 320 79); autoresize = W+BM; layer = <CALayer: 0xe9b1e10>>, indexPath => <NSIndexPath 0xe9c59d0> 2 indexes [0, 3]
2012-09-18 10:39:43.711 MyApp[28881:470f] -[RDRMarketBooksViewController configureCell:atIndexPath:] [Line 34] cell => <RDRMarketBooksCell: 0x13c6f230; baseClass = UITableViewCell; frame = (0 0; 320 79); autoresize = W+BM; layer = <CALayer: 0x13c6ed80>>, indexPath => <NSIndexPath 0xe9e0fb0> 2 indexes [0, 4]
2012-09-18 10:39:43.712 MyApp[28881:470f] -[RDRMarketBooksViewController configureCell:atIndexPath:] [Line 34] cell => <RDRMarketBooksCell: 0xe9e2230; baseClass = UITableViewCell; frame = (0 0; 320 79); autoresize = W+BM; layer = <CALayer: 0xe9b1a60>>, indexPath => <NSIndexPath 0xe9e0ea0> 2 indexes [0, 5]
2012-09-18 10:39:43.713 MyApp[28881:470f] -[RDRMarketBooksViewController configureCell:atIndexPath:] [Line 34] cell => <RDRMarketBooksCell: 0x13c6fef0; baseClass = UITableViewCell; frame = (0 0; 320 79); autoresize = W+BM; layer = <CALayer: 0x13c70010>>, indexPath => <NSIndexPath 0xe95dca0> 2 indexes [0, 6]
2012-09-18 10:39:43.714 MyApp[28881:470f] -[RDRMarketBooksViewController configureCell:atIndexPath:] [Line 34] cell => <RDRMarketBooksCell: 0xeb599f0; baseClass = UITableViewCell; frame = (0 0; 320 79); autoresize = W+BM; layer = <CALayer: 0xeb59b10>>, indexPath => <NSIndexPath 0xe9e2a10> 2 indexes [0, 7]
2012-09-18 10:39:43.715 MyApp[28881:470f] -[RDRMarketBooksViewController configureCell:atIndexPath:] [Line 34] cell => <RDRMarketBooksCell: 0x13c70d10; baseClass = UITableViewCell; frame = (0 0; 320 79); autoresize = W+BM; layer = <CALayer: 0x13c6fea0>>, indexPath => <NSIndexPath 0xe9e1790> 2 indexes [0, 8]
2012-09-18 10:39:43.717 MyApp[28881:470f] -[RDRMarketBooksViewController configureCell:atIndexPath:] [Line 34] cell => <RDRMarketBooksCell: 0xed616c0; baseClass = UITableViewCell; frame = (0 0; 320 79); autoresize = W+BM; layer = <CALayer: 0xed5ee80>>, indexPath => <NSIndexPath 0xe9e1d50> 2 indexes [0, 9]
2012-09-18 10:39:43.718 MyApp[28881:470f] -[RDRMarketBooksViewController configureCell:atIndexPath:] [Line 34] cell => <RDRMarketBooksCell: 0xe65bb00; baseClass = UITableViewCell; frame = (0 0; 320 79); autoresize = W+BM; layer = <CALayer: 0xe65b680>>, indexPath => <NSIndexPath 0xe65b1c0> 2 indexes [0, 11]
2012-09-18 10:39:43.720 MyApp[28881:470f] -[RDRMarketBooksViewController configureCell:atIndexPath:] [Line 34] cell => <RDRMarketBooksCell: 0x13c71c00; baseClass = UITableViewCell; frame = (0 0; 320 79); autoresize = W+BM; layer = <CALayer: 0x13c71770>>, indexPath => <NSIndexPath 0x13c6f1d0> 2 indexes [0, 13]
2012-09-18 10:39:43.721 MyApp[28881:470f] -[RDRMarketBooksViewController configureCell:atIndexPath:] [Line 34] cell => <RDRMarketBooksCell: 0xe65c900; baseClass = UITableViewCell; frame = (0 0; 320 79); autoresize = W+BM; layer = <CALayer: 0xe65c4a0>>, indexPath => <NSIndexPath 0xe659ce0> 2 indexes [0, 14]
2012-09-18 10:39:43.722 MyApp[28881:470f] -[RDRMarketBooksViewController configureCell:atIndexPath:] [Line 34] cell => <RDRMarketBooksCell: 0xe65d720; baseClass = UITableViewCell; frame = (0 0; 320 79); autoresize = W+BM; layer = <CALayer: 0xe65d2c0>>, indexPath => <NSIndexPath 0xe659de0> 2 indexes [0, 15]
2012-09-18 10:39:43.723 MyApp[28881:470f] -[RDRMarketBooksViewController configureCell:atIndexPath:] [Line 34] cell => <RDRMarketBooksCell: 0xe65e5a0; baseClass = UITableViewCell; frame = (0 0; 320 79); autoresize = W+BM; layer = <CALayer: 0xe65e110>>, indexPath => <NSIndexPath 0xe65d2a0> 2 indexes [0, 16]
2012-09-18 10:39:43.739 MyApp[28881:470f] -[RDRMarketBooksViewController configureCell:atIndexPath:] [Line 34] cell => <RDRMarketBooksCell: 0x91bf9e0; baseClass = UITableViewCell; frame = (0 0; 320 79); autoresize = W+BM; layer = <CALayer: 0x9197560>>, indexPath => <NSIndexPath 0x91c9960> 2 indexes [0, 17]
2012-09-18 10:39:43.740 MyApp[28881:470f] -[RDRMarketBooksViewController configureCell:atIndexPath:] [Line 34] cell => <RDRMarketBooksCell: 0x93e8d50; baseClass = UITableViewCell; frame = (0 0; 320 79); autoresize = W+BM; layer = <CALayer: 0x93dd090>>, indexPath => <NSIndexPath 0x93cb8b0> 2 indexes [0, 18]
2012-09-18 10:39:43.741 MyApp[28881:470f] -[RDRMarketBooksViewController configureCell:atIndexPath:] [Line 34] cell => <RDRMarketBooksCell: 0x13c727b0; baseClass = UITableViewCell; frame = (0 0; 320 79); autoresize = W+BM; layer = <CALayer: 0x13c728d0>>, indexPath => <NSIndexPath 0x13c72420> 2 indexes [0, 19]
2012-09-18 10:39:43.742 MyApp[28881:470f] -[RDRMarketBooksViewController configureCell:atIndexPath:] [Line 34] cell => <RDRMarketBooksCell: 0x93e6ff0; baseClass = UITableViewCell; frame = (0 0; 320 79); autoresize = W+BM; layer = <CALayer: 0x93da000>>, indexPath => <NSIndexPath 0x93d4d10> 2 indexes [0, 21]
2012-09-18 10:39:43.744 MyApp[28881:470f] -[RDRMarketBooksViewController configureCell:atIndexPath:] [Line 34] cell => <RDRMarketBooksCell: 0xed62510; baseClass = UITableViewCell; frame = (0 0; 320 79); autoresize = W+BM; layer = <CALayer: 0xed620a0>>, indexPath => <NSIndexPath 0xed5ed80> 2 indexes [0, 22]
2012-09-18 10:39:43.772 MyApp[28881:c07] -[RDRMarketBooksViewController controllerWillChangeContent:] [Line 236] controller => <NSFetchedResultsController: 0xe657570>
2012-09-18 10:39:43.777 MyApp[28881:c07] -[RDRMarketBooksViewController controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:] [Line 267] controller => <NSFetchedResultsController: 0xe657570>
(...19 similiar lines...)
2012-09-18 10:39:43.788 MyApp[28881:c07] -[RDRMarketBooksViewController controllerDidChangeContent:] [Line 301] controller => <NSFetchedResultsController: 0xe657570>
2012-09-18 10:39:43.790 MyApp[28881:c07] -[RDRMarketBooksViewController configureCell:atIndexPath:] [Line 34] cell => <RDRMarketBooksCell: 0x13c7be10; baseClass = UITableViewCell; frame = (0 0; 320 79); autoresize = W+BM; layer = <CALayer: 0x13c7bf30>>, indexPath => <NSIndexPath 0x13c7b080> 2 indexes [0, 0]
2012-09-18 10:39:43.791 MyApp[28881:c07] -[RDRMarketBooksViewController configureCell:atIndexPath:] [Line 34] cell => <RDRMarketBooksCell: 0xe9e2be0; baseClass = UITableViewCell; frame = (0 0; 320 79); autoresize = W+BM; layer = <CALayer: 0xe9e1b10>>, indexPath => <NSIndexPath 0xe977940> 2 indexes [0, 1]
2012-09-18 10:39:43.792 MyApp[28881:c07] -[RDRMarketBooksViewController configureCell:atIndexPath:] [Line 34] cell => <RDRMarketBooksCell: 0xe9e38b0; baseClass = UITableViewCell; frame = (0 0; 320 79); autoresize = W+BM; layer = <CALayer: 0xe9e2190>>, indexPath => <NSIndexPath 0xe9c0bc0> 2 indexes [0, 2]
2012-09-18 10:39:43.793 MyApp[28881:c07] -[RDRMarketBooksViewController configureCell:atIndexPath:] [Line 34] cell => <RDRMarketBooksCell: 0xe9e4680; baseClass = UITableViewCell; frame = (0 0; 320 79); autoresize = W+BM; layer = <CALayer: 0xe9e41f0>>, indexPath => <NSIndexPath 0xe9e3d60> 2 indexes [0, 3]
2012-09-18 10:39:43.794 MyApp[28881:c07] -[RDRMarketBooksViewController configureCell:atIndexPath:] [Line 34] cell => <RDRMarketBooksCell: 0x13c7ccc0; baseClass = UITableViewCell; frame = (0 0; 320 79); autoresize = W+BM; layer = <CALayer: 0x13c7cde0>>, indexPath => <NSIndexPath 0x13c7b270> 2 indexes [0, 4]
2012-09-18 10:39:43.802 MyApp[28881:c07] -[RDRMarketBooksViewController productsDidLoad:forCategory:error:] [Line 319] resource => <RDRRangedResource: 0x9533390> => {

以及我的 UITableViewController 子类中的相关方法:

- (void)configureCell:(RDRMarketBooksCell *)cell atIndexPath:(NSIndexPath *)indexPath {
    NSParameterAssert(cell);
    NSParameterAssert(indexPath);

    Trace(@"cell => %@, indexPath => %@", cell, indexPath);

    NSAssert([cell isKindOfClass:[RDRMarketBooksCell class]], @"should be a market books cell");
    id<RDRAPIProduct> product = [fetchedResultsController objectAtIndexPath:indexPath];
    cell.titleLabel.text = product.name;
}

#pragma mark -
#pragma mark UITableViewDelegate

// Configuring Rows for the Table View

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 79.0;
}

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (!tableViewIsUpdating) {
        [(RDRMarketBooksCell *)cell refreshSecondaryValues];
    }
}

// Managing Selections

- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    id<RDRAPIProduct> product = [fetchedResultsController objectAtIndexPath:indexPath];

    BOOL shouldSelect = YES;
    if (product) {
        shouldSelect = (product.slug != nil);
    }
    return (shouldSelect ? indexPath : nil);
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    id<RDRAPIProduct> product = [fetchedResultsController objectAtIndexPath:indexPath];

    if (RDRIsiPad()) {
        [[RDRApplicationDelegate sharedDelegate] presentDetailsWithProduct:product];
    }

    [self.delegate viewController:self didPickProduct:product];

    [tableView deselectRowAtIndexPath:indexPath animated:YES];
}

#pragma mark -
#pragma mark UITableViewDataSource

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    RDRMarketBooksCell *cell = [tableView dequeueReusableCellWithIdentifier:kRDRMarketBooksCellIdentifier];
    NSAssert(cell, @"cell should not be nil");

    [self configureCell:cell atIndexPath:indexPath];

    return cell;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return [[fetchedResultsController sections] count];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
    return [sectionInfo numberOfObjects];
}

#pragma mark -
#pragma mark NSFetchedResultsControllerDelegate

/*
 Assume self has a property 'tableView' -- as is the case for an instance of a UITableViewController
 subclass -- and a method configureCell:atIndexPath: which updates the contents of a given cell
 with information from a managed object at the given index path in the fetched results controller.
 */

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
    Trace(@"controller => %@", controller);
    if (controller == fetchedResultsController) {
        tableViewIsUpdating = YES;
        [self.tableView beginUpdates];
    }
}


- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
           atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
    Trace(@"controller => %@", controller);

    if (controller == fetchedResultsController) {
        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 {
    Trace(@"controller => %@", controller);

    if (controller == fetchedResultsController) {
        UITableView *tableView = self.tableView;

        switch(type) {

            case NSFetchedResultsChangeInsert:
                [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
                                 withRowAnimation:UITableViewRowAnimationFade];
                break;

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

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

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


- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    Trace(@"controller => %@", controller);
    if (controller == fetchedResultsController) {
        [self.tableView endUpdates];
        tableViewIsUpdating = NO;

//        double delayInSeconds = 0.1;
//        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
//        dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
//            [self fetchSecondaryValues];
//        });
    }
}
4

1 回答 1

1

解决了。NSFetchedResultsController 正在使用 RKManagedObjectStore 的 primaryManagedObjectContext。我将它切换到 mainQueueManagedObjectContext 并解决了问题。

我更仔细地查看了 RestKit 并且 mainQueueManagedObjectContext 是 primaryManagedObjectContext 的子项(然后发现文档中提到了这个事实)。我原本以为 RestKit 映射发生在 primaryManagedObjectContext 上,但现在我的猜测是它们发生在 mainQueueManagedObjectContext 上,并在低优先级线程上保存到 primaryManagedObjectContext,然后保存到持久存储。将我的 NSFetchedResultsController 先前附加到 primaryManagedObjectContext,这可以解释结果完全出现之前的长时间延迟。然而,在这种情况下使用 mainQueueManagedObjectContext 的需要在文档中并不明显。然而,回想起来,

我创建 NSFetchedResultsController 的代码现在如下所示:

    NSManagedObjectContext *context = [[RKManagedObjectStore defaultStore] mainQueueManagedObjectContext];
    NSFetchRequest *fr = [NSFetchRequest fetchRequestWithEntityName:@"RDRProduct"];
    fr.fetchBatchSize = 20;
    fr.predicate = [NSPredicate predicateWithFormat:@"ANY categories.slug == %@", category.slug];
    fr.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"sortingTitle" ascending:YES]];
    [NSFetchedResultsController deleteCacheWithName:category.slug];
    NSFetchedResultsController *fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fr
                                                                                               managedObjectContext:context
                                                                                                 sectionNameKeyPath:@"sortingSection"
                                                                                                          cacheName:category.slug];

我的 tableview 现在可以正常工作了。

于 2012-09-19T07:42:54.893 回答