85 votes

Exemple ou explication de la migration des données de base avec plusieurs passes?

Mon iPhone app a besoin de migrer sa base de données à stocker, et certains de ces bases de données sont assez grandes. La documentation d'Apple suggère d'utiliser "multiples entrées" migration des données afin de réduire l'utilisation de la mémoire. Toutefois, la documentation est très limitée et n'explique pas très bien comment le faire. Peut quelqu'un me diriger vers un bon exemple, ou d'expliquer en détail le processus de la façon dont ce pull off?

176voto

Nick Weaver Points 30418

J'ai compris ce que Apple astuces dans leur documentation. C'est en fait très facile, mais un long chemin à parcourir avant d'être évident. Je vais illustrer l'explication avec un exemple. La situation initiale est ceci:

Modèle De Données De La Version 1

enter image description hereenter image description here

C'est le modèle que vous obtenez lorsque vous créez un projet avec la "navigation en fonction de l'application avec base de données de stockage de modèle". J'ai compilé et fait de frapper fort avec l'aide d'une boucle for pour créer autour de 2k entrées de tous avec quelques valeurs différentes. Là nous allons à 2.000 événements avec un NSDate valeur.

Maintenant, nous ajoutons une deuxième version du modèle de données, qui ressemble à ceci:

enter image description here

Modèle De Données De La Version 2

La différence est là: Le Cas de l'entité est parti, et nous avons deux nouveaux. L'un, qui enregistre un horodatage comme un double et le second qui doit stocker une date NSString.

L'objectif est de transférer toutes les Version 1 Événements pour les deux nouvelles entités et de convertir les valeurs le long de la migration. Cela entraîne deux fois les valeurs de chaque comme un type différent dans un autre entitiy.

Pour migrer, nous avons choisi de migration de la main et ce que nous faisons avec les modèles de correspondance. C'est également la première partie de la réponse à votre question. Nous ferons de la migration en deux étapes, parce que c'est long à migrer 2k entrées et nous souhaitons conserver l'empreinte mémoire faible.

On pourrait même aller de l'avant et de diviser ces modèles de correspondance encore à migrer seulement des plages des entités. Dire que nous avons un million d'enregistrements, cela peut se bloquer l'ensemble du processus. Il est possible de réduire les entités extraites vers le bas avec un prédicat de Filtre.

De retour de nos deux modèles de correspondance.

Nous créons le premier modèle de cartographie comme ceci:

1. Nouveau Fichier -> Ressources -> Modèle De Cartographie enter image description here

2. Choisissez un nom, j'ai choisi StepOne

3. Définir la source et la destination du modèle de données

enter image description here

Modèle De Cartographie De La Première Étape

enter image description here

enter image description here

enter image description here

Le multi pass migration n'a pas besoin de l'entité personnalisée, les politiques de migration, mais nous allons le faire pour obtenir un peu plus de détails pour cet exemple. Donc, nous ajoutons une stratégie personnalisée à l'entité. C'est toujours une sous-classe de NSEntityMigrationPolicy.

enter image description here

Cette politique de classe implémente des méthodes pour rendre notre migration se produire. Toutefois, il est simple dans ce cas donc, nous aurons à mettre en œuvre qu'une seule méthode: createDestinationInstancesForSourceInstance:entityMapping:manager:error:.

La mise en œuvre ressemblera à ceci:

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

Dernière étape: la migration elle-même

Je vais passer la partie pour mettre en place le deuxième modèle de cartographie qui est presque identique, juste un timeIntervalSince1970 utilisé pour convertir les NSDate pour un double.

Enfin nous avons besoin pour déclencher la migration. Je vais sauter le code réutilisable pour l'instant. Si vous en avez besoin, je vais poster ici. Il peut être trouvé à la Personnalisation du Processus de Migration , c'est juste une fusion des deux premiers exemples de code. La troisième et dernière partie sera modifié comme suit: au Lieu d'utiliser la méthode de la classe de l' NSMappingModel classe mappingModelFromBundles:forSourceModel:destinationModel: , nous allons utiliser l' initWithContentsOfURL: car la méthode de classe sera de retour, un seul, peut-être la première, modèle de cartographie dans le bundle.

Nous avons maintenant les deux modèles de correspondance qui peut être utilisé dans toutes les passes de la lopp et envoyer le migrer méthode pour le gestionnaire de migration. C'est tout.

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

Notes

  • Un modèle de cartographie se termine en cdm dans l'ensemble.

  • La banque de destination doit être fourni et ne doit pas être la source de magasin. Vous pouvez après la réussite de la migration de supprimer l'ancien et renommer le nouveau.

  • J'ai fait quelques modifications au modèle de données après la création de la cartographie des modèles, ce qui a entraîné quelques erreurs de compatibilité, que je ne pouvais résoudre à recréer les modèles de correspondance.

3voto

occulus Points 10906

Ces questions sont liées:

Des problèmes de mémoire migration de grandes CoreData de banques de données sur iPhone

À passes multiples de Base de Données de Migration En Morceaux Avec iOS

Pour citer le premier lien:

Cette question est abordée dans l'officiel la documentation dans les "Multiples entrées" l'article, mais il ressemble à la leur approche suggérée est de diviser les votre migration par type d'entité, c'est à dire faire de multiples modèles de correspondance, chacun de qui migrent d'un sous-ensemble de l'entité les types du modèle de données complet.

-5voto

PapaSmurf Points 1023

Supposons que votre schéma de base de données dispose de 5 entités, par ex. une personne, d'étudiant, de cours, de classe, et de l'enregistrement pour utiliser le type standard de la exemple, où des étudiants sous-classes de la personne, de la classe implémente sûr, et l'enregistrement se joint à la classe et de l'élève. Si vous avez apporté des modifications à l'ensemble de ces définitions de table, il faut commencer par les classes de base, et de travailler votre chemin jusqu'à. Donc, vous ne pouvez pas démarrer avec la conversion d'enregistrements, parce que chaque dossier d'enregistrement dépend de la présence de la classe et des élèves. Donc, vous commencez avec la migration, seule une Personne de la table, la copie des lignes existantes dans la nouvelle table, et en remplissant ce que de nouveaux champs sont là (si possible) et le rejet de la supprimé les colonnes. Faire de chaque migration à l'intérieur d'un autorelease pool, de sorte qu'une fois que c'est fait, votre mémoire est de retour pour commencer.

Une fois que la Personne de table est fait, alors vous pouvez convertir la table des étudiants. Puis un saut à la course et puis la Classe, et enfin la table d'Inscription.

L'autre considération est le nombre de dossiers, comme si la Personne avait un millier de lignes, vous devez, tous les 100 ou alors, exécutez la NSManagedObject équivalent d'un communiqué de presse, qui est de dire la gestion du contexte de l'objet [moc refreshObject:ob mergeChanges:N]; Également vos données périmées minuterie de façon faible, de sorte que la mémoire est vidée souvent.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X