Przeczytałem wiele postów tutaj o NSManagedObjectContext i wielowątkowych aplikacjach. Przejrzałem także przykład CoreDataBooks, aby zrozumieć, w jaki sposób oddzielne wątki wymagają własnego NSManagedObjectContext i jak operacja składowania zostaje scalona z głównym NSManagedObjectContext. Uważam, że przykład jest dobry, ale także zbyt specyficzny dla aplikacji. Próbuję to uogólnić i zastanawiam się, czy moje podejście jest rozsądne.Ogólne podejście do NSManagedObjectContext w wielowątkowej aplikacji
Moje podejście polega na zastosowaniu ogólnej funkcji pobierania NSManagedObjectContext dla bieżącego wątku. Funkcja zwraca obiekt NSManagedObjectContext dla głównego wątku, ale utworzy nowy (lub pobierze go z pamięci podręcznej), jeśli zostanie wywołany z innego wątku. To jest następujące:
+(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];
}
Zapisywanie operacji jest proste, jeśli wywołane z głównego wątku. Zapisane operacje wywołane z innych wątków wymagają scalenia wewnątrz głównego wątku. Do tego mam rodzajowe commit
funkcję:
+(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];
}
}
w funkcji contextDidSave:
wykonujemy seryjnej, jeśli wywołana przez notyfikacji w commit
.
+(void)contextDidSave:(NSNotification*)saveNotification {
MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *moc = delegate.managedObjectContext;
[moc performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)
withObject:saveNotification
waitUntilDone:YES];
}
Wreszcie, oczyszczenia pamięci podręcznej NSManagedObjectContext z tym:
+(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];
}
To kompiluje i wydaje się działać, ale wiem, gwintowania problemów może być trudne ze względu na warunki wyścigu. Czy ktoś widzi problem z tym podejściem?
Używam tego również w kontekście asynchronicznego żądania (przy użyciu ASIHTTPRequest), które pobiera niektóre dane z serwera i aktualizuje i wstawia sklep do iPhone'a. Wydaje się, że NSThreadWillExitNotification nie uruchamia się po zakończeniu żądania, a następnie ten sam wątek jest używany do kolejnych żądań. Oznacza to, że ten sam NSManagedObjectContext jest używany do oddzielnych żądań w tym samym wątku. Czy to problem?
chris, mam do czynienia z podobnym problemem wielowątkowości podczas korzystania z jednego NSManagedObjectContext utworzonego w wątku głównym dla wszystkich operacji w kolejce NSoperation. Probkem przychodzi, gdy każdy wątek próbuje zapisać kontekst, aplikacja ulega awarii losowo rzucając wyjątek od danych podstawowych. Myślałem o zablokowaniu tego kontekstu podczas używania we wszystkich operacjach, aby każdy z nich miał wyłączny dostęp do kontekstu. Czytałem ur powyżej solution.sounds gud, couls u proszę wklej mi nowy kod, który użyłeś do scalenia kontekstu, a także skomentuj użycie blokady do gry –