NSoutlineView nicht aktualisiert, wenn Objekte aus NSOperationen zum verwalteten Objektkontext hinzugefügt werden

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

Frage

Hintergrund

  • Kakao -App mit Kerndaten zwei Prozesse - Daemon und eine Haupt Benutzeroberfläche
  • Daemon schreibt ständig in einen Datenspeicher
  • UI -Prozess liest sich aus demselben Datenspeicher
  • Spalten in nSoutlineView in UI, die an einen Nstreecontroller gebunden sind
  • Nstreecontrollers ManagedObjectContext ist an die Anwendung mit dem Schlüsselpfad des Delegiertens gebunden. InterpretierteMoc
  • NstreeControllers Entity ist auf die Trainingsgruppe eingestellt (NSManagedObject Subclass wird als JgtrainingGroup bezeichnet)

Was ich möchte

Wenn die Benutzeroberfläche aktiviert ist, sollte die Gliederungsansicht mit den neuesten vom Dämon eingefügten Daten aktualisieren.

Das Problem

Hauptfadenansatz

Ich hole alle Einheiten, an denen ich interessiert bin, und itere dann über sie und mache Refreshobject: Mergechanges: Ja. Dies funktioniert in Ordnung - die Artikel werden korrekt aktualisiert. Dies ist jedoch alles auf dem Hauptfaden läuft, sodass die Benutzeroberfläche 10-20 Sekunden lang aufschlägt, während sie aktualisiert wird. Gut, also lass uns diese Aktualisierungen in NSOperationen verschieben, die stattdessen im Hintergrund laufen.

NSOPERATION Multithreaded -Ansatz

Sobald ich das Refreshobject: Mergechanges: Rufen Sie in eine NSOPERATION, funktioniert die Aktualisierung nicht mehr. Wenn ich Protokollierungsnachrichten hinzufüge, ist klar, dass die neuen Objekte von der NSOPERATIONS -Unterklasse geladen und aktualisiert werden. Es scheint, dass die NSoutlineView egal was ich tue, nicht aktualisiert wird.

Was ich versucht habe

Ich habe 2 Tage lang mit dem herumgespielt und alles ausprobiert, was ich mir vorstellen kann.

  • Übergeben von Objektids an die NSOPERATION, um anstelle eines Entitätsnamens zu aktualisieren.
  • Zurücksetzen des InterpretedMoc an verschiedenen Stellen - nach dem Auffrischen der Daten und vor dem Nachladen der Umrissansicht.
  • Ich hatte NSoutlineView unterklassifiziert. Ich habe meine Unterklasse weggeworfen und die Ansicht wieder auf eine Instanz von NSoutlineView gestellt, nur für den Fall, dass hier lustige Gehen vorhanden sind.
  • Ein Aufruf von ReprobeObjects fügte den NstreeController hinzu, bevor die NSoutlineView -Daten neu geladen wurden.
  • Ich sorgte dafür, dass ich das Pensionsintervall auf alle verwendeten Objektkontexte auf 0 gesetzt hatte.

Ich habe das Gefühl, dass dieses Problem irgendwie mit Caching -Kerndatenobjekten im Speicher zusammenhängt. Aber ich habe alle meine Ideen, wie ich das zum Laufen bringt, total erschöpft.

Ich wäre jedem, der Licht werfen kann, ewig dankbar, warum dies möglicherweise nicht funktioniert.

Code

Hauptfadenansatz

// 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];
}

NSOPERATION Multithreaded -Ansatz

// 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

Update 1

Ich habe diese Frage gerade gefunden. Ich kann nicht verstehen, wie ich es verpasst habe, bevor ich meine gepostet habe, aber die Zusammenfassung ist: Kerndaten wurden nicht so konzipiert, was ich tue. Nur ein Prozess sollte einen Datenspeicher verwenden.

NSManagedObjectContext und NSArrayController -Problem/Aktualisieren

In einem anderen Bereich meiner Anwendung habe ich jedoch zwei Prozesse, in denen ein Datenspeicher mit einem nur Zugriff gelesen wurde, und dies schien einwandfrei zu funktionieren. Plus keine der Antworten auf meine Letzte Frage zu diesem Thema erwähnte, dass dies in Kerndaten nicht unterstützt wurde.

Ich werde meine App erneut anarchieren, damit nur ein Prozess gleichzeitig in den Datenspeicher schreibt. Ich bin immer noch skeptisch, dass dies mein Problem lösen wird. Für mich sieht es mir eher nach einem Auffrischungsproblem von NSoutlineView aus - die Objekte werden im Kontext erstellt. Es ist nur die Umrissansicht, die sie nicht aufnimmt.

War es hilfreich?

Lösung

Am Ende brachte ich meine App neu an. Ich importiere nur Elemente aus dem einen oder anderen Prozess gleichzeitig. Und es funktioniert perfekt. Hurra!

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top