2012-09-24 12 views
10

Używam MagicalRecord 2.0.3 i nie mogę dowiedzieć się, jak zapisać dane w tle.Jak utworzyć wiele obiektów w tle?

Zgodnie z dokumentacją, coś jak to powinno działać:

[MagicalRecord saveInBackgroundWithBlock:^(NSManagedObjectContext *localContext) { 
    // Do this hundreds of times 
    [MyObject createInContext:localContext]; 
}]; 

Jednak nic nie jest zapisywane do bazy danych. Widziałem wiele osób opublikowania rozwiązania podobnego do tego:

[MagicalRecord saveInBackgroundWithBlock:^(NSManagedObjectContext *localContext) { 
    // Do this hundreds of times 
    [MyObject createInContext:localContext]; 
} completion:^{ 
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 
     [[NSManagedObjectContext defaultContext] saveNestedContexts]; 
    }]; 
}]; 

ten zapisuje moich danych w bazie danych, ale skoro Zapisz się stanie w głównym wątku, moja aplikacja nie reaguje na chwilę (z mojego zbioru danych, około 3 sekund, czyli o wiele za długo).

Próbowałem już także to, ale także blokuje się podczas zapisywania:

self.queue = [[NSOperationQueue alloc] init]; 

[self.queue addOperationWithBlock:^{ 
    NSManagedObjectContext *localContext = [NSManagedObjectContext contextForCurrentThread]; 

    // Do this hundreds of times 
    [MyObject createInContext:localContext]; 

    [localContext saveNestedContexts]; 
}]; 

I wreszcie sam efekt blokujący z tym kodem:

dispatch_queue_t syncQueue = dispatch_queue_create("Sync queue", NULL); 
dispatch_async(syncQueue, ^{ 
    NSManagedObjectContext *localContext = [NSManagedObjectContext contextForCurrentThread]; 

    // Do this hundreds of times 
    [MyObject createInContext:localContext]; 

    [[NSManagedObjectContext contextForCurrentThread] saveNestedContexts]; 
}); 

Więc, co jest najlepszym sposobem rozwiązać ten problem? Potrzebuję utworzyć setki obiektów w tle, a aplikacja musi pozostać responsywna.

+0

Nowe konteksty zagnieżdżone zaczęły siać spustoszenie na wiele zapisywanie API w MagicalRecord. Chociaż zdaję sobie sprawę z tych problemów, a niektóre poprawki są obecnie omawiane, jestem zawsze otwarty na sugestie. – casademora

+2

Być może najlepiej wykorzystać Core Data bez frameworka takiego jak MR? – Hunter

+0

Czy kiedykolwiek znalazłeś rozwiązanie za pomocą MagicalRecord? Mam takie same problemy (blokowanie interfejsu użytkownika podczas aktualizacji w tle) i nie mogę znaleźć rozwiązania. Dzięki! – RyanG

Odpowiedz

6

MagicalRecord wykorzystuje kontekst dziecko podczas wykonywania pracy w tle. Działa to dobrze dla małych zmian, ale stworzy nadmiernego blokowania główny wątek podczas importowania dużych ilości danych.

Aby to zrobić, użyj równoległego obiektu NSManagedObjectContext i wykonaj scalenie z powiadomieniem NSManagedObjectContextDidSaveNotification i metodą mergeChangesFromContextDidSaveNotification. Zobacz testy wydajności tutaj: http://floriankugler.com/blog/2013/5/11/backstage-with-nested-managed-object-contexts

Podczas zapisywania zagnieżdżonych kontekstów wszystko musi zostać skopiowane do kontekstu macierzystego. W przeciwieństwie do tego, obiekty, które nie zostały pobrane (w kontekście, do którego łączą) nie zostaną połączone przez mergeChangesFromContextDidSaveNotification. To sprawia, że ​​jest to szybsze.

może napotkać problemy, jeśli chcesz wyświetlić te wyniki od razu po zapisaniu w partiach i przy użyciu NSFetchResultsController. Zobacz następujące pytanie rozwiązania: NSFetchedResultsController with predicate ignores changes merged from different NSManagedObjectContext

Aby uzyskać więcej wskazówek wydajności spojrzeć na to pytanie: Implementing Fast and Efficient Core Data Import on iOS 5

Stwórz swój własny kontekst.

NSManagedObjectContext *importContext = [[NSManagedObjectContext alloc] 
          initWithConcurrencyType:NSPrivateQueueConcurrencyType]; 
[importContext setPersistentStoreCoordinator:yourPersistentStoreCoordinator]; 
[importContext setUndoManager:nil]; // For importing you don't need undo: Faster 

// do your importing with the new importContext 
// … 

NSError* error = nil; 
if(importContext.hasChanges) { 
    if(![importContext save:&error]) { 
     NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 
    } 
} 

Upewnij się, że nasłuchujesz zapisów w kontekście obiektów zarządzanych.

[[NSNotificationCenter defaultCenter] 
       addObserver:singleton 
       selector:@selector(contextDidSave:) 
        name:NSManagedObjectContextDidSaveNotification object:nil]; 

W contextDidSave: sam scalacie zmiany.

- (void) contextDidSave:(NSNotification*) notification 
{ 
    if(![notification.object isEqual:self.mainContext]) { 
    dispatch_async(dispatch_get_main_queue(), ^{ 
     [self.mainContext mergeChangesFromContextDidSaveNotification:notification]; 
    }); 
    } 
} 
+0

jak to dokładnie działa? Mam do czynienia z problemem, że mój główny wątek jest blokowany z powodu dużej ilości danych do zaimportowania. Używam MagicalRecord, ale nie uda mi się osiągnąć tego, o czym tutaj piszesz ... – swalkner

+0

@swalkner Zmieniłem odpowiedź, aby podać więcej szczegółów. – Onato

1

Managed konteksty obiektu nie są wątku bezpieczne, więc jeśli kiedykolwiek trzeba zrobić jakiejkolwiek pracy tła z obiektami CoreData (czyli funkcja import długo działa/eksport bez blokowania głównego UI) będą chcesz zrobić to na wątku tła.

W takich przypadkach należy utworzyć nowy kontekst obiektu zarządzanego na wątku tła, powtórzyć operację coredata, a następnie powiadomić główny kontekst wprowadzonych zmian.

można znaleźć przykład, jak to może działać tutaj

Core Data and threads/Grand Central Dispatch

Powiązane problemy