Ich schreibe gerade die Sync-Engine meiner iOS-App. Eine der Methoden, die ich schreibe, ist eine Reload-Datenfunktion, bei der die App die Daten des Nutzers sowie alle seine Fotos erneut herunterlädt. Dies ist eine teure Operation (zeitweise), so dass ich eine NSOperation
Unterklasse, SSReloadDataOperation
, erstellt habe. Er lädt die Daten herunter, ruft die Entität currentUser ab, entfernt alle vorhandenen Fotos aus diesem currentUser und füllt sie erneut auf.Kerndaten Thread- und Sperrkonfliktprobleme
Obwohl ich dachte, das war thread-safe, manchmal während der Betrieb läuft und -currentUser
von woanders zugegriffen wird, stürzt die App, vermutlich beim Versuch, es zu holen. Andere Zeiten, die Benutzeroberfläche manchmal nur friert, und die Pause im Debugger zeigt es immer an einem -currentUser
NSFetchRequest
Ausführungsaufruf.
Wie mache ich diese Operation Thread-sicher und atomar, so dass ich herunterladen kann und neu zu besiedeln, ohne das Haupt UI-Thread zu blockieren, und -currentUser
noch zugänglich sein? Gibt es etwas, das mir in Bezug auf die Verwendung von Schlössern oder Architektur fehlt? Vielen Dank!
Code:
- (void)main
{
// Download the photo data
[[SyncEngine defaultEngine] getUserPhotosWithCompletionBlock:^(NSMutableArray *photos)
{
if (photos)
{
// Create a new NSManagedObjectContext for this operation
SSAppDelegate* appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext* localContext = [[NSManagedObjectContext alloc] init];
[localContext setPersistentStoreCoordinator:[[appDelegate managedObjectContext] persistentStoreCoordinator]];
NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self
selector:@selector(mergeChanges:)
name:NSManagedObjectContextDidSaveNotification
object:localContext];
NSError* error;
NSFetchRequest* request = [[[SyncEngine defaultEngine] managedObjectModel] fetchRequestFromTemplateWithName:@"CurrentUser" substitutionVariables:[[NSDictionary alloc] init]];
User* currentUser = [[localContext executeFetchRequest:request error:&error] objectAtIndex:0];
// Remove the old data
[currentUser setPhotos:[[NSSet alloc] init]];
// Iterate through photo data, repopulate
for (Photo* photo in photos) {
[currentUser addPhotosObject:photo];
}
if (! [localContext save:&error]) {
NSLog(@"Error saving: %@", error);
}
NSLog(@"Completed sync!");
}
} userId:[[[SyncEngine defaultEngine] currentUser] userId]];
}
-currentUser bequeme Methode, in der Regel aus dem Haupt-Thread genannt.
- (User *)currentUser
{
NSError* error;
NSFetchRequest* request = [self.managedObjectModel fetchRequestFromTemplateWithName:@"CurrentUser" substitutionVariables:[[NSDictionary alloc] init]];
NSArray* result = [self.managedObjectContext executeFetchRequest:request error:&error];
if ([result count] == 1) {
return [result objectAtIndex:0];
}
return nil;
}
Martin R muss auf diesem einspielen. – sangony
Was meinen Sie mit "normalerweise aus dem Hauptthread" (nicht immer)? Außerdem entfernen Sie den Beobachter nicht in Ihrem Abschlussblock. Bitte teilen Sie die Absturzmeldung mit. Schließlich wäre es schön, sich die 'mergeChanges:' Methode anzuschauen. –