データをダウンロードする Web サービスがあり、それをバックグラウンド スレッドで処理してコア データにインポートしたい (UI の実行を維持するため)。私がこれを設定した方法は、「非同期保存」の下のこのブログ投稿で説明されています http://www.cocoanetics.com/2012/07/multi-context-coredata/
このメソッドのように、ライター コンテキストを初期化します。
+ (HFCoreDataManager *)writerContext {
static HFCoreDataManager *writerContext = nil;
static dispatch_once_t onceToken;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_sync(queue, ^{
dispatch_once(&onceToken, ^{
writerContext = [[HFCoreDataManager alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[writerContext setPersistentStoreCoordinator:[appDelegate persistentStoreCoordinator]];
});
});
return writerContext;
}
このメソッドでメイン コンテキストを初期化します。
+ (HFCoreDataManager *)mainContext {
static HFCoreDataManager *mainContext = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
mainContext = [[HFCoreDataManager alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
mainContext.parentContext = [HFCoreDataManager writerContext];
});
return mainContext;
}
そして、このメソッドを使用して一時的なコンテキストをセットアップしました。
+ (HFCoreDataManager *)temporaryContext {
__block HFCoreDataManager *temporaryContext;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_sync(queue, ^{
temporaryContext = [[HFCoreDataManager alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
temporaryContext.parentContext = [HFCoreDataManager mainContext];
});
return temporaryContext;
}
今私が抱えている問題は、データをインポートして import メソッドでそれを返すときに、結果が得られないことがあります。結果が得られるかどうかは明らかに予測不可能であるため、何らかの競合状態が発生しているようです。これは、データをインポートし、コンテキストを保存して結果を返すメソッドです。
- (void)retrieveSuggestedEvents:(GetEventsBlock)completion {
[self retrieveCurrentUser:^(User *currentUser) {
NSArray *storedEvents = [[HFCoreDataManager mainContext] retrieveStoredSuggestedEventsForUser:currentUser];
if (storedEvents.count > 0) {
NSLog(@"SUCCESS: Retrieved %d suggested events from Core Data.", storedEvents.count);
if (completion) {
completion(storedEvents, NO);
}
}
[[HFNetworkManager shared] downloadSuggestedEvents:^(NSArray *events) {
NSLog(@"%d", events.count);
HFCoreDataManager *temporaryContext = [HFCoreDataManager temporaryContext];
[temporaryContext performBlock:^{
for (NSDictionary *objectDict in events) {
NSMutableDictionary *mutableObjectDict = [objectDict mutableCopy];
NSDictionary *locationDict = [mutableObjectDict objectForKey:@"location"];
[mutableObjectDict removeObjectForKey:@"location"];
NSDictionary *ownerDict = [mutableObjectDict objectForKey:@"owner"];
[mutableObjectDict removeObjectForKey:@"owner"];
NSArray *friendsArray = [mutableObjectDict objectForKey:@"friends_attending"];
[mutableObjectDict removeObjectForKey:@"friends_attending"];
Event *event = [temporaryContext createOrUpdateObjectOfEntity:[Event class] withData:mutableObjectDict];
// set up owner relation
User *owner = [temporaryContext createOrUpdateObjectOfEntity:[User class] withData:ownerDict];
event.owner = owner;
NSMutableSet *ownedEvents = [NSMutableSet setWithSet:owner.owned_events];
if (![ownedEvents containsObject:event]) {
[ownedEvents addObject:event];
owner.owned_events = ownedEvents;
}
// set up location relation
Location *location = [temporaryContext createOrUpdateObjectOfEntity:[Location class] withData:locationDict];
event.location = location;
NSMutableSet *locationEvents = [NSMutableSet setWithSet:location.events];
if (![locationEvents containsObject:event]) {
[locationEvents addObject:event];
location.events = locationEvents;
}
// set up suggested event relation
NSDictionary *idDict = [NSDictionary dictionaryWithObject:[NSNumber numberWithInteger:[HFCoreDataManager suggestedEventIdentifierFromUser:currentUser andEvent:event]] forKey:@"id"];
SuggestedEvent *suggestedEvent = [temporaryContext createOrUpdateObjectOfEntity:[SuggestedEvent class] withData:idDict];
suggestedEvent.event = event;
if (![event.suggested_users containsObject:suggestedEvent]) {
NSMutableSet *usersSet = [NSMutableSet setWithSet:event.suggested_users];
[usersSet addObject:suggestedEvent];
event.suggested_users = usersSet;
}
User *backgroundCurrentUser = [temporaryContext retrieveStoredCurrentUser];
suggestedEvent.user = backgroundCurrentUser;
if (![currentUser.suggested_events containsObject:suggestedEvent]) {
NSMutableSet *eventsSet = [NSMutableSet setWithSet:backgroundCurrentUser.suggested_events];
[eventsSet addObject:suggestedEvent];
backgroundCurrentUser.suggested_events = eventsSet;
}
NSMutableArray *friendsAttending = [NSMutableArray arrayWithCapacity:friendsArray.count];
for (NSString *friendID in friendsArray) {
User *friend = [temporaryContext createOrUpdateObjectOfEntity:[User class] withData:[NSDictionary dictionaryWithObject:friendID forKey:@"id"]];
if (![backgroundCurrentUser.friends containsObject:friend]) {
NSMutableSet *friendsSet = [NSMutableSet setWithSet:backgroundCurrentUser.friends];
[friendsSet addObject:friend];
backgroundCurrentUser.friends = friendsSet;
}
if (![friend.friends containsObject:backgroundCurrentUser]) {
NSMutableSet *friendsSet = [NSMutableSet setWithSet:friend.friends];
[friendsSet addObject:backgroundCurrentUser];
friend.friends = friendsSet;
}
[friendsAttending addObject:friend];
}
suggestedEvent.friends_attending = [NSSet setWithArray:friendsAttending];
}
[temporaryContext saveContext];
__block NSArray *suggestedEvents;
[[HFCoreDataManager mainContext] performBlockAndWait:^{
suggestedEvents = [[HFCoreDataManager mainContext] retrieveStoredSuggestedEventsForUser:currentUser];
}];
if (suggestedEvents.count == 0) {
NSLog(@"damn");
}
if (completion) {
NSLog(@"CALLING COMPLETION WITH %d EVENTS", suggestedEvents.count);
completion(suggestedEvents, YES);
}
}];
} failure:^(NSError *error) {
}];
}];
}
一番下の、temporaryContext で saveContext を呼び出しているところを見てください。
saveContext メソッドは次のようになります
- (void)saveContext {
NSError *error;
[self save:&error];
if (error) {
NSLog(@"ERROR SAVING MANAGED OBJECT CONTEXT %@: %@", self, error);
}
if (self.parentContext) {
HFCoreDataManager *parentContext = (HFCoreDataManager *)self.parentContext;
[parentContext performBlockAndWait:^{
[parentContext saveContext];
}];
}
}
この競合状態の原因が何であるかはわかりませんので、洞察があれば役立ちます。