Datos principales e hilos / Grand Central Dispatch

Soy un principiante con Grand Central Dispatch (GCD) y Core Data, y necesito su ayuda para usar Core Data con CGD, para que la UI no esté bloqueada mientras agrego 40,000 registros a Core Data.

Sé que el CD no es seguro para subprocesos, así que tengo que usar otro contexto, y luego guardar los datos y fusionar contextos, hasta donde pude entender en algunos artículos.

Lo que no pude hacer aún es juntar las piezas.

Entonces, en mi código, necesito tu ayuda sobre cómo hacerlo.

Yo tengo:

/*some other code*/ for (NSDictionary *memberData in arrayWithResult) { //get the Activities for this member NSArray *arrayWithMemberActivities = [activitiesDict objectForKey:[memberData objectForKey:@"MemberID"]]; //create the Member, with the NSSet of Activities [Members createMemberWithDataFromServer:memberData andActivitiesArray:arrayWithMemberActivities andStaffArray:nil andContactsArray:nil inManagedObjectContext:self.managedObjectContext]; } 

¿Cómo puedo transformar esto para trabajar en segundo plano, y luego, cuando termine de guardar, guardar los datos y actualizar la interfaz de usuario, sin bloquear la interfaz de usuario mientras guardo los 40,000 objetos?

Aquí hay un buen ejemplo para que intentes. No dude en volver si tiene alguna pregunta:

 self.mainThreadContext... // This is a reference to your main thread context NSPersistentStoreCoordinator *mainThreadContextStoreCoordinator = [self.mainThreadContext persistentStoreCoordinator]; dispatch_queue_t request_queue = dispatch_queue_create("com.yourapp.DescriptionOfMethod", NULL); dispatch_async(request_queue, ^{ // Create a new managed object context // Set its persistent store coordinator NSManagedObjectContext *newMoc = [[NSManagedObjectContext alloc] init]; [newMoc setPersistentStoreCoordinator:mainThreadContextStoreCoordinator]]; // Register for context save changes notification NSNotificationCenter *notify = [NSNotificationCenter defaultCenter]; [notify addObserver:self selector:@selector(mergeChanges:) name:NSManagedObjectContextDidSaveNotification object:newMoc]; // Do the work // Your method here // Call save on context (this will send a save notification and call the method below) BOOL success = [newMoc save:&error]; if (!success) // Deal with error [newMoc release]; }); dispatch_release(request_queue); 

Y en respuesta a la notificación de guardar contexto:

 - (void)mergeChanges:(NSNotification*)notification { dispatch_async(dispatch_get_main_queue(), ^{ [self.mainThreadContext mergeChangesFromContextDidSaveNotification:notification waitUntilDone:YES]; }); } 

Y no se olvide de eliminar el observador del centro de notificaciones una vez que haya terminado con el contexto del hilo de fondo.

 [[NSNotificationCenter defaultCenter] removeObserver:self]; 

Aquí hay un fragmento que cubre GCD y UI en sus términos más simples. Puede reemplazar doWork con su código que hace el trabajo de CoreData.

Con respecto a la seguridad de los CD y los hilos, una de las mejores partes de GCD es que puedes separar secciones de tu aplicación (subsistemas) para sincronizarlas y asegurarte de que se ejecuten en la misma fila. Puede ejecutar todo el trabajo de CoreData en una cola llamada com.yourcompany.appname.dataaccess.

En la muestra, hay un botón que invoca el trabajo de larga duración, una etiqueta de estado, y agregué un control deslizante para mostrar que puedo mover el control deslizante mientras el trabajo bg está hecho.

 // on click of button - (IBAction)doWork:(id)sender { [[self feedbackLabel] setText:@"Working ..."]; [[self doWorkButton] setEnabled:NO]; // async queue for bg work // main queue for updating ui on main thread dispatch_queue_t queue = dispatch_queue_create("com.sample", 0); dispatch_queue_t main = dispatch_get_main_queue(); // do the long running work in bg async queue // within that, call to update UI on main thread. dispatch_async(queue, ^{ [self performLongRunningWork]; dispatch_async(main, ^{ [self workDone]; }); }); // release queues created. dispatch_release(queue); } - (void)performLongRunningWork { // simulate 5 seconds of work // I added a slider to the form - I can slide it back and forth during the 5 sec. sleep(5); } - (void)workDone { [[self feedbackLabel] setText:@"Done ..."]; [[self doWorkButton] setEnabled:YES]; } 

Esta publicación de blog tiene una descripción detallada sobre la concurrencia de datos básicos y el código de muestra: http://www.duckrowing.com/2010/03/11/using-core-data-on-multiple-threads/

Agregar otra fuente de información que puede verificar

ThreadedCoreData

el código de muestra de la biblioteca para desarrolladores iOS de Apple, que se actualizó recientemente (2013-06-09)

Demuestra cómo utilizar los datos centrales en un entorno de subprocesos múltiples, siguiendo el primer patrón recomendado mencionado en la Guía de progtwigción de datos básicos.

Con base en la muestra SeismicXML, descarga y analiza un suministro RSS del Servicio Geológico de los Estados Unidos (USGS) que proporciona datos sobre terremotos recientes en todo el mundo. Lo que hace que esta muestra sea diferente es que almacena de manera persistente los terremotos utilizando Core Data. Cada vez que inicia la aplicación, descarga nuevos datos de terremotos, los analiza en una operación NSO que busca duplicados y almacena terremotos recién fundamentados como objetos administrados.

Para los nuevos en Core Data, puede ser útil comparar la muestra SeismicXML con esta muestra y observar los ingredientes necesarios para introducir Core Data en su aplicación.

Entonces la respuesta seleccionada para esto es de hace casi 2 años, y hay algunos problemas con esto:

  1. No es compatible con ARC – necesita eliminar la llamada de lanzamiento en newMoc – ARC ni siquiera comstackrá con eso
  2. Deberías estar haciendo la danza weakSelf / strongSelf dentro del bloque; de ​​lo contrario, probablemente estés creando un loop de retención en la creación del observador. Consulte el documento de Apple aquí: http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html
  3. @RyanG preguntó en un comentario por qué está bloqueando. Mi suposición es porque el método recientemente editado tiene waitUntilDone: YES, excepto que va a bloquear el hilo principal. Probablemente quieras waitUntilDone: NO, pero no sé si hay actualizaciones de UI activadas a partir de estos eventos de cambio, así que requerirá pruebas.

–Editar–

Mirando más adentro # 3 – waitUntilDone: YES no es un método válido. Firma para objetos de contexto administrados, entonces, ¿cómo funciona eso?

Es mucho más fácil hacerlo que conectar el coordinador de tienda persistente a un nuevo contexto, que tampoco es seguro para subprocesos, por cierto.

 NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrency]; [context setParentContext:
]; [context performBlock:^{ ... // Execute all code on current context ... }]; NSError *error = nil; [context save:&error]; if (!error) { [context.parentContext save:&error]; if (error) { NSLog(@"Could not save parent context: %@", error); } } else { NSLog(@"Could not save context: %@", error); }

Gran tutorial sobre cómo utilizar los datos principales de contexto múltiple:

http://www.cocoanetics.com/2012/07/multi-context-coredata/