#iphone #ios #core-data #nsmanagedobjectcontext
#iPhone #iOS #core-data #nsmanagedobjectcontext
Вопрос:
[РЕДАКТИРОВАТЬ: упрощенная версия вопроса]
-
mainMOC
является основным контекстом управляемого объекта -
editorMOC
создан ли контекст управляемого объекта вeditorViewController
с помощью диспетчера отмены, чтобы пользователь мог редактировать один управляемый объект -
после
editorMOC
сохраненияmainMOC
обновляет обновленный управляемый объект в обработчике уведомлений дляNSManagedObjectContextDidSaveNotification
-
В обработчике сохранения, если я использую
[mainMOC refreshObject:obj mergeChanges:YES]
обновления объекта, не отражаются вmainMOC
после обновления. Если я использую[mainMOC refreshObject:obj mergeChanges:NO]
, объект становится недействительным, и при следующей ошибке изменения отражаются в данных, загруженных из хранилища.
ВОПРОС: Почему объект не будет отражать обновление, когда mergeChanges:YES
указано?
[ОРИГИНАЛЬНЫЙ ВОПРОС]
У меня есть приложение на основе core data с несколькими контекстами управляемых объектов. Приложение сложное и проприетарное, поэтому я не могу просто поделиться кодом непосредственно из приложения. Я создал простое тестовое приложение в попытке воспроизвести мою проблему, но тестовое приложение не обнаруживает проблемы. Я не смог найти логическую разницу между реализациями. Я прошу прощения за то, что не опубликовал пример кода, я полагаю, что я подробно объяснил реализацию ниже. Если что-то непонятно, пожалуйста, спросите в комментариях, и я сделаю все возможное, чтобы прояснить.
Вот моя ситуация. Все описанное ниже выполняется в основном потоке.
-
Приложение имеет вызываемый контекст основного управляемого объекта,
mainMOC
доступ к которому осуществляется в главном потоке и используется сNSFetchedResultsControllers
для отображения данных в различных табличных представлениях. -
У меня есть вызываемый контроллер представления
EditorViewController
, который позволяет редактировать существующий объект определенного объекта. Этот контроллер представления создает свой собственный контекст управляемого объекта, вызываемыйeditorMOC
с использованием того же координатора постоянного хранилища с менеджером отмены, чтобы изменения можно было откатить или сохранить при закрытии редактора. -
EditorViewController
соблюдаетNSManagedObjectContextDidSaveNotification
. Когда возникает это уведомление, обработчик уведомлений вызывает[_mainMOC mergeChangesFromContextDidSaveNotification:notification]
, чтобы объединить изменения изeditorMOC
вmainMOC
. -
Контроллер представления таблицы, который использует
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, о существовании которого вы не знали / забыли. (Это случилось со мной.)
У меня было
mainMOC
editorMOC
viewerMOC
что произошло из-за случайного ошибочного подклассирования — предполагалось, что он просматривает основной MOC, но вместо этого создавал свой собственный и просматривал замороженное состояние. Связь «проверка на наличие изменений» шла в другом направлении, потому что ожидалось, что в этом сценарии это будет «редактор».- уведомление правильно вызывает обратный вызов, и данные правильно объединяются в основной MOC (что я мог сказать, потому что данные были правильными при повторном запуске)
примечание: refreshObject:mergeChanges:
не требуется. это то, что я тоже пробовал, когда это не работало, но объединение уведомления должно позаботиться обо всех объектах для вас.
- (void)twinStackUpdated:(NSNotification *)notification {
[[self managedObjectContext] mergeChangesFromContextDidSaveNotification:notification];
}
не уверен, что это была ваша проблема, но это может быть чья-то другая, так что это так.
Ответ №4:
В попытке принять что-то, даже если у меня нет окончательного решения, вот что я сделал для решения этой проблемы, и проблема, похоже, решена.
Я полагаю, что это была проблема с кэшем с NSFetchedResultsController. С тех пор я упростил этот код и убедился, что каждый NSFetchedResultsController использует свой собственный кэш. Я смог удалить вызов ‘refreshObject’, и, похоже, все работает правильно.