Bagaimana cara menangani instans NSManagedObject Sementara?


86

Saya perlu membuat NSManagedObjectinstance, melakukan beberapa hal dengannya, lalu membuangnya atau menyimpannya ke sqlite db. Masalahnya adalah, saya tidak dapat membuat contoh NSManagedObjectunconnected to NSManagedObjectContextdan ini berarti saya harus membersihkan entah bagaimana setelah saya memutuskan bahwa saya tidak memerlukan beberapa objek di db saya.

Untuk mengatasinya, saya telah membuat penyimpanan dalam memori menggunakan koordinator yang sama dan saya menempatkan objek sementara di sana dengan menggunakan assignObject:toPersistentStore.Sekarang, bagaimana cara memastikan bahwa objek sementara ini tidak mendapatkan data, yang saya ambil dari umum untuk konteks kedua toko? Atau apakah saya harus membuat konteks terpisah untuk tugas semacam itu?


UPD:

Sekarang saya berpikir tentang membuat konteks terpisah untuk penyimpanan dalam memori. Bagaimana cara memindahkan objek dari satu konteks ke konteks lainnya? Hanya menggunakan [konteks insertObject:]? Apakah akan berfungsi dengan baik dalam penyiapan ini? Jika saya memasukkan satu objek dari grafik objek, apakah seluruh grafik juga dimasukkan ke dalam konteks?


Ini harus menjadi pertanyaan terpisah karena Anda telah menandai pertanyaan ini sebagai telah dijawab. Buat pertanyaan baru dan jelaskan MENGAPA Anda merasa membutuhkan seluruh tumpukan Data Inti terpisah HANYA untuk penyimpanan dalam memori. Saya akan dengan senang hati mengeksplorasi pertanyaan ini bersama Anda.
Marcus S. Zarra

Bagian UPD sekarang tidak relevan, karena saya telah memilih pendekatan lain, lihat komentar terakhir saya untuk jawaban Anda.
fspirit

Jawaban:


146

CATATAN: Jawaban ini sangat tua. Lihat komentar untuk sejarah lengkap. Rekomendasi saya telah berubah dan saya tidak lagi merekomendasikan penggunaan NSManagedObjectinstans yang tidak terkait . Rekomendasi saya saat ini adalah menggunakan NSManagedObjectContextinstance anak sementara .

Jawaban Asli

Cara termudah untuk melakukannya adalah dengan membuat NSManagedObjectinstance Anda tanpa yang terkait NSManagedObjectContext.

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

Kemudian saat Anda ingin menyimpannya:

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

6
Jika unassociatedObject memiliki referensi ke objek tidak terkait lainnya, haruskah saya memasukkannya satu per satu atau myMOC cukup pintar untuk mengumpulkan semua referensi dan memasukkannya juga?
fspirit

6
Cukup pintar untuk menangani hubungan juga.
Marcus S. Zarra

2
Saya suka bahwa pendekatan ini memungkinkan Anda memperlakukan MO seperti objek data biasa sebelum Anda memutuskan untuk menyimpannya, tetapi khawatir tentang bagaimana "didukung" oleh kontrak CoreData dan oleh karena itu seberapa tahan masa depannya. Apakah apple menyebutkan atau menggunakan pendekatan ini di mana saja? Karena jika tidak, rilis iOS yang akan datang dapat mengubah properti dinamis agar bergantung pada MOC dan menghentikan pendekatan ini. Dokumen apple tidak jelas dalam hal ini: mereka menekankan pentingnya konteks dan penginisialisasi yang ditunjuk, tetapi ada satu penyebutan di dokumen MO yang mengatakan "jika konteks tidak nihil, maka ..." menunjukkan bahwa nil mungkin baik
Rhubarb

41
Saya menggunakan pendekatan ini beberapa waktu yang lalu tetapi mulai melihat perilaku aneh dan crash ketika saya memodifikasi objek tersebut dan / atau membuat hubungan untuk mereka sebelum memasukkannya ke dalam MOC. Saya membicarakan hal ini dengan insinyur Data Inti di WWDC dan dia mengatakan bahwa sementara API untuk objek yang tidak terkait ada di sana, dia sangat menyarankan agar tidak menggunakannya karena MOC sangat bergantung pada pemberitahuan KVO yang dikirim oleh objeknya. Dia menyarankan untuk menggunakan NSObject biasa untuk objek sementara karena itu jauh lebih aman.
Adrian Schönig

7
Ini tampaknya tidak berfungsi dengan baik dengan iOS 8, terutama dengan hubungan yang bertahan lama. Adakah yang bisa mengkonfirmasi ini?
Janum Trivedi

40

iOS5 memberikan alternatif yang lebih sederhana untuk jawaban Mike Weller. Sebaliknya, gunakan anak NSManagedObjectContext. Ini menghilangkan kebutuhan untuk trampolin melalui NSNotificationCenter

Untuk membuat konteks anak:

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

Kemudian buat objek Anda menggunakan konteks anak:

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

Perubahan hanya diterapkan ketika konteks anak disimpan. Jadi untuk membuang perubahan jangan simpan.

Masih ada batasan pada hubungan. yaitu Anda tidak dapat membuat hubungan dengan objek dalam konteks lain. Untuk menyiasati penggunaan objectID ini, untuk mendapatkan objek dari konteks anak. misalnya.

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

Catatan, menyimpan konteks anak menerapkan perubahan ke konteks induk. Menyimpan konteks induk mempertahankan perubahan.

Lihat wwdc 2012 sesi 214 untuk penjelasan lengkapnya.


1
Terima kasih telah menyarankan ini! Saya menulis demo yang menguji metode ini versus menggunakan konteks nihil dan setidaknya di OSX, ini bekerja saat memasukkan konteks nihil kehilangan atributnya saat menyimpan - demo di github.com/seltzered/CoreDataMagicalRecordTempObjectsDemo
Vivek Gani

Manakah yang ada mocdi cuplikan ketiga? Apakah itu childContextatau myMangedObjectContext?
bugloaf

Ini adalah childContext
railparade

solusi ini lebih baik daripada memiliki konteks nihil.
Akankah Y

Karena NSManagedObjectsudah menyediakan yang relevan NSManagedObjectContext, Anda dapat mengotomatiskan pilihan konteks: NSManagedObject* objectRelatedContextually = [objectWithRelationship.managedObjectContext objectWithID:objectRelated.objectID];dan kemudian objectWithRelationship.relationship = objectRelatedContextually;.
Gary

9

Cara yang benar untuk mencapai hal semacam ini adalah dengan konteks objek terkelola yang baru. Anda membuat konteks objek terkelola dengan penyimpanan persisten yang sama:

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

Kemudian Anda menambahkan objek baru, mengubahnya, dll.

Saat tiba waktunya untuk menyimpan, Anda perlu memanggil [tempContext save: ...] di tempContext, dan menangani notifikasi penyimpanan untuk menggabungkannya ke dalam konteks asli Anda. Untuk membuang objek, cukup lepaskan konteks sementara ini dan lupakan.

Jadi, saat Anda menyimpan konteks sementara, perubahan tersebut tetap ada di penyimpanan, dan Anda hanya perlu mengembalikan perubahan itu ke konteks utama Anda:

/* 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];

Ini juga merupakan cara Anda menangani operasi data inti multi-utas. Satu konteks per utas.

Jika Anda perlu mengakses objek yang ada dari konteks sementara ini (untuk menambahkan relasi, dll.), Anda perlu menggunakan ID objek untuk mendapatkan instance baru seperti ini:

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

Jika Anda mencoba menggunakan an NSManagedObjectdalam konteks yang salah, Anda akan mendapatkan pengecualian saat menyimpan.


Membuat konteks kedua hanya untuk ini sangat boros karena berdiri NSManagedObjectContextmahal di memori dan CPU. Saya menyadari ini awalnya di beberapa contoh Apple, tetapi mereka telah memperbarui dan memperbaiki contoh tersebut.
Marcus S. Zarra

2
Apple masih menggunakan teknik ini (membuat konteks objek terkelola kedua) untuk kode contoh CoreDataBooks.
raja nevan

1
Catatan Apple telah memperbarui CoreDataBooks, memang masih menggunakan dua konteks, tetapi sekarang konteks ke-2 adalah anak dari yang pertama. Teknik ini dibahas (dan direkomendasikan) dalam presentasi WWDC 2011 303 (apa yang baru di Core Data di iOS) dan disebutkan di sini (dengan banyak, JAUH, kode yang lebih sederhana untuk menggabungkan perubahan ke atas) stackoverflow.com/questions/9791469/…
Rhubarb

4
"Membuat konteks kedua hanya untuk ini sangat boros karena mendirikan NSManagedObjectContext mahal baik dalam memori dan CPU." . Tidak. Ketergantungan koordinator penyimpanan persisten (model objek terkelola dan penyimpanan beton) bukanlah konteksnya. Konteksnya ringan.
hentikan

3
@quellish Setuju. Apple telah menyatakan dalam pembicaraan kinerja data inti baru-baru ini di WWDC bahwa membuat konteks sangat ringan.
Jesse

9

Membuat objek sementara dari konteks nil berfungsi dengan baik sampai Anda benar-benar mencoba untuk memiliki hubungan dengan objek yang konteksnya! = Nil!

pastikan Anda setuju dengan itu.


Saya tidak setuju dengan itu
Charlie

8

Apa yang Anda gambarkan sebenarnya untuk apa NSManagedObjectContext.

Dari Panduan Pemrograman Data Inti: Dasar Data Inti

Anda dapat menganggap konteks objek terkelola sebagai papan gores yang cerdas. Saat Anda mengambil objek dari penyimpanan persisten, Anda membawa salinan sementara ke papan gores tempat objek tersebut membentuk grafik objek (atau kumpulan grafik objek). Anda kemudian dapat memodifikasi objek tersebut sesuka Anda. Kecuali Anda benar-benar menyimpan perubahan itu, penyimpanan persisten tetap tidak berubah.

Dan Panduan Pemrograman Data Inti: Validasi Objek Terkelola

Ini juga mendukung gagasan konteks objek terkelola yang merepresentasikan "papan awal" —secara umum, Anda dapat membawa objek terkelola ke papan gores dan mengeditnya sesuka Anda sebelum akhirnya melakukan perubahan atau membuangnya.

NSManagedObjectContexts dirancang agar ringan. Anda dapat membuat dan membuangnya sesuka hati - itu adalah koordinator penyimpanan yang gigih dan ketergantungannya yang "berat". Seorang koordinator toko persisten dapat memiliki banyak konteks yang terkait dengannya. Di bawah model kurungan utas yang lebih lama dan usang, ini berarti menyetel koordinator penyimpanan persisten yang sama pada setiap konteks. Hari ini berarti menghubungkan konteks bertingkat ke konteks akar yang terkait dengan koordinator penyimpanan persisten.

Buat konteks, buat dan modifikasi objek terkelola dalam konteks itu. Jika Anda ingin mempertahankannya dan mengomunikasikan perubahan itu, simpan konteksnya. Jika tidak, buang.

Mencoba membuat objek terkelola secara independen akan menimbulkan NSManagedObjectContextmasalah. Ingatlah bahwa Data Inti pada akhirnya adalah mekanisme pelacakan perubahan untuk grafik objek. Karena itu, objek yang dikelola benar-benar merupakan bagian dari konteks objek yang dikelola . Konteks mengamati siklus hidup mereka , dan tanpa konteks tidak semua fungsionalitas objek yang dikelola akan bekerja dengan benar.


6

Bergantung pada penggunaan Anda atas objek sementara, ada beberapa peringatan untuk rekomendasi di atas. Kasus penggunaan saya adalah saya ingin membuat objek sementara dan mengikatnya ke tampilan. Ketika pengguna memilih untuk menyimpan objek ini, saya ingin mengatur hubungan ke objek yang ada dan menyimpannya. Saya ingin melakukan ini untuk menghindari membuat objek sementara untuk menampung nilai-nilai itu. (Ya, saya bisa menunggu sampai pengguna menyimpan dan kemudian mengambil konten tampilan tetapi saya meletakkan tampilan ini di dalam tabel dan logika untuk melakukan ini kurang elegan.)

Opsi untuk objek sementara adalah:

1) (Lebih disukai) Buat objek sementara dalam konteks anak. Ini tidak akan berfungsi karena saya mengikat objek ke UI dan saya tidak dapat menjamin pengakses objek dipanggil pada konteks anak. (Saya tidak menemukan dokumentasi yang menyatakan sebaliknya, jadi saya harus berasumsi.)

2) Buat objek sementara dengan konteks objek nihil. Ini tidak berfungsi dan mengakibatkan kehilangan / kerusakan data.

Solusi Saya: Saya menyelesaikan ini dengan membuat objek sementara dengan konteks objek nihil tetapi ketika saya menyimpan objek, daripada memasukkannya sebagai # 2, saya menyalin semua atributnya ke dalam objek baru yang saya buat dalam konteks utama. Saya membuat metode pendukung dalam subkelas NSManagedObject saya yang disebut cloneInto: yang memungkinkan saya menyalin atribut dan hubungan dengan mudah untuk objek apa pun.


Itulah yang saya cari. Tapi keraguan saya adalah bagaimana Anda akan menangani atribut hubungan?
Mani

1

Bagi saya, jawaban Marcus tidak berhasil. Inilah yang berhasil untuk saya:

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

kemudian, jika saya memutuskan untuk menyimpannya:

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

Kita juga tidak boleh lupa untuk melepaskannya

[unassociatedObject release]

1

Saya menulis ulang jawaban ini untuk Swift karena semua pertanyaan serupa untuk segera dialihkan ke pertanyaan ini.

Anda dapat mendeklarasikan objek tanpa ManagedContext menggunakan kode berikut.

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

Nanti, untuk menyimpan objek, Anda dapat memasukkannya ke dalam konteks dan menyimpannya.

myContext.insert(unassociatedObject)
// Saving the object
do {
    try self.stack.saveContext()
    } catch {
        print("save unsuccessful")
    }
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.