同様の質問を何十も読んだ後、次のステートメントから始めたいと思います。「NSFetchedResultsControllerのデリゲートを設定しましたが、機能しませんでした。」
したがって、セルがNSFetchedResultsControllerで埋められている単純なTableViewControllerがあります。FRCの初期化コードは次のとおりです。
@property (strong, nonatomic) NSFetchedResultsController *frc;
...
- (NSFetchedResultsController *)frc
{
if (!_frc)
{
NSError *error = nil;
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:e_product];
request.sortDescriptors = [NSArray arrayWithObjects:
[NSSortDescriptor sortDescriptorWithKey:@"product_group.product_group_name" ascending:YES],
[NSSortDescriptor sortDescriptorWithKey:f_product_name ascending:YES],
nil];
_frc = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:[DTGGlobalSettings sharedInstance].moc sectionNameKeyPath:@"product_group.product_group_name" cacheName:nil];
_frc.delegate = self;
[_frc performFetch:&error];
if (error)
{
NSLog(@"Error while fetching products: %@!", error.userInfo);
}
}
return _frc;
}
また、いくつかのデバッグラベルを使用してNSFetchedResultsControllerデリゲートメソッドを実装しています。
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
// The fetch controller is about to start sending change notifications, so prepare the table view for updates.
NSLog(@"controllerWillChangeContent");
[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:
NSLog(@"didChangeObject - insert");
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
NSLog(@"didChangeObject - delete");
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
NSLog(@"didChangeObject - update");
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
NSLog(@"didChangeObject - move");
[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:
NSLog(@"didChangeSection - insert");
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
NSLog(@"didChangeSection - delete");
[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.
NSLog(@"controllerDidChangeContent");
[self.tableView endUpdates];
}
私のナビゲーションバーには更新ボタンがあり、その機能は次のことを行う製品カタログメソッドを起動することです:1。リモートMySQLサーバーから製品をフェッチします2. IDを持つ製品が存在しない場合-それを作成し、すべてを埋めますフィールド3.存在する場合、すべてのフィールドはフェッチされた値に従って更新されます
行2と3の後に、ストアが保存されます。アプリ全体のすべてのCoreData操作に単一のNSManagedObjectContextを使用します。
初めてアプリを起動したときは、まだ製品がフェッチされていないため、TVCは空です。[更新]を押すと、新しい製品がフェッチされてManagedObjectContextに挿入されますが、NSManagedObjectContextは変更を認識/認識しません。デリゲートメソッドが呼び出されないため、新しい行がTVCに追加されません。アプリを再起動すると、新しい製品がTVCに問題なくフェッチされます。
そこにいくつかの製品があるときにもう一度更新ボタンを押すと、少し遅れてそれらはすべて消えます。いくつかのデバッグは、エンティティのフィールドを更新し(実際には値は同じです)、ストアを保存した後に発生した場合に発生することを示しました。今回のデリゲートメソッドはチャームのように機能し、更新および保存されたエンティティコントローラーごとに:didChangeObject:atIndexPath:forChangeType:newIndexPathがforChangeType=NSFetchedResultsChangeDeleteで呼び出されます。
質問1:アプリを再起動しないとNSFetchedResultsControllerに挿入されたエンティティが表示されないのはなぜですか?質問2:NSFetchedResultsControllerが、すでにフェッチされたエンティティを「存在しない」(?)としてマークし、保存後にTVCから削除するのはなぜですか?
先週ずっとこの問題と戦っていたので、助けとアイデアをいただければ幸いです;(
UPD1(Jody用): ここにいくつかの詳細があります。カスタムクラスDTGGlobalSettingsを使用して、アプリ全体でいくつかの変数を共有しています。これが、sharedInstanceプロパティを初期化する方法です。
+(DTGGlobalSettings *)sharedInstance
{
static DTGGlobalSettings *myInstance = nil;
if (nil == myInstance)
{
myInstance = [[[self class] alloc] init];
// set values here
// try to acces MOC to init CoreData
[myInstance moc];
myInstance.baseURL = @"http://local.app/";
}
return myInstance;
}
CoreDataスタックを初期化するために、Appleのドキュメントの例を使用しました。これは、新しいプロジェクトを作成するときに、何らかの理由で[CoreDataを使用する]チェックボックスが表示されないためです。以前にXcodeで見たことがあると思いますが、今は見ていません;((Xcode 4.4.1):
#pragma mark Core Data stack
- (NSManagedObjectContext *) moc {
if (_moc != nil) {
return _moc;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_moc = [[NSManagedObjectContext alloc] init];
[_moc setPersistentStoreCoordinator: coordinator];
}
return _moc;
}
- (NSManagedObjectModel *)managedObjectModel {
if (_managedObjectModel != nil) {
return _managedObjectModel;
}
_managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
return _managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
NSURL *storeUrl = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
storeUrl = [storeUrl URLByAppendingPathComponent:DOC_NAME];
NSError *error;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error]) {
NSLog(@"Error adding a store to the coordinator: %@, %@", error, error.userInfo);
}
return _persistentStoreCoordinator;
}
-(void)saveDataStore
{
NSError *error;
if (![self.moc save:&error]) NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
}
DTGGlobalSettingsの「moc」(ManagedObjectContext)プロパティは、CoreDataにアクセスする必要があるアプリのあらゆる場所で使用されます。
次に、データベースの更新について説明します。リモートWebサーバーからJSON形式の新しいエンティティを取得し、結果のディクショナリを調べて、リクエスト結果で見つかったエンティティごとにcreateFromDictionaryメソッドを呼び出します。コードは次のとおりです。
+(Product *)createFromDictionary:(NSDictionary *)entityData inContext:(NSManagedObjectContext *)moc performUpdate:(BOOL)update
{
Product *result;
if (update)
{
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:e_product];
request.predicate = [NSPredicate predicateWithFormat:@"%K = %@", f_product_id, [entityData objectForKey:f_product_id]];
result = [[DTGGlobalSettings sharedInstance].moc executeFetchRequest:request error:nil].lastObject;
if (!result)
{
NSLog(@"Error updating Product ID %@! Cannot fetch entity.", [entityData objectForKey:f_product_id]);
return nil;
}
} else
{
result = [NSEntityDescription insertNewObjectForEntityForName:e_product inManagedObjectContext:[DTGGlobalSettings sharedInstance].moc];
}
result.product_id = [[DTGGlobalSettings sharedInstance].numFormatter numberFromString:[entityData objectForKey:f_product_id]];
result.product_image = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[DTGGlobalSettings sharedInstance].baseURL stringByAppendingString:[entityData objectForKey:f_product_image]]]];
result.product_name = [entityData objectForKey:f_product_name];
return result;
}
fethedエンティティが足りなくなったら、[[DTGGlobalSettings sharedInstance] saveDataStore]を呼び出します(上記を参照)。この時点で、TVCの既存の行(存在する場合)は消えます。新しいエンティティを追加した場合、保存の時点では何も起こりません。つまり、TVCはその行を更新しません。