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?

chris, ich stehe vor einem ähnlichen multi-threading-Problem, während die Verwendung einer einzelnen NSManagedObjectContext erstellt im Haupt-thread für alle Vorgänge im NSoperation Queue. Das probkem kommt bei jedem thread versucht zu retten, den Kontext, die app stürzt nach dem Zufallsprinzip eine Ausnahme von core data . ich dachte, die sperren, um diesen Kontext während der Verwendung in allen Operationen, so dass jeder von Ihnen einen exklusiven Zugriff auf den Kontext an . ich habe gelesen, ur obige Lösung.klingt gud , couls u bitte fügen Sie mich der neue code u verwendet haben, ist die Zusammenführung der Kontext-und auch bitte kommentieren Sie mit einer Sperre für playin

InformationsquelleAutor chris | 2010-08-13

Schreibe einen Kommentar