Estoy construyendo mi primera aplicación para iOS, que en teoría debería ser bastante sencilla, pero tengo dificultades para hacerlo suficientemente a prueba de balas para que me sienta seguro de enviarla a la tienda de aplicaciones.Uso de datos centrales simultánea y confiablemente
En pocas palabras, la pantalla principal tiene una vista de tabla, al seleccionar una fila, pasa a otra vista de tabla que muestra la información relevante para la fila seleccionada en una forma de detalle maestro. Los datos subyacentes se recuperan como datos JSON de un servicio web una vez al día y luego se guardan en caché en un almacén de datos centrales. Los datos anteriores a ese día se eliminan para evitar que el archivo de base de datos SQLite crezca indefinidamente. Todas las operaciones de persistencia de datos se realizan utilizando Datos centrales, con un NSFetchedResultsController
que sustenta la vista de tabla de detalles.
El problema que estoy viendo es que si cambia rápidamente entre las pantallas maestra y de detalle varias veces mientras se recuperan datos nuevos, se analizan y se guardan, la aplicación se congela o se cuelga por completo. Parece que hay algún tipo de condición de carrera, tal vez debido a que Core Data importa datos en segundo plano mientras el hilo principal intenta realizar una búsqueda, pero estoy especulando. He tenido problemas para capturar cualquier información de bloqueo significativa, por lo general es un SIGSEGV en la pila de Core Data.
La siguiente tabla muestra el orden real de eventos que ocurren cuando se carga el detalle controlador de vista tabla:
Main Thread Background Thread viewDidLoad Get JSON data (using AFNetworking) Create child NSManagedObjectContext (MOC) Parse JSON data Insert managed objects in child MOC Save child MOC Post import completion notification Receive import completion notification Save parent MOC Perform fetch and reload table view Delete old managed objects in child MOC Save child MOC Post deletion completion notification Receive deletion completion notification Save parent MOC
Una vez que el bloque de terminación AFNetworking se activa cuando ha llegado los datos JSON, se crea un anidado NSManagedObjectContext
y se pasa a un objeto "importador" que analiza los datos JSON y guarda los objetos en el almacén de datos centrales. El importador realiza utilizando el nuevo performBlock
método introducido en iOS 5:
NSManagedObjectContext *child = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[child setParentContext:self.managedObjectContext];
[child performBlock:^{
// Create importer instance, passing it the child MOC...
}];
El objeto importador observa de su propio MOC NSManagedObjectContextDidSaveNotification
y luego registra su propia notificación que se observa por el controlador detalle vista de tabla. Cuando se publica esta notificación, el controlador de vista de tabla realiza un guardado en su propio MOC (principal).
Uso el mismo patrón básico con un objeto "eliminador" para eliminar los datos antiguos después de que se hayan importado los datos nuevos del día. Esto ocurre de forma asincrónica una vez que el controlador de resultados recuperado ha obtenido los nuevos datos y se ha vuelto a cargar la vista de tabla de detalles.
Una cosa que no estoy haciendo es observar cualquier combinación de notificaciones o bloquear ninguno de los contextos de objetos administrados o el coordinador de tienda persistente. ¿Esto es algo que debería estar haciendo? No estoy seguro de cómo diseñar esto correctamente, así que agradecería cualquier consejo.
Me gusta esta idea, me gusta ** mucho **. También tiene la ventaja de que eliminar un sistema de archivos del archivo SQLite será mucho más rápido que tener los datos centrales eliminar los objetos administrados en el gráfico del objeto, aunque, por supuesto, en realidad el rendimiento de eliminación no importa porque una tienda persistente diferente será usado de todos modos. Voy a dar un enfoque a este fin de semana. –