私が一言で言いたいのは、WebサービスからCore Data Sqlite3データベースにプルされたJSONオブジェクトをバックグラウンドキューに保存することです。保存はGCDを介して作成したシリアライズされたバックグラウンドキューで行われ、バックグラウンドキュー用に作成されたNSManagedObjectContextのセカンダリインスタンスに保存されます。保存が完了したら、メインスレッド上のNSManagedObjectContextのインスタンスを新しく作成/更新したオブジェクトで更新する必要があります。主なスレッド上のNSManagedObjectContextのインスタンスは、バックグラウンドコンテキストに保存されたオブジェクトを見つけることができません。以下は、コードサンプルで実行しているアクションのリストです。私が間違ってやっていることに関する考えは?バックグラウンド問題のコアデータ保存オブジェクト
- 、GCDを経由してバックグラウンドのキューを作成し、すべての前処理ロジックを実行し、そのスレッド上でバックグラウンド・コンテキストを保存:
。
// process in the background queue
dispatch_async(backgroundQueue, ^(void){
if (savedObjectIDs.count > 0) {
[savedObjectIDs removeAllObjects];
}
if (savedObjectClass) {
savedObjectClass = nil;
}
// set the thead name
NSThread *currentThread = [NSThread currentThread];
[currentThread setName:VS_CORE_DATA_MANAGER_BACKGROUND_THREAD_NAME];
// if there is not already a background context, then create one
if (!_backgroundQueueManagedObjectContext) {
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_backgroundQueueManagedObjectContext = [[NSManagedObjectContext alloc] init];
[_backgroundQueueManagedObjectContext setPersistentStoreCoordinator:coordinator];
}
}
// save the JSON dictionary starting at the upper most level of the key path, and return all created/updated objects in an array
NSArray *objectIds = [self saveJSON:jsonDict objectMapping:objectMapping class:managedObjectClass managedObjectContext:_backgroundQueueManagedObjectContext level:0];
// save the object IDs and the completion block to global variables so we can access them after the save
if (objectIds) {
[savedObjectIDs addObjectsFromArray:objectIds];
}
if (completion) {
saveCompletionBlock = completion;
}
if (managedObjectClass) {
savedObjectClass = managedObjectClass;
}
// save all changes object context
[self saveManagedObjectContext];
});
「saveManagedObjectContext」方法は、基本的には、スレッドが実行されているを見て、適切なコンテキストを保存します。このメソッドが正しく機能していることを確認しましたので、ここにコードを配置しません。
このコードのすべては、シングルトンに常駐し、シングルトンのinitメソッドでは、私は「NSManagedObjectContextDidSaveNotification」のリスナーを追加してい、それはmergeChangesFromContextDidSaveNotification:メソッドを呼び出し
。
// merge changes from the context did save notification to the main context
- (void)mergeChangesFromContextDidSaveNotification:(NSNotification *)notification
{
NSThread *currentThread = [NSThread currentThread];
if ([currentThread.name isEqual:VS_CORE_DATA_MANAGER_BACKGROUND_THREAD_NAME]) {
// merge changes to the primary context, and wait for the action to complete on the main thread
[_managedObjectContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:) withObject:notification waitUntilDone:YES];
// on the main thread fetch all new data and call the completion block
dispatch_async(dispatch_get_main_queue(), ^{
// get objects from the database
NSMutableArray *objects = [[NSMutableArray alloc] init];
for (id objectID in savedObjectIDs) {
NSError *error;
id object = [_managedObjectContext existingObjectWithID:objectID error:&error];
if (error) {
[self logError:error];
} else if (object) {
[objects addObject:object];
}
}
// remove all saved object IDs from the array
[savedObjectIDs removeAllObjects];
savedObjectClass = nil;
// call the completion block
//completion(objects);
saveCompletionBlock(objects);
// clear the saved completion block
saveCompletionBlock = nil;
});
}
}
私は「mergeChangesFromContextDidSaveNotification:」呼び出しています上記のあなたが方法で見ることができるようにメインスレッド上を、そして私が行っまで待機するアクションを設定しています。リンゴの文書によれば、バックグラウンドスレッドは、その呼び出しがその呼び出しの下のコードの残りの部分を続ける前に、そのアクションが完了するまで待つべきです。上で述べたように、このコードを実行するとすべて動作するように見えますが、フェッチされたオブジェクトをコンソールに出力しようとすると、何も戻ってこないのです。マージは実際には起こっていないか、残りのコードが実行される前に終了していない可能性があります。マージが完了したことを確認するためにリッスンする必要があるという別の通知がありますか?または、マージの後で、主オブジェクトのコンテキストを保存する必要がありますか?
また、悪いコードの書式設定についてお詫び申し上げますが、SOのコードタグはメソッド定義を好まないようです。
ありがとうございました!
UPDATE:
私は、以下の推奨された変更を行ったが、それでも同じ問題を抱えてきました。以下は私が持っている更新されたコードです。
だから、私が見つけた:
この
は、バックグラウンドスレッドの節約を呼び出すコードは、これは// merge changes from the context did save notification to the main context
- (void)mergeChangesFromBackground:(NSNotification *)notification
{
// kill the listener
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:_backgroundQueueManagedObjectContext];
NSThread *currentThread = [NSThread currentThread];
// merge changes to the primary context, and wait for the action to complete on the main thread
[[self managedObjectContext] performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:) withObject:notification waitUntilDone:YES];
// dispatch the completion block
dispatch_async(dispatch_get_main_queue(), ^{
// get objects from the database
NSMutableArray *objects = [[NSMutableArray alloc] init];
for (id objectID in savedObjectIDs) {
NSError *error;
id object = [[self managedObjectContext] existingObjectWithID:objectID error:&error];
if (error) {
[self logError:error];
} else if (object) {
[objects addObject:object];
}
}
// remove all saved object IDs from the array
[savedObjectIDs removeAllObjects];
savedObjectClass = nil;
// call the completion block
//completion(objects);
saveCompletionBlock(objects);
// clear the saved completion block
saveCompletionBlock = nil;
});
}
UPDATE NSManagedObjectContextDidSaveNotification通知によってで呼び出されたコードがある
// process in the background queue
dispatch_async(backgroundQueue, ^(void){
if (savedObjectIDs.count > 0) {
[savedObjectIDs removeAllObjects];
}
if (savedObjectClass) {
savedObjectClass = nil;
}
// set the thead name
NSThread *currentThread = [NSThread currentThread];
[currentThread setName:VS_CORE_DATA_MANAGER_BACKGROUND_THREAD_NAME];
// if there is not already a background context, then create one
if (!_backgroundQueueManagedObjectContext) {
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_backgroundQueueManagedObjectContext = [[NSManagedObjectContext alloc] init];
[_backgroundQueueManagedObjectContext setPersistentStoreCoordinator:coordinator];
}
}
// save the JSON dictionary starting at the upper most level of the key path
NSArray *objectIds = [self saveJSON:jsonDict objectMapping:objectMapping class:managedObjectClass managedObjectContext:_backgroundQueueManagedObjectContext level:0];
// save the object IDs and the completion block to global variables so we can access them after the save
if (objectIds) {
[savedObjectIDs addObjectsFromArray:objectIds];
}
if (completion) {
saveCompletionBlock = completion;
}
if (managedObjectClass) {
savedObjectClass = managedObjectClass;
}
// listen for the merge changes from context did save notification
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mergeChangesFromBackground:) name:NSManagedObjectContextDidSaveNotification object:_backgroundQueueManagedObjectContext];
// save all changes object context
[self saveManagedObjectContext];
});
処理していますソリューション。私がバックグラウンドスレッドにオブジェクトIDを保存しておき、メインスレッドでそれらを使って再フェッチしようとしていたのはうまくいかなかったということです。だから私は、NSManagedObjectContextDidSaveNotification通知で送信されたuserInfo辞書から挿入/更新されたオブジェクトを取得してしまいました。以下は、現在動作している私の更新されたコードです。
このコードは、前prossesingと保存ロジックを開始する前にのよう
// process in the background queue
dispatch_async(backgroundQueue, ^(void){
// set the thead name
NSThread *currentThread = [NSThread currentThread];
[currentThread setName:VS_CORE_DATA_MANAGER_BACKGROUND_THREAD_NAME];
[self logMessage:[NSString stringWithFormat:@"(%@) saveJSONObjects:objectMapping:class:completion:", [managedObjectClass description]]];
// if there is not already a background context, then create one
if (!_backgroundQueueManagedObjectContext) {
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_backgroundQueueManagedObjectContext = [[NSManagedObjectContext alloc] init];
[_backgroundQueueManagedObjectContext setPersistentStoreCoordinator:coordinator];
}
}
// save the JSON dictionary starting at the upper most level of the key path
[self saveJSON:jsonDict objectMapping:objectMapping class:managedObjectClass managedObjectContext:_backgroundQueueManagedObjectContext level:0];
// save the object IDs and the completion block to global variables so we can access them after the save
if (completion) {
saveCompletionBlock = completion;
}
// listen for the merge changes from context did save notification
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mergeChangesFromBackground:) name:NSManagedObjectContextDidSaveNotification object:_backgroundQueueManagedObjectContext];
// save all changes object context
[self saveManagedObjectContext];
});
背景にあなたの文章は、MOCので、これはあなたのケースでNSManagedObjectContextDidSaveNotification
- (void)mergeChangesFromBackground:(NSNotification *)notification
{
// kill the listener
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:_backgroundQueueManagedObjectContext];
// merge changes to the primary context, and wait for the action to complete on the main thread
[[self managedObjectContext] performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:) withObject:notification waitUntilDone:YES];
// dispatch the completion block
dispatch_async(dispatch_get_main_queue(), ^{
// pull the objects that were saved from the notification so we can get them on the main thread MOC
NSDictionary *userInfo = [notification userInfo];
NSMutableArray *modifiedObjects = [[NSMutableArray alloc] init];
NSSet *insertedObject = (NSSet *)[userInfo objectForKey:@"inserted"];
NSSet *updatedObject = (NSSet *)[userInfo objectForKey:@"updated"];
if (insertedObject && insertedObject.count > 0) {
[modifiedObjects addObjectsFromArray:[insertedObject allObjects]];
}
if (updatedObject && updatedObject.count > 0) {
[modifiedObjects addObjectsFromArray:[updatedObject allObjects]];
}
NSMutableArray *objects = [[NSMutableArray alloc] init];
// iterate through the updated objects and find them in the main thread MOC
for (NSManagedObject *object in modifiedObjects) {
NSError *error;
NSManagedObject *obj = [[self managedObjectContext] existingObjectWithID:object.objectID error:&error];
if (error) {
[self logError:error];
}
if (obj) {
[objects addObject:obj];
}
}
modifiedObjects = nil;
// call the completion block
saveCompletionBlock(objects);
// clear the saved completion block
saveCompletionBlock = nil;
});
}
お返事ありがとうございます。 saveManagedObjectContextメソッド呼び出しのすぐ上にリスナー(以下に掲載)を追加しようとしましたが、私はまだ同じ結果を得ています。オブザーバーを正しく追加しているのですか、オブザーバーを特定のスレッドに追加する別の方法がありますか? //コンテキストからのマージ変更をリッスンしました [[NSNotificationCenter defaultCenter] addObserver:自己セレクタ:@selector(mergeChangesFromContextDidSaveNotification :) name:NSManagedObjectContextDidSaveNotificationオブジェクト:_backgroundQueueManagedObjectContext]; –
私はコードサンプルで答えを更新します –
うーん...これは狂った問題です。私はあなたが提案した更新を行ったが、メインスレッドMOCからバックグラウンドMOCに保存された新しいオブジェクトをまだ取得していない。私は、ファクトリメソッドを使用してインスタンス化されたものとすべてを確認するためにメインスレッドのMOC呼び出しをすべて更新しましたが、保存されたオブジェクトはまだ見つかりません。私はまた、savedObjectIDs配列を出力して、IDを正しく取得しています。あなたは間違っているかもしれない何かを見ることができますか?助けてくれてありがとう!また、元の投稿を更新して、更新されたコードを取得しました。 –