一時的なNSManagedObjectインスタンスを処理する方法は?


86

NSManagedObjectインスタンスを作成し、それらを使用していくつかの処理を行ってから、それらをゴミ箱に移動するか、sqlitedbに保存する必要があります。問題は、にNSManagedObject接続されていないインスタンスを作成できないことNSManagedObjectContextです。これは、データベース内のオブジェクトの一部が不要であると判断した後、なんらかの方法でクリアする必要があることを意味します。

これに対処するために、同じコーディネーターを使用してメモリ内ストアを作成し、assignObject:toPersistentStore.Nowを使用して一時オブジェクトをそこに配置しています。これらの一時オブジェクトが、からフェッチしたデータに到達しないようにするにはどうすればよいですか。両方のストアコンテキストに共通ですか?または、そのようなタスク用に個別のコンテキストを作成する必要がありますか?


UPD:

今、私はインメモリストア用に別のコンテキストを作成することを考えています。オブジェクトをあるコンテキストから別のコンテキストに移動するにはどうすればよいですか?[context insertObject:]を使用するだけですか?この設定では問題なく動作しますか?オブジェクトのグラフから1つのオブジェクトを挿入すると、グラフ全体もコンテキストに挿入されますか?


これは回答済みとしてフラグを立てているため、これは別の質問になります。新しい質問を作成し、説明なぜあなたは、スタックの別々のコア全体のデータを必要と感じJUSTメモリ内の店舗のために。私はあなたと一緒に質問を調査させていただきます。
マーカスS.ザラ2010

UPDセクションは現在関連性がありません。別のアプローチを選択したため、あなたの回答に対する最後のコメントを参照してください。
fspirit 2010

回答:


146

注:この回答は非常に古いものです。完全な履歴についてはコメントを参照してください。その後、推奨事項が変更され、関連付けられていないNSManagedObjectインスタンスの使用は推奨されなくなりました。私の現在の推奨事項は、一時的な子NSManagedObjectContextインスタンスを使用することです。

元の回答

これを行う最も簡単な方法はNSManagedObject、を関連付けずにインスタンスを作成することNSManagedObjectContextです。

NSEntityDescription *entity = [NSEntityDescription entityForName:@"MyEntity" inManagedObjectContext:myMOC];
NSManagedObject *unassociatedObject = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];

次に、それを保存する場合:

[myMOC insertObject:unassociatedObject];
NSError *error = nil;
if (![myMoc save:&error]) {
  //Respond to the error
}

6
unassociatedObjectに他の関連付けられていないオブジェクトへの参照がある場合、それらを1つずつ挿入する必要がありますか、それともmyMOCはすべての参照を収集して挿入するのに十分賢いですか?
fspirit 2010

6
関係も処理するのに十分賢いです。
マーカスS.ザラ2010

2
このアプローチでは、MOを保存する前に、通常のデータオブジェクトのように扱うことができますが、CoreDataコントラクトによってどのように「サポート」されているか、したがって、MOがどの程度将来性があるかが心配です。アップルはこのアプローチについてどこでも言及または使用していますか?そうでない場合、将来のiOSリリースでは、動的プロパティが変更されてMOCに依存し、このアプローチが破られる可能性があるためです。リンゴのドキュメントは、この上でクリアされていません。彼らは、コンテキストと指定イニシャライザの重要性を強調するが、1つの言及は、「コンテキストがnilでない場合、...」というnilを示唆することはOKであるかもしれないと言っMOドキュメントにあります
ルバーブ2012

41
私はしばらく前にこのアプローチを使用しましたが、MOCに挿入する前にそれらのオブジェクトを変更したり、それらの関係を作成したりすると、奇妙な動作やクラッシュが発生し始めました。私はこれをWWDCのCoreDataエンジニアと話しましたが、関連付けられていないオブジェクトのAPIはありますが、MOCはオブジェクトから送信されるKVO通知に大きく依存しているため、使用しないことを強くお勧めします。彼は、一時オブジェクトには通常のNSObjectを使用することを提案しました。これは、はるかに安全だからです。
エイドリアンショニグ2013

7
これは、iOS 8、特に永続的な関係ではうまく機能しないようです。他の誰かがこれを確認できますか?
Janum Trivedi

40

iOS5は、MikeWellerの答えに代わるより簡単な方法を提供します。代わりに、NSManagedObjectContextを使用してください。NSNotificationCenterを介してトランポリンする必要がなくなります

子コンテキストを作成するには:

NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
childContext.parentContext = myMangedObjectContext;

次に、子コンテキストを使用してオブジェクトを作成します。

NSManagedObject *o = [NSEntityDescription insertNewObjectForEntityForName:@"MyObject" inManagedObjectContext:childContext];

変更は、子コンテキストが保存されている場合にのみ適用されます。したがって、変更を破棄するには、保存しないでください。

関係にはまだ制限があります。つまり、他のコンテキストのオブジェクトとの関係を作成することはできません。これを回避するには、objectIDを使用して、子コンテキストからオブジェクトを取得します。例えば。

NSManagedObjectID *mid = [myManagedObject objectID];
MyManagedObject *mySafeManagedObject = [childContext objectWithID:mid];
object.relationship=mySafeManagedObject;

子コンテキストを保存すると、変更が親コンテキストに適用されることに注意してください。親コンテキストを保存すると、変更が保持されます。

詳細な説明については、wwdc2012セッション214を参照してください。


1
これを提案してくれてありがとう!このメソッドをnilコンテキストを使用するのではなくテストするデモを作成しましたが、少なくともOSXでは、nilコンテキストを挿入すると、保存時に属性が失われました。デモはgithub.com/seltzered/CoreDataMagicalRecordTempObjectsDemo
Vivek Gani

moc3番目のスニペットにはどれがありますか?それはありますchildContextmyMangedObjectContext
バグローフ2014年

それはchildContextある
railwayparade

このソリューションは、nilコンテキストを持つよりも優れています。
Y

NSManagedObjectすでに関連するを提供しているのでNSManagedObjectContext、コンテキストの選択を自動化できます:NSManagedObject* objectRelatedContextually = [objectWithRelationship.managedObjectContext objectWithID:objectRelated.objectID];そしてobjectWithRelationship.relationship = objectRelatedContextually;
ゲイリー

9

この種のことを実現する正しい方法は、新しい管理対象オブジェクトコンテキストを使用することです。同じ永続ストアを使用して管理対象オブジェクトコンテキストを作成します。

NSManagedObjectContext *tempContext = [[[NSManagedObjectContext alloc] init] autorelease];
[tempContext setPersistentStore:[originalContext persistentStore]];

次に、新しいオブジェクトを追加したり、それらを変更したりします。

保存するときは、tempContextで[tempContext save:...]を呼び出し、保存通知を処理して元のコンテキストにマージする必要があります。オブジェクトを破棄するには、この一時的なコンテキストを解放して、忘れてください。

したがって、一時コンテキストを保存すると、変更はストアに保持され、それらの変更をメインコンテキストに戻す必要があります。

/* Called when the temp context is saved */
- (void)tempContextSaved:(NSNotification *)notification {
    /* Merge the changes into the original managed object context */
    [originalContext mergeChangesFromContextDidSaveNotification:notification];
}

// Here's where we do the save itself

// Add the notification handler
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(tempContextSaved:)
                                             name:NSManagedObjectContextDidSaveNotification
                                           object:tempContext];

// Save
[tempContext save:NULL];
// Remove the handler again
[[NSNotificationCenter defaultCenter] removeObserver:self
                                                name:NSManagedObjectContextDidSaveNotification
                                              object:tempContext];

これは、マルチスレッドのコアデータ操作を処理する方法でもあります。スレッドごとに1つのコンテキスト。

この一時的なコンテキストから既存のオブジェクトにアクセスする必要がある場合(リレーションを追加するなど)、オブジェクトのIDを使用して次のような新しいインスタンスを取得する必要があります。

NSManagedObject *objectInOriginalContext = ...;
NSManagedObject *objectInTemporaryContext = [tempContext objectWithID:[objectInOriginalContext objectID]];

NSManagedObject間違ったコンテキストで使用しようとすると、保存中に例外が発生します。


立ち上がることNSManagedObjectContextはメモリとCPUの両方でコストがかかるため、このためだけに2番目のコンテキストを作成することは非常に無駄です。私はこれがもともとAppleの例のいくつかにあったことを理解していますが、それらはそれらの例を更新して修正しました。
マーカスS.ザラ2010

2
Appleは、CoreDataBooksのサンプルコードにこの手法(2番目の管理対象オブジェクトコンテキストの作成)を引き続き使用しています。
ネヴァンキング2011

1
AppleがCoreDataBooksを更新したことに注意してください。実際、2つのコンテキストを使用していますが、2番目のコンテキストは最初のコンテキストの子です。この手法は、WWDC 2011で議論(と推奨)されたプレゼンテーション303(iOSの中でコアデータの新機能)及び(上向きの変更をマージするためには、はるかに、シンプルなコードで)ここに記載されてstackoverflow.com/questions/9791469/...
Rhubarb 2012

4
「NSManagedObjectContextを立ち上げることはメモリとCPUの両方でコストがかかるため、このためだけに2番目のコンテキストを作成することは非常に無駄です。」。いいえ、ちがいます。永続ストアコーディネーターの依存関係(管理対象オブジェクトモデルと具象ストア)は、コンテキストではありません。コンテキストは軽量です。
quellish 2014

3
@quellish同意しました。Appleは、WWDCでの最近のコアデータパフォーマンスに関する講演で、コンテキストの作成は非常に軽量であると述べています。
ジェシー2014

9

nilコンテキストから一時オブジェクトを作成することは、実際にcontext!= nil!

それで大丈夫であることを確認してください。


私はそれで大丈夫ではありません
チャーリー

8

あなたが説明しているのは、まさにそのNSManagedObjectContext目的です。

コアデータの基本:コアデータプログラミングガイド

管理対象オブジェクトのコンテキストは、インテリジェントなスクラッチパッドと考えることができます。永続ストアからオブジェクトをフェッチするときは、一時的なコピーをスクラッチパッドに移動し、そこでオブジェクトグラフ(またはオブジェクトグラフのコレクション)を形成します。その後、これらのオブジェクトを好きなように変更できます。ただし、これらの変更を実際に保存しない限り、永続ストアは変更されません。

およびCoreDataプログラミングガイド:管理対象オブジェクトの検証

これは、「スクラッチパッド」を表す管理対象オブジェクトコンテキストの概念もサポートします。一般に、管理対象オブジェクトをスクラッチパッドに移動して、最終的に変更をコミットするか破棄する前に、必要に応じて編集できます。

NSManagedObjectContextsは軽量になるように設計されています。それらは自由に作成および破棄できます。永続的なストアコーディネーターであり、依存関係が「重い」のです。単一の永続ストアコーディネーターには、多くのコンテキストを関連付けることができます。古い、廃止されたスレッド制限モデルでは、これは、各コンテキストに同じ永続ストアコーディネーターを設定することを意味します。今日では、ネストされたコンテキストを、永続ストアコーディネーターに関連付けられているルートコンテキストに接続することを意味します。

コンテキストを作成し、そのコンテキスト内で管理対象オブジェクトを作成および変更します。それらを永続化し、それらの変更を伝達したい場合は、コンテキストを保存します。それ以外の場合は破棄してください。

NSManagedObjectContextは独立して管理対象オブジェクトを作成しようとすると、問題が発生します。Core Dataは、最終的にはオブジェクトグラフの変更追跡メカニズムであることを忘れないでください。このため、管理対象オブジェクトは実際には管理対象オブジェクトコンテキストの一部です。コンテキストは、そのライフサイクルを観察し、コンテキストなしていないすべての管理対象オブジェクト機能の正常に動作します。


6

一時オブジェクトの使用に応じて、上記の推奨事項にはいくつかの注意事項があります。私のユースケースは、一時オブジェクトを作成してビューにバインドしたいというものです。ユーザーがこのオブジェクトを保存することを選択した場合、既存のオブジェクトとの関係を設定して保存したいと思います。これらの値を保持する一時オブジェクトを作成しないようにするために、これを実行したいと思います。(はい、ユーザーが保存してからビューの内容を取得するまで待つこともできますが、これらのビューをテーブル内に配置しているため、これを行うロジックはあまり洗練されていません。)

一時オブジェクトのオプションは次のとおりです。

1)(推奨)子コンテキストで一時オブジェクトを作成します。オブジェクトをUIにバインドしていて、オブジェクトアクセサーが子コンテキストで呼び出されることを保証できないため、これは機能しません。(私は他のことを述べている文書を見つけられなかったので、私は仮定しなければなりません。)

2)nilオブジェクトコンテキストで一時オブジェクトを作成します。これは機能せず、データの損失/破損につながります。

私の解決策:nilオブジェクトコンテキストで一時オブジェクトを作成することでこれを解決しましたが、オブジェクトを#2として挿入するのではなく保存するときに、メインコンテキストで作成する新しいオブジェクトにすべての属性をコピーします。NSManagedObjectサブクラスにcloneIntoというサポートメソッドを作成しました。これにより、任意のオブジェクトの属性と関係を簡単にコピーできます。


それが私が探しているものです。しかし、私の疑問は、関係属性をどのように処理するかということです。
マニ

1

私にとってマーカスの答えはうまくいきませんでした。これが私のために働いたものです:

NSEntityDescription entityForName:@"MyEntity" inManagedObjectContext:myMOC];
NSManagedObject *unassociatedObject = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];

次に、それを保存することにした場合:

[myMOC insertObject:unassociatedObjet];
NSError *error = nil;
[myMoc save:&error];
//Check the error!

また、リリースすることを忘れてはなりません

[unassociatedObject release]

1

迅速なすべての同様の質問がこの質問にリダイレクトされるので、私はSwiftのこの回答を書き直しています

次のコードを使用して、ManagedContextなしでオブジェクトを宣言できます。

let entity = NSEntityDescription.entity(forEntityName: "EntityName", in: myContext)
let unassociatedObject = NSManagedObject.init(entity: entity!, insertInto: nil)

後で、オブジェクトを保存するために、それをコンテキストに挿入して保存することができます。

myContext.insert(unassociatedObject)
// Saving the object
do {
    try self.stack.saveContext()
    } catch {
        print("save unsuccessful")
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.