ios - operador - NSFetchedResultsController no muestra actualizaciones desde un contexto diferente
ios 12 (2)
Solo tuve el mismo problema, que resolví con contextos de padres / hijos. Este es el problema que tuve.
Estaba actualizando mi gráfico de objetos de Core Data en un hilo de fondo que tenía su propio managedObjectContext
(que es obligatorio), y mi fetchedResultsController
no pudo recoger los cambios realizados en la base de datos.
Después de que lo resolví, tomé algunas notas:
ManagedObjectContext
s no es seguro para subprocesos, lo que significa que managedObjectContext
no se puede compartir con otros subprocesos. Si un hilo necesita usar un managedObjectContext
, entonces inicializará su propio managedObjectContext.
Para inicializar managedObjectContext
hay dos formas:
alloc/init
luego establece su propiedadpersistentStoreCoordinator
alloc/init
establece luego una propiedadparentContext
lugar de una propiedadpersistentStoreCoordinator
nota: uno no puede establecer tanto la propiedad persistentStoreCoordinator
como la propiedad parentContext
de un managedObjectContext
.
El uso de contextos padre / hijo es necesario cuando el contexto se ejecuta en una secuencia de fondo que está "vinculada a controladores y objetos de interfaz de usuario que deben utilizarse solo en el hilo principal" (documentación de datos básicos).
Estos son los requisitos necesarios para los contextos padre / hijo:
el contexto principal vive en el hilo principal
el contexto del niño vive en el hilo de fondo
ambos contextos deben inicializarse con un método
initWithConcurrencyType:NSMainQueueConcurrencyType
.cuando el lote de cambios se ha realizado en el contexto secundario, ambos contextos deben realizar una operación de salvar. Estas operaciones de salvar deben estar anidadas en los métodos performBlock, es decir:
childContext performBlock:^{ [childContext save:nil]; [self.parentContext performBlock:^{ [self.parentContext save:nil]; }]; }];
EDITAR: El código anterior es en realidad una mala idea por 2 razones:
1) Funciona sin guardar el contexto principal.
2) El hilo principal está bloqueado si el contexto principal se ejecuta en él.
¡Espero que haya sido de ayuda!
EDITAR: Aquí hay un hilo de StackOverflow que me ayudó mucho: ¿necesita un ManagedObjectContext principal de Core Data compartir un tipo de concurrencia con el contexto hijo?
Tengo un NSFetchedResultsController
y unas pocas operaciones actualizan los objetos administrados en hilos separados a través de NSOperationQueue
.
El FRC (con su predicado) se ve así:
- (NSFetchedResultsController*)fetchedResultsController
{
if(fetchedResultsController) return fetchedResultsController;
NSManagedObjectContext* mainContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:@"Check" inManagedObjectContext:mainContext]];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"isSync == %@", [NSNumber numberWithBool:NO]]];
[fetchRequest setFetchBatchSize:10];
fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:mainContext sectionNameKeyPath:nil cacheName:nil];
fetchedResultsController.delegate = self;
[fetchRequest release], fetchRequest = nil;
return fetchedResultsController;
}
El hilo principal y la operación roscada tienen sus propios contextos de objetos gestionados. Solo comparten el mismo coordinador.
Dentro de la operación con hilos, cambio la propiedad isSync
de NO
a YES
. Para saber qué es la entidad Check
para actualizar, el contexto principal pasa al elemento con subprocesos un NSManagedObjectID
. La operación con hebra recupera el objeto administrado de la siguiente manera:
-(void)main
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSManagedObjectContext *exportContext = [[NSManagedObjectContext alloc] init];
[exportContext setPersistentStoreCoordinator:[self persistentStoreCoordinator]];
//...
Check* check = (Check*)[exportContext existingObjectWithID:objID error:&error];
check.isSync = [NSNumber numberWithBool:YES];
//...
[exportContext save:&error];
[pool release], pool = nil;
}
Cuando la operación de subproceso llama a save
llama a la notificación mergeChangesFromContextDidSaveNotification
y el contexto principal combina los cambios.
- (void)contextChanged:(NSNotification*)notification
{
if ([notification object] == [self managedObjectContext]) return;
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:@selector(contextChanged:) withObject:notification waitUntilDone:YES];
return;
}
[[self managedObjectContext] mergeChangesFromContextDidSaveNotification:notification];
}
El registro de la descripción de la notification
lleva a verificar que los cambios se realicen correctamente.
Mi problema
Los métodos delegados de NSFetchedResultsControllerDelegate
no son invocados.
Esto es bastante extraño ya que tratar con el mismo contexto, el principal, permite escuchar los cambios y se llaman los métodos de delegados, por ejemplo, eliminar un objeto de fila en UITableView
.
He encontrado algunos temas sobre SO con el mismo problema. He intentado todas las soluciones pero no puedo encontrar una solución valiosa:
Gracias de antemano.
Editar
El código anterior funcionaba en un modelo anterior. Luego creé un nuevo modelo para copiar (y pegar) entidades del anterior y ahora ya no funciona.
Sugerencias?
Editar 2
Este es el predicado que estoy usando en NSFetchedResultsController
getter. Es mi culpa, pero cuando escribí la publicación no la copié.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"insertionDate" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
// previous code here
[fetchRequest setSortDescriptors:sortDescriptors];
Ahora, sobre Jody último comentario
En la parte principal () de su NSOperation, está cargando nuevos objetos, y allí parece que está configurando isSync en YES para cada nuevo objeto. El predicado que utiliza para fetchedResultsController busca solo objetos que tengan isSync == NO.
Supongo que cuando la propiedad isSync
se establece en SÍ, NSFetchedResultsController
observa que los cambios y elimina las filas que no coinciden con el predicado. ¿Me equivoco?
Recuerde que al fusionar los cambios del fondo al hilo principal, puedo ver que pocos objetos han actualizado su propiedad isSync
.
Tienes la idea básica, por lo que probablemente haya un error en algún lugar de tu código ...
Verifique que se está registrando correctamente para recibir la notificación del MOC de fondo.
Regístrese para recibir todas las notificaciones de todos los objetos. En ese método, registre el evento y todos sus datos. Cuando el objeto es un MOC, vacíe todas sus propiedades (especialmente las listas de objetos registrados, insertados, actualizados y eliminados).
Coloque una declaración de registro justo antes y después de la llamada guardada, y en el controlador de notificación para fusionar la notificación.
Además, omitiste muchos códigos, por lo que es difícil saber lo que realmente estás haciendo, pero el ejemplo del código que incluiste parece que es una configuración difícil. Sincroniza con SÍ para todos los objetos que se cargan, pero tu solicitud de búsqueda solo quiere aquellos con el conjunto isSync. a NO. Ninguno de esos objetos nuevos pasará ese predicado.
Finalmente, verifique dos veces la definición de su modelo y asegúrese de estar usando el tipo de número correcto. Esto puede ser una gran fuente de problemas.
EDITAR
Oh, sí, lo olvidé ... tu solicitud de búsqueda no tiene un descriptor de ordenación. Cuando crea un FRC, su solicitud de búsqueda debe contener al menos un descriptor de clasificación ... si tiene varias secciones, el primer descriptor de clasificación se usa para agrupar los objetos en secciones.
Para seguir con el comentario de Alexsander ... Aludí al principio de mi publicación, pero ciertamente no quieres escuchar las notificaciones de un MOC a menos que sea bien conocido como uno de los tuyos (a menos, por supuesto, que seas simplemente registrando para propósitos de depuración). Debes saber sobre el MOC que estás usando.
Además, sugeriría usar MOC padre / hijo para este tipo de procesamiento, pero lo que está haciendo debería funcionar si se hace correctamente.
Principal (tipo de concurrencia privada) Principal (tipo de concurrencia principal)
Luego, con sus MOC de fondo, solo pídales que configuren el moc principal como su padre. Cuando guardan, sus objetos se inyectan directamente en el MOC principal. El MOC principal puede entonces emite rescates en tiempos posteriores para ponerlos en el disco.
O bien, puede convertir su MOC de fondo en "padre" y luego el MOC "principal" puede volver a emitir la recuperación para obtener los datos del padre.