ドキュメント内のネストされたコンテキストの説明は限られており、「iOSv5.0のCoreDataリリースノート」とにのみ表示されUIManagedDocument
ます。コンテキストのフェッチとネストに関する唯一のコメントは次のとおりです。
フェッチおよび保存操作は、コーディネーターではなく親コンテキストによって仲介されます。
ネストされたコンテキストでのバッチフェッチの機能に関する免責事項がないことを考えると、バッチフェッチとネストされたコンテキストに互換性がないことは予想されないことをお勧めします。ただし、最も基本的な例が機能しないため、これが当てはまるようです。(以下のテストコードを参照してください)。
ここに同じ問題を説明するオープンレーダーの提出もあります:http://openradar.appspot.com/11235622、およびFetchedResultsControllersとネストされたコンテキストで指摘された他の問題:子のManagedObjectContextによって行われた変更がプッシュ(保存)されたときのエンティティの複製その親に。
考えられる部分的な解決策は、を提供するという唯一の目的のために、同じものNSManagedObjectContext
にNSMainQueueConcurrencyType
直接の追加を追加することです。その後、ユーザーがアイテムを選択したときにObjectIDをネストされた子コンテキストに戻すことができ、その後の編集はネストされたコンテキストで実行できます。NSPersistentStoreCoordinator
NSFetchedResultsController
これにより、ネストされたコンテキストを使用するメリットが明らかに減少し、ネストされたコンテキストとコンテキストの間で同期するために、より頻繁に保存する必要がありNSFetchedResultsControllers
ます。ただし、アプリケーションの設計と、ネストされたコンテキストとバッチロードの相対的な利点によっては、これが役立つ場合があります。(以下のサンプルコードを参照してください)
ネストされたコンテキストでの最も単純なケースのバッチフェッチの失敗を示すテストコード:
#import "AppDelegate.h"
// Xcode 4.3.3:
// Create a new iOS Master-Detail project called "BatchTest" tick the "Use Core Data" check box.
// Delete all files except the AppDelegate and the BatchTest data model (leave supporting files).
// Delete all properties and methods from AppDelegate.h
// Paste this code into AppDelegate.m
// Switch on core data debugging by editing the "BatchTest" scheme and adding
// -com.apple.CoreData.SQLDebug 1
// To the "arguments passed on launch" list in the "Run" step
// Run.
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
/////////////////////////////////////////////////////////////////////////////////////
// Setup the core data stack.
/////////////////////////////////////////////////////////////////////////////////////
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"BatchTest" withExtension:@"momd"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
NSURL *appDocsDirectory = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSURL *storeURL = [appDocsDirectory URLByAppendingPathComponent:@"BatchTest.sqlite"];
NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
[coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:nil];
NSManagedObjectContext *parentContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
parentContext.persistentStoreCoordinator = coordinator;
NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
childContext.parentContext = parentContext;
/////////////////////////////////////////////////////////////////////////////////////
// Load some test data and reset the context.
/////////////////////////////////////////////////////////////////////////////////////
[parentContext performBlockAndWait:^{
for (int i=0; i<1000; i++) {
[NSEntityDescription insertNewObjectForEntityForName:@"Event" inManagedObjectContext:parentContext];
}
[parentContext save:nil];
[parentContext reset];
}];
/////////////////////////////////////////////////////////////////////////////////////
// Test Batched Fetching
/////////////////////////////////////////////////////////////////////////////////////
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Event"];
request.fetchBatchSize = 10;
// Fetch from the child.
NSArray *results = [childContext executeFetchRequest:request error:nil];
NSLog(@"Object 500: %@", [results objectAtIndex:500]);
// Result is all 1000 rows fetched in full, no subsequent batch fetching for event 500.
[childContext reset];
[parentContext performBlockAndWait:^{
[parentContext reset];
// Fetch from the parent.
NSArray *results = [parentContext executeFetchRequest:request error:nil];
NSLog(@"Object 500: %@", [results objectAtIndex:500]);
// Result is 1000 primary keys fetched, followed by a batch of 10 rows to find event 500.
}];
return YES;
}
@end
NSFetchedResultsController
バッチ処理を機能させるための追加のコンテキストの使用を示すサンプルコード:
#import "AppDelegate.h"
// Xcode 4.3.3:
// Create a new iOS Master-Detail project called "BatchTest" tick the "Use Core Data" check box.
// Delete all files except the AppDelegate and the BatchTest data model (leave supporting files).
// Delete all properties and methods from AppDelegate.h
// Paste this code into AppDelegate.m
// Switch on core data debugging by editing the "BatchTest" scheme and adding
// -com.apple.CoreData.SQLDebug 1
// To the "arguments passed on launch" list in the "Run" step
// Run.
@interface AppDelegate () {
NSManagedObjectContext *backgroundContext;
NSManagedObjectContext *editingContext;
NSManagedObjectContext *fetchedResultsControllerContext;
NSManagedObject *selectedObject;
}
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
/////////////////////////////////////////////////////////////////////////////////////
// Setup the core data stack.
/////////////////////////////////////////////////////////////////////////////////////
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"BatchTest" withExtension:@"momd"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
NSURL *appDocsDirectory = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSURL *storeURL = [appDocsDirectory URLByAppendingPathComponent:@"BatchTest.sqlite"];
NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
[coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:nil];
backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
backgroundContext.persistentStoreCoordinator = coordinator;
editingContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
editingContext.parentContext = backgroundContext;
fetchedResultsControllerContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
fetchedResultsControllerContext.persistentStoreCoordinator = coordinator;
/////////////////////////////////////////////////////////////////////////////////////
// Load some test data and reset the context.
/////////////////////////////////////////////////////////////////////////////////////
[backgroundContext performBlockAndWait:^{
for (int i=0; i<1000; i++) {
[NSEntityDescription insertNewObjectForEntityForName:@"Event" inManagedObjectContext:backgroundContext];
}
[backgroundContext save:nil];
[backgroundContext reset];
}];
/////////////////////////////////////////////////////////////////////////////////////
// Example of three contexts performing different roles.
/////////////////////////////////////////////////////////////////////////////////////
// The fetchedResultsControllerContext will batch correctly as it is tied directly
// to the persistent store. It can be used to drive the UI as it is a Main Queue context.
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Event"];
request.fetchBatchSize = 10;
NSArray *fetchResults = [fetchedResultsControllerContext executeFetchRequest:request error:nil];
// User selects an object in the fetchedResultsControllerContext (i.e. in a UITableView).
selectedObject = [fetchResults lastObject];
NSLog(@"**** selectedObject.timeStamp before editing:%@", [selectedObject valueForKey:@"timeStamp"]);
// Pass the object to the editing context for editing using its objectID.
NSManagedObjectID *selectedObjectID = selectedObject.objectID;
NSManagedObject *objectForEditing = [editingContext objectWithID:selectedObjectID];
// Edit the object
[objectForEditing setValue:[NSDate date] forKey:@"timeStamp"];
// Subscribe to save notifications of the background context so the
// fetchedResultsControllerContext will be updated after the background save occurs.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(backgroundContextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:backgroundContext];
// Save the editing context to push changes up to the parent, then background save.
[editingContext save:nil];
[backgroundContext performBlock:^{
[backgroundContext save:nil];
}];
return YES;
}
- (void)backgroundContextDidSave:(NSNotification *)notification {
[fetchedResultsControllerContext mergeChangesFromContextDidSaveNotification:notification];
NSLog(@"**** selectedObject.timeStamp after editing:%@", [selectedObject valueForKey:@"timeStamp"]);
// Merging changes into the fetchedResultsControllerContext would trigger updates
// to an NSFetchedResultsController and it's UITableView where these set up.
}
@end