Ich habe eine Reihe von Posts über NSManagedObjectContext und Multithread-Anwendungen gelesen. Ich bin auch über das CoreDataBooks-Beispiel gegangen, um zu verstehen, wie separate Threads ihren eigenen NSManagedObjectContext benötigen und wie eine Sicherungsoperation mit dem Haupt-NSManagedObjectContext zusammengeführt wird. Ich fand das Beispiel gut, aber auch zu anwendungsspezifisch. Ich versuche das zu verallgemeinern und frage mich, ob meine Herangehensweise vernünftig ist.Generischer Ansatz für NSManagedObjectContext in Multi-Thread-Anwendung
Mein Ansatz besteht darin, eine generische Funktion zum Abrufen des NSManagedObjectContext für den aktuellen Thread zu haben. Die Funktion gibt den NSManagedObjectContext für den Hauptthread zurück, erstellt jedoch einen neuen (oder ruft ihn aus einem Cache ab), wenn er in einem anderen Thread aufgerufen wird. 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];
}
Speichern Operationen sind einfach, wenn sie aus dem Hauptthread aufgerufen werden. Speicheroperationen, die von anderen Threads aufgerufen werden, müssen im Hauptthread zusammengeführt werden. 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 wir die Zusammenführung durchführen, durch die Benachrichtigung in commit
aufgerufen, wenn.
+(void)contextDidSave:(NSNotification*)saveNotification {
MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *moc = delegate.managedObjectContext;
[moc performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)
withObject:saveNotification
waitUntilDone:YES];
}
Schließlich haben wir den Cache NSManagedObjectContext mit diesem clean-up:
+(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];
}
Dies kompiliert und scheint zu funktionieren, aber ich weiß Threading Probleme Rennbedingungen aufgrund schwierig sein kann. Sieht jemand ein Problem mit diesem Ansatz?
Auch ich benutze dies aus dem Kontext einer asynchronen Anfrage (mit ASIHTTPRequest), die einige Daten von einem Server holt und aktualisiert und fügt den Speicher auf dem iPhone ein. Es scheint, dass NSThreadWillExitNotification nicht ausgelöst wird, nachdem die Anforderung abgeschlossen wurde, und derselbe Thread wird dann für nachfolgende Anforderungen verwendet. Dies bedeutet, dass derselbe NSManagedObjectContext für separate Anforderungen für denselben Thread verwendet wird. Ist das ein Problem?
Chris, ich bin mit einem ähnlichen Multi Threading-Problem konfrontiert, während eine einzige NSManagedObjectContext im Haupt-Thread für alle Operationen in NSOperation Queue erstellt. Das Problem tritt auf, wenn jeder Thread versucht, den Kontext zu speichern. Die App stürzt zufällig ab und löst eine Ausnahme aus den Kerndaten aus. Ich dachte daran, diesen Kontext während der Verwendung aller Operationen zu sperren, so dass jeder von ihnen einen exklusiven Zugriff auf den Kontext hat. Ich habe gelesen, dass Ihr oben solution.sounds gud, bitte fügen Sie mir den neuen Code, den Sie für die Zusammenführung des Kontextes verwendet haben und auch kommentieren Sie bitte mit einem Schloss für playin –