webentwicklung-frage-antwort-db.com.de

Beispiel oder Erläuterung der Kerndatenmigration mit mehreren Durchläufen?

Meine iPhone-App muss ihren Kerndatenspeicher migrieren, und einige der Datenbanken sind ziemlich groß. In der Dokumentation von Apple wird die Verwendung von "Mehrfachdurchläufen" zum Migrieren von Daten empfohlen, um die Speichernutzung zu verringern. Die Dokumentation ist jedoch sehr begrenzt und erklärt nicht sehr gut, wie dies tatsächlich durchgeführt wird. Kann mich jemand auf ein gutes Beispiel hinweisen oder im Detail erklären, wie man dies tatsächlich umsetzt?

84
Jason

Ich habe herausgefunden, was Apple Hinweise in ihrer Dokumentation . Es ist eigentlich sehr einfach, aber ein langer Weg, bevor es offensichtlich ist. Ich werde die Erklärung mit einem veranschaulichen Beispiel: Die Ausgangssituation ist folgende:

Datenmodell Version 1

enter image description hereenter image description here

Es ist das Modell, das Sie erhalten, wenn Sie ein Projekt mit der Vorlage "Navigationsbasierte App mit Kerndatenspeicher" erstellen. Ich habe es kompiliert und mit Hilfe einer for-Schleife einige harte Schläge ausgeführt, um ungefähr 2k Einträge mit unterschiedlichen Werten zu erstellen. Dort gehen wir 2.000 Ereignisse mit einem NSDate-Wert.

Nun fügen wir eine zweite Version des Datenmodells hinzu, die so aussieht:

enter image description here

Datenmodell Version 2

Der Unterschied ist: Die Event-Entität ist verschwunden, und wir haben zwei neue. Einer, der einen Zeitstempel als double speichert, und der zweite, der ein Datum als NSString speichern sollte.

Ziel ist es, alle Ereignisse der Version 1 auf die beiden neuen Entitäten zu übertragen und die Werte während der Migration zu konvertieren. Dies führt dazu, dass die Werte doppelt so groß sind wie die Werte eines anderen Typs in einer separaten Entität.

Für die Migration wählen wir die manuelle Migration und dies tun wir mit Mapping-Modellen. Dies ist auch der erste Teil der Antwort auf Ihre Frage. Wir werden die Migration in zwei Schritten durchführen, da die Migration von 2k-Einträgen viel Zeit in Anspruch nimmt und wir den Speicherbedarf gerne gering halten.

Sie können diese Zuordnungsmodelle sogar weiter aufteilen, um nur Bereiche der Entitäten zu migrieren. Angenommen, wir haben eine Million Datensätze. Dies kann den gesamten Prozess zum Absturz bringen. Es ist möglich, die abgerufenen Objekte mit einem Filter-Prädikat einzugrenzen.

Zurück zu unseren beiden Mapping-Modellen.

Wir erstellen das erste Mapping-Modell wie folgt:

1. Neue Datei -> Ressource -> Zuordnungsmodell enter image description here

2. Wähle einen Namen, ich habe StepOne gewählt

3. Quell- und Zieldatenmodell setzen

enter image description here

Mapping-Modell Schritt Eins

enter image description here

enter image description here

enter image description here

Für die Migration mit mehreren Durchläufen sind keine benutzerdefinierten Entitätsmigrationsrichtlinien erforderlich. Wir werden dies jedoch tun, um in diesem Beispiel ein bisschen mehr Details zu erhalten. Daher fügen wir der Entität eine benutzerdefinierte Richtlinie hinzu. Dies ist immer eine Unterklasse von NSEntityMigrationPolicy .

enter image description here

Diese Richtlinienklasse implementiert einige Methoden, um die Migration durchzuführen. In diesem Fall ist es jedoch einfach, sodass wir nur eine Methode implementieren müssen: createDestinationInstancesForSourceInstance:entityMapping:manager:error: .

Die Implementierung sieht folgendermaßen aus:

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;
}

Letzter Schritt: die Migration selbst

Ich überspringe den Teil zum Einrichten des zweiten Mapping-Modells, das fast identisch ist, nur ein timeIntervalSince1970, mit dem das NSDate in ein double konvertiert wurde.

Schließlich müssen wir die Migration auslösen. Ich werde den Kesselschild-Code für den Moment überspringen. Wenn Sie es brauchen, werde ich hier posten. Sie finden es unter Anpassen des Migrationsprozesses Dies ist nur eine Zusammenführung der ersten beiden Codebeispiele. Der dritte und letzte Teil werden wie folgt geändert: Anstatt die Klassenmethode der NSMappingModel Klasse mappingModelFromBundles:forSourceModel:destinationModel: wir werden das initWithContentsOfURL: , da die Klassenmethode nur ein, möglicherweise das erste gefundene Zuordnungsmodell im Bundle zurückgibt.

Jetzt haben wir die beiden Zuordnungsmodelle, die in jedem Durchgang der Schleife verwendet werden können, und senden die Migrationsmethode an den Migrationsmanager. Das ist es.

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];
} 

Notizen

  • Ein Mapping-Modell endet im Bundle mit cdm.

  • Der Zielspeicher muss bereitgestellt werden und sollte nicht der Quellspeicher sein. Sie können nach erfolgreicher Migration die alte löschen und die neue umbenennen.

  • Ich habe nach der Erstellung der Mapping-Modelle einige Änderungen am Datenmodell vorgenommen. Dies führte zu einigen Kompatibilitätsfehlern, die ich nur durch die Neuerstellung der Mapping-Modelle beheben konnte.

171
Nick Weaver

Diese Fragen beziehen sich auf:

Speicherprobleme beim Migrieren großer CoreData-Datenspeicher auf dem iPhone

Kerndatenmigration mit mehreren Durchläufen in Stücken mit iOS

So zitieren Sie den ersten Link:

Dies wird in der offiziellen Dokumentation im Abschnitt "Mehrere Durchläufe" erörtert. Es sieht jedoch so aus, als ob der vorgeschlagene Ansatz darin besteht, die Migration nach Entitätstyp zu unterteilen, dh mehrere Zuordnungsmodelle zu erstellen, von denen jedes eine Teilmenge der Entitätstypen aus dem migriert vollständiges Datenmodell.

3
occulus