保存されていないスクラッチパッドMOC(Webからダウンロードされた一連のオブジェクトを含む)と、オブジェクトを永続化する別の永続的なMOCがあるかなり標準的なセットアップだと思います。ユーザーがスクラッチMOCからオブジェクトを選択してライブラリに追加する場合、1)スクラッチMOCからオブジェクトを削除してパーマネントMOCに挿入するか、2)オブジェクトをパーマネントMOCにコピーします。 Core Data FAQ は次のようにオブジェクトをコピーできると言っています:
NSManagedObjectID *objectID = [managedObject objectID];
NSManagedObject *copy = [context2 objectWithID:objectID];
(この場合、context2はpermanentMOCになります。)しかし、これを行うと、コピーされたオブジェクトに障害が発生します。データは最初は未解決です。解決されると、後ですべての値がnilになります。元のmanagedObjectのデータ(属性または関係)が実際にコピーまたは参照されることはありません。したがって、このobjectWithID:メソッドを使用することと、insertNewObjectForEntityForName:を使用して完全に新しいオブジェクトをpermanentMOCに挿入するだけの違いはわかりません。
私は、permanentMOCで新しいオブジェクトを作成し、古いオブジェクトから各キーと値のペアを手動でコピーできることを理解していますが、その解決策にはあまり満足していません。 (私にはこの問題があるさまざまな管理対象オブジェクトがあるので、copy:メソッドの開発と開発を続けながら、それらすべてのメソッドを作成および更新する必要はありません。)より良い方法はありますか?
まず、1つのスレッドで複数のNSManagedObjectContext
を使用することは、標準構成ではありません。 99%の場合、必要なコンテキストは1つだけであり、それによってこの状況が解決されます。
複数のNSManagedObjectContext
が必要だと思うのはなぜですか?
これは、実際に私がこれまでに理解した数少ない使用例の1つです。これを行うには、あるコンテキストから別のコンテキストにオブジェクトの再帰的なコピーを行う必要があります。ワークフローは次のようになります。
-dictionaryWithValuesForKeys
および-[NSEntityDescription attributesByName]
を使用します。-setValuesForKeysWithDictionary
を使用)-[NSEntityDescription relationshipsByName]
を使用して関係を歩く必要があります。別の人が述べたように、私の本のサンプルコードを The Pragmatic Programmers Core Data Book からダウンロードして、この問題の1つの解決策を見ることができます。もちろん本の中で私はそれについてより深く議論します:)
ドキュメントは誤解を招きやすく、不完全です。 objectIDメソッド自体はオブジェクトをコピーせず、必要な特定のオブジェクトを取得したことを保証するだけです。
この例のcontext2
は、実際にはソースコンテキストであり、宛先ではありません。宛先コンテキストにそのIDのオブジェクトがないため、nilを取得しています。
管理対象オブジェクトのコピーは、オブジェクトグラフの複雑さとコンテキストによるグラフの管理方法のため、かなり複雑です。コピーしたオブジェクトを新しいコンテキストで詳細に再作成する必要があります。
ここにいくつかのサンプルコードがあります のサンプルコードから抜粋 The Pragmatic Programmer'sCore Data:Apple's API for Persisting Data on Mac OS X 。 (Pragmaticサイトで本を購入しなくてもプロジェクトコード全体をダウンロードできる場合があります。)コンテキスト間でオブジェクトをコピーする方法の大まかなアイデアが得られるはずです。
オブジェクトをコピーするいくつかの基本コードを作成できますが、各オブジェクトグラフの関係の詳細は通常、各データモデルごとにカスタマイズする必要があることを意味します。
私自身も同じ問題を抱えていて、後でコンテキストに追加できる切断されたエンティティが作成されているこの記事を見つけました: http://locassa.com/temporary-storage-in-apples-coredata/
データベースにオブジェクトを格納するので、NSManagedObjectがあるという考えです。私のハードルは、これらのオブジェクトの多くがHTTP APIを介してダウンロードされ、セッションの最後にそれらのほとんどを破棄したいということでした。ユーザー投稿のストリームを考えてみてください。お気に入りに追加されたもの、または下書きとして保存されたものだけを保存したいと思います。
を使用してすべての投稿を作成します
+ (id)newPost {
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Post" inManagedObjectContext:self.managedObjectContext];
Post *post = [[Post alloc] initWithEntity:entityDescription insertIntoManagedObjectContext:nil];
return post;
}
投稿がお気に入りに追加されると、ローカルの管理オブジェクトコンテキストに挿入されます
+ (BOOL)favoritePost:(Post *)post isFavorite:(BOOL)isFavorite
{
// Set the post's isFavorite flag
post.isFavorite = [NSNumber numberWithBool:isFavorite];
// If the post is being favorited and not yet in the local database, add it
NSError *error;
if (isFavorite && [self.managedObjectContext existingObjectWithID:post.objectID error:&error] == nil) {
[self.managedObjectContext insertObject:post];
}
// Else if the post is being un-favorited and is in the local database, delete it
else if (!isFavorite && [self.managedObjectContext existingObjectWithID:post.objectID error:&error] != nil) {
[self.managedObjectContext deleteObject:post];
}
// If there was an error, output and return NO to indicate a failure
if (error) {
NSLog(@"error: %@", error);
return NO;
}
return YES;
}
お役に立てば幸いです。
managedObject
が存在するコンテキストを保存していることを確認する必要があります。同じオブジェクトを別のコンテキストでフェッチするには、永続ストアに存在している必要があります。
ドキュメント によると、objectWithID:
は常にオブジェクトを返します。したがって、障害がオブジェクトに解決されるという事実は、すべてのnil
値が永続ストアでオブジェクトを見つけられないことを意味します。
Swift 5
2020年にNSManagedObjectsをコピーする方法を知りたい場合は、以下のコードでうまくいきます。
// `Restaurant` is the name of my managed object subclass.
// I like to have Xcode auto generate my subclasses (Codegen
// set to "Class Definition") & then just extend them with
// whatever functionality I need.
extension Restaurant {
public func copy() -> Restaurant? {
let attributes = entity.attributesByName.map { $0.key }
let dictionary = dictionaryWithValues(forKeys: attributes)
guard let context = AppDelegate.shared?.persistentContainer.viewContext,
let restaurantCopy = NSEntityDescription.insertNewObject(forEntityName: Restaurant.entityName, into: context) as? Restaurant
else
{
return nil
}
restaurantCopy.setValuesForKeys(dictionary)
return restaurantCopy
}
}