2011-01-15 18 views
5

He leído el capítulo de Marcus Zarra sobre multiprocesamiento en su libro Core Data y he examinado bastante de cerca su código de muestra. Pero su código y otros que he encontrado en otros lugares parecen estar enfocados en procesos en segundo plano que no son y no que necesitan conocerse. Estos ejemplos son buenos para importar una estructura de árbol, pero no abordan la importación de una estructura más general (compleja), como un gráfico acíclico dirigido.Core Data y multiprocesamiento

En mi caso, estoy tratando de analizar una jerarquía de clases C++ y me gustaría usar tantas NSOperations como sea posible. Me gustaría crear una instancia de NSManagedObject para cada clase encontrada y me gustaría fusionar diferentes NSManagedObjectContexts cada vez que se guarde uno.

Como nota aparte: puedo hacer que las cosas funcionen con una sola operación NSO que itera de archivos y analiza de a uno por vez. En esta implementación, el enfoque -mergeChanges: que llama a -mergeChangesFromContextDidSaveNotification: en el MOC del subproceso principal funciona bien.

Pero idealmente, tendría un NSOperation iterar sobre los archivos fuente y engendrar NSOperations para analizar cada archivo. He intentado varios enfoques, pero parece que no puedo hacerlo bien. Lo más prometedor fue que cada NSOperation observara NSManagedObjectContextDidSaveNotification. Con -mergeChanges: aspecto:

- (void) mergeChanges:(NSNotification *)notification 
{ 
// If locally originated, then trigger main thread to merge. 
if ([notification object] == [self managedObjectContext]) 
    { 
    AppDelegate *appDelegate = (AppDelegate*)[[NSApplication sharedApplication] delegate]; 
    NSManagedObjectContext *mainContext = [appDelegate managedObjectContext]; 

    // Merge changes into the main context on the main thread 
    [mainContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:) 
     withObject:notification 
     waitUntilDone:YES]; 
    return; 
    } 
    // If not locally originated, then flag need to merge with in this NSOperation's thread. 
[self setNeedsToMerge:YES]; 
[self setMergeNotification:notification]; 
} 

En esencia, el análisis de la NSOperation principal() verificaron Ivar 'needsToMerge' periódicamente. Si era cierto, se invocó a -mergeChangesFromContextDidSaveNotification: en MOC local con NSNotifications almacenadas en caché. Y luego se restauró needsToMerge. Si la notificación se había originado localmente, se le indicó al subproceso principal que realizara -mergeChangesFromContextDidSaveNotification: en su MOC.

estoy seguro de que hay una buena razón por la que esto no funcionó y por qué me sale esto:

advertencia: Cancelación de llamada - código objc en la pila del hilo actual hace esta inseguro.

También han tratado de utilizar el bloqueo de NSPeristentStoreCoordinator para controlar el acceso - pero esto es problemático si se lleva a cabo durante una llamada para -save de NSManagedObjectContext: método porque -save: notificará a los observadores interesados ​​de guardar el evento y -mergeChangesFromContextDidSaveNotification: parece bloquear el intentar adquirir el bloqueo de PSC.

Parece que esto debería ser mucho más fácil.

Respuesta

2

creo que hade el mismo problema y aquí está cómo lo resuelvo:

crear una clase NSOperation personalizada donde se define:

NSMutableArray * changeNotifications; 
NSLock * changeNotificationsLock; 
NSManagedObjectContext * localManagedObjectContext; 

En su principal método NSOperation antes de guardar el contexto primera aplican todos los cambios solicitados:

[self.changeNotificationsLock lock]; 
for(NSNotification * change in self.changeNotifications){ 
    [self.localManagedObjectContext mergeChangesFromContextDidSaveNotification:change]; 
} 
if([self.changeNotifications count] >0){ 
    [self.changeNotifications removeAllObjects]; 
} 
[self.changeNotificationsLock unlock]; 

NSError *error = nil; 
[self.localManagedObjectContext save:&error] 

en cuenta que he utilizado un bloqueo, esto porque no se NSMutableArray hilo de seguridad y quiero acceder con seguridad changeNotifications. changeNotifications es la matriz donde se almacenan todos los cambios que deben aplicarse antes de guardar el contexto.

Y aquí está su método de fusión, modificado para que todos los cambios que necesita combinar su NSOperation se combinen utilizando el hilo correcto. Tenga en cuenta que estos métodos es llamado por otros hilos que su NSOperation uno, por lo tanto, lo que necesita para bloquear el acceso a self.changeNotifications

- (void) mergeChanges:(NSNotification *)notification 
{ 
// If not locally originated, then add notification into change notification array 
// this notification will be treated by the NSOperation thread when needed. 
if ([notification object] != self.localManagedObjectContext) 
    { 
    [self.changeNotificationsLock lock]; 
    [self.changeNotifications addObject:notification]; 
    [self.changeNotificationsLock unlock]; 
    } 

//Here you may want to trigger the main thread to update the main context  

} 

Esperanza esta ayuda! Este método no es 100% sólido, puede haber algunos casos en los que una notificación de cambio llegue demasiado tarde. En ese caso, el método de guardar contexto devolverá un error y deberá volver a cargar NSManagedObject y guardarlo nuevamente. En caso de que se necesiten más detalles, por favor avíseme.

+0

esta es una buena solución. Para mí, probablemente necesitaría aplicar changeNotifications antes de buscar un objeto además de antes de guardarlo, pero sería fácil. Según lo entiendo, todos los hilos (excepto el hilo principal) serían instancias de esta subclase. Es ese el caso? – westsider

+0

@Westsider todo NSOperation que necesita fusionar el contexto debe ser una subclase de la misma. Básicamente sugiero crear una clase NSOperationManagedOobjectContextAware que tenga un método mergeChanges y las 3 propiedades changeNotifications, changeNotificationsLock, localManagedObjectContext –

0

Este dos de paz de código de trabajo ahora correctamente en mi solicitud:

- (void)mergeChanges:(NSNotification *)notification; 
{ 
//AppDelegate *appDelegate = [[NSApplication sharedApplication] delegate]; 
NSManagedObjectContext *mainContext = [appDelegate managedObjectContext]; 

// Merge changes into the main context on the main thread 
[mainContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:) 
           withObject:notification 
          waitUntilDone:YES]; 
} 

-(void) main { 
// Register context with the notification center 
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; 
[nc addObserver:self 
     selector:@selector(mergeChanges:) 
      name:NSManagedObjectContextDidSaveNotification 
     object:managedObjectContext]; 

Por supuesto, managedObjectContext significa:

managedObjectContext = [[NSManagedObjectContext alloc] init]; 
[managedObjectContext setPersistentStoreCoordinator:[appDelegate persistentStoreCoordinator]]; 
[managedObjectContext setUndoManager:nil]; 

tener cuidado si u necesidad de eliminar algo antes de que u haga actualizaciones de principal moc. Tengo un tiempo loco y un montón de errores difíciles de depurar, mientras entiendo que no puedo usar moc del hilo principal mientras en cualquier lugar procese otros cambios con el mismo contenido.

+0

No veo cómo esto podría funcionar para resolver mi problema. Parece que * all * merging se realizará en el hilo principal. Lo que estoy buscando es una manera de que la fusión ocurra en múltiples subprocesos no principales, de modo que varios subprocesos podrían analizar diferentes archivos pero compartir su progreso incremental. – westsider

+0

Esta es una forma de poner parte del análisis al moc principal. Tienes que hacer tu código en el hilo principal más tarde, ya que te entiendo. – Alex

+0

Estoy tratando de obtener varios hilos que lean * y * escribiendo al coordinador de la tienda persistente compartido, de modo que cada objeto recién agregado esté disponible para * todos * los hilos tan pronto como se agreguen. Esto es diferente de simplemente tener el contexto de objetos administrados del subproceso principal teniendo en cuenta todos los objetos recién agregados. – westsider