2010-12-17 13 views
7

alt textHilfe mit Multithread Core-Daten App-Design

Oben ist eine Vereinfachung, wie mein Modell aussieht. Meine App hat ein NSWindowController Objekt, das zwei NSViewController Objekte für die Benutzer und Konto Entitäten steuert. Wenn sich ein Benutzer bei der App anmeldet, kann er Benutzer- oder Kontoinformationen ändern, indem er den entsprechenden View-Controller aufruft. Im Hintergrund habe ich die Anwendung, die regelmäßig die Benutzerprotokolle in dem Anwendungsdelegaten in einem separaten Thread auffüllt.

Ich verwende eine separate NSManagedObjectContext für den Hintergrund-Thread und die Anwendung Delegate NSManagedObjectContext für die Dateneingabe in den View-Controller. Ich würde gerne ein paar Dinge wissen:

1) ist das eine gute Praxis? Soll ich für jeden View-Controller eine NSManagedObjectContext erstellen und die Kontexte dann zusammenführen, wenn der Benutzer Änderungen vorgenommen hat?

2) Da die log Entität im Hintergrund-Thread erstellt wird, hat sie es selbst ist NSManagedObjectContext. Jedes Protokoll enthält jedoch Informationen von den Benutzer und Konto Entitäten, die in NSManagedObjectContext des Anwendungsdelegaten erstellt werden. Dies ist, wie ich einen Benutzer bin holen:

- (NSManagedObjectID*) fetchUser:(NSString*) userID { 
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; 
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"user":inManagedObjectContext:self.managedObjectContext]; 
    /** snip **/ 
} 

Diese Methode wird von dem Hintergrund-Thread wird wie folgt aufgerufen:

NSManagedObjectID* userObjectID = [self fetchUser:userID]; 
NSManagedObject* userObject = [self.logsManagedObjectContext objectWithID:userObjectID]; 

Ist das, was in fetchUser thread-safe ich tue? Muss ich den Hauptkontext des verwalteten Objekts sperren, während ich einen Benutzer abrufe, falls eine der Ansichten denselben Benutzer ändert? Von this article verstehe ich (vielleicht falsch), dass ich es tun muss. Bisher habe ich noch keine Probleme bekommen, aber ich möchte keinen potentiellen Randfall hinter mir lassen.

3) Wenn eine der View-Controller NSManagedObjectContext Änderungen an der Anwendung Delegierten macht es eine Benachrichtigung Beiträge, die behandelt wird, wie folgt:

- (void)contextDidSave:(NSNotification *)notification { 
    SEL selector = @selector(mergeChangesFromContextDidSaveNotification:); 
    [self.logManagedObectContext performSelector:selector onThread:backgroundThread withObject:notification waitUntilDone:NO]; 
} 

das ist, wie ich die Zusammenführung behandeln soll oder sollte ich die Verschmelzung werden Anwendungsdelegate NSManagedObjectContext stattdessen? Ich habe festgestellt, dass das (auf dem Hauptthread) die Benutzeroberfläche gesperrt hat.

Jede Hilfe wird geschätzt.

Antwort

9

NSManagedObjectContext Objekte sind nicht threadsicher. Dies bedeutet, dass Sie, wenn Sie aus mehreren Threads auf Core Data zugreifen möchten, für jeden Thread einen benötigen (und auch für den Thread erstellt haben). Jeder von diesen kann denselben NSPersistentStoreCoordinator verwenden, der den Zugriff auf den persistenten Speicher serialisieren wird.

Dies geschieht, weil jeder NSManagedObjectContextweiß, wie richtig die NSPersistentStoreCoordinator zu sperren, wenn es in Gebrauch ist, Vermeidung von Kollisionen. Wenn Sie diese Regeln befolgen, sollten Sie threadsicher bleiben.

Wie Sie bereits tun, sollten NSManagedObjectID Objekte verwendet werden, um Core Data-Objekte von einem MOC zum anderen (und von einem Thread zum anderen) zu übergeben.Allerdings rufen Sie fetchUser:, die die MOC aus Ihrem Haupt-Thread, auf einem Hintergrund verwendet. Das ist nicht korrekt. Der Methodenaufruf fetchUser: muss vom Hauptthread aufgerufen werden. Natürlich gibt es nichts, was Sie daran hindern könnte, den Benutzer im Hintergrund-Thread mit dem Hintergrund-MOC abzurufen.

Zusammengefasst machen immer Anrufe an eine NSManagedObjectContext aus dem Thread es erstellt wurde.

Die hier Trick, um sicherzustellen, dass beide MOCs über die anderen wissen, speichert, so dass Sie die Meldungen von zu erhalten, müssen registrieren jeder Kontext. Sie sollten dann die mergeChangesFromContextDidSaveNotification: aus dem entsprechenden Thread für den MOC durchführen. Momentan wird Ihr Hintergrundkontext über Änderungen aus dem Kontext des Hauptthreads benachrichtigt, aber nicht umgekehrt.

Oh, und es gibt keinen Bedarf, einen separaten Kontext für jede NSViewController zu haben. Als UI-Elemente werden ihre Interaktionen mit dem Kontext im selben (Haupt-) Thread ausgeführt, sodass die Freigabe in Ordnung ist.

+0

Ich wusste nicht, ich könnte nur den Benutzer aus dem Hintergrund MOC abrufen. Alles, was ich tun muss, ist ihnen den gleichen ** NSPersistentStoreCoordinator **? Wenn ich das tue, muss ich Objekt-IDs abrufen oder kann ich das Objekt direkt abrufen? – David

+0

Sie können die Objekte direkt abrufen. Achten Sie darauf, Code zu schreiben, um Änderungen bei der Benachrichtigung von anderen MOCs, die denselben Koordinator verwenden, zusammenzuführen. – paulbailey