2012-09-18 3 views
2

NSFetchedResultsController에 의해 구동되는 UITableView가 있습니다. tableview에는 사용자 정의 꼬리말보기가 있으며, API에서 데이터를 가져와 응답을 코어 데이터에 매핑하는 데 RestKit을 사용하고 있습니다.UITableView 특유의 2 단계 업데이트

RestKit 요청이 반환 된 후 응답이 핵심 데이터로 매핑되고 FetchedResultsController가 대리자 (내 tableview 컨트롤러) 업데이트를 보내고 tableview가 부분적으로 업데이트되는 경우가 매우 적습니다. 1 초. 그런 다음 몇 초 후 tableview 업데이트가 완료됩니다. 첫 번째로 tablefooterview가 다시 그려지지 않으며 그 아래에 셀이 추가되기 때문에 tableview가 부분적으로 업데이트됩니다. 꼬리말이 자리로 옮겨지기까지는 그렇게되지 않습니다.

저는 계측기로 앱을 프로파일 링 해 보았습니다. 그리기/로딩 셀에서 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를 사용할 필요는 문서에서 분명하지 않습니다. 그러나, docs는 mainQueueManagedObjectContext의 목적은 UI를 더듬지 않게하면서 primaryManagedObjectContext를 통해 영구 저장소에 데이터를 쓸 수 있도록한다는 것을 언급합니다.

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가 정상적으로 작동 :처럼 지금 NSFetchedResultsController를 만들 수

내 코드 보인다.

+0

나는 당신을 사랑합니다 http://xkcd.com/979/ –