10

MagicalRecord 2.0.3を使用していますが、バックグラウンドでデータを保存する方法がわかりません。

ドキュメントによると、このようなものが機能するはずです:

[MagicalRecord saveInBackgroundWithBlock:^(NSManagedObjectContext *localContext) {
    // Do this hundreds of times
    [MyObject createInContext:localContext];
}];

ただし、データベースには何も保存されません。私はこれに似た解決策を投稿している複数の人々を見てきました:

[MagicalRecord saveInBackgroundWithBlock:^(NSManagedObjectContext *localContext) {
    // Do this hundreds of times
    [MyObject createInContext:localContext];
} completion:^{
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        [[NSManagedObjectContext defaultContext] saveNestedContexts];
    }];
}];

これにより、データベースにデータが保存されますが、保存はメインスレッドで行われるため、アプリケーションはしばらく応答しません(データセットでは、約3秒で長すぎます)。

私もこれを試しましたが、保存中にブロックされます:

self.queue = [[NSOperationQueue alloc] init];

[self.queue addOperationWithBlock:^{
    NSManagedObjectContext *localContext = [NSManagedObjectContext contextForCurrentThread];

    // Do this hundreds of times
    [MyObject createInContext:localContext];

    [localContext saveNestedContexts];
}];

そして最後に、このコードと同じブロッキング効果:

dispatch_queue_t syncQueue = dispatch_queue_create("Sync queue", NULL);
dispatch_async(syncQueue, ^{
    NSManagedObjectContext *localContext = [NSManagedObjectContext contextForCurrentThread];

    // Do this hundreds of times
    [MyObject createInContext:localContext];

    [[NSManagedObjectContext contextForCurrentThread] saveNestedContexts];
});

それで、これを解決するための最良の方法は何ですか?バックグラウンドで何百ものオブジェクトを作成する必要があり、アプリは応答性を維持する必要があります。

4

2 に答える 2

6

MagicalRecordは、バックグラウンドで作業を行うときに子コンテキストを使用します。これは小さな変更では問題なく機能しますが、大量のデータをインポートするときに過度のメインスレッドブロッキングが発生します。

これを行う方法は、並列NSManagedObjectContextを使用し、NSManagedObjectContextDidSaveNotification通知とmergeChangesFromContextDidSaveNotificationメソッドを使用して自分自身をマージすることです。ここでパフォーマンステストを参照してください:http://floriankugler.com/blog/2013/5/11/backstage-with-nested-managed-object-contexts

ネストされたコンテキストを保存するときは、すべてを親コンテキストにコピーする必要があります。これとは対照的に、(マージしているコンテキストで)フェッチされていないオブジェクトは。によってマージされませんmergeChangesFromContextDidSaveNotification。これがそれをより速くするものです。

バッチで保存してNSFetchResultsControllerを使用した直後にこれらの結果を表示したい場合は、問題が発生する可能性があります。解決策については、次の質問を参照してください。 述語を持つNSFetchedResultsControllerは、異なるNSManagedObjectContextからマージされた変更を無視します。

パフォーマンスのヒントについては、次の質問をご覧ください:iOS5での高速で効率的なコアデータインポートの実装

独自のコンテキストを作成します。

NSManagedObjectContext *importContext = [[NSManagedObjectContext alloc] 
                          initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[importContext setPersistentStoreCoordinator:yourPersistentStoreCoordinator];
[importContext setUndoManager:nil]; // For importing you don't need undo: Faster

// do your importing with the new importContext
// …

NSError* error = nil;
if(importContext.hasChanges) {
  if(![importContext save:&error]) {
      NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
  } 
}

管理対象オブジェクトコンテキストへの保存をリッスンしていることを確認してください。

[[NSNotificationCenter defaultCenter] 
              addObserver:singleton 
                 selector:@selector(contextDidSave:)
                     name:NSManagedObjectContextDidSaveNotification object:nil];

contextDidSave:変更を自分でマージします。

- (void) contextDidSave:(NSNotification*) notification
{
  if(![notification.object isEqual:self.mainContext]) {
    dispatch_async(dispatch_get_main_queue(), ^{
      [self.mainContext mergeChangesFromContextDidSaveNotification:notification];
    });
  }
}
于 2013-07-11T19:32:57.213 に答える
1

管理対象オブジェクトのコンテキストはスレッドセーフではないため、Coredataオブジェクトで何らかのバックグラウンド作業を行う必要がある場合(つまり、メインUIをブロックせずに長時間実行されるインポート/エクスポート機能)、バックグラウンドスレッドで行う必要があります。

このような場合、バックグラウンドスレッドで新しい管理対象オブジェクトコンテキストを作成し、coredata操作を繰り返してから、変更をメインコンテキストに通知する必要があります。

これがどのように機能するかの例をここで見つけることができます

コアデータとスレッド/GrandCentral Dispatch

于 2013-12-26T15:03:07.203 に答える