Mi aplicación de iPhone necesita migrar su almacén de datos principal y algunas de las bases de datos son bastante grandes. La documentación de Apple sugiere usar "múltiples pases" para migrar datos para reducir el uso de memoria. Sin embargo, la documentación es muy limitada y no explica muy bien cómo hacer esto. ¿Alguien puede señalarme un buen ejemplo o explicar en detalle el proceso de cómo llevarlo a cabo?
85
Respuestas:
He descubierto lo que Apple sugiere en su documentación . En realidad, es muy fácil, pero queda un largo camino por recorrer antes de que sea obvio. Ilustraré la explicación con un ejemplo. La situación inicial es esta:
Modelo de datos versión 1
Es el modelo que obtiene cuando crea un proyecto con la plantilla "aplicación basada en navegación con almacenamiento de datos centrales". Lo compilé e hice algunos golpes con la ayuda de un bucle for para crear alrededor de 2k entradas, todas con algunos valores diferentes. Ahí vamos 2.000 eventos con un valor NSDate.
Ahora agregamos una segunda versión del modelo de datos, que se ve así:
Modelo de datos versión 2
La diferencia es: la entidad Event se ha ido y tenemos dos nuevas. Uno que almacena una marca de tiempo como
double
y el segundo que debería almacenar una fecha comoNSString
.El objetivo es transferir todos los eventos de la versión 1 a las dos nuevas entidades y convertir los valores a lo largo de la migración. Esto da como resultado el doble de valores, cada uno como un tipo diferente en una entidad separada.
Para migrar, elegimos la migración a mano y esto lo hacemos con modelos de mapeo. Esta es también la primera parte de la respuesta a su pregunta. Haremos la migración en dos pasos, porque la migración de 2k entradas lleva mucho tiempo y nos gusta mantener baja la huella de memoria.
Incluso podría seguir adelante y dividir estos modelos de mapeo aún más para migrar solo rangos de las entidades. Digamos que tenemos un millón de registros, esto puede bloquear todo el proceso. Es posible reducir las entidades buscadas con un predicado de filtro .
Volvamos a nuestros dos modelos de mapeo.
Creamos el primer modelo de mapeo así:
1. Nuevo archivo -> Recurso -> Modelo de mapeo
2. Elija un nombre, elegí StepOne
3. Establecer el modelo de datos de origen y destino
Modelo de mapeo, paso uno
La migración de múltiples pases no necesita políticas de migración de entidades personalizadas, sin embargo, lo haremos para obtener un poco más de detalles para este ejemplo. Entonces agregamos una política personalizada a la entidad. Esta es siempre una subclase de
NSEntityMigrationPolicy
.Esta clase de política implementa algunos métodos para que suceda nuestra migración. Sin embargo, es simple en este caso por lo que tendremos que aplicar un solo método:
createDestinationInstancesForSourceInstance:entityMapping:manager:error:
.La implementación se verá así:
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; }
Paso final: la migración en sí
Saltaré la parte para configurar el segundo modelo de mapeo que es casi idéntico, solo un timeIntervalSince1970 usado para convertir el NSDate en un doble.
Finalmente, necesitamos activar la migración. Saltaré el código repetitivo por ahora. Si lo necesita, lo publicaré aquí. Se puede encontrar en Personalización del proceso de migración , es solo una combinación de los dos primeros ejemplos de código. La tercera y última parte se modificará de la siguiente manera: en lugar de usar el método de clase de la
NSMappingModel
clasemappingModelFromBundles:forSourceModel:destinationModel:
, usaremos elinitWithContentsOfURL:
porque el método de clase devolverá solo uno, tal vez el primero, modelo de mapeo encontrado en el paquete.Ahora tenemos los dos modelos de mapeo que se pueden usar en cada paso del ciclo y enviar el método de migración al administrador de migración. Eso 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]; }
Notas
Un modelo de mapeo termina
cdm
en el paquete.Se debe proporcionar la tienda de destino y no debe ser la tienda de origen. Después de una migración exitosa, puede eliminar el antiguo y cambiar el nombre del nuevo.
Hice algunos cambios en el modelo de datos después de la creación de los modelos de mapeo, esto resultó en algunos errores de compatibilidad, que solo pude resolver recreando los modelos de mapeo.
fuente
Estas preguntas están relacionadas:
Problemas de memoria al migrar grandes almacenes de datos CoreData en iPhone
Migración de datos principales de múltiples pases en fragmentos con iOS
Para citar el primer enlace:
fuente
Suponga que el esquema de su base de datos tiene 5 entidades, por ejemplo, persona, estudiante, curso, clase y registro para usar el tipo estándar de ejemplo, donde el estudiante subclasifica persona, la clase implementa el curso y el registro se une a la clase y al estudiante. Si ha realizado cambios en todas estas definiciones de tabla, debe comenzar en las clases base y avanzar. Por lo tanto, no puede comenzar con la conversión de registros, porque cada registro de registro depende de que haya clase y estudiantes allí. Por lo tanto, comenzaría migrando solo la tabla Person, copiando las filas existentes en la nueva tabla y completando los campos nuevos que estén allí (si es posible) y descartando las columnas eliminadas. Realice cada migración dentro de un grupo de liberación automática, de modo que una vez que haya terminado, su memoria vuelva a comenzar.
Una vez que la tabla de personas está lista, puede convertir la tabla de estudiantes. Luego salte a Curso y luego Clase, y finalmente a la tabla de Registro.
La otra consideración es el número de registros, si como Person tuviera mil filas, tendría que, cada 100 aproximadamente, ejecutar el NSManagedObject equivalente de una versión, que es decirle al contexto del objeto administrado [moc refreshObject: ob mergeChanges: NO]; También configure su temporizador de datos obsoletos muy bajo, para que la memoria se vacíe con frecuencia.
fuente