Antecedentes¿Cómo comparto un almacén de datos centrales entre procesos utilizando NSDistributedNotifications?
Ya hemos publicado una pregunta sobre los fundamentos de sharing a Core Data store between processes.
Estoy tratando de implementar las recomendaciones dadas y estoy teniendo problemas.
Mi meta
que tienen 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 Helper haya guardado datos nuevos en la tienda.
el flujo del programa actual
El proceso de aplicación auxiliar escribe datos en la tienda.
En la aplicación de ayuda, escucho para las notificaciones NSManagedObjectContextDidSaveNotification.
Cuando se guarda el contexto, codificar los objetos insertados, eliminados y actualizados utilizando sus representaciones URI y NSArchiver.
envío un NSNotification a la NSDistributedNotificationCenter con este diccionario codificado como el userInfo.
El proceso de IU está escuchando para guardar la notificación. Cuando recibe la notificación, desarchiva el userInfo utilizando NSUnarchiver.
Se busca todos los objetos actualizados/insertadas/suprimidas de los URIs dados y los reemplaza con NSManagedObjects.
construye un NSNotification con los objetos actualizados/insertadas/borrados.
Llamo a mergeChangesFromContextDidSaveNotification: en el contexto del objeto gestionado del proceso UI, pasando NSNotification I construido en el paso anterior.
El problema
objetos insertados se criticaron en el interfaz de usuario de objeto gestionado bien Contexto y aparecen en la interfaz de usuario. El problema viene con los objetos actualizados. Simplemente no se actualizan.
Lo que he tratado
Lo más obvio sería intentar sea para pasar el Notificación Guardar en el proceso de aplicación auxiliar al proceso interfaz de usuario. Fácil, ¿verdad? Bueno no. Notificaciones distribuido no permitirán que haga que a medida que el diccionario userInfo no está en la derecha formato. Es por eso que estoy haciendo todas las cosas de NSArchiving .
He intentado llamar refreshObject: mergeChanges: SÍ a la los NSManagedObjects a ser actualizados, pero esto no parece tener ningún efecto .
He intentado realizar la mergeChangesFromContextDidSaveNotification: selector en el hilo principal y el hilo actual . Tampoco parece afectar el resultado.
He intentado usar mergeChangesFromContextDidSaveNotification: antes entre los hilos, que de curso es mucho más simple y funcionó perfectamente . Pero necesito esta misma funcionalidad entre procesos.
¿Alternativas?
¿Falta algo aquí? Constantemente estoy sintiendo que estoy haciendo esto mucho más complejo de lo necesario, pero después de leer la documentación varias veces y pasar unos días seguidos sobre esto, no veo otra forma de actualizar el MOC de la interfaz de usuario.
¿Hay una manera más elegante de hacer esto? ¿O estoy cometiendo un error 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.
ayudante Código aplicación
-(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];
}
¿Hay algún motivo por el que intente utilizar la comunicación entre procesos en lugar de mirar el archivo de almacenamiento persistente para ver cuándo se actualiza? –
No realmente. Pero si vi el archivo de la tienda, necesitaría refrescar el contexto del objeto administrado y la única manera que puedo encontrar para hacerlo es mediante el método mergeChangesFromContextDidSaveNotification :. Sospecho que me falta una forma muy obvia de refrescar el MOC sin depender de este método. –
'for (id thisSavedObject en [thisSavedObjectSet allObjects])' podría escribirse como 'for (id thisSavedObject en thisSavedObjectSet)' – user102008