¿Cómo comparto un almacén de Core Data entre procesos usando NSDistributedNotifications?

StackOverflow https://stackoverflow.com/questions/1815518

  •  07-07-2019
  •  | 
  •  

Pregunta

Trasfondo

Ya publiqué una pregunta sobre los conceptos básicos de compartir un almacén de datos principales entre procesos .

Estoy tratando de implementar las recomendaciones dadas y me encuentro con problemas.

Mi objetivo

Tengo dos procesos: la aplicación de ayuda y la interfaz de usuario. Ambos comparten un único almacén de datos. Quiero que la interfaz de usuario actualice su NSManagedObjectContext cuando la aplicación auxiliar haya guardado nuevos datos en la tienda.

Flujo actual del programa

  1. El proceso de aplicación auxiliar escribe datos en la tienda.

  2. En la aplicación de ayuda, escucho las notificaciones NSManagedObjectContextDidSaveNotification.

  3. Cuando se guarda el contexto, codifico los objetos insertados, eliminados y actualizados utilizando sus representaciones de URI y NSArchiver.

  4. Envío una NSNotification al NSDistributedNotificationCenter con este diccionario codificado como userInfo.

  5. El proceso de IU está escuchando la notificación de guardado. Cuando recibe la notificación, desarchiva la información de usuario usando NSUnarchiver.

  6. Busca todos los objetos actualizados / insertados / eliminados de los URI dados y los reemplaza con NSManagedObjects.

  7. Construye una NSNotification con los objetos actualizados / insertados / eliminados.

  8. Llamo a mergeChangesFromContextDidSaveNotification: en el contexto del objeto gestionado del proceso de IU, pasando la NSNotification que construí en el paso anterior.

El problema

Los objetos insertados tienen errores en el contexto del objeto gestionado de la IU y aparecen en la IU. El problema viene con objetos actualizados. Simplemente no se actualizan.

Lo que he probado

  1. Lo más obvio para intentar sería pasar la Notificación de guardado del proceso de la aplicación de ayuda a la Proceso de IU. Fácil, verdad? Bueno no. Notificaciones distribuidas no permíteme hacer eso como userInfo el diccionario no está en la derecha formato. Por eso estoy haciendo todo el NSArchiving cosas.

  2. He intentado llamar refreshObject: mergeChanges: YES on los NSManagedObjects que se actualizarán, pero esto no parece tener ningún efecto.

  3. He intentado realizar el mergeChangesFromContextDidSaveNotification: selector en el hilo principal y el hilo actual. Ninguno de los dos parece afectar el resultado.

  4. He intentado usar mergeChangesFromContextDidSaveNotification: antes entre hilos, cuál de el curso es mucho más simple y funcionó perfectamente. Pero necesito esto mismo funcionalidad entre procesos.

Alternativas?

¿Me estoy perdiendo algo aquí? Constantemente tengo la sensación de que estoy haciendo esto mucho más complejo de lo que debe ser, pero después de leer la documentación varias veces y pasar unos días completos en esto, no veo otra forma de actualizar el MOC de la interfaz de usuario.

¿Hay una manera más elegante de hacer esto? ¿O solo estoy cometiendo un error tonto en algún lugar de mi código?

El Código

He intentado que sea lo más legible posible, pero sigue siendo un desastre. Lo siento.

Código de la aplicación auxiliar

   -(void)workerThreadObjectContextDidSave:(NSNotification *)saveNotification {
        NSMutableDictionary *savedObjectsEncodedURIs = [NSMutableDictionary dictionary];
        NSArray *savedObjectKeys = [[saveNotification userInfo] allKeys];

        for(NSString *thisSavedObjectKey in savedObjectKeys) {
            // This is the set of updated/inserted/deleted NSManagedObjects.
            NSSet *thisSavedObjectSet = [[saveNotification userInfo] objectForKey:thisSavedObjectKey];
            NSMutableSet *thisSavedObjectSetEncoded = [NSMutableSet set];

            for(id thisSavedObject in [thisSavedObjectSet allObjects]) {
                // Construct a set of URIs that will be encoded as NSData
                NSURL *thisSavedObjectURI = [[(NSManagedObject *)thisSavedObject objectID] URIRepresentation];
                [thisSavedObjectSetEncoded addObject:thisSavedObjectURI];
            }
            // Archive the set of URIs.
            [savedObjectsEncodedURIs setObject:[NSArchiver archivedDataWithRootObject:thisSavedObjectSetEncoded] forKey:thisSavedObjectKey];
        }

        if ([[savedObjectsEncodedURIs allValues] count] > 0) {
            // Tell UI process there are new objects that need merging into it's MOC
            [[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"com.synapticmishap.lapsus.save" object:@"HelperApp" userInfo:(NSDictionary *)savedObjectsEncodedURIs];
        }
    }

Código de interfaz de usuario

-(void)mergeSavesIntoMOC:(NSNotification *)notification {
    NSDictionary        *objectsToRefresh        = [notification userInfo];
    NSMutableDictionary *notificationUserInfo    = [NSMutableDictionary dictionary];
    NSArray *savedObjectKeys = [[notification userInfo] allKeys];

    for(NSString *thisSavedObjectKey in savedObjectKeys) {
        // Iterate through all the URIs in the decoded set. For each URI, get the NSManagedObject and add it to a set.
        NSSet *thisSavedObjectSetDecoded = [NSUnarchiver unarchiveObjectWithData:[[notification userInfo] objectForKey:thisSavedObjectKey]];
        NSMutableSet *savedManagedObjectSet = [NSMutableSet set];

        for(NSURL *thisSavedObjectURI in thisSavedObjectSetDecoded) {
            NSManagedObject *thisSavedManagedObject = [managedObjectContext objectWithID:[persistentStoreCoordinator managedObjectIDForURIRepresentation:thisSavedObjectURI]];
            [savedManagedObjectSet addObject:thisSavedManagedObject];
            // If the object is to be updated, refresh the object and merge in changes.
            // This doesn't work!
            if ([thisSavedObjectKey isEqualToString:NSUpdatedObjectsKey]) {
                [managedObjectContext refreshObject:thisSavedManagedObject mergeChanges:YES];
                [managedObjectContext save:nil];
            }
        }
        [notificationUserInfo setObject:savedManagedObjectSet forKey:thisSavedObjectKey];
    }
    // Build a notification suitable for merging changes into MOC.
    NSNotification *saveNotification = [NSNotification notificationWithName:@"" object:nil userInfo:(NSDictionary *)notificationUserInfo];
    [managedObjectContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)
                                    withObject:saveNotification
                                 waitUntilDone:YES];
}
¿Fue útil?

Solución

Estás buscando - (vacío) refreshObject: (NSManagedObject *) object mergeChanges: (BOOL) flag, creo.

Esto actualizará el objeto con la información en el almacén persistente, fusionando los cambios si lo desea.

Otros consejos

Usé el método en

http://www.mlsite.net/blog/?p=518

entonces cada objeto tiene una falla correcta, pero las fallas se obtienen en la memoria caché, por lo que aún no hay actualización

tuve que hacer [moc stalenessInterval = 0];

Y finalmente funcionó, con relación.

Iría con la sugerencia de Mike y solo miraría el archivo de la tienda para ver los cambios.

Aunque puede que no sea el más eficiente, he tenido éxito usando - [NSManagedObjectContext reset] de un segundo proceso cuando hay un cambio en una tienda. En mi caso, el código es bastante lineal & # 8212; todo lo que hago es ejecutar una solicitud de búsqueda de algunos datos después de reiniciar. No sé cómo funcionará esto con los enlaces y una IU complicada, pero es posible que pueda publicar una notificación para actualizar manualmente las cosas si no se maneja automáticamente.

Tuve exactamente el mismo problema con una aplicación para iPhone en la que he estado trabajando. En mi caso, la solución consistía en establecer el intervalo de inactividad del contexto en algo adecuadamente infinitesimal (por ejemplo, 0,5 segundos).

Esto funciona, excepto para las aplicaciones de sandboxes. No puede enviar una notificación con una información de usuario dict. En su lugar, considere algún otro IPC como XPC o DO.

En una nota al margen, usar NSDustributedNotificationCenter no siempre es 100% si el sistema está ocupado.

Establecimiento de stalenessInterval del contexto del objeto gestionado funciona. Sin embargo, mi caso involucra múltiples hilos en lugar de proceso.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top