Implementando Importación de Datos Core Rápida y Eficiente en iOS 5

Pregunta: ¿Cómo puedo hacer que el contexto de mi hijo vea los cambios persistidos en el contexto principal para que activen mi NSFetchedResultsController para actualizar la interfaz de usuario?

Aquí está la configuración:

Tiene una aplicación que descarga y agrega una gran cantidad de datos XML (aproximadamente 2 millones de registros, cada uno aproximadamente del tamaño de un párrafo normal del texto). El archivo .sqlite tiene un tamaño de unos 500 MB. Agregar este contenido a los Datos básicos lleva tiempo, pero desea que el usuario pueda usar la aplicación mientras los datos se cargan en el almacén de datos de manera incremental. Debe ser invisible e imperceptible para el usuario que se mueven grandes cantidades de datos, por lo que no se cuelga, no hay nerviosismo: se desplaza como la mantequilla. Aún así, la aplicación es más útil, cuantos más datos se agregan a ella, por lo que no podemos esperar para que los datos se agreguen al almacén de Core Data. En el código, esto significa que realmente me gustaría evitar un código como este en el código de importación:

<code>[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]];
</code>

La aplicación es solo para iOS 5, por lo que el dispositivo más lento que debe admitir es un iPhone 3GS.

Estos son los recursos que he usado hasta ahora para desarrollar mi solución actual:

Guía de programación de datos básicos de Apple: Importación eficiente de datos

Use Autorelease Pools para mantener la memoria bajaCosto de las relaciones. Importación plana, luego parchear relaciones al finalNo preguntes si puedes ayudarlo, esto ralentiza las cosas de una manera O (n ^ 2)Importar en lotes: guardar, restablecer, drenar y repetirDesactivar el Administrador de Deshacer en la importación

iDeveloper TV - Rendimiento de datos básicos

Utilice 3 contextos: tipos de contexto Master, Main y Confinement

iDeveloper TV - Datos básicos para actualización de Mac, iPhone y iPad

Ejecutar guarda en otras colas con performBlock hace que las cosas sean más rápidas.El cifrado ralentiza las cosas, apáguelo si puede.

Importación y visualización de grandes conjuntos de datos en datos básicos por Marcus Zarra

Puede ralentizar la importación dando tiempo al bucle de ejecución actual, para que las cosas se sientan suaves para el usuario.El código de ejemplo demuestra que es posible realizar grandes importaciones y mantener la interfaz de usuario receptiva, pero no tan rápido como con 3 contextos y el ahorro asíncrono en el disco.Mi solucion actual

Tengo 3 instancias de NSManagedObjectContext:

masterManagedObjectContext - Este es el contexto que tiene NSPersistentStoreCoordinator y es responsable de guardarlo en el disco. Hago esto para que mis partidas puedan ser asíncronas y, por lo tanto, muy rápidas. Lo creo en el lanzamiento de esta manera:

<code>masterManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[masterManagedObjectContext setPersistentStoreCoordinator:coordinator];
</code>

mainManagedObjectContext - Este es el contexto que la interfaz de usuario utiliza en todas partes. Es un hijo del masterManagedObjectContext. Lo creo así:

<code>mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[mainManagedObjectContext setUndoManager:nil];
[mainManagedObjectContext setParentContext:masterManagedObjectContext];
</code>

fondoContexto - Este contexto se crea en mi subclase NSOperation que es responsable de importar los datos XML a los Datos Core. Lo creo en el método principal de la operación y lo vinculo al contexto maestro allí.

<code>backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
[backgroundContext setUndoManager:nil];
[backgroundContext setParentContext:masterManagedObjectContext];
</code>

Esto realmente funciona muy, muy rápido. ¡Al hacer esta configuración de 3 contextos, pude mejorar mi velocidad de importación más de 10 veces! Honestamente, esto es difícil de creer. (Este diseño básico debe ser parte de la plantilla estándar de Datos Core ...)

Durante el proceso de importación guardo 2 formas diferentes. Cada 1000 elementos que guardo en el contexto de fondo:

<code>BOOL saveSuccess = [backgroundContext save:&error];
</code>

Luego, al final del proceso de importación, guardo en el contexto maestro / padre que, aparentemente, elimina las modificaciones a los otros contextos secundarios, incluido el contexto principal:

<code>[masterManagedObjectContext performBlock:^{
   NSError *parentContextError = nil;
   BOOL parentContextSaveSuccess = [masterManagedObjectContext save:&parentContextError];
}];
</code>

Problema: El problema es que mi interfaz de usuario no se actualizará hasta que vuelva a cargar la vista.

Tengo un UIViewController simple con un UITableView que se alimenta de datos utilizando un NSFetchedResultsController. Cuando finaliza el proceso de Importación, el NSFetchedResultsController no ve cambios en el contexto principal / maestro, por lo que la interfaz de usuario no se actualiza automáticamente como estoy acostumbrado a ver. Si saco el UIViewController de la pila y lo vuelvo a cargar, todos los datos están allí.

Pregunta: ¿Cómo puedo hacer que el contexto de mi hijo vea los cambios persistidos en el contexto principal para que activen mi NSFetchedResultsController para actualizar la interfaz de usuario?

He intentado lo siguiente que simplemente cuelga la aplicación:

<code>- (void)saveMasterContext {
    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];    
    [notificationCenter addObserver:self selector:@selector(contextChanged:) name:NSManagedObjectContextDidSaveNotification object:masterManagedObjectContext];

    NSError *error = nil;
    BOOL saveSuccess = [masterManagedObjectContext save:&error];

    [notificationCenter removeObserver:self name:NSManagedObjectContextDidSaveNotification object:masterManagedObjectContext];
}

- (void)contextChanged:(NSNotification*)notification
{
    if ([notification object] == mainManagedObjectContext) return;

    if (![NSThread isMainThread]) {
        [self performSelectorOnMainThread:@selector(contextChanged:) withObject:notification waitUntilDone:YES];
        return;
    }

    [mainManagedObjectContext mergeChangesFromContextDidSaveNotification:notification];
}
</code>

Respuestas a la pregunta(1)

Su respuesta a la pregunta