5

Tengo un problema con un objeto NSManagedObject que no refleja los cambios realizados en un almacén persistente después de que un hilo de fondo haya guardado su contexto.NSManagedObject no refleja cambios después del subproceso en segundo plano NSManagedObjectContextDidSaveNotification

La instalación de

En una aplicación sencilla prueba que tengo una sola ventana que muestra todos los objetos en mi datos básicos almacenamiento persistente, un cuadro de búsqueda para filtrar los resultados y un campo de texto para mostrar el nombre del elemento seleccionado y permite cambiar el nombre.

enlaces son los siguientes:

 
ArrayController --> AppDelegate --> ManagedObjectContext 
TableView Col 1 --> ArrayController --> values --> arrangedObjects.widgetName 
TableView Col 2 --> ArrayController --> values --> arrangedObjects.uid 

SearchField --> ArrayController --> predicate --> filterPredicate 

TextField --> ArrayController --> value --> selection.widgetName 

También tengo un botón que inicia un fondo (NSOperation) traiga de datos de un servidor web.

El Proceso

Cuando el usuario hace clic en el botón de actualización, una NSOperation es pateado fuera que va y agarra los widgets de forma asíncrona, analizar la respuesta, los cheques para los widgets locales para borrar que no están en la respuesta, nuevos widgets para agregar que no se almacenan localmente y widgets locales existentes que deben actualizarse con los datos recuperados del servidor.

Una vez que el procesamiento ha terminado, el contexto principal es notificado usando:

[mainContext performSelectorOnMainThread: 
     @selector(mergeChangesFromContextDidSaveNotification:) 
           withObject:notification 
          waitUntilDone:YES]; 

tengo un observador en el controlador principal para la prueba que muestra los cambios fueron a través de bien y el controlador principal Got notificado.

El problema

Si hago un cambio en un objeto seleccionado con el campo de texto, cuando los datos sobre el hilo de fondo se guardan, el objeto en la interfaz de usuario no se actualiza para reflejar esos cambios (es decir, no sobrescribe la UI con los cambios del servidor).

Por ejemplo, dada la siguiente tres widgets y el ID de:

 
Test Name 1 | ID 123 
Test Name 2 | ID 234 
Test Name 3 | ID 345 

Si cambio el nombre en la interfaz de usuario de Test Name 2-Renamed 2 Tengo el siguiente:

 
Test Name 1 | ID 123 
Renamed 2 | ID 234 
Test Name 3 | ID 345 

I Cuando Actualizar en el fondo, quiero que la lista refleje el estado del servidor, es decir, volver a:

 
Test Name 1 | ID 123 
Test Name 2 | ID 234 
Test Name 3 | ID 345 

En su lugar, sigue siendo:

 
Test Name 1 | ID 123 
Renamed 2 | ID 234 
Test Name 3 | ID 345 

sé el almacén persistente se actualiza porque si mato a la aplicación de XCode y relanzamiento, se muestra la información deseada. Si salgo de la aplicación normalmente, el valor modificado se escribe en la tienda al cierre de la aplicación y la reapertura muestra el valor renombrado.

lo que he tratado

Sé que el mensaje se está enviando forma los antecedentes del contexto principal y sé que los datos están siendo persistió a la tienda. Por lo tanto, creo que el problema es que el contexto principal no se está fusionando como yo esperaba, o necesito forzar de algún modo al controlador de arrays a buscar desde la tienda persistente y descartar su contexto.

  • me han tratado processPendingChanges: previa notificación de la tienda de guardar, pero sospecho que sólo estoy escribiendo el Renamed 2 a la tienda.
  • He intentado hacer un rearrangeObjects en la controladora de la matriz, pero a medida que el Controlador de array está tratando con el contexto principal Sospecho que esto no está haciendo nada
  • He intentado hacer un fetch:nil en el controlador de matriz para hacer una zona de alcance de la tienda persistente, pero una vez más sospecho que el contexto principal está sobrescribiendo el valor de Renamed 2 porque aún no está guardado.
  • He tratado fetchWithRequest:nil merge:NO error:&error en la controladora de la matriz de acuerdo con la documentación de Apple, pero aún esto no parece cambiar el valor mostrado

Lo que que tiene que ocurrir es que el controlador de array para guardar sus datos hasta la tienda persistente antes de escribir los datos de la tienda de fondo para que una búsqueda en el controlador de la matriz haga que los datos sean precisos según mis expectativas. Y si este es realmente el caso, ¿cómo le diría al controlador de array que haga esto, o el controlador de arrays simplemente sabría de los cambios a través de enlaces si el managedObjectContext principal se hubiera guardado de alguna manera?

Puedo hackear la solución haciendo una búsqueda desde la tienda persistente, poniendo esa información en una matriz y haciendo un setContent: en el controlador de matriz y luego repitiendo esto cuando la tienda persistente se guarda, pero esto parece simplemente incorrecto, no mencionar el problema de tener que rastrear el estado seleccionado del controlador de array (y potencialmente cualquier selección de sub-array que pueda estar ocurriendo como resultado de esa selección primaria).

¿Estoy fuera de base? Obviamente me falta algo aquí.

Cualquier palabra de sabiduría o consejo sería muy apreciada.

Respuesta

3

Ah el poder de la frustración junto con la determinación. No estoy seguro de que este sea necesariamente el enfoque mejor o más recomendado, pero ciertamente cumple mis necesidades.

Mi objetivo era que los cambios no persistentes persistan antes de que se guarde una actualización de fondo o se descartan antes de que se guarde una actualización de fondo. Ambos tienen el mismo objetivo en mi mundo (los datos del servidor siempre son correctos).

resulta que simplemente tenía que añadir un observador en mi NSOperation:

NSNotificationCenter * nc = [NSNotificationCenter defaultCenter]; 
[nc addObserver:self 
     selector:@selector(prepareMerge:) 
      name:NSManagedObjectContextWillSaveNotification object:ctx]; 

que llama a un método en la operación:

-(void)prepareMerge:(NSNotification *)notification { 

    [[NSNotificationCenter defaultCenter] 
     postNotificationOnMainThreadName:@"SaveNow" 
            object:nil]; 
} 

La notificación se envía al hilo principal (cortesía de una categoría en NSNotificationCenter publicada en cocoanetics.com) que se escucha en una clase relevante en el hilo principal:

[[NSNotificationCenter defaultCenter] addObserver:self 
             selector:@selector(saveNow:) 
              name:@"SaveNow" 
              object:nil]; 

Y, por supuesto, el método que en realidad hace el cambio:

-(void)saveNow:(NSNotification *)aNote { 

    [[self managedObjectContext] rollback]; 
} 

El controlador de matrices y cualquier otro componente de interfaz de usuario que han actualizado los valores rollback a la derecha antes de que el ahorro se ha comprometido. El guardado se realiza y los viejos valores locales se reemplazan por nuevos valores.

Trabajo hecho.

Cuestiones relacionadas