NSManagedObject не отражает изменения после фонового потока NSManagedObjectContextDidSaveNotification - PullRequest
6 голосов
/ 25 марта 2011

У меня проблемы с 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 Docs, но, похоже, это не меняет отображаемое значение

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

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

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

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

1 Ответ

3 голосов
/ 27 марта 2011

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

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

Оказывается, мне просто нужно было добавить наблюдателя в мою 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];
}

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

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

...