Что может привести к тому, что mergeChangesFromContextDidSaveNotification не объединит / не сделает недействительными объекты, которые были обновлены?

#iphone #ios #core-data #nsmanagedobjectcontext

#iPhone #iOS #core-data #nsmanagedobjectcontext

Вопрос:

[РЕДАКТИРОВАТЬ: упрощенная версия вопроса]

  1. mainMOC является основным контекстом управляемого объекта

  2. editorMOC создан ли контекст управляемого объекта в editorViewController с помощью диспетчера отмены, чтобы пользователь мог редактировать один управляемый объект

  3. после editorMOC сохранения mainMOC обновляет обновленный управляемый объект в обработчике уведомлений для NSManagedObjectContextDidSaveNotification

  4. В обработчике сохранения, если я использую [mainMOC refreshObject:obj mergeChanges:YES] обновления объекта, не отражаются в mainMOC после обновления. Если я использую [mainMOC refreshObject:obj mergeChanges:NO] , объект становится недействительным, и при следующей ошибке изменения отражаются в данных, загруженных из хранилища.

ВОПРОС: Почему объект не будет отражать обновление, когда mergeChanges:YES указано?

[ОРИГИНАЛЬНЫЙ ВОПРОС]

У меня есть приложение на основе core data с несколькими контекстами управляемых объектов. Приложение сложное и проприетарное, поэтому я не могу просто поделиться кодом непосредственно из приложения. Я создал простое тестовое приложение в попытке воспроизвести мою проблему, но тестовое приложение не обнаруживает проблемы. Я не смог найти логическую разницу между реализациями. Я прошу прощения за то, что не опубликовал пример кода, я полагаю, что я подробно объяснил реализацию ниже. Если что-то непонятно, пожалуйста, спросите в комментариях, и я сделаю все возможное, чтобы прояснить.

Вот моя ситуация. Все описанное ниже выполняется в основном потоке.

  1. Приложение имеет вызываемый контекст основного управляемого объекта, mainMOC доступ к которому осуществляется в главном потоке и используется с NSFetchedResultsControllers для отображения данных в различных табличных представлениях.

  2. У меня есть вызываемый контроллер представления EditorViewController , который позволяет редактировать существующий объект определенного объекта. Этот контроллер представления создает свой собственный контекст управляемого объекта, вызываемый editorMOC с использованием того же координатора постоянного хранилища с менеджером отмены, чтобы изменения можно было откатить или сохранить при закрытии редактора.

  3. EditorViewController соблюдает NSManagedObjectContextDidSaveNotification . Когда возникает это уведомление, обработчик уведомлений вызывает [_mainMOC mergeChangesFromContextDidSaveNotification:notification] , чтобы объединить изменения из editorMOC в mainMOC .

  4. Контроллер представления таблицы, который использует NSFetchedResultsController , обрабатывает сообщения делегата контроллера.

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

  • Я вижу, что объект изменен и сохранен в редакторе.
  • Я вижу, что NSManagedObjectContextDidSaveNotification вызывается и что обновленный объект включен.
  • Я вижу, что контроллер полученных результатов получает controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: сообщение протокола.
  • Я вижу, что это mainMOC не отражает обновления и что обновленный объект не был признан недействительным mergeChangesFromContextDidSaveNotification: .
  • Если я завершу работу и перезапущу приложение, обновления будут зафиксированы

Для справки, как мое основное приложение, так и тестовое приложение реализуют вышеуказанную функциональность, но тестовое приложение показывает, что обновления объединены правильно, а основное приложение — нет.

Я ищу предложения о том, что может привести к тому, что mergeChangesFromContextDidSaveNotification: не удастся успешно объединить и / или сделать недействительным обновленный объект.

Спасибо!

Комментарии:

1. Такая же проблема … замена [mainMOC refreshObject:obj mergeChanges: YES] на [mainMOC save:nil] работает (например, [mainMOC refreshObject:obj mergeChanges: NO]). Не понимаю, почему… КСТАТИ: настройки политики слияния не имеют последствий.

Ответ №1:

Контроллер полученных результатов не получает controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: сообщение делегата. Вместо этого он отправляет его делегату в ответ на изменение контекста. Если это сообщение делегата отправляется и действительно содержит соответствующий объект, то основной контекст осведомлен об изменениях.

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

Комментарии:

1. спасибо за ваш ответ, я знаю, что это сложная проблема для диагностики. К сожалению, это не проблема. Я проверил, что при вызове обработчика уведомлений [_mainMOC mergeChangesFromContextDidSaveNotification:notification] обновленный объект неправильно объединяется с _mainMOC . Я проверил это, просмотрев данные объекта после слияния, а затем после принудительного аннулирования и сбоя объекта. После слияния я вижу старые (неправильные) данные. После принудительной аннулирования я вижу новые (правильные) данные. Все это не зависит от NSFetchedResultsController .

2. Вы могли бы попробовать использовать диагностический подкласс NSManagedObjectContext и переопределить mergeChangesFrom... , чтобы вы могли перехватить уведомление. (Возможно, вы также сможете перехватить это в отладчике.) Возможно, уведомление не содержит той информации, о которой вы думаете. Вы также должны проверить политику объединения в основном контексте. Есть ли у вас какая-либо пользовательская логика проверки вставки?

3. Я проверил информацию в уведомлении, и она выглядит правильно (обновленный объект присутствует с правильными данными). Я явно не установил политику слияния, поэтому будет использоваться по умолчанию. Разве политика слияния не вступает в действие только при сохранении контекста? Просматривая документы на refreshObject:mergeChanges , есть комментарии о том, как выполняется слияние (загрузка из хранилища / кэша, применение слияния, повторное применение локальных обновлений), которые имеют смысл, но у меня нет локальных обновлений. Кроме того, отсутствует пользовательская логика проверки вставки. Я обошел это, сделав недействительным, но я, должно быть, чего-то не хватает.

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

5. Просто внезапная мысль, может ли диспетчер отмены что-то перепутать? Я ничего не могу придумать, но мы ищем что-то, что вызовет возврат к старым данным.

Ответ №2:

Проблема, из-за которой mergeChangesFromContextDidSaveNotification не работала, заключалась в том, что я создал новый NSPersistentStoreCoordinator для каждого нового NSManagedObjectContext. Когда я разделил NSPersistentStoreCoordinator между всеми MOC, mergeChangesFromContextDidSaveNotification работает отлично. Кроме того, убедитесь, что mergeChangesFromContextDidSaveNotification вызывается в потоке, которому принадлежит MOC.

Из проведенного мной исследования можно безопасно совместно использовать NSPersistentStoreCoordinator между потоками для использования с NSManagedObjectContext. NSManagedObjectContext заблокирует постоянное хранилище по мере необходимости.

Комментарии:

1. Спасибо. моя проблема была другой. У меня есть только один persistentStoreCoordinator .

2. Помогает мне устранить проблему с обновлениями под iOS6. Начиная с iOS7, mergeChangesFromContextDidSaveNotification: также отлично работает с 2 NSPersistentStoreCoordinator.

Ответ №3:

Один из возможных ответов: у вас есть третий MOC, о существовании которого вы не знали / забыли. (Это случилось со мной.)

У меня было

  1. mainMOC
  2. editorMOC
  3. viewerMOC что произошло из-за случайного ошибочного подклассирования — предполагалось, что он просматривает основной MOC, но вместо этого создавал свой собственный и просматривал замороженное состояние. Связь «проверка на наличие изменений» шла в другом направлении, потому что ожидалось, что в этом сценарии это будет «редактор».
  4. уведомление правильно вызывает обратный вызов, и данные правильно объединяются в основной MOC (что я мог сказать, потому что данные были правильными при повторном запуске)

примечание: refreshObject:mergeChanges: не требуется. это то, что я тоже пробовал, когда это не работало, но объединение уведомления должно позаботиться обо всех объектах для вас.

 - (void)twinStackUpdated:(NSNotification *)notification {
  [[self managedObjectContext] mergeChangesFromContextDidSaveNotification:notification];
}
  

не уверен, что это была ваша проблема, но это может быть чья-то другая, так что это так.

Ответ №4:

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

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