Allgemeiner Ansatz für NSManagedObjectContext in multi-threaded-Anwendung
Gelesen habe ich eine Reihe von posts hier über NSManagedObjectContext und multi-Thread-Anwendungen. Ich habe auch den Weg über die CoreDataBooks Beispiel zu verstehen, wie separate threads benötigen eine eigene NSManagedObjectContext, und wie ein speichern-operation wird zusammengeführt mit den wichtigsten NSManagedObjectContext. Ich fand das Beispiel gut zu sein, sondern auch zu Anwendung spezifisch. Ich bin versucht, zu verallgemeinern und Frage mich, ob mein Ansatz ist der Klang.
Mein Ansatz ist es, eine generische Funktion für das abrufen der NSManagedObjectContext für den aktuellen thread. Die Funktion gibt die NSManagedObjectContext für den Haupt-thread, aber einen neuen erstellen (oder Holen es aus einem cache) wenn der Aufruf aus einem anderen thread. Das geht wie folgt:
+(NSManagedObjectContext *)managedObjectContext {
MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *moc = delegate.managedObjectContext;
NSThread *thread = [NSThread currentThread];
if ([thread isMainThread]) {
return moc;
}
//a key to cache the context for the given thread
NSString *threadKey = [NSString stringWithFormat:@"%p", thread];
//delegate.managedObjectContexts is a mutable dictionary in the app delegate
NSMutableDictionary *managedObjectContexts = delegate.managedObjectContexts;
if ( [managedObjectContexts objectForKey:threadKey] == nil ) {
//create a context for this thread
NSManagedObjectContext *threadContext = [[[NSManagedObjectContext alloc] init] autorelease];
[threadContext setPersistentStoreCoordinator:[moc persistentStoreCoordinator]];
//cache the context for this thread
[managedObjectContexts setObject:threadContext forKey:threadKey];
}
return [managedObjectContexts objectForKey:threadKey];
}
Sparen Operationen sind einfach, wenn der Aufruf aus der Haupt-thread. Speichern von Vorgängen aufgerufen, aus anderen threads erfordern die Zusammenführung innerhalb des Haupt-thread. Dafür habe ich eine generische commit
Funktion:
+(void)commit {
//get the moc for this thread
NSManagedObjectContext *moc = [self managedObjectContext];
NSThread *thread = [NSThread currentThread];
if ([thread isMainThread] == NO) {
//only observe notifications other than the main thread
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(contextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:moc];
}
NSError *error;
if (![moc save:&error]) {
//fail
}
if ([thread isMainThread] == NO) {
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSManagedObjectContextDidSaveNotification
object:moc];
}
}
In der contextDidSave:
- Funktion, die wir ausführen des Seriendrucks, wenn durch die Benachrichtigung in commit
.
+(void)contextDidSave:(NSNotification*)saveNotification {
MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *moc = delegate.managedObjectContext;
[moc performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)
withObject:saveNotification
waitUntilDone:YES];
}
Endlich, wir bereinigen den cache NSManagedObjectContext:
+(void)initialize {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(threadExit)
name:NSThreadWillExitNotification
object:nil];
}
+(void)threadExit {
MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
NSString *threadKey = [NSString stringWithFormat:@"%p", [NSThread currentThread]];
NSMutableDictionary *managedObjectContexts = delegate.managedObjectContexts;
[managedObjectContexts removeObjectForKey:threadKey];
}
Diese kompiliert und scheint zu funktionieren, aber ich weiß, threading-Probleme kann tückisch sein, aufgrund von race conditions. Hat jemand ein problem mit diesem Ansatz?
Auch, ich bin mit diesem aus dem Kontext, dass eine asynchrone Anfrage (mit ASIHTTPRequest), die holt sich bestimmte Daten von einem server und Aktualisierungen und Einfügungen, die der store auf dem iPhone. Es scheint NSThreadWillExitNotification nicht ausgelöst, nachdem die Anforderung abgeschlossen ist, und der gleiche thread ist dann für nachfolgende Anforderungen verwendet. Dies bedeutet, dass die gleichen NSManagedObjectContext verwendet wird, für die separate Anfragen auf dem gleichen thread. Ist das ein problem?
InformationsquelleAutor chris | 2010-08-13
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ein Jahr nach der Veröffentlichung dieser Frage, die ich schließlich baute einen Rahmen zu verallgemeinern und vereinfachen meine Arbeit mit Core Data. Es geht über die ursprüngliche Frage, und fügt eine Reihe von Funktionen, die Core-Daten-Interaktionen viel einfacher. Details hier: https://github.com/chriscdn/RHManagedObject
InformationsquelleAutor chris
Ich habe eine Lösung gefunden, nachdem endlich das problem zu verstehen besser. Meiner Lösung nicht direkt auf die Frage oben, aber nicht das problem, warum ich zu tun hatte mit threads in den ersten Platz.
Meine Anwendung verwendet das ASIHTTPRequest-Bibliothek für asynchrone Anfragen. Ich hol ein paar Daten aus dem server, und verwenden Sie die delegate -
requestFinished
Funktion zum hinzufügen/ändern/löschen Sie meine Kern-Daten-Objekte. DierequestFinished
Funktion ausgeführt wurde, in einem anderen thread, und ich nahm an, dies war eine Natürliche Nebenwirkung von asynchronen Anfragen.Danach, tiefer zu Graben fand ich, dass ASIHTTPRequest bewusst läuft die Anfrage in einem separaten thread, aber können überschrieben werden, in meinem Unterklasse von ASIHTTPRequest:
Diese kleine änderung bringt
requestFinished
im main-thread, der das beseitigt hat meine kümmern müssen threads in meiner Anwendung.Ich fand, dass requestFinished nicht auf dem Haupt-thread, bis ich fügte hinzu, diese drei Zeilen oben. Ist es möglich, dies hat sich geändert mit ASIHTTPRequest? Ich bin mit v1.7.
Sind Sie richtig. Das ASIHTTPRequest-Delegat wird im Hauptthread ausgeführt, aber ich war die Implementierung eine Unterklasse von ASIHTTPRequest und setzen meinen code in der
requestFinished:
Methode. Dies muss nicht unbedingt aufgerufen werden, auf dem Haupt-thread. DankInformationsquelleAutor chris