Ogni Core Data Relationship deve avere un Inverse?


150

Diciamo che ho due classi Entity: SocialAppeSocialAppType

In SocialAppHo un attributo: appURLe uno di relazione: type.

In SocialAppTypeHo tre attributi: baseURL, namee favicon.

La destinazione della SocialApprelazione typeè un singolo record in SocialAppType.

Ad esempio, per più account Flickr, ci sarebbe un numero di SocialApprecord, con ogni record che contiene un collegamento all'account di una persona. Ci sarebbe un SocialAppTyperecord per il tipo "Flickr", a cui SocialAppfarebbero riferimento tutti i record.

Quando creo un'applicazione con questo schema, ricevo un avviso che non esiste alcuna relazione inversa tra SocialAppTypee SocialApp.

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

Ho bisogno di un contrario, e perché?

Risposte:


117

In pratica, non ho avuto alcuna perdita di dati a causa della non inversione, almeno di cui sono a conoscenza. Un rapido Google suggerisce che dovresti usarli:

Una relazione inversa non solo rende le cose più ordinate, ma in realtà viene utilizzata da Core Data per mantenere l'integrità dei dati.

- Cocoa Dev Central

In genere è necessario modellare le relazioni in entrambe le direzioni e specificare le relazioni inverse in modo appropriato. Core Data utilizza queste informazioni per garantire la coerenza del grafico degli oggetti se viene apportata una modifica (vedere "Manipolazione delle relazioni e integrità del grafico degli oggetti"). Per una discussione di alcuni dei motivi per cui potresti non voler modellare una relazione in entrambe le direzioni e alcuni dei problemi che potrebbero sorgere in caso contrario, vedi "Relazioni unidirezionali".

- Guida alla programmazione dei dati di base


5
Vedi la risposta di MadNik (in fondo alla pagina al momento in cui sto scrivendo questo) per una spiegazione più approfondita del significato dei documenti quando affermano che le relazioni inverse aiutano a mantenere l'integrità dei dati.
Mark Amery,

135

La documentazione di Apple ha un ottimo esempio che suggerisce una situazione in cui potresti avere problemi non avendo una relazione inversa. Mappiamolo in questo caso.

Supponi di averlo modellato come segue: inserisci qui la descrizione dell'immagine

Nota che hai una relazione uno a uno chiamata " tipo ", da SocialAppa SocialAppType. La relazione non è facoltativa e ha una regola di eliminazione "nega" .

Ora considera quanto segue:

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

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

Ciò che ci aspettiamo è fallire il salvataggio di questo contesto poiché abbiamo impostato la regola di eliminazione su Nega mentre la relazione non è facoltativa.

Ma qui il salvataggio ha successo.

Il motivo è che non abbiamo stabilito una relazione inversa . Per questo motivo, l'istanza di socialApp non viene contrassegnata come modificata quando viene eliminato appType. Quindi non viene eseguita alcuna convalida per socialApp prima del salvataggio (presuppone che non sia necessaria alcuna convalida poiché non è avvenuta alcuna modifica). Ma in realtà è successo un cambiamento. Ma non si riflette.

Se ricordiamo appType da

SocialAppType *appType = [socialApp socialAppType];

appType è zero.

Strano, vero? Otteniamo zero per un attributo non facoltativo?

Quindi non hai problemi se hai impostato la relazione inversa. Altrimenti devi forzare la convalida scrivendo il codice come segue.

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];

9
Questa è un'ottima risposta; grazie per aver spiegato chiaramente in dettaglio qual è - dai documenti e da tutto ciò che ho letto - l'argomento principale a favore di avere inversioni per tutto, anche dove la relazione inversa non è umanamente significativa. Questa dovrebbe davvero essere la risposta accettata. Tutto questo thread di domande che manca ora è un'esplorazione più dettagliata dei motivi per cui potresti scegliere di non avere inversioni, toccate nella risposta di Rose Perrone (e anche nella documentazione, un po ').
Mark Amery,

46

Parafraserò la risposta definitiva che ho trovato in More iPhone 3 Development di Dave Mark e Jeff LeMarche.

In genere Apple consiglia di creare e specificare sempre l'inverso, anche se non si utilizza la relazione inversa nella propria app. Per questo motivo, ti avvisa quando non riesci a fornire un inverso.

Non è necessario che le relazioni abbiano un inverso, in quanto vi sono alcuni scenari in cui la relazione inversa potrebbe danneggiare le prestazioni. Ad esempio, supponiamo che la relazione inversa contenga un numero estremamente elevato di oggetti. La rimozione dell'inverso richiede l'iterazione sull'insieme che rappresenta la prestazione inversa e indebolente.

Ma a meno che tu non abbia un motivo specifico per non farlo, modella l'inverso . Aiuta Core Data a garantire l'integrità dei dati. Se si verificano problemi di prestazioni, è relativamente semplice rimuovere la relazione inversa in un secondo momento.


24

La domanda migliore è "c'è un motivo per non avere un contrario "? Core Data è in realtà un framework di gestione dei grafici a oggetti, non un framework di persistenza. In altre parole, il suo compito è gestire le relazioni tra gli oggetti nel grafico degli oggetti. Le relazioni inverse lo rendono molto più semplice. Per tale motivo, Core Data prevede relazioni inverse ed è scritto per quel caso d'uso. Senza di essi, dovrai gestire tu stesso la coerenza del grafico a oggetti. In particolare, è probabile che troppe relazioni senza una relazione inversa vengano corrotte da Core Data a meno che non si lavori molto duramente per far funzionare le cose. Il costo in termini di dimensioni del disco per le relazioni inverse è davvero insignificante rispetto al vantaggio che ti guadagna.


20

Esiste almeno uno scenario in cui è possibile fare un buon caso per una relazione di dati di base senza un inverso: quando esiste già un'altra relazione di dati di base tra i due oggetti, che gestirà il mantenimento del grafico degli oggetti.

Ad esempio, un libro contiene molte pagine, mentre una pagina è in un libro. Questa è una relazione bidirezionale bidirezionale. L'eliminazione di una pagina annulla solo la relazione, mentre l'eliminazione di un libro eliminerà anche la pagina.

Tuttavia, potresti anche voler tenere traccia della pagina corrente da leggere per ogni libro. Questo potrebbe essere fatto con una proprietà "currentPage" nella pagina , ma è quindi necessaria altra logica per garantire che solo una pagina del libro sia contrassegnata come pagina corrente in qualsiasi momento. Invece, stabilire una relazione currentPage da Book a una singola pagina assicurerà che ci sia sempre solo una pagina corrente contrassegnata, e inoltre a questa pagina sarà possibile accedere facilmente con un riferimento al libro con semplicemente book.currentPage.

Presentazione grafica della relazione di dati di base tra libri e pagine

Quale sarebbe la relazione reciproca in questo caso? Qualcosa in gran parte senza senso. "myBook" o simile potrebbe essere aggiunto nuovamente nell'altra direzione, ma contiene solo le informazioni già contenute nella relazione "libro" per la pagina e quindi crea i propri rischi. Forse in futuro, il modo in cui stai usando una di queste relazioni è cambiato, con conseguenti cambiamenti nella configurazione dei tuoi dati principali. Se page.myBook è stato utilizzato in alcuni punti in cui page.book avrebbe dovuto essere utilizzato nel codice, potrebbero esserci problemi. Un altro modo per evitarlo in modo proattivo sarebbe anche quello di non esporre myBook nella sottoclasse NSManagedObject utilizzata per accedere alla pagina. Tuttavia, si può sostenere che è più semplice non modellare l'inverso in primo luogo.

Nell'esempio delineato, la regola di eliminazione per la relazione currentPage deve essere impostata su "Nessuna azione" o "Cascata", poiché non esiste alcuna relazione reciproca con "Annulla". (Cascade implica che stai strappando ogni pagina dal libro mentre lo leggi, ma ciò potrebbe essere vero se hai particolarmente freddo e hai bisogno di carburante.)

Quando si può dimostrare che l'integrità del grafico a oggetti non è a rischio, come in questo esempio, e la complessità e la manutenibilità del codice sono migliorate, si può sostenere che una relazione senza un inverso può essere la decisione corretta.


Grazie Duncan - questo è estremamente simile a un caso d'uso che ho. In sostanza, devo essere in grado di ottenere l' ultima pagina di un libro. Ho bisogno che questo sia un valore persistente in modo da poterlo utilizzare in un predicato di recupero. Avevo creato un "bookIAmLastPageOf" inverso, ma è piuttosto privo di senso e sta causando altri problemi (non capisco correttamente). Sai se esiste un modo per disabilitare l'avviso per un singolo caso?
Benjohn,

C'è un modo per disabilitare gli avvisi? Ora ho 2: ... dovrebbe avere una regola di cancellazione inversa e nessuna azione ... può comportare relazioni con oggetti eliminati . E può currentPageessere contrassegnato come Optional?
SwiftArchitect,

Memorizzare la CurrentPage come NSManagedObjectID invece di una relazione sarebbe un approccio valido?
user965972,

4

Mentre i documenti non sembrano richiedere un inverso, ho solo risolto uno scenario che in effetti ha comportato una "perdita di dati" non avendo un inverso. Ho un oggetto report che ha una relazione a molti su oggetti da segnalare. Senza la relazione inversa, eventuali modifiche alla relazione a molti andavano perse al riavvio. Dopo aver esaminato il debug di Core Data era evidente che, anche se stavo salvando l'oggetto report, gli aggiornamenti al grafico degli oggetti (relazioni) non venivano mai effettuati. Ho aggiunto un contrario, anche se non lo uso, e voilà, funziona. Quindi potrebbe non dire che è richiesto, ma le relazioni senza inversioni possono sicuramente avere strani effetti collaterali.


0

Gli inversi vengono utilizzati anche per Object Integrity(per altri motivi, vedere le altre risposte):

L'approccio raccomandato è di modellare le relazioni in entrambe le direzioni e specificare le relazioni inverse in modo appropriato. Core Data utilizza queste informazioni per garantire la coerenza del grafico degli oggetti se viene apportata una modifica

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

Il link fornito ti dà idee sul perché dovresti avere un inverseset. Senza di essa, puoi perdere dati / integrità. Inoltre, è più probabile che tu acceda a un oggetto che è nil.


0

Non è necessario un rapporto inverso in generale. Ma ci sono alcune stranezze / bug nei dati core in cui è necessaria una relazione inversa. Ci sono casi in cui mancano relazioni / oggetti, anche se non si verificano errori durante il salvataggio del contesto, se mancano relazioni inverse. Controlla questo esempio , che ho creato per dimostrare la mancanza di oggetti e come risolvere il problema, lavorando con i dati Core

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.