¿Cada relación de datos centrales debe tener un inverso?

150

Digamos que tengo dos clases de entidad: SocialAppySocialAppType

En SocialAppTengo un Atributo: appURLy uno civil: type.

En SocialAppTypeTengo tres atributos: baseURL, namey favicon.

El destino de la SocialApprelación typees un registro único en SocialAppType.

Como ejemplo, para varias cuentas de Flickr, habría una cantidad de SocialAppregistros, y cada registro tendría un enlace a la cuenta de una persona. Habría un SocialAppTyperegistro para el tipo "Flickr", al que SocialAppapuntarían todos los registros.

Cuando construyo una aplicación con este esquema, recibo una advertencia de que no hay una relación inversa entre SocialAppTypey SocialApp.

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

¿Necesito un inverso y por qué?

Alex Reynolds
fuente

Respuestas:

117

En la práctica, no he tenido ninguna pérdida de datos por no tener un inverso, al menos que yo sepa. Un rápido Google sugiere que debería usarlos:

Una relación inversa no solo hace las cosas más ordenadas, sino que Core Data la usa para mantener la integridad de los datos.

- Cocoa Dev Central

Por lo general, debe modelar las relaciones en ambas direcciones y especificar las relaciones inversas de manera adecuada. Core Data utiliza esta información para garantizar la coherencia del gráfico de objetos si se realiza un cambio (consulte “Manipulación de relaciones e integridad del gráfico de objetos”). Para una discusión de algunas de las razones por las que es posible que no desee modelar una relación en ambas direcciones, y algunos de los problemas que podrían surgir si no lo hace, consulte "Relaciones unidireccionales".

- Guía de programación de datos básicos

Matthew Schinckel
fuente
55
Consulte la respuesta de MadNik (al final de la página en el momento en que escribo esto) para obtener una explicación más detallada de lo que significan los documentos cuando dicen que las relaciones inversas ayudan a mantener la integridad de los datos.
Mark Amery
135

La documentación de Apple tiene un gran ejemplo que sugiere una situación en la que podría tener problemas al no tener una relación inversa. Vamos a mapearlo en este caso.

Suponga que lo modeló de la siguiente manera: ingrese la descripción de la imagen aquí

Tenga en cuenta que tiene una relación uno a uno llamada " tipo ", de SocialAppa SocialAppType. La relación no es opcional y tiene una regla de eliminación "denegar" .

Ahora considere lo siguiente:

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

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

Lo que esperamos es fallar este contexto, ya que hemos establecido la regla de eliminación como Denegar, mientras que la relación no es opcional.

Pero aquí la salvación tiene éxito.

La razón es que no hemos establecido una relación inversa . Debido a eso, la instancia de socialApp no ​​se marca como modificada cuando se elimina appType. Por lo tanto, no se realiza ninguna validación para socialApp antes de guardar (se supone que no se necesita validación ya que no ocurrió ningún cambio). Pero en realidad ocurrió un cambio. Pero no se refleja.

Si recordamos appType por

SocialAppType *appType = [socialApp socialAppType];

appType es nulo.

Extraño, ¿no es así? ¿Obtenemos nil para un atributo no opcional?

Por lo tanto, no tiene problemas si ha establecido la relación inversa. De lo contrario, debe forzar la validación escribiendo el código de la siguiente manera.

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
fuente
9
Esta es una respuesta genial; Gracias por detallar con claridad cuál es, de los documentos y de todo lo que he leído, el argumento principal a favor de tener inversas para todo, incluso cuando la relación inversa no es humanamente significativa. Esta realmente debería ser la respuesta aceptada. Todo este hilo de preguntas que falta ahora es una exploración más detallada de las razones por las que podría elegir no tener inversas, abordadas en la respuesta de Rose Perrone (y también en la documentación, un poco).
Mark Amery
46

Parafrasearé la respuesta definitiva que encontré en Más desarrollo de iPhone 3 por Dave Mark y Jeff LeMarche.

Apple generalmente recomienda que siempre cree y especifique el inverso, incluso si no usa la relación inversa en su aplicación. Por esta razón, le advierte cuando no proporciona un inverso.

No se requiere que las relaciones tengan un inverso, porque hay algunos escenarios en los que la relación inversa podría perjudicar el rendimiento. Por ejemplo, suponga que la relación inversa contiene un número extremadamente grande de objetos. Eliminar el inverso requiere iterar sobre el conjunto que representa el rendimiento inverso y debilitado.

Pero a menos que tenga una razón específica para no hacerlo, modele el inverso . Ayuda a Core Data a garantizar la integridad de los datos. Si se encuentra con problemas de rendimiento, es relativamente fácil eliminar la relación inversa más adelante.

Rose Perrone
fuente
24

La mejor pregunta es, "¿hay alguna razón para no tener un inverso"? Core Data es realmente un marco de gestión de gráficos de objetos, no un marco de persistencia. En otras palabras, su trabajo es administrar las relaciones entre los objetos en el gráfico de objetos. Las relaciones inversas hacen esto mucho más fácil. Por esa razón, Core Data espera relaciones inversas y está escrito para ese caso de uso. Sin ellos, tendrá que administrar la consistencia del gráfico de objetos usted mismo. En particular, es muy probable que Core Data corrompa las relaciones de muchos a muchos sin una relación inversa, a menos que trabaje muy duro para que todo funcione. El costo en términos de tamaño de disco para las relaciones inversas es realmente insignificante en comparación con el beneficio que le brinda.

Barry Wark
fuente
20

Hay al menos un escenario en el que se puede hacer un buen caso para una relación de datos centrales sin una inversa: cuando ya existe otra relación de datos centrales entre los dos objetos, que se encargará de mantener el gráfico del objeto.

Por ejemplo, un libro contiene muchas páginas, mientras que una página está en un libro. Esta es una relación bidireccional de muchos a uno. Eliminar una página solo anula la relación, mientras que eliminar un libro también eliminará la página.

Sin embargo, es posible que también desee realizar un seguimiento de la página actual que se lee para cada libro. Esto podría hacerse con una propiedad "currentPage" en la página , pero luego necesita otra lógica para asegurarse de que solo una página del libro esté marcada como la página actual en cualquier momento. En cambio, establecer una relación de Página actual de Libro a una sola página asegurará que siempre haya solo una página actual marcada y, además, que se pueda acceder fácilmente a esta página con una referencia al libro con simplemente book.currentPage.

Presentación gráfica de la relación de datos básicos entre libros y páginas.

¿Cuál sería la relación recíproca en este caso? Algo en gran medida sin sentido. "myBook" o similar podría agregarse en la otra dirección, pero contiene solo la información ya contenida en la relación "libro" para la página, y por lo tanto crea sus propios riesgos. Quizás en el futuro, la forma en que está utilizando una de estas relaciones cambia, lo que resulta en cambios en la configuración de sus datos principales. Si page.myBook se ha usado en algunos lugares donde page.book debería haberse usado en el código, podría haber problemas. Otra forma de evitar esto de manera proactiva también sería no exponer myBook en la subclase NSManagedObject que se utiliza para acceder a la página. Sin embargo, se puede argumentar que es más simple no modelar el inverso en primer lugar.

En el ejemplo descrito, la regla de eliminación para la relación de página actual debe establecerse en "Sin acción" o "Cascada", ya que no existe una relación recíproca con "Anular". (Cascade implica que está arrancando cada página del libro a medida que lo lee, pero eso podría ser cierto si tiene mucho frío y necesita combustible).

Cuando se puede demostrar que la integridad del gráfico de objetos no está en riesgo, como en este ejemplo, y se mejora la complejidad del código y la capacidad de mantenimiento, se puede argumentar que una relación sin una inversa puede ser la decisión correcta.

Duncan Babbage
fuente
Gracias Duncan, esto es muy parecido a un caso de uso que tengo. Esencialmente, necesito poder obtener la última página de un libro. Necesito que este sea un valor persistente para poder usarlo en un predicado de recuperación. Había configurado un inverso "bookIAmLastPageOf", pero es bastante absurdo y está causando otros problemas (no lo entiendo correctamente). ¿Sabes si hay una manera de deshabilitar la advertencia para un solo caso?
Benjohn
¿Hay alguna forma de desactivar las advertencias? Ahora tengo 2: ... debería tener una regla de eliminación inversa y sin acción ... puede dar lugar a relaciones con objetos eliminados . ¿Y se currentPagepuede marcar como Optional?
Arquitecto Swift
¿Almacenar la página actual como NSManagedObjectID en lugar de una relación sería un enfoque válido?
user965972
4

Si bien los documentos no parecen requerir un inverso, simplemente resolví un escenario que de hecho resultó en "pérdida de datos" al no tener un inverso. Tengo un objeto de informe que tiene una relación de muchos a objetos informables. Sin la relación inversa, cualquier cambio en la relación de muchos se perdió en el relanzamiento. Después de inspeccionar la depuración de Core Data, era evidente que aunque estaba guardando el objeto de informe, nunca se realizaron las actualizaciones al gráfico del objeto (relaciones). Agregué un inverso, aunque no lo uso, y listo, funciona. Por lo tanto, podría no decir que es obligatorio, pero las relaciones sin inversas definitivamente pueden tener efectos secundarios extraños.

Greg
fuente
0

Las inversiones también se usan para Object Integrity(por otras razones, vea las otras respuestas):

El enfoque recomendado es modelar las relaciones en ambas direcciones y especificar las relaciones inversas de manera adecuada. Core Data utiliza esta información para garantizar la coherencia del gráfico de objetos si se realiza un cambio

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

El enlace proporcionado le da ideas de por qué debería tener un inverseconjunto. Sin ella, puede perder datos / integrety. Además, la posibilidad de acceder a un objeto que niles más probable.

J. Doe
fuente
0

No hay necesidad de una relación inversa en general. Pero hay algunas peculiaridades / errores en los datos principales en los que necesita una relación inversa. Hay casos en los que faltan relaciones / objetos, aunque no haya ningún error al guardar el contexto, si faltan relaciones inversas. Consulte este ejemplo , que creé para demostrar los objetos que faltan y cómo solucionarlos, mientras trabajaba con datos Core

Dhilip
fuente