これが私のコントローラークラスのインターフェースです:
@interface ProjectListViewController : UITableViewController <NSFetchedResultsControllerDelegate> {
NSFetchedResultsController *fetchedResultsController;
NSManagedObjectContext *managedObjectContext;
}
@end
次のコードを使用してfetchedResultsControllerを初期化します。
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
// Create and configure a fetch request with the Project entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Project" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
// Create the sort descriptors array.
NSSortDescriptor *projectIdDescriptor = [[NSSortDescriptor alloc] initWithKey:@"projectId" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:projectIdDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Create and initialize the fetch results controller.
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
self.fetchedResultsController = aFetchedResultsController;
fetchedResultsController.delegate = self;
ご覧のとおり、コントローラー クラスで定義されているのと同じmanagedObjectContextを使用しています。
以下は、NSFetchedResultsControllerDelegate プロトコルの採用です。
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
// The fetch controller is about to start sending change notifications, so prepare the table view for updates.
[self.tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
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:
[self _configureCell:(TDBadgedCell *)[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
if (newIndexPath != nil) {
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
}
else {
[tableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationFade];
}
break;
}
}
- (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)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.tableView endUpdates];
}
_configureCell:atIndexPath: メソッドの内部には、次のコードがあります。
NSFetchRequest *issuesNumberRequest = [NSFetchRequest new];
NSEntityDescription *issueEntity = [NSEntityDescription entityForName:@"Issue" inManagedObjectContext:managedObjectContext];
[issuesNumberRequest setEntity:issueEntity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"projectId == %@", project.projectId];
[issuesNumberRequest setPredicate:predicate];
NSUInteger issuesNumber = [managedObjectContext countForFetchRequest:issuesNumberRequest error:nil];
[issuesNumberRequest release];
managedObjectContext を再び使用しています。
しかし、新しいプロジェクトを挿入しようとすると、次の例外でアプリがクラッシュします:
-[UITableView _endCellAnimationsWithContext:]、/SourceCache/UIKit_Sim/UIKit-984.38/UITableView.m:774 でのアサーションの失敗、キャッチされない例外 'NSInternalInconsistencyException' によるアプリの終了、理由: '無効な更新: セクション 0 の行数が無効です。更新後に既存のセクションに含まれる行数 (4) は、更新前にそのセクションに含まれる行数 (4) に、そのセクションから挿入または削除された行数をプラスまたはマイナスした値 (1 挿入、0削除されました)」
幸いなことに、回避策を見つけました: _configureCell:atIndexPath: メソッド内で別の NSManagedObjectContext を作成して使用すると、アプリはクラッシュしません!
私が知りたいのは、この動作が正しいかどうかです。
これがサンプルプロジェクトです。 CrashMeViewController の '_configureCell:atIndexPath:' および '_addSomeRows' メソッドに注意してください