Помощь в разработке многопоточных приложений Core Data - PullRequest
7 голосов
/ 17 декабря 2010

alt text

Выше приведено упрощение того, как выглядит моя модель. В моем приложении есть объект NSWindowController, управляющий двумя объектами NSViewController для объектов user и account . Когда пользователь входит в приложение, он может изменить информацию о пользователе или учетной записи, вызвав соответствующий контроллер представления. В фоновом режиме у меня есть приложение, периодически заполняющее журналы пользователя в делегате приложения в отдельном потоке.

Я использую отдельный NSManagedObjectContext для фонового потока и делегат приложения NSManagedObjectContext для ввода данных в контроллеры представления. Я хотел бы знать несколько вещей:

1) это хорошая практика? Должен ли я создать NSManagedObjectContext для каждого контроллера представления, а затем объединять контексты всякий раз, когда пользователь делает изменения?

2) Поскольку сущность log создается в фоновом потоке, она имеет собственный NSManagedObjectContext. Однако каждый журнал содержит информацию от пользователя и учетных записей , которые создаются в делегате приложения NSManagedObjectContext. Вот как я выбираю пользователя:

- (NSManagedObjectID*) fetchUser:(NSString*) userID {   
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];   
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"user":inManagedObjectContext:self.managedObjectContext];
    /** snip **/
}

Этот метод вызывается фоновым потоком следующим образом:

NSManagedObjectID* userObjectID = [self fetchUser:userID];
NSManagedObject* userObject = [self.logsManagedObjectContext objectWithID:userObjectID];

Что я делаю в fetchUser поточно-ориентированном? Нужно ли блокировать основной контекст управляемого объекта при извлечении пользователя в случае, если одно из представлений модифицирует того же пользователя? Из этой статьи я понимаю (возможно, неправильно), что мне, возможно, придется это сделать. До сих пор у меня не было никаких проблем, но я не хочу оставлять потенциальный крайний случай.

3) Когда один из контроллеров представления вносит изменения в NSManagedObjectContext делегата приложения, он отправляет уведомление, которое обрабатывается следующим образом:

- (void)contextDidSave:(NSNotification *)notification {
    SEL selector = @selector(mergeChangesFromContextDidSaveNotification:);
    [self.logManagedObectContext performSelector:selector onThread:backgroundThread withObject:notification waitUntilDone:NO];
}

Это то, как я должен обрабатывать слияние, или я должен вместо этого слить делегат приложения NSManagedObjectContext? Я обнаружил, что выполнение этого (в основном потоке) заблокировало пользовательский интерфейс.

Любая помощь будет оценена.

1 Ответ

9 голосов
/ 23 декабря 2010

NSManagedObjectContext объекты не являются потокобезопасными.Это означает, что если вы хотите получить доступ к Базовым данным из нескольких потоков, вам потребуется один для каждого потока (и созданный в потоке тоже).Каждый из них может использовать тот же NSPersistentStoreCoordinator, который будет сериализовать доступ к постоянному хранилищу.

Это происходит потому, что каждый NSManagedObjectContext знает, как правильно заблокировать NSPersistentStoreCoordinator, когда он используется, избегая столкновений .Следуя этим правилам, вы должны оставаться поточно-ориентированным.

Как вы уже делаете, объекты NSManagedObjectID должны использоваться для передачи объектов Core Data из одного MOC в другой (и, соответственно, из одного потока вдругой).Однако вы звоните fetchUser:, который использует MOC из вашего основного потока, в фоновом режиме.Это не правильно.Этот fetchUser: вызов метода должен вызываться из основного потока.Конечно, ничто не мешает вам получить пользователя в фоновом потоке, используя фоновый MOC.

Таким образом, всегда выполняйте вызовы NSManagedObjectContext из потока, в котором он был создан.

Хитрость заключается в том, чтобы убедиться, что оба MOC знают о сохранении другого, поэтому вы должныЗарегистрируйтесь, чтобы получать уведомления от каждого контекста.Затем вы должны выполнить mergeChangesFromContextDidSaveNotification: из соответствующего потока для MOC.В данный момент ваш фоновый контекст уведомляется об изменениях из контекста основного потока, но не наоборот.

О, и нет необходимости иметь отдельный контекст для каждого NSViewController.Что касается элементов пользовательского интерфейса, их взаимодействие с контекстом будет происходить в одном и том же (главном) потоке, поэтому совместное использование возможно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...