Pertanyaan : Bagaimana cara mendapatkan konteks anak saya untuk melihat perubahan bertahan pada konteks induk sehingga memicu NSFetchedResultsController saya untuk memperbarui UI?
Berikut pengaturannya:
Anda memiliki aplikasi yang mengunduh dan menambahkan banyak data XML (sekitar 2 juta catatan, masing-masing kira-kira seukuran paragraf teks normal). File .sqlite berukuran sekitar 500 MB. Menambahkan konten ini ke Data Inti membutuhkan waktu, tetapi Anda ingin pengguna dapat menggunakan aplikasi saat data dimuat ke penyimpanan data secara bertahap. Harus tidak terlihat dan tidak terlihat oleh pengguna bahwa sejumlah besar data sedang dipindahkan, jadi tidak ada hang, tidak ada kegugupan: menggulir seperti mentega. Namun, aplikasi ini lebih berguna, semakin banyak data ditambahkan ke dalamnya, jadi kami tidak bisa menunggu selamanya untuk data ditambahkan ke penyimpanan Data Inti. Dalam kode, ini berarti saya sangat ingin menghindari kode seperti ini di kode impor:
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]];
Aplikasinya hanya untuk iOS 5, jadi perangkat paling lambat yang perlu didukung adalah iPhone 3GS.
Berikut adalah sumber daya yang telah saya gunakan sejauh ini untuk mengembangkan solusi saya saat ini:
Panduan Pemrograman Data Inti Apple: Mengimpor Data Secara Efisien
- Gunakan Kumpulan Autorelease untuk menjaga memori tetap rendah
- Biaya Hubungan. Impor rata, lalu perbaiki hubungan di bagian akhir
- Jangan bertanya jika Anda dapat membantunya, ini memperlambat segalanya dengan cara O (n ^ 2)
- Impor dalam Batch: simpan, setel ulang, tiriskan, dan ulangi
- Matikan Undo Manager saat mengimpor
iDeveloper TV - Performa Data Inti
- Gunakan 3 Konteks: Tipe konteks Master, Main dan Confinement
iDeveloper TV - Core Data untuk Pembaruan Mac, iPhone & iPad
- Menjalankan menghemat antrean lain dengan performBlock membuat segalanya menjadi cepat.
- Enkripsi memperlambat segalanya, matikan jika Anda bisa.
Mengimpor dan Menampilkan Kumpulan Data Besar dalam Data Inti oleh Marcus Zarra
- Anda dapat memperlambat pengimporan dengan memberikan waktu untuk putaran proses saat ini, sehingga semuanya terasa lancar bagi pengguna.
- Kode Sampel membuktikan bahwa dimungkinkan untuk melakukan impor besar dan menjaga UI tetap responsif, tetapi tidak secepat dengan 3 konteks dan penyimpanan asinkron ke disk.
Solusi Saya Saat Ini
Saya punya 3 contoh NSManagedObjectContext:
masterManagedObjectContext - Ini adalah konteks yang memiliki NSPersistentStoreCoordinator dan bertanggung jawab untuk menyimpan ke disk. Saya melakukan ini agar penyimpanan saya tidak sinkron dan karenanya sangat cepat. Saya membuatnya saat peluncuran seperti ini:
masterManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[masterManagedObjectContext setPersistentStoreCoordinator:coordinator];
mainManagedObjectContext - Ini adalah konteks yang digunakan UI di mana-mana. Ini adalah turunan dari masterManagedObjectContext. Saya membuatnya seperti ini:
mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[mainManagedObjectContext setUndoManager:nil];
[mainManagedObjectContext setParentContext:masterManagedObjectContext];
backgroundContext - Konteks ini dibuat di subclass NSOperation saya yang bertanggung jawab untuk mengimpor data XML ke Core Data. Saya membuatnya dalam metode utama operasi dan menautkannya ke konteks utama di sana.
backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
[backgroundContext setUndoManager:nil];
[backgroundContext setParentContext:masterManagedObjectContext];
Ini sebenarnya bekerja sangat, SANGAT cepat. Hanya dengan melakukan pengaturan 3 konteks ini, saya dapat meningkatkan kecepatan impor saya lebih dari 10x! Sejujurnya, ini sulit dipercaya. (Desain dasar ini harus menjadi bagian dari templat Data Inti standar ...)
Selama proses impor saya menyimpan 2 cara berbeda. Setiap 1000 item yang saya simpan pada konteks latar belakang:
BOOL saveSuccess = [backgroundContext save:&error];
Kemudian di akhir proses impor, saya menyimpan konteks master / induk yang, seolah-olah, mendorong modifikasi ke konteks anak lainnya termasuk konteks utama:
[masterManagedObjectContext performBlock:^{
NSError *parentContextError = nil;
BOOL parentContextSaveSuccess = [masterManagedObjectContext save:&parentContextError];
}];
Masalah : Masalahnya adalah UI saya tidak akan diperbarui hingga saya memuat ulang tampilan.
Saya memiliki UIViewController sederhana dengan UITableView yang sedang diberi makan data menggunakan NSFetchedResultsController. Ketika proses Impor selesai, NSFetchedResultsController tidak melihat perubahan dari konteks induk / master sehingga UI tidak otomatis diperbarui seperti yang biasa saya lihat. Jika saya mengeluarkan UIViewController dari tumpukan dan memuatnya lagi semua data ada di sana.
Pertanyaan : Bagaimana cara mendapatkan konteks anak saya untuk melihat perubahan bertahan pada konteks induk sehingga memicu NSFetchedResultsController saya untuk memperbarui UI?
Saya telah mencoba yang berikut ini yang hanya membuat aplikasi hang:
- (void)saveMasterContext {
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self selector:@selector(contextChanged:) name:NSManagedObjectContextDidSaveNotification object:masterManagedObjectContext];
NSError *error = nil;
BOOL saveSuccess = [masterManagedObjectContext save:&error];
[notificationCenter removeObserver:self name:NSManagedObjectContextDidSaveNotification object:masterManagedObjectContext];
}
- (void)contextChanged:(NSNotification*)notification
{
if ([notification object] == mainManagedObjectContext) return;
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:@selector(contextChanged:) withObject:notification waitUntilDone:YES];
return;
}
[mainManagedObjectContext mergeChangesFromContextDidSaveNotification:notification];
}