Frage

Ich bin mit Grand Central Dispatch (GCD) in meiner Anwendung einig schweres Heben zu tun. Die Anwendung wird mit Kern-Daten für Datenspeicherzwecke. Hier ist mein Szenario (zusammen mit relevanter Frage):

dispatch_queue_t main_queue = dispatch_get_main_queue();
dispatch_queue_t request_queue = dispatch_queue_create("com.app.request", NULL);

dispatch_async(request_queue, ^{
    MyNSManagedObject *mObject = [self.fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];

    // … 
    // <heavy lifting>
    // … 

    // … 
    // <update mObject>
    // … 

    [self saveManagedObjectContext];
});     

Als Ergebnis [self saveManagedObjectContext] werden fetchResultsController Delegatmethoden automatisch aufgerufen. Folglich sind die UI-Aktualisierungs-Logik Kicks.

Nun meine Frage ist, brauche ich main_queue für -saveManagedObjectContext zu benutzen? Soll ich alle Operationen auf meinem NSManagedObject in main_queue durchführen? Einige der Operationen, die die NSManagedObject aktualisieren könnten 2-3 Sekunden dauern. Bitte beraten.

War es hilfreich?

Lösung

Wie Sie wahrscheinlich wissen, oder haben Sie bemerkt, UI-Operationen auf dem Haupt-Thread ausführen muss. Wie Sie erwähnen ist es, wenn Sie das UI-Update stattfindet speichern. Sie können dieses Problem lösen, indem ein Aufruf an dispatch_sync auf dem Hauptthread nisten.

dispatch_queue_t main_queue = dispatch_get_main_queue();
dispatch_queue_t request_queue = dispatch_queue_create("com.app.request", NULL);

__block __typeof__(self) blockSelf = self;

dispatch_async(request_queue, ^{
    MyNSManagedObject *mObject = [blockSelf.fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];

    // update and heavy lifting...

    dispatch_sync(main_queue, ^{
      [blockSelf saveManagedObjectContext];
    });
});     

Die Verwendung von blockSelf ist die Schaffung Referenzzyklus versehentlich zu vermeiden. ( praktische Blöcke )

Andere Tipps

Es gibt eine goldene Regel, wenn es um Core Data kommt - ein Managed Object Context pro Thread. Verwaltetes Objekt Kontexte Thread nicht sicher so, wenn Sie arbeiten in einem Hintergrundaufgabe tun Sie entweder den Haupt-Thread verwenden, um zu vermeiden Konflikte mit UI-Operationen Threading, oder Sie erstellen einen neuen Kontext die Arbeit in zu tun. Wenn die Arbeit geht zu nehmen ein paar Sekunden, dann sollten Sie das letztere tun, um Ihre UI Blockieren zu stoppen.

Dazu müssen Sie einen neuen Kontext schaffen und geben ihm den gleichen persistenten Speicher als Haupt-Kontext:

NSManagedObjectContext *backgroundContext = [[[NSManagedObjectContext alloc] init] autorelease];
[backgroundContext setPersistentStoreCoordinator:[mainContext persistentStoreCoordinator]];

tun, was Operationen, die Sie tun müssen, dann, wenn Sie speichern diesem neuen Kontext Sie die speichern Benachrichtigung behandeln müssen und die Änderungen in Ihr Haupt Zusammenhang mit der mergeChangesFromContextDidSaveNotification: Nachricht verschmelzen. Der Code soll wie folgt aussehen:

/* Save notification handler for the background context */
- (void)backgroundContextDidSave:(NSNotification *)notification {
    /* Make sure we're on the main thread when updating the main context */
    if (![NSThread isMainThread]) {
        [self performSelectorOnMainThread:@selector(backgroundContextDidSave:)
                               withObject:notification
                            waitUntilDone:NO];
        return;
    }

    /* merge in the changes to the main context */
    [self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
}

/* ... */

/* Save the background context and handle the save notification */
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(backgroundContextDidSave:)
                                             name:NSManagedObjectContextDidSaveNotification
                                           object:backgroundContext];

[backgroundContext save:NULL];

[[NSNotificationCenter defaultCenter] removeObserver:self
                                                name:NSManagedObjectContextDidSaveNotification
                                              object:syncContext];

Handhabung des Speicherns notifcation und Zusammenführung wichtig ist, sonst wird Ihr Haupt-UI / Kontext werden die Änderungen nicht sehen Sie gemacht. Durch die Zusammenführung, Haupt fetchResultsController etc. Änderungsereignisse erhalten und Ihre UI zu aktualisieren, wie Sie es erwarten würden.

Eine weitere wichtige Sache zu beachten ist, dass NSManagedObject Instanzen können nur im Kontext verwendet werden, dass sie aus abgerufen wurden. Wenn Ihr Betrieb einen Verweis auf ein Objekt benötigt, dann müssen Sie das objectID Objekt übergeben den Betrieb und erneut abzurufen eine NSManagedObject Instanz aus dem neuen Kontext mit existingObjectWithID:. So etwas wie:

/* This can only be used in operations on the main context */
MyNSManagedObject *objectInMainContext =
    [self.fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];

/* This can now be used in your background context */
MyNSManagedObject *objectInBackgroundContext =
    (MyNSManagedObject *) [backgroundContext existingObjectWithID:[objectInMainContext objectID]];

Da Core Data eine Managed Object Context pro Thread erfordert, ist eine mögliche Lösung wäre, einen Kontext pro Thema in einem globalen Manager zu verfolgen, dann speichern Benachrichtigungen verfolgen und propagieren zu allen Themen:

Unter der Annahme:

@property (nonatomic, strong) NSDictionary* threadsDictionary;

Hier ist, wie das verwaltete Objekt zu erhalten (pro Thread):

- (NSManagedObjectContext *) managedObjectContextForThread {

// Per thread, give one back
NSString* threadName = [NSString stringWithFormat:@"%d",[NSThread currentThread].hash];

NSManagedObjectContext * existingContext = [self.threadsDictionary objectForKey:threadName];
if (existingContext==nil){
    existingContext = [[NSManagedObjectContext alloc] init];
    [existingContext setPersistentStoreCoordinator: [self persistentStoreCoordinator]];
    [self.threadsDictionary setValue:existingContext forKey:threadName];
}

return existingContext;

}

An einem gewissen Punkt in der Init-Methode des globalen Managers (I Singleton verwendet wird):

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(backgroundContextDidSave:)                                                    name:NSManagedObjectContextDidSaveNotification                                                   object:nil];

Dann speichern Benachrichtigung zu erhalten und zu allen anderen verwalteten Kontext Objekten propagieren:

- (void)backgroundContextDidSave:(NSNotification *)notification {
    /* Make sure we're on the main thread when updating the main context */
    if (![NSThread isMainThread]) {
        [self performSelectorOnMainThread:@selector(backgroundContextDidSave:)
                               withObject:notification
                            waitUntilDone:NO];
        return;
    }

    /* merge in the changes to the main context */
    for (NSManagedObjectContext* context in [self.threadsDictionary allValues]){
            [context mergeChangesFromContextDidSaveNotification:notification];
    }
}

(einige andere Methoden wurden zur Klarheit entfernt)

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