8

次のような coredata モデルのセットアップがあります。

TileList <->> TileListOrder
TileListOrder <<-> Tile

そして、以下で作成された NSFetchedResultsController:

NSPredicate* predicate = [NSPredicate predicateWithFormat:@"tile <> nil AND tileList <> nil AND tile.removed == 0 AND  tileList.removed == 0"];
NSSortDescriptor* sortTileListDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"tileList.order" ascending:YES] autorelease];
NSSortDescriptor* sortTileDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"order" ascending:YES] autorelease];

NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setPredicate:predicate];

[fetchRequest setSortDescriptors:sortDescriptors];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"TileListOrder" inManagedObjectContext:coreManagedObjectContext];
[fetchRequest setEntity:entity];

NSFetchedResultsController* fetchedResults = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                                                                 managedObjectContext:coreManagedObjectContext
                                                                                   sectionNameKeyPath:@"tileList.order"
                                                                                            cacheName:nil];

また、セカンダリ NSManagedObjectContext にデータをインポートし、これらの変更を次のようにマージします。

- (void) localContextDidSave:(NSNotification*) notification {
        dispatch_sync(dispatch_get_main_queue(), ^{
    [coreManagedObjectContext mergeChangesFromContextDidSaveNotification:notification];

        [self didSaveModelToCoreDataPersistentStore];

    });

    [super didEndUpdatingObjectInBackgroundThread];

}

TileListOrders オブジェクトを追加および削除しています。6 つのオブジェクトと 3 つのセクションで開始し、終了すると元の 6 つのオブジェクトが削除され、新しいオブジェクトが 6 つ挿入されます。

今私の問題は、NSFetchedResultsController デリゲートで、mergeChangesFromContextDidSaveNotification:notification の直後に呼び出されることです。

-(void)controllerDidChangeContent:(NSFetchedResultsController *)controller{

 NSLog(@"controller has %d sections, %d results:",[[controller sections] count], [[controller fetchedObjects] count]);
}

最初に 12 個のオブジェクトと 3 つのセクションを印刷し、2 回目には 3 つのセクションに 6 つのオブジェクトを印刷するときに 2 回呼び出されます。

fetchedObjects を調べると、最初に挿入された 6 つのオブジェクトと、削除された他の 6 つの元のオブジェクトがあります。2 回目には、3 つのセクションに予想される 6 つのオブジェクトが含まれています。

見つめている:

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject

   atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type

  newIndexPath:(NSIndexPath *)newIndexPath{
}

controllerDidChangeContent が初めて呼び出されたときは、NSFetchedResultsChangeInsert の変更のみが処理され、2 回目は NSFetchedResultsChangeDelete の変更のみが処理されます。

controllerDidChangeContent は、すべての変更が処理された後にのみ呼び出されると予想していました。これが tableView を駆動している場合、tableview が 2 回アニメーション化されることを意味します。挿入されたアイテムに対して 1 回、削除されたアイテムに対してもう 1 回。問題は、controllerDidChangeContent が初めて呼び出されたときに、NSFetchedResultsController 内のオブジェクトが削除され、まだ結果配列に存在しているときに、それらのプロパティに無効な値が含まれているため、これらのアイテムを表示する tableView がクラッシュする可能性があることです。

controllerDidChangeContent が mergeChangesFromContextDidSaveNotification 通知の後に一度だけ呼び出されるように、変更を結合する方法を知っている人はいますか?

-----------更新---------------

いくつかの詳細情報。

私は NSManagedObjectContextDidSaveNotification を監視しているだけで、localContextDidSave は 1 回だけ呼び出されます。

NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
[defaultCenter addObserver:self selector:@selector(localContextDidSave:) name:NSManagedObjectContextDidSaveNotification object:managedObjectContext];

これがスタックトレースです。controllerDidChangeContent が初めて呼び出されたとき、INSERTED オブジェクトに関連する変更のみが報告され、削除されたオブジェクトは NSFetchedResultsController の結果にまだ存在します。ご覧のとおり、それはによって引き起こされます

#0  0x001fb735 in -[HomeViewController controllerDidChangeContent:] 
#1  0x02947f9a in -[NSFetchedResultsController(PrivateMethods) _managedObjectContextDidChange:] ()
#2  0x022d34f9 in __57-[NSNotificationCenter addObserver:selector:name:object:]_block_invoke_0 ()
#3  0x02e1c0c5 in ___CFXNotificationPost_block_invoke_0 ()
#4  0x02d76efa in _CFXNotificationPost ()
#5  0x02207bb2 in -[NSNotificationCenter postNotificationName:object:userInfo:] ()
#6  0x0285a163 in -[NSManagedObjectContext(_NSInternalNotificationHandling) _postObjectsDidChangeNotificationWithUserInfo:] ()
#7  0x0286dc71 in -[NSManagedObjectContext _mergeChangesFromDidSaveDictionary:usingObjectIDs:] ()
#8  0x0286ce2e in -[NSManagedObjectContext mergeChangesFromContextDidSaveNotification:] ()
#9  0x00044be2 in __50-[CoreDataUpdateableObject localContextDidSave:]_block_invoke 
#10 0x0306f731 in _dispatch_barrier_sync_f_slow_invoke ()
#11 0x0307e014 in _dispatch_client_callout ()
#12 0x0306e7d5 in _dispatch_main_queue_callback_4CF ()
#13 0x02d68af5 in __CFRunLoopRun ()
#14 0x02d67f44 in CFRunLoopRunSpecific ()
#15 0x02d67e1b in CFRunLoopRunInMode ()
#16 0x038617e3 in GSEventRunModal ()
#17 0x03861668 in GSEventRun ()
#18 0x01756ffc in UIApplicationMain ()
#19 0x000022c6 in main 

controllerDidChangeContent が 2 回目に呼び出されたときのスタック トレースは次のとおりです。

#0  0x001fb735 in -[HomeViewController controllerDidChangeContent:] 
#1  0x02947f9a in -[NSFetchedResultsController(PrivateMethods) _managedObjectContextDidChange:] ()
#2  0x022d34f9 in __57-[NSNotificationCenter addObserver:selector:name:object:]_block_invoke_0 ()
#3  0x02e1c0c5 in ___CFXNotificationPost_block_invoke_0 ()
#4  0x02d76efa in _CFXNotificationPost ()
#5  0x02207bb2 in -[NSNotificationCenter postNotificationName:object:userInfo:] ()
#6  0x0285a163 in -[NSManagedObjectContext(_NSInternalNotificationHandling) _postObjectsDidChangeNotificationWithUserInfo:] ()
#7  0x028f3d2f in -[NSManagedObjectContext(_NSInternalChangeProcessing) _createAndPostChangeNotification:withDeletions:withUpdates:withRefreshes:] ()
#8  0x02855596 in -[NSManagedObjectContext(_NSInternalChangeProcessing) _processRecentChanges:] ()
#9  0x02854869 in -[NSManagedObjectContext processPendingChanges] ()
#10 0x02828e38 in _performRunLoopAction ()
#11 0x02d8aafe in __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ ()
#12 0x02d8aa3d in __CFRunLoopDoObservers ()
#13 0x02d687c2 in __CFRunLoopRun ()
#14 0x02d67f44 in CFRunLoopRunSpecific ()
#15 0x02d67e1b in CFRunLoopRunInMode ()
#16 0x038617e3 in GSEventRunModal ()
#17 0x03861668 in GSEventRun ()
#18 0x01756ffc in UIApplicationMain ()
#19 0x000022c6 in main

そして今回は、[NSManagedObjectContext processPendingChanges] () によってトリガーされます。この 2 回目の NSFetchedResultsController の結果は、実際には望ましい状態です。削除されたオブジェクトは削除され、挿入されたオブジェクトは存在します。

ここでの私の問題は、最初の mergeChanges が、結合された変更で controllerWillChangeContent をトリガーする必要があるため、結果が一貫した状態になることです。

投稿された元のコードから、[coreManagedObjectContext processPendingChanges]; を追加しました。[coreManagedObjectContext mergeChangesFromContextDidSaveNotification:通知] の後。これがないと、2 番目の呼び出しは実行ループの次の実行でのみトリガーされるためです。

[coreManagedObjectContext setPropagatesDeletesAtEndOfEvent:NO]; も試しました。しかし、動作は同じです。削除されたオブジェクトは、2 回目の呼び出しでのみ削除されます。

4

0 に答える 0