Каждое ли базовое отношение данных должно иметь обратную сторону?

150

Допустим, у меня есть два класса сущностей: SocialAppиSocialAppType

В У SocialAppменя есть один атрибут: appURLи одно отношение: type.

В У SocialAppTypeменя есть три атрибута: baseURL, nameи favicon.

Пункт назначения SocialAppотношений type- одна запись в SocialAppType.

Например, для нескольких учетных записей Flickr будет несколько SocialAppзаписей, каждая из которых будет содержать ссылку на учетную запись человека. Для SocialAppTypeтипа «Flickr» будет одна запись, на которую SocialAppбудут указывать все записи.

Когда я создаю приложение с этой схемой, я получаю предупреждение об отсутствии обратной зависимости между SocialAppTypeи SocialApp.

 /Users/username/Developer/objc/TestApp/TestApp.xcdatamodel:SocialApp.type: warning: SocialApp.type -- relationship does not have an inverse

Нужно ли обратное и почему?

Алекс Рейнольдс
источник

Ответы:

117

На практике у меня не было потери данных из-за отсутствия инверсии - по крайней мере, об этом я знаю. Быстрый Google предлагает вам использовать их:

Обратные отношения не просто делают вещи более аккуратными, они фактически используются Core Data для поддержания целостности данных.

- Cocoa Dev Central

Обычно вы должны моделировать отношения в обоих направлениях и указывать обратные отношения соответствующим образом. Базовые данные используют эту информацию для обеспечения согласованности графа объекта при внесении изменения (см. «Управление отношениями и целостность графа объекта»). Для обсуждения некоторых причин, по которым вы можете не моделировать отношения в обоих направлениях, а также некоторых проблем, которые могут возникнуть, если вы этого не сделаете, см. «Однонаправленные отношения».

- Руководство по программированию основных данных

Мэтью Шинкель
источник
5
Посмотрите ответ MadNik (внизу страницы в тот момент, когда я пишу это) для более подробного объяснения того, что означают документы, когда говорят, что обратные отношения помогают поддерживать целостность данных.
Марк Эмери
135

В документации Apple есть отличный пример, который предлагает ситуацию, в которой у вас могут возникнуть проблемы из-за отсутствия обратной связи. Давайте сопоставим это в этом случае.

Предположим, вы смоделировали это следующим образом: введите описание изображения здесь

Обратите внимание, что у вас есть отношение « один к одному», называемое « тип », от SocialAppдо SocialAppType. Отношения не являются необязательными и имеют «запрещающее» правило удаления .

Теперь рассмотрим следующее:

SocialApp *socialApp;
SocialAppType *appType;
// assume entity instances correctly instantiated

[socialApp setSocialAppType:appType];
[managedObjectContext deleteObject:appType];
BOOL saved = [managedObjectContext save:&error];

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

Но здесь сохранение удается.

Причина в том, что мы не установили обратную связь . Из-за этого экземпляр socialApp не помечается как измененный при удалении appType. Таким образом, проверка достоверности для socialApp перед сохранением не происходит (предполагается, что проверка не требуется, поскольку никаких изменений не произошло). Но на самом деле произошли изменения. Но это не отражается.

Если мы вспомним appType от

SocialAppType *appType = [socialApp socialAppType];

appType равен нулю.

Странно, не правда ли? Мы получаем ноль для необязательного атрибута?

Таким образом, у вас нет проблем, если вы установили обратные отношения. В противном случае вы должны выполнить принудительную проверку, написав код следующим образом.

SocialApp *socialApp;
SocialAppType *appType;
// assume entity instances correctly instantiated

[socialApp setSocialAppType:appType];
[managedObjectContext deleteObject:appType];

[socialApp setValue:nil forKey:@"socialAppType"]
BOOL saved = [managedObjectContext save:&error];
MadNik
источник
9
Это отличный ответ; спасибо за то, что подробно объяснили, что - из документов и из всего, что я прочитал - главный аргумент в пользу наличия инверсий для всего, даже если обратные отношения не имеют человеческого смысла. Это действительно должен быть принятый ответ. Вся эта цепочка вопросов теперь отсутствует - это более подробное исследование причин, по которым вы можете отказаться от инверсий, затронутых в ответе Роуз Перроне (а также в документации, немного).
Марк Амери
46

Я перефразирую окончательный ответ, который я нашел в More iPhone 3 Development от Dave Mark и Jeff LeMarche.

Apple обычно рекомендует всегда создавать и указывать обратное, даже если вы не используете обратное отношение в своем приложении. По этой причине он предупреждает вас, если вы не предоставите обратное.

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

Но если у вас нет конкретной причины не делать этого, смоделируйте обратное . Это помогает Core Data обеспечить целостность данных. Если у вас возникнут проблемы с производительностью, обратное отношение будет относительно легко удалить позже.

Роза Перроне
источник
24

Лучший вопрос: есть ли причина не иметь обратного? Core Data на самом деле является структурой управления графами объектов, а не структурой постоянства. Другими словами, его работа заключается в управлении отношениями между объектами в графе объектов. Обратные отношения делают это намного проще. По этой причине Core Data ожидает обратных связей и написана для этого варианта использования. Без них вам придется самостоятельно управлять согласованностью графов объектов. В частности, к-многим без обратной связи, очень вероятно, будут повреждены Core Data , если вы работаете очень трудно держать вещи работать. Стоимость с точки зрения размера диска для обратных связей действительно незначительна по сравнению с выгодой, которую она вам приносит.

Барри Уарк
источник
20

Существует, по крайней мере, один сценарий, в котором можно привести хороший пример отношения ядра с данными без обратного: когда уже есть другое отношение ядра данных между двумя объектами, которое будет обрабатывать поддержание графа объектов.

Например, книга содержит много страниц, в то время как страница находится в одной книге. Это двусторонние отношения многие-к-одному. Удаление страницы просто аннулирует отношения, тогда как удаление книги также удалит страницу.

Тем не менее, вы также можете отслеживать текущую страницу для каждой книги. Это можно сделать с помощью свойства «currentPage» на странице , но тогда вам понадобится другая логика, чтобы гарантировать, что только одна страница в книге будет помечена как текущая страница в любое время. Вместо этого создание отношения currentPage из Book на одну страницу гарантирует, что всегда будет помечена только одна текущая страница, и, кроме того, к этой странице можно легко получить доступ со ссылкой на книгу просто с book.currentPage.

Графическое представление взаимосвязи основных данных между книгами и страницами

Какими будут взаимные отношения в этом случае? Что-то в значительной степени бессмысленное. «myBook» или подобное может быть добавлено обратно в другом направлении, но оно содержит только информацию, уже содержащуюся в связи «book» для страницы, и, таким образом, создает свои собственные риски. Возможно, в будущем способ использования одного из этих отношений изменится, что приведет к изменениям в конфигурации ваших основных данных. Если page.myBook использовался в некоторых местах, где page.book должен был использоваться в коде, могут возникнуть проблемы. Другим способом предотвращения этого также было бы не показывать myBook в подклассе NSManagedObject, который используется для доступа к странице. Тем не менее, можно утверждать, что проще не моделировать обратное в первую очередь.

В приведенном примере правило удаления для отношения currentPage должно быть установлено на «No Action» или «Cascade», так как нет обратной связи для «Nullify». (Каскад подразумевает, что вы вырываете каждую страницу из книги, когда читаете ее, но это может быть правдой, если вам особенно холодно и вам нужно топливо.)

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

Дункан Бэббидж
источник
Спасибо Дункан - это очень близко к случаю использования, который у меня есть. По сути, мне нужно иметь возможность получить последнюю страницу книги. Мне нужно, чтобы это было постоянное значение, чтобы я мог использовать его в предикате выборки. Я настроил инверсию bookIAmLastPageOf, но это довольно бессмысленно и вызывает другие проблемы (я не совсем понимаю). Знаете ли вы, есть ли способ отключить предупреждение для одного случая?
Бенджон
Есть ли способ отключить предупреждения? Теперь у меня есть 2: ... должно быть обратное и правило удаления без действия ... может привести к связям с удаленными объектами . А можно currentPageпометить как Optional?
SwiftArchitect
Будет ли сохранение currentPage как NSManagedObjectID вместо взаимосвязи правильным способом?
user965972
4

В то время как документы, кажется, не требуют обратного, я только решил сценарий, который фактически привел к "потере данных", не имея обратного. У меня есть объект отчета, который имеет отношение ко многим в отчетных объектах. Без обратной связи любые изменения в отношении ко многим были потеряны при перезапуске. После проверки отладки Core Data стало очевидно, что даже при сохранении объекта отчета обновления графа объектов (отношений) никогда не производились. Я добавил обратное, хотя я им не пользуюсь, и вуаля, это работает. Таким образом, он может не сказать, что это необходимо, но отношения без инверсий могут иметь странные побочные эффекты.

Greg
источник
0

Инверсы также используются для Object Integrity(по другим причинам, см. Другие ответы):

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

От: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreData/HowManagedObjectsarerelated.html#//apple_ref/doc/uid/TP40001075-CH17-SW1

Приведенная ссылка дает вам идеи, почему вы должны иметь inverseнабор. Без этого вы можете потерять данные / целостность. Кроме того, вероятность того, что вы получите доступ к объекту, который nilявляется более вероятным.

Дж. Доу
источник
0

Как правило, нет необходимости в обратных отношениях. Но в Базовых данных есть несколько причуд / ошибок, когда вам нужны обратные отношения. Существуют случаи, когда отношения / объекты пропадают, даже если при сохранении контекста не возникает ошибки, если отсутствуют обратные отношения. Посмотрите на этот пример , который я создал, чтобы продемонстрировать отсутствие объектов и способы их обхода при работе с основными данными.

Dhilip
источник