NSManagedObject не отражает изменения после фонового потока NSManagedObjectContextDidSaveNotification

У меня проблема с NSManagedObject, не отражающим изменения, внесенные в постоянное хранилище после того, как фоновый поток сохранил свой контекст.

Настройка

В простом тестовом приложении у меня есть одно окно, в котором перечислены все объекты в моем постоянном хранилище основных данных, поле поиска для фильтрации результатов и текстовое поле для отображения имени выбранного элемента и возможности изменения имени.

Привязки следующие:

ArrayController --> AppDelegate --> ManagedObjectContext
TableView Col 1 --> ArrayController --> values --> arrangedObjects.widgetName
TableView Col 2 --> ArrayController --> values --> arrangedObjects.uid

SearchField --> ArrayController --> predicate --> filterPredicate

TextField --> ArrayController --> value --> selection.widgetName

У меня также есть кнопка, которая запускает фоновую (NSOperation) выборку данных с веб-сервера.

Процесс

Когда пользователь нажимает кнопку обновления, запускается операция NSOperation, которая асинхронно захватывает виджеты, анализирует ответ, проверяет наличие локальных виджетов для удаления, которых нет в ответе, новых виджетов для добавления, которые не хранятся локально и существующих локальных виджеты, которые должны быть обновлены данными, полученными с сервера.

После завершения обработки основной контекст уведомляется с помощью:

[mainContext performSelectorOnMainThread:
       @selector(mergeChangesFromContextDidSaveNotification:) 
                              withObject:notification 
                           waitUntilDone:YES];

У меня есть наблюдатель в главном контроллере для тестирования, который показывает, что изменения прошли нормально, и главный контроллер получил уведомление.

Проблема

Если я вношу изменение в выбранный объект с помощью текстового поля, при сохранении данных в фоновом потоке объект в пользовательском интерфейсе не обновляется, чтобы отразить эти изменения (т.е. он не перезаписывает пользовательский интерфейс изменениями из сервер).

Например, с учетом следующих трех виджетов и идентификаторов:

Test Name 1 | ID 123
Test Name 2 | ID 234
Test Name 3 | ID 345

Если я изменю имя в пользовательском интерфейсе Test Name 2 на Renamed 2, у меня будет следующее:

Test Name 1 | ID 123
Renamed 2   | ID 234
Test Name 3 | ID 345

Когда я обновляюсь в фоновом режиме, я хочу, чтобы список отражал состояние сервера, то есть возвращался к:

Test Name 1 | ID 123
Test Name 2 | ID 234
Test Name 3 | ID 345

Вместо этого остается:

Test Name 1 | ID 123
Renamed 2   | ID 234
Test Name 3 | ID 345

Я знаю, что постоянное хранилище было обновлено, потому что если я убью приложение из XCode и перезапущу его, отобразится желаемая информация. Если я выхожу из приложения в обычном режиме, измененное значение записывается в хранилище при закрытии приложения, и при повторном открытии отображается переименованное значение.

Что я пробовал

Я знаю, что сообщение отправляется из фона в основной контекст, и я знаю, что данные сохраняются в магазине. Следовательно, проблема, я считаю, заключается в том, что основной контекст не объединяется, как я ожидал, или мне нужно каким-то образом заставить контроллер массива выполнять выборку из постоянного хранилища и отбрасывать его контекст.

  • Я попробовал processPendingChanges: при получении уведомления о сохранении в магазине, но подозреваю, что просто пишу Renamed 2 в магазин.
  • Я пробовал сделать rearrangeObjects на контроллере массива, но поскольку контроллер массива имеет дело с основным контекстом, я подозреваю, что это ничего не делает
  • Я попытался выполнить fetch:nil на контроллере массива, чтобы выполнить выборку из постоянного хранилища, но я снова подозреваю, что основной контекст перезаписывает значение Renamed 2, потому что оно еще не сохранено.
  • Я пробовал fetchWithRequest:nil merge:NO error:&error на контроллере массива в соответствии с документами Apple, но, похоже, это не меняет отображаемое значение

Я думаю, что должно произойти, чтобы контроллер массива сохранил свои данные в постоянном хранилище, прежде чем я запишу данные фонового хранилища, так что выборка на контроллере массива приведет к тому, что данные будут точными, как согласно моим ожиданиям. И если это действительно так, как я могу сказать контроллеру массива сделать это, или контроллер массива просто узнал бы об изменениях через привязки, если бы основной managedObjectContext был каким-то образом сохранен?

Я могу взломать решение, выполнив выборку из постоянного хранилища, поместив эти данные в массив и выполнив setContent: на контроллере массива, а затем повторив это, когда постоянное хранилище сохранено, но это кажется просто неправильным, не говоря уже о проблеме о необходимости отслеживать выбранное состояние контроллера массива (и, возможно, любые выборки подмассивов, которые могут произойти в результате этого первичного выбора).

Я не на базе? Я явно что-то здесь упускаю.

Будем очень признательны за любые мудрые слова или совет.




Ответы (1)


Ах, сила разочарования в сочетании с решимостью. Я не уверен, что это лучший или рекомендуемый подход, но он определенно отвечает моим потребностям.

Моя цель состояла в том, чтобы любые непостоянные изменения либо сохранялись до сохранения фонового обновления, либо отбрасывались до сохранения фонового обновления. Оба служат одной и той же цели в моем мире (данные сервера всегда верны).

Оказывается, мне просто нужно было добавить наблюдателя в мою NSOperation:

NSNotificationCenter * nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self 
       selector:@selector(prepareMerge:) 
           name:NSManagedObjectContextWillSaveNotification object:ctx];

Что вызывает метод в операции:

-(void)prepareMerge:(NSNotification *)notification {

    [[NSNotificationCenter defaultCenter] 
         postNotificationOnMainThreadName:@"SaveNow"
                                   object:nil];
}

Уведомление отправляется в основной поток (любезно предоставлено категорией на NSNotificationCenter, размещенной по адресу cocoanetics.com), который прослушивается в соответствующем классе в основном потоке:

[[NSNotificationCenter defaultCenter] addObserver:self 
                                         selector:@selector(saveNow:) 
                                             name:@"SaveNow" 
                                           object:nil];

И, конечно же, метод, который на самом деле вносит изменения:

-(void)saveNow:(NSNotification *)aNote {

    [[self managedObjectContext] rollback];
}

Контроллер массива и любые другие компоненты пользовательского интерфейса, которые обновили значения, откатываются прямо перед сохранением. Сохранение выполняется, и все старые локальные значения заменяются новыми.

Работа выполнена.

person Hooligancat    schedule 26.03.2011