web-dev-qa-db-ja.com

iOS Core Dataはいつコンテキストを保存しますか?

同時実行性とマルチスレッドが原因で、コアデータがランダムにクラッシュします。 CoreDataはスレッドセーフではないことを私は知っています。また、ThreadedDataServiceを作成し、スレッドごとに個別のコンテキストをインスタンス化する方法について、他にもいくつか回答がありました。

これは私が今のところ飲み込むには少し多すぎるので、私はもっと簡単な方法を見つけようとしています。

私が現在試している解決策は単純です:メインスレッドを介してデータを保存します。しかし、今度は新しい問題が発生します:デッドロック。新しいNSManagedObjectを挿入するたびに、保存の呼び出しが続くため、アプリが応答しなくなります。 (それが私の最善の推測です)。

App Delegateのドキュメントを読んで、applicationWillTerminateにコンテキストを保存するようにアドバイスされていることに気付きました。

私の質問はこれです:毎分新しいイベントを挿入する長時間実行操作の場合、ユーザーは更新がすべてのコントローラーにすぐに伝達されるのを確認する必要はありません。コンテキストを保存するのに適した時期はいつですか?各レコードのコンテキストを保存するのはやり過ぎかもしれないと感じていますか?

-(void)insertNewEvent
{


    // Create a new instance of the entity managed by the fetched results controller.
    NSManagedObjectContext *context = [self.fetchedResultsController.managedObjectContext];
    NSEntityDescription *entity = [[self.fetchedResultsControllerfetchRequest] entity];
    Event*newManagedObject = (Event*)[NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];

//use accessor methods to set default values

    // Save the context. > IS THIS REALLY NEEDED HERE?
    NSError *error = nil;
    if (![context save:&error])
    {


    }else
    {
        if(newManagedObject!=nil)
        {
            currentState= newManagedObject;
            [currentRecord addEvent:newManagedObject];
//Is this call needed?
            [self saveApplicationRecords];      
        }
    }

}

すべての管理対象オブジェクトに対してこのようなメソッドを定義していますが、レコードを挿入するたびに保存するのではなく、メインスレッドでこのようなメソッドを10〜15分ごとに呼び出して、保留中の変更を保存するだけで十分ですか?

-(void)saveApplicationRecords
{
     NSLog(@"saveApplicationRecords");
    NSManagedObjectContext *context = [self.applicationRecordsController.managedObjectContext];
    // Save the context.
    NSError *error = nil;
    if (![context save:&error])
    {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);

    }

}

macbirdieの応答を読んだ後の追加の質問:この種の方法はコアデータで合法ですか?

-(Event*)insertAndReturnNewEventWithDate:(NSDate*)date_ type:(int)type
{
 NSManagedObjectContext *context = [self.dreamEventsController managedObjectContext];
    NSEntityDescription *entity = [[self.dreamEventsController fetchRequest] entity];
    DreamEvent *newManagedObject = (Event*)[NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];

//handle properties

 NSError *error = nil;
    if (![context save:&error])
    {
return nil;

    }else
    {
return newManagedObject ;
}

}

ありがとうございました!

19
Alex Stone

特に後でオブジェクトを変更する場合は、プロセスの早い段階でコンテキストを保存する必要はありません。

ほとんどの場合、データベースで実行しようとしている変更に対して、個別のNSManagedObjectContextを作成する必要があります。そのため、その上にオブジェクトを作成し、必要なプロパティを入力してから、saveを送信し、メインコンテキストでmergeChangesFromContextDidSaveNotification:トリック全体を実行します(ほとんどの場合、メインスレッドで実行されるため、performSelectorOnMainThread...メッセージ)。

デフォルトでは、NSManagedObjectContextによって作成および返されるオブジェクトは自動解放されます。たとえば、新しいオブジェクトを作成し、それをフォームシートで編集する場合は、オブジェクトを作成する前に、管理対象オブジェクトコンテキストに対してYESを指定してsetRetainsRegisteredObjects:を呼び出すことができるため、作成されたオブジェクトは、 'それで終わりです。 NSManagedObjectsのライフサイクルを自分で管理することはお勧めしません。NSManagedObjectContextに管理させる必要があります。したがって、それを念頭に置いて、NSManagedObjectを保持する必要はありません。保存操作の後、コンテキストによって登録が解除され、メモリから削除されます。あなたの側には何も必要ありません。

更新された質問部分への回答

オブジェクト自体の代わりに([object objectID]を使用して)NSManagedObjectIDを返した方がおそらく良いでしょう。これにより、コンテキストごとにオブジェクトを安全に破棄でき、さらに編集したりデータを取得したりするためにオブジェクトが必要な場合(他のコンテキストなどから)、ストアから個別にフェッチできます。

コンテキストを保存しなくても、新しく作成されたオブジェクトがそこにあるので、オブジェクトを保持するかどうかを決定できます。

一方、保存後、コンテキストがまだ存在する場合は、データベースにアクセスせずに、指定されたNSManagedObjectIDのオブジェクトをメモリキャッシュから返す場合があります。

一方、;)の場合、オブジェクトを作成するNSManagedObjectContextがまだ存在しているため、オブジェクトをほぼ安全に返すことができます。したがって、オブジェクトは、すでに自動解放プールにありますが、引き続き存在します。

17
macbirdie