Soru : Alt bağlamımın, NSFetchedResultsController'ımı kullanıcı arabirimini güncelleştirmesi için tetiklemesi için ana bağlamda kalıcı olan değişiklikleri görmesini nasıl sağlayabilirim?
Kurulum şu şekildedir:
Çok sayıda XML verisi indiren ve ekleyen bir uygulamanız var (yaklaşık 2 milyon kayıt, her biri kabaca normal bir metin paragrafı boyutunda) .sqlite dosyasının boyutu yaklaşık 500 MB olur. Bu içeriğin Çekirdek Verilere eklenmesi zaman alır, ancak verilerin veri deposuna artımlı olarak yüklenirken kullanıcının uygulamayı kullanabilmesini istersiniz. Görünmez olmalı ve kullanıcı için büyük miktarda verinin hareket ettiği, yani takılmalar, titremeler yok: Tereyağı gibi kaydırmalar. Yine de, uygulama daha kullanışlıdır, ona daha fazla veri eklenir, bu nedenle verilerin Core Data deposuna eklenmesini sonsuza kadar bekleyemeyiz. Kodda bu, içe aktarma kodunda böyle bir koddan kaçınmak istediğim anlamına gelir:
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]];
Uygulama yalnızca iOS 5 olduğundan desteklemesi gereken en yavaş cihaz iPhone 3GS'dir.
Şu ana kadar mevcut çözümümü geliştirmek için kullandığım kaynaklar şunlardır:
Apple'ın Temel Veri Programlama Kılavuzu: Verileri Verimli Bir Şekilde İçe Aktarma
- Belleği düşük tutmak için Otomatik Yayın Havuzlarını kullanın
- İlişkiler Maliyeti. Düz olarak içe aktarın, ardından sonunda ilişkileri düzeltin
- Yardım edebilirseniz sorgulama, işleri O (n ^ 2) bir şekilde yavaşlatır
- Toplu İçe Aktarma: kaydedin, sıfırlayın, boşaltın ve tekrarlayın
- İçe aktarma sırasında Geri Alma Yöneticisini kapatın
iDeveloper TV - Temel Veri Performansı
- 3 Bağlam kullanın: Ana, Ana ve Sınırlama bağlam türleri
iDeveloper TV - Mac, iPhone ve iPad Güncellemesi için Temel Veri
- PerformBlock ile çalıştırmak diğer kuyruklarda tasarruf sağlar, işleri hızlandırır.
- Şifreleme işleri yavaşlatır, yapabiliyorsanız kapatın.
Marcus Zarra tarafından Çekirdek Verilerdeki Büyük Veri Kümelerini İçe Aktarma ve Görüntüleme
- Geçerli çalışma döngüsüne zaman tanıyarak içe aktarmayı yavaşlatabilirsiniz, böylece kullanıcı için işler sorunsuz hale gelir.
- Örnek Kod, büyük içeri aktarmalar yapmanın ve UI'yi duyarlı tutmanın mümkün olduğunu, ancak 3 bağlam ve diske eşzamansız kaydetme kadar hızlı olmadığını kanıtlıyor.
Mevcut Çözümüm
NSManagedObjectContext'in 3 örneği var:
masterManagedObjectContext - Bu, NSPersistentStoreCoordinator'a sahip olan ve diske kaydetmekten sorumlu olan bağlamdır. Bunu, kaydettiklerimin eşzamansız ve bu nedenle çok hızlı olması için yapıyorum. Bunu başlangıçta şöyle oluşturuyorum:
masterManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[masterManagedObjectContext setPersistentStoreCoordinator:coordinator];
mainManagedObjectContext - Bu, kullanıcı arayüzünün her yerde kullandığı bağlamdır. MasterManagedObjectContext öğesinin bir alt öğesidir. Bunu şöyle yaratıyorum:
mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[mainManagedObjectContext setUndoManager:nil];
[mainManagedObjectContext setParentContext:masterManagedObjectContext];
backgroundContext - Bu bağlam, XML verilerini Core Data'ya aktarmaktan sorumlu olan NSOperation alt sınıfımda oluşturulur. Bunu işlemin ana yönteminde oluşturuyorum ve oradaki ana bağlamla ilişkilendiriyorum.
backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
[backgroundContext setUndoManager:nil];
[backgroundContext setParentContext:masterManagedObjectContext];
Bu aslında çok çok hızlı çalışıyor. Sadece bu 3 bağlam kurulumunu yaparak içe aktarma hızımı 10 kattan fazla artırabildim! Dürüst olmak gerekirse, buna inanmak zor. (Bu temel tasarım, standart Temel Veri şablonunun bir parçası olmalıdır ...)
İçe aktarma işlemi sırasında 2 farklı şekilde kaydediyorum. Arka planda kaydettiğim her 1000 öğe:
BOOL saveSuccess = [backgroundContext save:&error];
Daha sonra, içe aktarma işleminin sonunda, görünüşte, değişiklikleri ana bağlam dahil olmak üzere diğer alt bağlamlara iten ana / üst bağlamı kaydederim:
[masterManagedObjectContext performBlock:^{
NSError *parentContextError = nil;
BOOL parentContextSaveSuccess = [masterManagedObjectContext save:&parentContextError];
}];
Sorun : Sorun , görünümü yeniden yükleyene kadar kullanıcı arayüzümün güncellenmemesidir.
NSFetchedResultsController kullanarak veri beslenen bir UITableView ile basit bir UIViewController var. İçe aktarma işlemi tamamlandığında, NSFetchedResultsController üst / ana bağlamda hiçbir değişiklik görmez ve bu nedenle kullanıcı arabirimi, benim görmeye alıştığım gibi otomatik olarak güncellenmez. UIViewController'ı yığından çıkarırsam ve tekrar yüklersem tüm veriler orada olur.
Soru : Alt bağlamımın, NSFetchedResultsController'ımı kullanıcı arabirimini güncelleştirmesi için tetiklemesi için ana bağlamda kalıcı olan değişiklikleri görmesini nasıl sağlayabilirim?
Uygulamayı kapatan aşağıdakileri denedim:
- (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];
}