要するに問題
永続ストア コーディネーターのない NSManagedObjectContext は setFetchBatchSize セレクターをサポートしていないため、この投稿のソリューションを使用しましたが、解決したい特定の問題で動作します。
以下は、括弧内の用語を含むデータベース スキームと Coredata 構造です。テスト アプリケーションには、チャットのリストを含むマスター テーブルと、メッセージのリストを含む詳細テーブルの 2 つの画面があります。マスター画面は、フェッチ コントローラーでメイン MOC を使用してテーブルにデータを表示し、ワーカー MOC を使用してチャットとメッセージを作成します。詳細画面では、Fetch MOC を使用してテーブルにデータを表示します。
マスター画面でメッセージ付きの新しいチャットを作成し、階層内のすべての MOC で保存を呼び出してそれらを保存した後、詳細画面で選択したチャットでメッセージを取得できません。コンソールに表示されるのは、「CoreData: 注釈: フェッチの合計実行時間: 0 行で 0.0000 秒」です。アプリの再起動後にこのデータを取得することができます。
メイン MOC にあるチャットとは異なる objectID を持つチャットとの障害関係を持つ Fetch MOC の障害メッセージと関係があるようです。Fetch MOC で Chat オブジェクトをフェッチし、それをメッセージの検索に使用すると、すべてが正常に機能するためです。
誰かが Fetch MOC でこの問題を解決するのを手伝ってくれたら幸いです。または、すべてのオブジェクト グラフの概念を台無しにして、リレーションを使用する代わりに自分の ID フィールドでデータを取得しても問題ないかもしれません。
いくつかのコード
didFinishLaunchingWithOptions で行われる Coredata スタックの初期化は次のとおりです。
- (void)initializeCoreDataStack
{
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"FaultsFetching" withExtension:@"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:_managedObjectModel];
_writerMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_writerMOC setUndoManager:nil];
[_writerMOC setPersistentStoreCoordinator:_persistentStoreCoordinator];
_mainThreadMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_mainThreadMOC setUndoManager:nil];
[_mainThreadMOC setParentContext:_writerMOC];
_fetchMainThreadMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_fetchMainThreadMOC setUndoManager:nil];
[_fetchMainThreadMOC setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
[_fetchMainThreadMOC setPersistentStoreCoordinator:_persistentStoreCoordinator];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(backgroundContextDidSave:) name:NSManagedObjectContextDidSaveNotification object:_writerMOC];
NSURL *storeURL = [APP_DOC_DIR URLByAppendingPathComponent:@"FaultsFetching.sqlite"];
NSError *error = nil;
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error])
{
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}
- (void)backgroundContextDidSave:(NSNotification *)notification
{
[_fetchMainThreadMOC mergeChangesFromContextDidSaveNotification:notification];
NSLog(@"Yep, everything is merged");
}
ワーカー MOC の作成方法は次のとおりです。
+ (NSManagedObjectContext *)createPrivateMOC
{
CoreDataManager *scope = [CoreDataManager sharedInstance];
NSManagedObjectContext *workerMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
workerMOC.parentContext = scope.mainThreadMOC;
[workerMOC setUndoManager:nil];
workerMOC.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
return workerMOC;
}
マルチコンテキストの保存は次のようになります。引数 async は YES です。当然、このセレクターはワーカー MOC の performBlock セレクター内で呼び出されます
+ (void)writeToDiskAsync:(BOOL)async
{
CoreDataManager *scope = [CoreDataManager sharedInstance];
NSManagedObjectContext *writeManagedObjectContext = scope.writerMOC;
NSManagedObjectContext *mainManagedObjectContext = scope.mainThreadMOC;
PerformBlock mainMOCBlock = ^
{
NSError *mainError = nil;
if ([mainManagedObjectContext hasChanges] && ![mainManagedObjectContext save:&mainError])
{
ALog(@"Unresolved error %@, %@", mainError, [mainError userInfo]);
}
PerformBlock writerBlock = ^
{
NSError *writeError = nil;
if ([writeManagedObjectContext hasChanges] && ![writeManagedObjectContext save:&writeError])
{
ALog(@"Unresolved error %@, %@", writeError, [writeError userInfo]);
}
NSLog(@"Yep, everything is saved");
};
[scope performBlock:writerBlock onMOC:writeManagedObjectContext async:async];
};
[scope performBlock:mainMOCBlock onMOC:mainManagedObjectContext async:async];
}
- (void)performBlock:(PerformBlock)block onMOC:(NSManagedObjectContext *)target async:(BOOL)async
{
if (async)
[target performBlock:block];
else
[target performBlockAndWait:block];
}
詳細画面のフェッチ結果コントローラーは次のとおりです。ここで、「detailItem」はマスター画面から設定されたチャット エンティティであり、「[CoreDataManager sharedInstance]」はシングルトンです。
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
if (self.detailItem == nil)
return nil;
NSManagedObjectContext *fetchMOC = [CoreDataManager sharedInstance].fetchMainThreadMOC;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Messages" inManagedObjectContext:fetchMOC];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"sentDate" ascending:NO];
[fetchRequest setSortDescriptors:@[sortDescriptor]];
NSPredicate *chatPredicate = [NSPredicate predicateWithFormat:@"relatedChat=%@", self.detailItem.objectID];
[fetchRequest setPredicate:chatPredicate];
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:fetchMOC sectionNameKeyPath:@"sectionIdentifier" cacheName:nil];
_fetchedResultsController.delegate = self;
NSError *error = nil;
if (![_fetchedResultsController performFetch:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
return _fetchedResultsController;
}
背景のビット
- 親/子 MOC は、最初から適切に記述されていなかったアプリの安定性と応答性を改善するために使用されました。ただし、Coredata に関連するすべてが多かれ少なかれ集中化されているため、スタックを別のものに変更することができます。
- SectionIdentifier は、次のようにメッセージを日ごとにグループ化するために使用されます: http://i.imgur.com/17tuKS7.png
- 後で追加するかもしれない何か、リンクと画像についても申し訳ありません:評判とばかげたもの