Moja aplikacja na iPhone'a wymaga migracji podstawowego magazynu danych, a niektóre bazy danych są dość duże. Dokumentacja Apple sugeruje użycie „wielu przebiegów” do migracji danych w celu zmniejszenia zużycia pamięci. Jednak dokumentacja jest bardzo ograniczona i nie wyjaśnia zbyt dobrze, jak to zrobić. Czy ktoś może wskazać mi dobry przykład lub szczegółowo wyjaśnić proces, w jaki sposób to osiągnąć?
85
Odpowiedzi:
Dowiedziałem się, co podpowiada Apple w ich dokumentacji . W rzeczywistości jest to bardzo łatwe, ale długa droga, zanim stanie się to oczywiste. Zilustruję wyjaśnienie na przykładzie. Sytuacja początkowa jest następująca:
Wersja modelu danych 1
Jest to model, który otrzymujesz podczas tworzenia projektu za pomocą szablonu „aplikacja oparta na nawigacji z podstawowym magazynem danych”. Skompilowałem go i zrobiłem trochę mocnego uderzenia z pomocą pętli for, aby utworzyć około 2k wpisów, wszystkie z różnymi wartościami. Idziemy do 2.000 zdarzeń z wartością NSDate.
Teraz dodajemy drugą wersję modelu danych, która wygląda następująco:
Wersja modelu danych 2
Różnica polega na tym, że jednostka Event zniknęła, a mamy dwie nowe. Jeden, który przechowuje znacznik czasu jako a,
double
a drugi, który powinien przechowywać datę jakoNSString
.Celem jest przeniesienie wszystkich zdarzeń wersji 1 do dwóch nowych jednostek i konwersja wartości podczas migracji. Powoduje to dwukrotność wartości, z których każda jest innego typu w oddzielnej jednostce.
Aby przeprowadzić migrację, wybieramy migrację ręcznie i robimy to z mapowaniem modeli. To także pierwsza część odpowiedzi na Twoje pytanie. Migrację wykonamy w dwóch krokach, ponieważ migracja 2k wpisów zajmuje dużo czasu, a my chcemy, aby zużycie pamięci było niskie.
Możesz nawet dalej podzielić te modele mapowania, aby migrować tylko zakresy jednostek. Powiedzmy, że mamy milion rekordów, to może zawiesić cały proces. Możliwe jest zawężenie pobieranych jednostek za pomocą predykatu filtru .
Wróćmy do naszych dwóch modeli mapowania.
Pierwszy model mapowania tworzymy w ten sposób:
1. Nowy plik -> Zasób -> Model mapowania
2. Wybierz nazwę, wybrałem StepOne
3. Ustaw źródłowy i docelowy model danych
Mapowanie modelu - krok pierwszy
Migracja wieloprzebiegowa nie wymaga niestandardowych zasad migracji encji, jednak zrobimy to, aby uzyskać więcej szczegółów w tym przykładzie. Więc dodajemy niestandardowe zasady do encji. To jest zawsze podklasa
NSEntityMigrationPolicy
.Ta klasa strategii implementuje pewne metody, aby dokonać migracji. Jednak to proste w tym przypadku tak będziemy musieli realizować tylko jedną metodę:
createDestinationInstancesForSourceInstance:entityMapping:manager:error:
.Implementacja będzie wyglądać następująco:
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; }
Ostatni krok: sama migracja
Pominię część dotyczącą konfigurowania drugiego modelu mapowania, który jest prawie identyczny, tylko timeIntervalSince1970 użyty do konwersji NSDate na podwójną.
Wreszcie musimy uruchomić migrację. Na razie pominę kod standardowy. Jeśli potrzebujesz, napiszę tutaj. Można go znaleźć na stronie Dostosowywanie procesu migracji, to tylko połączenie dwóch pierwszych przykładów kodu. Trzecia i ostatnia część zostanie zmodyfikowana w następujący sposób: Zamiast używać metody
NSMappingModel
klasymappingModelFromBundles:forSourceModel:destinationModel:
, użyjemy metody,initWithContentsOfURL:
ponieważ metoda klasowa zwróci tylko jeden, być może pierwszy, znaleziony model mapowania w pakiecie.Teraz mamy dwa modele mapowania, których można użyć w każdym przejściu pętli i wysłać metodę migracji do menedżera migracji. Otóż to.
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]; }
Uwagi
Model mapowania kończy się w
cdm
pakiecie.Należy podać magazyn docelowy i nie powinien to być magazyn źródłowy. Po udanej migracji możesz usunąć starą i zmienić nazwę nowej.
Dokonałem pewnych zmian w modelu danych po utworzeniu modeli mapujących, co spowodowało pewne błędy kompatybilności, które mogłem rozwiązać tylko odtwarzając modele mapowania.
źródło
Te pytania są powiązane:
Problemy z pamięcią podczas migracji dużych baz danych CoreData na iPhone'a
Wieloprzebiegowa migracja danych podstawowych w fragmentach z iOS
Aby zacytować pierwszy link:
źródło
Załóżmy, że Twój schemat bazy danych zawiera 5 encji, np. Osoba, student, kurs, klasa i rejestracja, aby użyć standardowego przykładu, w którym uczeń tworzy podklasy osoba, klasa implementuje kurs, a rejestracja dołącza do klasy i studenta. Jeśli dokonałeś zmian we wszystkich tych definicjach tabel, musisz zacząć od klas podstawowych i przechodzić w górę. Nie możesz więc zacząć od konwersji rejestracji, ponieważ każdy rekord rejestracji zależy od posiadania tam klasy i uczniów. Tak więc należy rozpocząć od migracji tylko tabeli Person, skopiowania istniejących wierszy do nowej tabeli i wypełnienia wszelkich nowych pól (jeśli to możliwe) i odrzucenia usuniętych kolumn. Wykonuj każdą migrację w ramach puli automatycznego uwalniania, aby po jej zakończeniu pamięć wróciła do pracy.
Po utworzeniu tabeli Person możesz przekonwertować tabelę uczniów. Następnie przejdź do Course, potem Class, a na końcu do tabeli rejestracyjnej.
Inną kwestią jest liczba rekordów, jeśli tak jak osoba ma tysiąc wierszy, musiałbyś co 100 lub więcej wykonywać odpowiednik NSManagedObject wydania, który ma na celu przekazanie kontekstu zarządzanego obiektu [moc refreshObject: ob mergeChanges: NIE]; Ustaw również czasomierz nieaktualnych danych na niski poziom, aby pamięć była często opróżniana.
źródło