2009-04-18 680 views
132

Digamos que tengo dos clases de entidades: SocialApp y SocialAppType¿Tiene cada relación de datos centrales tener un inverso?

En SocialApp que tengo un Atributo: appURL y uno civil: type.

En SocialAppType Tengo tres atributos: baseURL, name y favicon.

El destino de la relación SocialApp es un registro único en SocialAppType.

Como ejemplo, para múltiples cuentas de Flickr, habría un número de registros SocialApp, con cada registro sosteniendo un enlace a la cuenta de una persona. Habría un registro SocialAppType para el tipo "Flickr", que todos los registros SocialApp apuntarían.

Cuando construyo una aplicación con este esquema, recibo una advertencia de que no hay una relación inversa entre SocialAppType y 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é?

Respuesta

107

En la práctica, no he tenido ninguna pérdida de datos debido a no tener una inversa, al menos que yo sepa. Una búsqueda rápida en Google le sugiere que debe usarlos:

una relación inversa no sólo hacer las cosas más ordenada, en realidad es utilizado por datos básicos para mantener los datos integridad.

- Cocoa Dev Central

Debe modelar normalmente relaciones en ambas direcciones, y especificar las relaciones inversas apropiadamente. Core Data usa esta información para garantizar la consistencia del gráfico de objetos si se realiza un cambio en (consulte "Manipulación de relaciones y Object Graph Integrity"). Para una discusión de algunas de las razones por las es posible que desee no modelar una relación en ambas direcciones, y algunos de los problemas que pudieran surgir si no la tiene, consulte “unidireccionales Relaciones.”

- Core Data Programming Guide

+5

Consulte la respuesta de MadNik (en la parte inferior de la página en el momento en que estoy escribiendo esto) para 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. –

21

La mejor pregunta es, "¿hay alguna razón no tener una inversa"? Core Data es realmente un marco de gestión de gráficos de objeto, 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 que mucho sea más fácil. Por esa razón, Core Data espera relaciones inversas y está escrito para ese caso de uso. Sin ellos, usted tendrá que administrar la consistencia del gráfico del objeto usted mismo.En particular, muchas de las relaciones sin una relación inversa son muy susceptibles de ser corrompidas por Core Data a menos que trabaje muy difícil de mantener las cosas funcionando. El costo en términos de tamaño del disco para las relaciones inversas realmente es insignificante en comparación con el beneficio que le proporciona.

44

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 la inversa, incluso si no utiliza la relación inversa en su aplicación. Por esta razón, le advierte cuando no puede proporcionar un inverso.

Las relaciones no son requirieron para tener una inversa, porque hay algunos escenarios en los que la relación inversa podría perjudicar el rendimiento. Por ejemplo, supongamos que la relación inversa contiene una cantidad extremadamente grande de objetos. La eliminación de lo inverso requiere iterar sobre el conjunto que representa el rendimiento inverso y debilitante.

Pero a menos que tenga una razón específica para no, 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.

112

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.

Supongamos que la modeló la siguiente manera: enter image description here

Nota Tiene una relación a uno llamado "tipo", desde SocialApp a SocialAppType. La relación es no 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 no guardar este contexto ya que hemos establecido la norma de supresión como Deny mientras que la relación no es opcional.

Pero aquí el guardado tiene éxito.

La razón es que no hemos establecido una relación inversa . Por eso, la instancia de socialApp no ​​se marca como cambiada cuando se elimina appType. Por lo tanto, no ocurre ninguna validación para socialApp antes de guardar (no se requiere validación porque no se produjo ningún cambio). Pero en realidad sucedió un cambio. Pero no se refleja

Si recordamos Tipo de Aplicación por

SocialAppType *appType = [socialApp socialAppType]; 

de Tipo de Aplicación es nula.

Weird, is not is? Obtenemos nil por un atributo no opcional?

Así que no tiene problemas si ha configurado 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]; 
+7

Esta es una gran respuesta; gracias por deletrear en detalle lo que 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. Esto realmente debería ser la respuesta aceptada.Todo este hilo de preguntas falta ahora es una exploración más detallada de las razones por las que puede elegir * no * tener inversas, tocadas en la respuesta de Rose Perrone (y también en la documentación, un poco). –

17

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

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

Sin embargo, también es posible que desee realizar un seguimiento de la página actual que se lee para cada libro. Esto podría hacerse con una "currentPage" propiedad en Página, pero entonces 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, al hacer una relación actual de la página del Libro a una sola página, se asegurará de que siempre haya solo una página actual marcada, y además de que se puede acceder fácilmente a esta página con una referencia al libro simplemente book.currentPage.

Graphical presentation of core data relationship between books and pages

¿Cómo sería la relación recíproca ser en este caso? Algo en gran parte sin sentido. "myBook" o similar podría agregarse en la otra dirección, pero contiene solo la información que ya figura en la relación "libro" para la página, y por lo tanto crea sus propios riesgos. Quizás en el futuro, cambie la forma en que usa una de estas relaciones, lo que generará cambios en la configuración de datos básicos. Si page.myBook se ha utilizado en algunos lugares donde page.book debería haberse utilizado 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 lo inverso en primer lugar.

En el ejemplo descrito, la regla de eliminación para la relación de la 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á extrayendo cada página del libro mientras lo lee, pero eso podría ser cierto si está particularmente 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 y capacidad de mantenimiento del código, se puede argumentar que una relación sin inversión puede ser la decisión correcta.

+0

Gracias Duncan: esto es extremadamente parecido a un caso de uso que tengo. Básicamente, necesito poder obtener la ** última página ** de un libro. Necesito que este sea un valor persistente para que pueda usarlo en un predicado de búsqueda. Había configurado una inversión "bookIAmLastPageOf", pero no tiene sentido y está causando otros problemas (no entiendo correctamente). ¿Sabes si hay una forma de desactivar la advertencia para un solo caso? – Benjohn

+0

¿Hay alguna manera de desactivar las advertencias? Ahora tengo 2: * ... debería tener un inverso * y * Ninguna acción Eliminar regla ... puede dar como resultado relaciones con objetos eliminados *. ¿Y puede 'currentPage' marcarse como' Opcional'? – SwiftArchitect

+0

¿Sería un enfoque válido almacenar la página actual como un NSManagedObjectID en lugar de una relación? – user965972

4

Si bien los documentos no parecen requerir una inversión, simplemente resolví un escenario que de hecho resultó en "pérdida de datos" al no tener una inversión. Tengo un objeto de informe que tiene una relación de muchos en objetos reportables. Sin la relación inversa, cualquier cambio en la relación de muchos se perdió al relanzar. 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 del gráfico de objeto (relaciones). Agregué un inverso, aunque no lo uso, y listo, funciona. Por lo tanto, podría no decirse que sea necesario, pero las relaciones sin inversos definitivamente pueden tener efectos secundarios extraños.

Cuestiones relacionadas