2012-09-24 20 views
10

Estoy usando MagicalRecord 2.0.3 y realmente no puedo entender cómo guardar datos en segundo plano.¿Cómo crear múltiples objetos en el fondo?

Según la documentación, algo como esto debería funcionar:

[MagicalRecord saveInBackgroundWithBlock:^(NSManagedObjectContext *localContext) { 
    // Do this hundreds of times 
    [MyObject createInContext:localContext]; 
}]; 

Sin embargo, nada se guarda en la base de datos. He visto a varias personas publicar soluciones similares a esta:

[MagicalRecord saveInBackgroundWithBlock:^(NSManagedObjectContext *localContext) { 
    // Do this hundreds of times 
    [MyObject createInContext:localContext]; 
} completion:^{ 
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 
     [[NSManagedObjectContext defaultContext] saveNestedContexts]; 
    }]; 
}]; 

Esto hace guardar mis datos en la base de datos, sin embargo, ya que el ahorro que sucede en el hilo principal, mi solicitud no responde por un tiempo (con mi conjunto de datos, unos 3 segundos, que es demasiado tiempo).

También he probado esto, pero también bloquea a la vez que ahorra:

self.queue = [[NSOperationQueue alloc] init]; 

[self.queue addOperationWithBlock:^{ 
    NSManagedObjectContext *localContext = [NSManagedObjectContext contextForCurrentThread]; 

    // Do this hundreds of times 
    [MyObject createInContext:localContext]; 

    [localContext saveNestedContexts]; 
}]; 

Y, por último, el mismo efecto de bloqueo con este código:

dispatch_queue_t syncQueue = dispatch_queue_create("Sync queue", NULL); 
dispatch_async(syncQueue, ^{ 
    NSManagedObjectContext *localContext = [NSManagedObjectContext contextForCurrentThread]; 

    // Do this hundreds of times 
    [MyObject createInContext:localContext]; 

    [[NSManagedObjectContext contextForCurrentThread] saveNestedContexts]; 
}); 

Así que, ¿cuál es la mejor manera de para resolver esto? Necesito crear cientos de objetos en segundo plano y la aplicación debe seguir siendo receptiva.

+0

Los nuevos contextos anidados han comenzado a causar estragos en gran parte de las API de verano en MagicalRecord. Si bien conozco estos problemas y ahora se están discutiendo algunas soluciones, siempre estoy abierto a sugerencias. – casademora

+2

Tal vez sea mejor que use Core Data sin un framework como MR? – Hunter

+0

¿Alguna vez encontró una solución usando MagicalRecord? Tengo los mismos problemas (bloqueo de UI mientras actualizo en segundo plano) y no puedo encontrar una solución. ¡Gracias! – RyanG

Respuesta

6

MagicalRecord usa un contexto secundario cuando se trabaja en segundo plano. Esto funciona bien para pequeños cambios, pero creará un bloqueo excesivo del hilo principal al importar grandes cantidades de datos.

La forma de hacerlo es utilizar un NSManagedObjectContext paralelo y hacer la fusión usted mismo con la notificación NSManagedObjectContextDidSaveNotification y el método mergeChangesFromContextDidSaveNotification. Consulte las pruebas de rendimiento aquí: http://floriankugler.com/blog/2013/5/11/backstage-with-nested-managed-object-contexts

Al guardar un contexto anidado, todo tiene que copiarse en el contexto principal. A diferencia de esto, los objetos que no se han recuperado (en el contexto en el que se está fusionando) no serán fusionados por mergeChangesFromContextDidSaveNotification. Esto es lo que lo hace más rápido.

Es posible que encuentre problemas si desea mostrar estos resultados de inmediato después de guardarlos en lotes y usar NSFetchResultsController. Vea la siguiente pregunta para una solución: NSFetchedResultsController with predicate ignores changes merged from different NSManagedObjectContext

Para más consejos de rendimiento echar un vistazo a esta pregunta: Implementing Fast and Efficient Core Data Import on iOS 5

Crear su propio contexto.

NSManagedObjectContext *importContext = [[NSManagedObjectContext alloc] 
          initWithConcurrencyType:NSPrivateQueueConcurrencyType]; 
[importContext setPersistentStoreCoordinator:yourPersistentStoreCoordinator]; 
[importContext setUndoManager:nil]; // For importing you don't need undo: Faster 

// do your importing with the new importContext 
// … 

NSError* error = nil; 
if(importContext.hasChanges) { 
    if(![importContext save:&error]) { 
     NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 
    } 
} 

Asegúrese de estar atento a los cambios en los contextos de objetos administrados.

[[NSNotificationCenter defaultCenter] 
       addObserver:singleton 
       selector:@selector(contextDidSave:) 
        name:NSManagedObjectContextDidSaveNotification object:nil]; 

En el contextoDidSave: fusiona el cambio usted mismo.

- (void) contextDidSave:(NSNotification*) notification 
{ 
    if(![notification.object isEqual:self.mainContext]) { 
    dispatch_async(dispatch_get_main_queue(), ^{ 
     [self.mainContext mergeChangesFromContextDidSaveNotification:notification]; 
    }); 
    } 
} 
+0

¿cómo funciona esto exactamente? Estoy enfrentando el problema de que mi hilo principal está bloqueado debido a la gran cantidad de datos para importar. Estoy usando MagicalRecord pero no logro lo que está escribiendo aquí ... – swalkner

+0

@swalkner He editado la respuesta para proporcionar más detalles. – Onato

1

contextos de objetos gestionados no es seguro para subprocesos por lo que si alguna vez tiene que hacer ningún tipo de trabajo de fondo con sus objetos CoreData (es decir, una función de importación de larga data/exportación sin bloquear la interfaz de usuario principal) tendrá que hacer eso en un hilo de fondo.

En estos casos, deberá crear un nuevo contexto de objeto gestionado en la cadena de fondo, repetir su operación de coredata y luego notificar al contexto principal de sus cambios.

Usted puede encontrar un ejemplo de cómo esto podría funcionar aquí

Core Data and threads/Grand Central Dispatch

Cuestiones relacionadas