Todo Relacionamento de Dados Principais precisa ter um Inverso?

150

Digamos que eu tenho duas classes de entidade: SocialAppeSocialAppType

Em SocialAppTenho um atributo: appURLe um relacionamento: type.

Em SocialAppTypeTenho três atributos: baseURL, namee favicon.

O destino do SocialApprelacionamento typeé um registro único em SocialAppType.

Como exemplo, para várias contas do Flickr, haveria vários SocialAppregistros, com cada registro mantendo um link para a conta de uma pessoa. Haveria um SocialAppTyperegistro para o tipo "Flickr", para o qual todos os SocialAppregistros apontariam.

Quando crio um aplicativo com esse esquema, recebo um aviso de que não há relação inversa entre SocialAppTypee SocialApp.

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

Preciso de um inverso e por quê?

Alex Reynolds
fonte

Respostas:

117

Na prática, não tive nenhuma perda de dados devido a não ter uma inversa - pelo menos que eu saiba. Um rápido Google sugere que você os use:

Um relacionamento inverso não apenas torna as coisas mais organizadas, mas na verdade é usado pelo Core Data para manter a integridade dos dados.

- Cocoa Dev Central

Normalmente, você deve modelar relacionamentos nas duas direções e especificar os relacionamentos inversos adequadamente. Os Dados Principais usam essas informações para garantir a consistência do gráfico de objetos se uma alteração for feita (consulte “Manipulando Relacionamentos e Integridade do Gráfico de Objetos”). Para uma discussão de algumas das razões pelas quais você pode não modelar um relacionamento em ambas as direções, e alguns dos problemas que podem surgir se não o fizer, consulte "Relacionamentos unidirecionais".

- Guia de programação de dados principais

Matthew Schinckel
fonte
5
Veja a resposta de MadNik (na parte inferior da página no momento em que estou escrevendo isso) para obter uma explicação mais completa do que os documentos significam quando dizem que relacionamentos inversos ajudam a manter a integridade dos dados.
Mark Amery
135

A documentação da Apple tem um ótimo exemplo que sugere uma situação em que você pode ter problemas por não ter um relacionamento inverso. Vamos mapeá-lo neste caso.

Suponha que você modelou da seguinte maneira: insira a descrição da imagem aqui

Observe que você tem um relacionamento pessoal chamado " tipo ", de SocialApppara SocialAppType. O relacionamento não é opcional e possui uma regra de exclusão "negar" .

Agora considere o seguinte:

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

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

O que esperamos é deixar de salvar o contexto, pois definimos a regra de exclusão como Negar, enquanto o relacionamento não é opcional.

Mas aqui a defesa é bem-sucedida.

A razão é que não estabelecemos um relacionamento inverso . Por esse motivo, a instância socialApp não é marcada como alterada quando appType é excluído. Portanto, nenhuma validação acontece para o socialApp antes de salvar (ela não assume nenhuma validação necessária, pois nenhuma alteração ocorreu). Mas, na verdade, uma mudança aconteceu. Mas isso não se reflete.

Se lembrarmos de appType por

SocialAppType *appType = [socialApp socialAppType];

appType é nulo.

Estranho, não é? Ficamos nulos por um atributo não opcional?

Portanto, você não terá problemas se tiver estabelecido um relacionamento inverso. Caso contrário, você precisará forçar a validação escrevendo o código da seguinte maneira.

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
fonte
9
Esta é uma ótima resposta; obrigado por esclarecer com detalhes claros o que é - dos documentos e de tudo o que li - o principal argumento a favor de ter inversões para tudo, mesmo quando o relacionamento inverso não é humanamente significativo. Esta realmente deve ser a resposta aceita. Agora, todo esse segmento de perguntas está faltando é uma exploração mais detalhada das razões pelas quais você pode optar por não ter inversos, abordadas na resposta de Rose Perrone (e também na documentação, um pouco).
Mark Amery
46

Parafraseando a resposta definitiva que encontrei no More iPhone 3 Development, de Dave Mark e Jeff LeMarche.

A Apple geralmente recomenda que você sempre crie e especifique o inverso, mesmo se você não usar o relacionamento inverso no seu aplicativo. Por esse motivo, ele avisa quando você falha em fornecer um inverso.

Não é necessário que os relacionamentos tenham um inverso, porque existem alguns cenários nos quais o relacionamento inverso pode prejudicar o desempenho. Por exemplo, suponha que o relacionamento inverso contenha um número extremamente grande de objetos. A remoção do inverso requer iteração sobre o conjunto que representa o desempenho inverso e enfraquecedor.

Mas, a menos que você tenha um motivo específico para não, modele o inverso . Ajuda o Core Data a garantir a integridade dos dados. Se você tiver problemas de desempenho, é relativamente fácil remover o relacionamento inverso posteriormente.

Rose Perrone
fonte
24

A melhor pergunta é: "existe uma razão para não ter um inverso"? O Core Data é realmente uma estrutura de gerenciamento de gráfico de objetos, não uma estrutura de persistência. Em outras palavras, seu trabalho é gerenciar os relacionamentos entre objetos no gráfico de objetos. Relações inversas tornam isso muito mais fácil. Por esse motivo, o Core Data espera relacionamentos inversos e é gravado para esse caso de uso. Sem eles, você precisará gerenciar a consistência do gráfico de objetos. Em particular, é provável que relacionamentos com muitos sem um relacionamento inverso sejam corrompidos pelo Core Data, a menos que você se esforce muito para manter as coisas funcionando. O custo em termos de tamanho do disco para os relacionamentos inversos é realmente insignificante em comparação com o benefício que você obtém.

Barry Wark
fonte
20

Há pelo menos um cenário em que um bom argumento pode ser feito para um relacionamento de dados principais sem um inverso: quando já existe outro relacionamento de dados principais entre os dois objetos, que cuidará da manutenção do gráfico de objetos.

Por exemplo, um livro contém muitas páginas, enquanto uma página está em um livro. Esse é um relacionamento bidirecional de muitos para um. A exclusão de uma página apenas anula o relacionamento, enquanto a exclusão de um livro também exclui a página.

No entanto, você também pode acompanhar a página atual que está sendo lida para cada livro. Isso pode ser feito com uma propriedade "currentPage" na página , mas você precisa de outra lógica para garantir que apenas uma página do livro seja marcada como a página atual a qualquer momento. Em vez disso, estabelecer um relacionamento currentPage de Book para uma única página garantirá que sempre haverá apenas uma página atual marcada e, além disso, que essa página possa ser acessada facilmente com uma referência ao livro com simplesmente book.currentPage.

Apresentação gráfica da relação de dados principais entre livros e páginas

Qual seria a relação recíproca neste caso? Algo em grande parte sem sentido. "myBook" ou similar pode ser adicionado novamente na outra direção, mas contém apenas as informações já contidas no relacionamento "book" da página e, portanto, cria seus próprios riscos. Talvez no futuro, a maneira como você está usando um desses relacionamentos seja alterada, resultando em alterações na configuração dos dados principais. Se o page.myBook tiver sido usado em alguns lugares onde o page.book deveria ter sido usado no código, pode haver problemas. Outra maneira de evitar isso proativamente também seria não expor o myBook na subclasse NSManagedObject que é usada para acessar a página. No entanto, pode-se argumentar que é mais simples não modelar o inverso em primeiro lugar.

No exemplo descrito, a regra de exclusão para o relacionamento currentPage deve ser definida como "No Action" ou "Cascade", pois não há relacionamento recíproco para "Nullify". (Cascade implica que você está rasgando todas as páginas do livro enquanto o lê, mas isso pode ser verdade se você estiver particularmente frio e precisar de combustível.)

Quando for demonstrado que a integridade do gráfico de objeto não corre risco, como neste exemplo, e a complexidade e a manutenção do código são aprimoradas, pode-se argumentar que um relacionamento sem inverso pode ser a decisão correta.

Duncan Babbage
fonte
Obrigado Duncan - isso é extremamente próximo de um caso de uso que eu tenho. Essencialmente, preciso conseguir a última página de um livro. Eu preciso que esse seja um valor persistente para que eu possa usá-lo em um predicado de busca. Eu havia configurado um inverso "bookIAmLastPageOf", mas é um absurdo e está causando outros problemas (não entendi direito). Você sabe se existe uma maneira de desativar o aviso para um único caso?
Benjohn
Existe uma maneira de desativar os avisos? Agora tenho 2: ... deve ter uma regra de exclusão inversa e nenhuma ação ... pode resultar em relacionamentos com objetos excluídos . E pode currentPageser marcado como Optional?
SwiftArchitect
Armazenar o currentPage como um NSManagedObjectID em vez de um relacionamento seria uma abordagem válida?
user965972
4

Embora os documentos não pareçam exigir um inverso, apenas resolvi um cenário que de fato resultou em "perda de dados" por não ter um inverso. Eu tenho um objeto de relatório que tem um relacionamento com muitos em objetos reportáveis. Sem o relacionamento inverso, quaisquer alterações no relacionamento para muitos eram perdidas no relançamento. Após inspecionar a depuração do Core Data, ficou claro que, embora eu estivesse salvando o objeto de relatório, as atualizações no gráfico do objeto (relacionamentos) nunca estavam sendo feitas. Eu adicionei um inverso, mesmo que não o use, e pronto, ele funciona. Portanto, pode não dizer que é necessário, mas relacionamentos sem inversos podem definitivamente ter efeitos colaterais estranhos.

greg
fonte
0

Inversos também são usados ​​para Object Integrity(por outros motivos, consulte as outras respostas):

A abordagem recomendada é modelar relacionamentos nas duas direções e especificar os relacionamentos inversos adequadamente. Os Dados Principais usam essas informações para garantir a consistência do gráfico do objeto se uma alteração for feita

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

O link fornecido fornece idéias de por que você deveria ter um inverseconjunto. Sem ele, você pode perder dados / integridade. Além disso, a chance de você acessar um objeto que nilé mais provável.

J. Doe
fonte
0

Não há necessidade de relacionamento inverso em geral. Mas existem algumas peculiaridades / erros nos dados principais em que você precisa de um relacionamento inverso. Há casos em que os relacionamentos / objetos desaparecem, mesmo que não haja erro ao salvar o contexto, se houver um relacionamento inverso ausente. Verifique este exemplo , que eu criei para demonstrar a falta de objetos e como solução alternativa, enquanto trabalhava com dados principais

Dhilip
fonte