2012-09-18 65 views
2

我有一個由NSFetchedResultsController驅動的UITableView。 tableview有一個自定義頁腳視圖,我使用RestKit從api獲取數據並將響應映射到Core Data。UITableView特有的兩階段更新

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]; 
//  }); 
    } 
} 

回答

1

解決。 NSFetchedResultsController正在使用RKManagedObjectStore的primaryManagedObjectContext。我將它切換到mainQueueManagedObjectContext並解決了問題。

我更加關注RestKit,mainQueueManagedObjectContext是primaryManagedObjectContext的子節點(然後發現文檔中提到了這個事實)。我原本以爲RestKit映射發生在primaryManagedObjectContext上,但現在我的猜測是它們出現在mainQueueManagedObjectContext上,並且保存在低優先級的線程上,保存在primaryManagedObjectContext中,然後保存到持久性存儲中。用我的NSFetchedResultsController先前連接到primaryManagedObjectContext,這將解釋結果完全出現之前的長時間延遲。然而,在這種情況下使用mainQueueManagedObjectContext的需求在文檔中並不明顯。然而,回想起來,文檔確實提到了mainQueueManagedObjectContext的目的是通過primaryManagedObjectContext將數據寫入永久存儲,而不會導致UI出現口吃。

我的代碼現在創建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現在正常工作。

+0

我愛你http://xkcd.com/979/ –