私はNSFetchedResultsControllerによって駆動されるUITableViewを持っています。テーブルビューにはカスタム フッター ビューがあり、RestKit を使用して API からデータを取得し、応答をコア データにマップしています。
RestKit リクエストが返された後、レスポンスがコア データにマップされ、FetchedResultsController がそのデリゲート (テーブルビュー コントローラー) の更新を送信し、テーブルビューが部分的に更新されるという非常に奇妙な動作がすべて 1 秒未満で発生します。その後、数秒後、テーブルビューの更新が終了します。最初はテーブルフッタービューが再描画されず、その下にセルが追加されるため、テーブルビューが部分的に更新されると言います。フッターが所定の位置に移動するまで、遅延はありません。
計測器を使用してアプリのプロファイリングを試みましたが、これはセルの描画/読み込みにおける 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];
// });
}
}