Birden fazla geçişle Temel Veri Taşıma örneği veya açıklaması?


85

İPhone uygulamamın temel veri deposunu taşıması gerekiyor ve bazı veritabanları oldukça büyük. Apple'ın belgeleri, bellek kullanımını azaltmak için verileri taşımak için "birden çok geçiş" kullanılmasını önerir. Ancak, dokümantasyon çok sınırlıdır ve bunun nasıl yapılacağını çok iyi açıklamaz. Biri beni ya iyi bir örneğe yönlendirebilir ya da bunu nasıl başaracağına dair süreci ayrıntılı olarak açıklayabilir mi?


aslında hafıza problemleri yaşadınız mı? Geçişiniz hafif mi veya NSMigrationManager kullanmak mı istiyorsunuz?
Nick Weaver

Evet, GDB konsolu bellek uyarıları olduğunu gösterdi ve ardından sınırlı bellek nedeniyle uygulama çöktü. Hem hafif geçişi hem de NSMigrationManager'ı denedim, ancak şu anda NSMigrationManager'ı kullanmaya çalışıyorum.
Jason

tamam, neyin değiştiğini biraz daha detaylandırabilir misin?
Nick Weaver

nihayet buldum, cevabımı oku.
Nick Weaver

Merhaba Jason, sorudaki beğeniyi düzeltir misin?
Yuchen Zhong

Yanıtlar:


175

Apple'ın belgelerinde neyi ima ettiğini buldum . Aslında çok kolay ama bariz olmadan önce gidilmesi gereken uzun bir yol. Açıklamayı bir örnekle açıklayacağım. İlk durum şudur:

Veri Modeli Sürümü 1

görüntü açıklamasını buraya girin görüntü açıklamasını buraya girin

"Çekirdek veri depolamaya sahip navigasyon tabanlı uygulama" şablonuyla bir proje oluşturduğunuzda elde ettiğiniz modeldir. Bunu derledim ve hepsi farklı değerlere sahip yaklaşık 2k giriş oluşturmak için bir for döngüsü yardımıyla bazı sert vuruşlar yaptım. NSDate değerine sahip 2.000 etkinliğe gidiyoruz.

Şimdi veri modelinin şuna benzeyen ikinci bir sürümünü ekliyoruz:

görüntü açıklamasını buraya girin

Veri Modeli Versiyon 2

Aradaki fark şudur: Event varlığı gitti ve bizim iki yenimiz var. Bir zaman damgasını a olarak saklayan doubleve ikincisi bir tarihi olarak depolaması gereken NSString.

Amaç, tüm Sürüm 1 Etkinliklerini iki yeni varlığa aktarmak ve geçiş sırasında değerleri dönüştürmektir. Bu, ayrı bir varlıkta her biri farklı türde iki kat değerle sonuçlanır.

Geçiş yapmak için elle geçişi seçiyoruz ve bunu haritalama modelleri ile yapıyoruz. Bu aynı zamanda sorunuzun cevabının ilk kısmıdır. Taşıma işlemini iki adımda yapacağız, çünkü 2 bin girişi taşımak uzun sürüyor ve bellek ayak izini düşük tutmak istiyoruz.

Hatta devam edip bu eşleme modellerini yalnızca varlık aralıklarını taşımak için daha da bölebilirsiniz. Bir milyon kaydımız olduğunu varsayalım, bu tüm süreci çökertebilir. Getirilen varlıkları bir Filtre koşulu ile daraltmak mümkündür .

İki haritalama modelimize geri dönün.

İlk haritalama modelini şu şekilde oluşturuyoruz:

1. Yeni Dosya -> Kaynak -> Eşleme Modeli görüntü açıklamasını buraya girin

2. Bir ad seçin, StepOne'ı seçtim

3. Kaynak ve hedef veri modelini ayarlayın

görüntü açıklamasını buraya girin

Eşleme Modeli Adım Bir

görüntü açıklamasını buraya girin

görüntü açıklamasını buraya girin

görüntü açıklamasını buraya girin

Çoklu geçiş geçişi özel varlık taşıma politikalarına ihtiyaç duymaz, ancak bu örnek için biraz daha fazla ayrıntı elde etmek için yapacağız. Bu yüzden varlığa özel bir politika ekliyoruz. Bu her zaman bir alt sınıfıdır NSEntityMigrationPolicy.

görüntü açıklamasını buraya girin

Bu politika sınıfı, geçişimizi gerçekleştirmek için bazı yöntemler uygular. Sadece bir yöntemi uygulamak zorunda kalacak, böylece Ancak bu durumda basit: createDestinationInstancesForSourceInstance:entityMapping:manager:error:.

Uygulama şöyle görünecek:

StepOneEntityMigrationPolicy.m

#import "StepOneEntityMigrationPolicy.h"


@implementation StepOneEntityMigrationPolicy

- (BOOL)createDestinationInstancesForSourceInstance:(NSManagedObject *)sInstance 
                                      entityMapping:(NSEntityMapping *)mapping 
                                            manager:(NSMigrationManager *)manager 
                                              error:(NSError **)error
{
    // Create a new object for the model context
    NSManagedObject *newObject = 
        [NSEntityDescription insertNewObjectForEntityForName:[mapping destinationEntityName] 
                                      inManagedObjectContext:[manager destinationContext]];

    // do our transfer of nsdate to nsstring
    NSDate *date = [sInstance valueForKey:@"timeStamp"];
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setTimeStyle:NSDateFormatterMediumStyle];
    [dateFormatter setDateStyle:NSDateFormatterMediumStyle];    

    // set the value for our new object
    [newObject setValue:[dateFormatter stringFromDate:date] forKey:@"printedDate"];
    [dateFormatter release];

    // do the coupling of old and new
    [manager associateSourceInstance:sInstance withDestinationInstance:newObject forEntityMapping:mapping];

    return YES;
}

Son adım: geçişin kendisi

Neredeyse aynı olan ikinci haritalama modelini kurma kısmını atlayacağım, sadece bir timeIntervalSince1970 NSDate'i bir double'a dönüştürmek için kullanılır.

Son olarak göçü tetiklememiz gerekiyor. Şimdilik standart kodu atlayacağım. İhtiyacın olursa, buraya göndereceğim. Geçiş İşlemini Özelleştirme bölümünde bulunabilir, bu sadece ilk iki kod örneğinin bir birleşimidir. Aşağıdaki gibi üçüncü ve son bölümü değiştirilmiş olacaktır: Yerine sınıf yöntemi kullanarak NSMappingModelsınıfın mappingModelFromBundles:forSourceModel:destinationModel:biz kullanacağız initWithContentsOfURL:sınıf yöntemi yalnızca bir tane paketteki belki ilk, bulunan haritalama modeli dönecektir çünkü.

Şimdi döngünün her geçişinde kullanılabilen ve taşıma yöntemini taşıma yöneticisine gönderen iki eşleme modelimiz var. Bu kadar.

NSArray *mappingModelNames = [NSArray arrayWithObjects:@"StepOne", @"StepTwo", nil];
NSDictionary *sourceStoreOptions = nil;

NSURL *destinationStoreURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"CoreDataMigrationNew.sqlite"];

NSString *destinationStoreType = NSSQLiteStoreType;

NSDictionary *destinationStoreOptions = nil;

for (NSString *mappingModelName in mappingModelNames) {
    NSURL *fileURL = [[NSBundle mainBundle] URLForResource:mappingModelName withExtension:@"cdm"];

    NSMappingModel *mappingModel = [[NSMappingModel alloc] initWithContentsOfURL:fileURL];

    BOOL ok = [migrationManager migrateStoreFromURL:sourceStoreURL
                                               type:sourceStoreType
                                            options:sourceStoreOptions
                                   withMappingModel:mappingModel
                                   toDestinationURL:destinationStoreURL
                                    destinationType:destinationStoreType
                                 destinationOptions:destinationStoreOptions
                                              error:&error2];
    [mappingModel release];
} 

Notlar

  • Bir eşleme modeli cdmpakette biter .

  • Hedef deponun sağlanması gerekir ve kaynak depo olmamalıdır. Başarılı bir geçişten sonra eskisini silebilir ve yenisini yeniden adlandırabilirsiniz.

  • Haritalama modellerini oluşturduktan sonra veri modelinde bazı değişiklikler yaptım, bu bazı uyumluluk hatalarıyla sonuçlandı ve bunları yalnızca eşleme modellerini yeniden oluşturarak çözebildim.


59
Karmaşık olan lanet olası cehennem. Apple ne düşünüyordu?
00

7
Bilmiyorum, ancak ne zaman temel verilerin iyi bir fikir olduğunu düşünürsem, daha basit ve daha sürdürülebilir bir çözüm bulmaya çalışıyorum.
Nick Weaver

5
Teşekkürler! Bu harika bir cevap. Karmaşık görünüyor, ancak adımları öğrendikten sonra o kadar da kötü değil. En büyük sorun, dokümantasyonun bunu sizin için heceleyememesi.
bentford

2
Geçiş Sürecini Özelleştirme için güncellenmiş bağlantı burada. Bu yazı yazıldığından beri taşındı. developer.apple.com/library/ios/documentation/Cocoa/Conceptual/…
user1021430

@NickWeaver, destinationStoreURL'yi nasıl belirliyorsunuz? Onu siz mi oluşturuyorsunuz yoksa taşıma işlemi sırasında çekirdek veri sistemi tarafından oluşturuluyor ????
dev gr

3

Bu sorular birbiriyle ilgilidir:

İPhone'da büyük CoreData veri depolarını taşırken bellek sorunları

İOS ile Parçalarda Çoklu Geçiş Çekirdek Veri Taşıma

İlk bağlantıdan alıntı yapmak için:

Bu, "Çoklu Geçişler" bölümündeki resmi belgelerde tartışılmıştır, ancak önerdikleri yaklaşım, geçişinizi varlık türüne göre bölmek, yani birden fazla eşleme modeli yapmak gibi görünmektedir; bunların her biri, varlık türlerinin bir alt kümesini öğesinden eksiksiz veri modeli.


1
Bağlantılar için teşekkürler. Sorun şu ki, kimse bunu birden çok geçişte nasıl kuracağını ayrıntılı olarak açıklamıyor . Etkili çalışması için birden çok eşleme modelini nasıl kurmalıyım?
Jason

-5

Veritabanı şemanızda, öğrenci alt sınıfları kişisinin, sınıfın kursu uyguladığı ve kayıtların sınıfa ve öğrenciye katıldığı standart türde bir örnek kullanmak için kişi, öğrenci, kurs, sınıf ve kayıt gibi 5 varlık olduğunu varsayalım. Tüm bu tablo tanımlarında değişiklikler yaptıysanız, temel sınıflardan başlamanız ve yukarı çıkmanız gerekir. Dolayısıyla, kayıtları dönüştürmeye başlayamazsınız çünkü her kayıt kaydı, sınıf ve öğrencilerin orada bulunmasına bağlıdır. Bu nedenle, yalnızca Kişi tablosunu taşımakla, mevcut satırları yeni tabloya kopyalamakla ve varsa yeni alanları doldurarak (mümkünse) ve kaldırılan sütunları atarak başlarsınız. Her geçişi bir otomatik yayın havuzunun içinde yapın, böylece tamamlandığında belleğiniz yeniden başlar.

Kişi tablosu tamamlandıktan sonra öğrenci tablosunu değiştirebilirsiniz. Ardından Kursa, ardından Sınıfa ve son olarak Kayıt tablosuna gidin.

Göz önünde bulundurulması gereken diğer husus kayıtların sayısıdır, eğer Person gibi bin satır varsa, her 100 veya daha fazla bir sürümün NSManagedObject eşdeğerini çalıştırmanız gerekir, bu da yönetilen nesne bağlamını [moc renewObject: ob mergeChanges: HAYIR]; Ayrıca, eski veri zamanlayıcınızı çok düşük ayarlayın, böylece bellek sık sık temizlenir.


Öyleyse, esasen eski şemanın parçası olmayan yeni bir çekirdek veri şemasına sahip olmayı ve verileri yeni şemaya elle kopyalamayı mı öneriyorsunuz?
Jason

-1 Veritabanınızı manuel olarak eşlemek gerekli değildir. Hafif geçiş kullanarak veya açık MappingModels kullanarak konuşlandırılmış veritabanlarını geçirebilirsiniz.
bentford
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.