更新3空のデータストアで初めて実行された後のログです。 FRCフェッチことNSFetchedResultsControllerを使用したUITableViewで2回目の時間が読み込まれない
2013-02-07 20:57:06.708 Five Hundred Things[14763:c07] mainMOC = <NSManagedObjectContext: 0x7475a90>
2013-02-07 20:57:06.711 Five Hundred Things[14763:1303] Import started
2013-02-07 20:57:06.712 Five Hundred Things[14763:1303] backgroundMOC = <NSManagedObjectContext: 0x8570070>
2013-02-07 20:57:06.717 Five Hundred Things[14763:c07] FRC fetch performed
2013-02-07 20:57:06.718 Five Hundred Things[14763:c07] numberOfSectionsInTableView returns 1
2013-02-07 20:57:06.720 Five Hundred Things[14763:c07] numberOfSectionsInTableView returns 1
2013-02-07 20:57:06.720 Five Hundred Things[14763:c07] numberOfRowsInSection returns 0
2013-02-07 20:57:06.728 Five Hundred Things[14763:1303] call contextDidSave
2013-02-07 20:57:06.736 Five Hundred Things[14763:1303] call contextDidSave
2013-02-07 20:57:06.736 Five Hundred Things[14763:c07] numberOfSectionsInTableView returns 1
2013-02-07 20:57:06.737 Five Hundred Things[14763:c07] numberOfSectionsInTableView returns 1
2013-02-07 20:57:06.737 Five Hundred Things[14763:c07] numberOfRowsInSection returns 5
2013-02-07 20:57:06.758 Five Hundred Things[14763:1303] call contextDidSave
2013-02-07 20:57:06.759 Five Hundred Things[14763:1303] Refresh complete
2013-02-07 20:57:06.759 Five Hundred Things[14763:c07] numberOfSectionsInTableView returns 1
2013-02-07 20:57:06.760 Five Hundred Things[14763:c07] numberOfSectionsInTableView returns 1
2013-02-07 20:57:06.761 Five Hundred Things[14763:c07] numberOfRowsInSection returns 5
注が行われ、セクションの行数は0であるが、第2 contextDidSave後、データストア内のカテゴリの数と一致する5に変わり。 FRCが初期化され、直後のカテゴリは、彼らがFRCに実際にあることを示すためにログインしている
2013-02-07 21:01:11.578 Five Hundred Things[14800:c07] mainMOC = <NSManagedObjectContext: 0x8225650>
2013-02-07 21:01:11.581 Five Hundred Things[14800:1303] Import started
2013-02-07 21:01:11.582 Five Hundred Things[14800:1303] backgroundMOC = <NSManagedObjectContext: 0x7439850>
2013-02-07 21:01:11.592 Five Hundred Things[14800:c07] FRC fetch performed
2013-02-07 21:01:11.594 Five Hundred Things[14800:c07] cat = Attraction
2013-02-07 21:01:11.594 Five Hundred Things[14800:c07] cat = Beverage
2013-02-07 21:01:11.595 Five Hundred Things[14800:c07] cat = Entertainment
2013-02-07 21:01:11.595 Five Hundred Things[14800:c07] cat = Hotel
2013-02-07 21:01:11.596 Five Hundred Things[14800:c07] cat = Restaurant
2013-02-07 21:01:11.597 Five Hundred Things[14800:c07] numberOfSectionsInTableView returns 1
2013-02-07 21:01:11.598 Five Hundred Things[14800:c07] numberOfSectionsInTableView returns 1
2013-02-07 21:01:11.599 Five Hundred Things[14800:c07] numberOfRowsInSection returns 0
2013-02-07 21:01:11.602 Five Hundred Things[14800:1303] call contextDidSave
2013-02-07 21:01:11.610 Five Hundred Things[14800:1303] call contextDidSave
:クラッシュとセカンドランで
は、ここにログです。ただし、セクションの行数は0であり、決して更新されません。代わりに、下のスタックでアプリがクラッシュします。 3回目以降の実行で、これは、ログは次のようになります。
2013-02-07 21:03:55.560 Five Hundred Things[14815:c07] mainMOC = <NSManagedObjectContext: 0x8128860>
2013-02-07 21:03:55.563 Five Hundred Things[14815:1e03] Import started
2013-02-07 21:03:55.564 Five Hundred Things[14815:1e03] backgroundMOC = <NSManagedObjectContext: 0x822b5d0>
2013-02-07 21:03:55.569 Five Hundred Things[14815:c07] FRC fetch performed
2013-02-07 21:03:55.571 Five Hundred Things[14815:c07] cat = Attraction
2013-02-07 21:03:55.572 Five Hundred Things[14815:c07] cat = Beverage
2013-02-07 21:03:55.572 Five Hundred Things[14815:c07] cat = Entertainment
2013-02-07 21:03:55.573 Five Hundred Things[14815:c07] cat = Hotel
2013-02-07 21:03:55.573 Five Hundred Things[14815:c07] cat = Restaurant
2013-02-07 21:03:55.574 Five Hundred Things[14815:c07] numberOfSectionsInTableView returns 1
2013-02-07 21:03:55.576 Five Hundred Things[14815:c07] numberOfSectionsInTableView returns 1
2013-02-07 21:03:55.576 Five Hundred Things[14815:c07] numberOfRowsInSection returns 5
2013-02-07 21:03:55.581 Five Hundred Things[14815:1e03] call contextDidSave
2013-02-07 21:03:55.592 Five Hundred Things[14815:1e03] call contextDidSave
2013-02-07 21:03:55.593 Five Hundred Things[14815:c07] numberOfSectionsInTableView returns 1
2013-02-07 21:03:55.594 Five Hundred Things[14815:c07] numberOfSectionsInTableView returns 1
2013-02-07 21:03:55.595 Five Hundred Things[14815:c07] numberOfRowsInSection returns 5
2013-02-07 21:03:55.606 Five Hundred Things[14815:1e03] call contextDidSave
2013-02-07 21:03:55.606 Five Hundred Things[14815:1e03] Refresh complete
これは、動作はセカンドランでどのように見えるべきかです。データはすでにストアに格納されており、セクションの行数は5を返し、カテゴリはすぐにテーブルビューに表示されます。
アップデート2はここでクラッシュが発生する場所であるメインスレッドのスタックトレースです。メインスレッドで発生しているので、私はそれがUITableViewと関係があると考えています。私はNSDictionaryまたはNSMutableDictionaryをUITableViewで使用していません。私の考えは、今度はnumberOfRowsInSection
が2回目の実行で0を返して問題を引き起こしているが、解決方法がわからないということだ。それは、3番目の実行で正しい番号(私が使用しているデータで5)を返し、最初の実行時にデータストアを正しく読み込んでいるように見えるので、2番目の実行で0を返し、更新しないでください。
frame #0: 0x013ede52 libobjc.A.dylib`objc_exception_throw
frame #1: 0x020330de CoreFoundation`-[__NSDictionaryM setObject:forKey:] + 158
frame #2: 0x01211d7a CoreData`-[NSFetchedResultsController(PrivateMethods) _preprocessUpdatedObjects:insertsInfo:deletesInfo:updatesInfo:sectionsWithDeletes:newSectionNames:treatAsRefreshes:] + 1994
frame #3: 0x01212ed7 CoreData`-[NSFetchedResultsController(PrivateMethods) _managedObjectContextDidChange:] + 2455
frame #4: 0x00b9e4f9 Foundation`__57-[NSNotificationCenter addObserver:selector:name:object:]_block_invoke_0 + 40
frame #5: 0x0200a0c5 CoreFoundation`___CFXNotificationPost_block_invoke_0 + 85
frame #6: 0x01f64efa CoreFoundation`_CFXNotificationPost + 2122
frame #7: 0x00ad2bb2 Foundation`-[NSNotificationCenter postNotificationName:object:userInfo:] + 98
frame #8: 0x01125163 CoreData`-[NSManagedObjectContext(_NSInternalNotificationHandling) _postObjectsDidChangeNotificationWithUserInfo:] + 83
frame #9: 0x011bed2f CoreData`-[NSManagedObjectContext(_NSInternalChangeProcessing) _createAndPostChangeNotification:withDeletions:withUpdates:withRefreshes:] + 367
frame #10: 0x01121128 CoreData`-[NSManagedObjectContext(_NSInternalChangeProcessing) _postRefreshedObjectsNotificationAndClearList] + 136
frame #11: 0x0111f8c0 CoreData`-[NSManagedObjectContext(_NSInternalChangeProcessing) _processRecentChanges:] + 80
frame #12: 0x0111f869 CoreData`-[NSManagedObjectContext processPendingChanges] + 41
frame #13: 0x010f3e38 CoreData`_performRunLoopAction + 280
frame #14: 0x01f78afe CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 30
frame #15: 0x01f78a3d CoreFoundation`__CFRunLoopDoObservers + 381
frame #16: 0x01f567c2 CoreFoundation`__CFRunLoopRun + 1106
frame #17: 0x01f55f44 CoreFoundation`CFRunLoopRunSpecific + 276
frame #18: 0x01f55e1b CoreFoundation`CFRunLoopRunInMode + 123
frame #19: 0x01f0a7e3 GraphicsServices`GSEventRunModal + 88
frame #20: 0x01f0a668 GraphicsServices`GSEventRun + 104
frame #21: 0x00021ffc UIKit`UIApplicationMain + 1211
frame #22: 0x000022dd Five Hundred Things`main(argc=1, argv=0xbffff31c) + 141 at main.m:16
frame #23: 0x00002205 Five Hundred Things`start + 53
アップデート:私は、実際のクラッシュだけではなく無応答を得ることができました。
*キャッチされない例外により 'NSInvalidArgumentException'、理由にアプリを終了: '* setObjectForKey:オブジェクトがnilすることはできません(キー:_ContentChange_OldIndexPathKey')
This SO questionは、私が見つけることができる最も近いですエラーですが、キーの代わりに値がnilであることについて説明します。カテゴリがコアデータストアに保存されているにもかかわらず、すべてのカテゴリに値があるように見えます。整数16 カテゴリ名 - -
エンティティカテゴリ CATEGORY_IDを含むString
それは実体シングと対多の関係を持っていますが、このコードの特定の部分はその関係に何もしていません。 category_idとcategory_nameのみを設定しています。インポートの後半(問題のMOC保存後)は、関係が設定されたときです。インポート操作から当該
コード:
//import categories
NSString *categoryPath = [[NSBundle mainBundle] pathForResource:@"category" ofType:@"json"];
NSData *categoryData = [NSData dataWithContentsOfFile:categoryPath];
NSDictionary *categoryResults = [NSJSONSerialization
JSONObjectWithData:categoryData
options:NSJSONReadingMutableLeaves
error:&error];
NSEntityDescription *categoryEntity = [NSEntityDescription entityForName:@"Category"
inManagedObjectContext:context];
NSMutableArray *categories = [[NSMutableArray alloc] init];
NSString *categoryPredicateString = [NSString stringWithFormat: @"category_id == $CATEGORY_ID"];
NSPredicate *categoryPredicate = [NSPredicate predicateWithFormat:categoryPredicateString];
for (NSDictionary *categoryKey in categoryResults){
NSFetchRequest *categoryFetchRequest = [[NSFetchRequest alloc] init];
[categoryFetchRequest setEntity:categoryEntity];
NSNumber *categoryID = [NSNumber numberWithInt:[[categoryKey objectForKey:@"category_id"] integerValue]];
[categories addObject:categoryID];
NSDictionary *categoryVariables = [NSDictionary dictionaryWithObject:categoryID forKey:@"CATEGORY_ID"];
NSPredicate *catSubPredicate = [categoryPredicate predicateWithSubstitutionVariables:categoryVariables];
[categoryFetchRequest setPredicate:catSubPredicate];
NSArray *categoryArray = [[NSArray alloc] init];
categoryArray = [context executeFetchRequest:categoryFetchRequest error:&error];
Category *categoryObject = [categoryArray lastObject];
NSNumber *categoryNum = [categoryObject valueForKey:@"category_id"];
NSInteger categoryInt = [categoryNum integerValue];
if (categoryInt != [[categoryKey objectForKey:@"category_id"] integerValue]){
categoryObject = [NSEntityDescription
insertNewObjectForEntityForName:@"Category"
inManagedObjectContext:context];
categoryObject.category_id = [NSNumber numberWithInt:[[categoryKey objectForKey:@"category_id"] intValue]];
}
if (categoryObject.category_name != [categoryKey objectForKey:@"category"]){
categoryObject.category_name = [categoryKey objectForKey:@"category"];
}
}
//Remove unneeded Categories from Core Data Store
NSFetchRequest *removeUnusedCategories = [[NSFetchRequest alloc] init];
[removeUnusedCategories setEntity:categoryEntity];
NSArray *fetchedCategories = [context executeFetchRequest:removeUnusedCategories error:&error];
for (Category *fetchedCategory in fetchedCategories){
if (![categories containsObject:fetchedCategory.category_id]){
[context deleteObject:fetchedCategory];
NSLog(@"Object deleted");
}
}
if (![context save:&error]) {
NSLog(@"Whoops, couldn't save: %@", [error localizedDescription]);
}
[context save]
背景MOCに発生し、通知センターを通じて(アプリケーションデリゲートで)メインMOCに同期されます。それはNSManagedObjectContextDidSaveNotification
を聞いて、メインMOCでmergeChangesFromContextDidSaveNotification:
を実行します。
最初の実行と3番目の実行は完全に機能します。それは常に2回目の実行で発生します。
iOSプロジェクトでCore Dataを使用していますが、これまでのところ1つの問題を除いてうまくいきました。
アプリは、JSONファイルからコアデータストアにデータを取り込み、最初のUITableViewControllerは必要に応じてアニメーションを読み込みます。しかし、アプリケーションが2回目に起動すると、最初のUITableViewは空白になります。私は複数の場所をチェックして、データは2番目の起動が開始されたときにコアデータストアにありますが、UITableViewまたはNSFetchedResultsControllerメソッドのどれも呼び出されません。
最初の起動時に、セクション内の行数は0を返しますが、コアデータストアがロードされた後は、必要なときに5が返されます。 2回目の起動時には、セクション内の行数(1つのセクションのみ)が0を返して更新されません。 3回目以降のすべての起動では、セクション内の行数が5を返します。
UITableViewのcellForRowAtIndexPath
とNSFetchedResultsControllerのdidChangeObject
メソッドのどちらも、アプリケーションの2回目の起動時に呼び出されません。 UITableViewControllerは、UITableViewDelegate
,UITableViewDataSource
、およびNSFetchedResultsControllerDelegate
です。
Core Dataガイドラインに示されているように、アプリケーションデリゲートとテーブルビューコントローラは、別のスレッドのバックグラウンドMOCでデータロードが行われている間にマネージオブジェクトコンテキストを共有します。コンテキストのsaveメソッドがmergeChangesFromContextDidSaveNotification:
から呼び出されたときに、これらは同期されます。
再生するには、シミュレータからアプリを削除し、一度実行するとデータベースが読み込まれ、アプリが正しく表示されます。私は、アプリを停止し、再度実行し、何も表示されません。私は、アプリケーションを停止し、3回目を実行し、正しく表示されます。
これはすべて、アプリケーションを起動する2回目を除いて正しく動作しているようです。 1回目と3回目は正しく動作します。私は何が欠けていますか?
私のコードは、ここに何を入れるべきか分かりません。 UITableViewControllerの実装から始めましょう。 sectionNameKeyPath
は、テーブルビューのセクションを作成するために指定されている場合、私は上記のコメントで想定するものに
@implementation FTWTMasterViewController
@synthesize managedObjectContext;
@synthesize categoryController = _categoryController;
@synthesize catLocViewController;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (NSFetchedResultsController *)categoryController {
if (_categoryController != nil) {
return _categoryController;
}
NSLog(@"tableview MOC = %@", self.managedObjectContext);
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Category" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc]
initWithKey:@"category_name" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:@"CategoryTable"];
_categoryController = theFetchedResultsController;
_categoryController.delegate = self;
return _categoryController;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.dataSource = self;
self.tableView.delegate = self;
NSError *error;
if (![[self categoryController] performFetch:&error]) {
// Update to handle the error appropriately.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
exit(-1); // Fail
}
NSLog(@"Fetch called");
self.title = @"Categories";
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
NSLog(@"number of sections = %lu", (unsigned long)[[self.categoryController sections] count]);
return [[self.categoryController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id sectionInfo =
[[_categoryController sections] objectAtIndex:section];
NSLog(@"numberOfObjects = %lu", (unsigned long)[sectionInfo numberOfObjects]);
return [sectionInfo numberOfObjects];
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
Category *category = [_categoryController objectAtIndexPath:indexPath];
cell.textLabel.text = category.category_name;
NSLog(@"config cell %@", category.category_name);
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(@"tableView setup");
static NSString *CellIdentifier = @"categoryCell";
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Set up the cell...
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
Category *aCategory = [self.categoryController objectAtIndexPath:indexPath];
if (self.catLocViewController == nil){
FTWTCatLocationViewController *aCatLocController = [[FTWTCatLocationViewController alloc] init];
self.catLocViewController = aCatLocController;
}
self.catLocViewController.selectedCat = aCategory;
aCategory = nil;
self.catLocViewController.managedObjectContext = self.managedObjectContext;
[self.navigationController pushViewController:self.catLocViewController animated:YES];
self.catLocViewController = nil;
}
#pragma mark - Fetched results controller delegate
- (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 {
NSLog(@"didChangeObject");
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:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray
arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray
arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id)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 {
// The fetch controller has sent all current change notifications, so tell the table view to process all updates.
[self.tableView endUpdates];
}
@end
フェッチされた結果コントローラでperformFetch:を呼び出すことはありますか?最初に読み込んだときにmanagedobjectcontextを保存しますか? – wattson12
'performFetch:'は 'viewDidLoad'で呼び出され(上記参照)、各JSONファイルのインポート後にMOCが保存されます。 – scottoliver
viewDidLoadは、アプリを起動するたびに呼び出されます(特に2回目のチェック)。あなたのnumberOfRowsInSectionでは** categoryController **の実際のインスタンス変数にアクセスしているので、これは不思議です。だから、これはゲッターを呼び出さず、遅延インスタンス化によって正しく設定できないかもしれません。 numberOfRows ...でself.categoryControllerを使ってみましたか?ちょうど最初の考え。 – Firo