NsoutlineView no refrescante cuando los objetos agregados al contexto de objetos administrados desde NSOPERACIONES

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

Pregunta

Fondo

  • Aplicación de cacao utilizando datos básicos Dos procesos: demonio y una interfaz de usuario principal
  • Daemon escribiendo constantemente en un almacén de datos
  • El proceso de interfaz de usuario se lee en el mismo almacén de datos
  • Columnas en nsoutlineview en la interfaz de usuario vinculada a un nstreecontroller
  • NstreeControllers ManagedObjectContext está vinculado a la aplicación con la ruta clave de Delegate.interpretedMoc
  • La entidad nstreeControllers está establecida en el grupo de entrenamiento (la subclase nsmanageDobject se llama JGTrainingGroup)

Lo que quiero

Cuando se activa la interfaz de usuario, la vista de esquema debe actualizarse con los últimos datos insertados por el demonio.

El problema

Enfoque de hilo principal

Llevo a todas las entidades que me interesan, luego itero sobre ellas, haciendo FRESHOBJECT: MergeChanges: Sí. Esto funciona bien: los elementos se actualizan correctamente. Sin embargo, todo esto se ejecuta en el hilo principal, por lo que la interfaz de usuario se bloquea durante 10-20 segundos mientras se refresca. Bien, así que mueva estas actualizaciones a las operaciones que se ejecutan en segundo plano.

Enfoque multiproceso de la operación

Tan pronto como muevo el Bold de actualización: MergeChanges: llame a una operación n, la actualización ya no funciona. Cuando agrego mensajes de registro, está claro que los nuevos objetos están cargados por la subclase NSOperation y se actualizan. Parece que no importa lo que haga, el NsoutLineView no se actualizará.

Lo que he probado

Me he metido con esto durante 2 días sólidos e intenté todo lo que puedo pensar.

  • Pasando objetos a la operación ns para actualizar en lugar de un nombre de entidad.
  • Restablecer el InterpretedMoc en varios puntos: después de la actualización de los datos y antes de la recarga de la vista de esquema.
  • Subclasgué nsoutlineview. Descargué mi subclase y configuré la vista para ser una instancia de NsoutLineView, en caso de que hubiera algo divertido aquí.
  • Se agregó una llamada ReordRangeObjects al NSTREEController antes de recargar los datos NSOUTLINEW.
  • Se aseguró de haber establecido el intervalo de acalidez en 0 en todos los contextos de objetos administrados que estaba usando.

Tengo la sensación de que este problema está relacionado de alguna manera con el almacenamiento en caché de los objetos de datos principales en la memoria. Pero he agotado totalmente todas mis ideas sobre cómo hago que esto funcione.

Estaría eternamente agradecido con cualquiera que pueda arrojar cualquier luz de por qué esto podría no estar funcionando.

Código

Enfoque de hilo principal

// In App Delegate
-(void)applicationDidBecomeActive:(NSNotification *)notification {
    // Delay to allow time for the daemon to save
    [self performSelector:@selector(refreshTrainingEntriesAndGroups) withObject:nil afterDelay:3];
}

-(void)refreshTrainingEntriesAndGroups {
    NSSet *allTrainingGroups    = [[[NSApp delegate] interpretedMOC] fetchAllObjectsForEntityName:kTrainingGroup];
    for(JGTrainingGroup *thisTrainingGroup in allTrainingGroups)
        [interpretedMOC refreshObject:thisTrainingGroup mergeChanges:YES];

    NSError *saveError = nil;
    [interpretedMOC save:&saveError];
    [windowController performSelectorOnMainThread:@selector(refreshTrainingView) withObject:nil waitUntilDone:YES];
}

// In window controller class
-(void)refreshTrainingView {
    [trainingViewTreeController rearrangeObjects]; // Didn't really expect this to have any effect. And it didn't.
    [trainingView reloadData];
}

Enfoque multiproceso de la operación

// In App Delegate (just the changed method)
-(void)refreshTrainingEntriesAndGroups {
    JGRefreshEntityOperation  *trainingGroupRefresh = [[JGRefreshEntityOperation alloc] initWithEntityName:kTrainingGroup];
    NSOperationQueue          *refreshQueue = [[NSOperationQueue alloc] init];
    [refreshQueue setMaxConcurrentOperationCount:1];
    [refreshQueue addOperation:trainingGroupRefresh];

    while ([[refreshQueue operations] count] > 0) {
        [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.05]];

    // At this point if we do a fetch of all training groups, it's got the new objects included. But they don't show up in the outline view.
    [windowController performSelectorOnMainThread:@selector(refreshTrainingView) withObject:nil waitUntilDone:YES];
}

// JGRefreshEntityOperation.m
@implementation JGRefreshEntityOperation

@synthesize started;
@synthesize executing;
@synthesize paused;
@synthesize finished;

-(void)main {
    [self startOperation];

    NSSet *allEntities    = [imoc fetchAllObjectsForEntityName:entityName];
    for(id thisEntity in allEntities)
        [imoc refreshObject:thisEntity mergeChanges:YES];

    [self finishOperation];
}

-(void)startOperation {
    [self willChangeValueForKey:@"isExecuting"];
    [self willChangeValueForKey:@"isStarted"];
    [self setStarted:YES];
    [self setExecuting:YES];
    [self didChangeValueForKey:@"isExecuting"];
    [self didChangeValueForKey:@"isStarted"];

    imoc = [[NSManagedObjectContext alloc] init];
    [imoc setStalenessInterval:0];
    [imoc setUndoManager:nil];
    [imoc setPersistentStoreCoordinator:[[NSApp delegate] interpretedPSC]];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(mergeChanges:) 
                                                 name:NSManagedObjectContextDidSaveNotification 
                                               object:imoc];
}

-(void)finishOperation {
    saveError = nil;

    [imoc save:&saveError];
    if (saveError) {
        NSLog(@"Error saving. %@", saveError);
    }

    imoc = nil;

    [self willChangeValueForKey:@"isExecuting"];
    [self willChangeValueForKey:@"isFinished"];
    [self setExecuting:NO];
    [self setFinished:YES];
    [self didChangeValueForKey:@"isExecuting"];
    [self didChangeValueForKey:@"isFinished"];
}

-(void)mergeChanges:(NSNotification *)notification {
    NSManagedObjectContext *mainContext = [[NSApp delegate] interpretedMOC];
    [mainContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)
                                  withObject:notification
                               waitUntilDone:YES];  

}

-(id)initWithEntityName:(NSString *)entityName_ {
    [super init];
    [self setStarted:false];
    [self setExecuting:false];
    [self setPaused:false];
    [self setFinished:false];
    [NSThread setThreadPriority:0.0];
    entityName = entityName_;
    return self;
}

@end

// JGRefreshEntityOperation.h
@interface JGRefreshEntityOperation : NSOperation {
    NSString *entityName;
    NSManagedObjectContext  *imoc;
    NSError *saveError;
    BOOL started;
    BOOL executing;
    BOOL paused;
    BOOL finished;
}

@property(readwrite, getter=isStarted) BOOL started;
@property(readwrite, getter=isPaused) BOOL paused;
@property(readwrite, getter=isExecuting) BOOL executing;
@property(readwrite, getter=isFinished) BOOL finished;

-(void)startOperation;

-(void)finishOperation;

-(id)initWithEntityName:(NSString *)entityName_;

-(void)mergeChanges:(NSNotification *)notification;

@end

Actualización 1

Acabo de encontrar esta pregunta. No puedo entender cómo me lo perdí antes de publicar el mío, pero el resumen es: Los datos principales no estaban diseñados para hacer lo que estoy haciendo. Solo un proceso debe usar un almacén de datos.

NsmanagedObjectContext y nsArrayController restablecer/actualizar problema

Sin embargo, en un área diferente de mi aplicación tengo dos procesos que comparten un almacén de datos con uno que solo lee solo el acceso y esto pareció funcionar bien. Además, ninguna de las respuestas a mi Última pregunta sobre este tema mencionó que esto no fue compatible con los datos básicos.

Voy a rearquitectar mi aplicación para que solo un proceso escriba en el almacén de datos en cualquier momento. Sin embargo, todavía soy escéptico de que esto resolverá mi problema. Me parece más como un problema refrescante de NsoutlineView: los objetos se crean en el contexto, es solo que la vista de esquema no los recoge.

¿Fue útil?

Solución

Terminé rearquitectando mi aplicación. Solo estoy importando elementos de un proceso u otro a la vez. Y funciona perfectamente. ¡Hurra!

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