Muss jede Kerndatenbeziehung eine Inverse haben?

150

Angenommen, ich habe zwei Entitätsklassen: SocialAppundSocialAppType

In SocialAppIch habe ein Attribut: appURLund eine Beziehung : type.

In SocialAppTypeIch habe drei Attribute : baseURL, nameund favicon.

Das Ziel der SocialAppBeziehung typeist ein einzelner Datensatz in SocialAppType.

Für mehrere Flickr-Konten gibt es beispielsweise eine Reihe von SocialAppDatensätzen, wobei jeder Datensatz einen Link zum Konto einer Person enthält. Es würde einen SocialAppTypeDatensatz für den Typ "Flickr" geben, auf den alle SocialAppDatensätze verweisen würden.

Wenn ich eine Anwendung mit diesem Schema erstelle, wird eine Warnung angezeigt, dass zwischen SocialAppTypeund keine umgekehrte Beziehung besteht SocialApp.

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

Benötige ich eine Umkehrung und warum?

Alex Reynolds
quelle

Antworten:

117

In der Praxis hatte ich keinen Datenverlust, weil ich keine Inverse hatte - zumindest ist mir das bewusst. Ein kurzer Google schlägt vor, dass Sie sie verwenden sollten:

Eine umgekehrte Beziehung sorgt nicht nur für Ordnung, sondern wird auch von Core Data verwendet, um die Datenintegrität aufrechtzuerhalten.

- Cocoa Dev Central

Normalerweise sollten Sie Beziehungen in beide Richtungen modellieren und die inversen Beziehungen entsprechend angeben. Core Data verwendet diese Informationen, um die Konsistenz des Objektdiagramms sicherzustellen, wenn eine Änderung vorgenommen wird (siehe „Bearbeiten von Beziehungen und Objektdiagrammintegrität“). Eine Erläuterung einiger Gründe, warum Sie eine Beziehung möglicherweise nicht in beide Richtungen modellieren möchten, und einiger Probleme, die auftreten können, wenn Sie dies nicht tun, finden Sie unter „Unidirektionale Beziehungen“.

- Core Data Programming Guide

Matthew Schinckel
quelle
5
In der Antwort von MadNik (am Ende der Seite, in der ich dies schreibe) finden Sie eine ausführlichere Erklärung der Bedeutung der Dokumente, wenn sie sagen, dass inverse Beziehungen zur Aufrechterhaltung der Datenintegrität beitragen.
Mark Amery
135

Die Apple-Dokumentation enthält ein hervorragendes Beispiel, das auf eine Situation hinweist, in der Sie möglicherweise Probleme haben, wenn Sie keine umgekehrte Beziehung haben. Lassen Sie es uns in diesen Fall abbilden.

Angenommen, Sie haben es wie folgt modelliert: Geben Sie hier die Bildbeschreibung ein

Beachten Sie, dass Sie eine Eins-zu-Eins- Beziehung namens " Typ " von SocialAppbis haben SocialAppType. Die Beziehung ist nicht optional und hat eine Löschregel "Verweigern" .

Betrachten Sie nun Folgendes:

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

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

Wir erwarten, dass das Speichern dieses Kontexts fehlschlägt, da wir die Löschregel auf Verweigern festgelegt haben, während die Beziehung nicht optional ist.

Aber hier gelingt das Speichern.

Der Grund ist, dass wir keine umgekehrte Beziehung festgelegt haben . Aus diesem Grund wird die socialApp-Instanz beim Löschen von appType nicht als geändert markiert. Daher erfolgt vor dem Speichern keine Validierung für socialApp (es wird davon ausgegangen, dass keine Validierung erforderlich ist, da keine Änderung vorgenommen wurde). Aber tatsächlich ist eine Veränderung passiert. Aber es wird nicht reflektiert.

Wenn wir uns an appType von erinnern

SocialAppType *appType = [socialApp socialAppType];

appType ist null.

Seltsam, nicht wahr? Wir bekommen Null für ein nicht optionales Attribut?

Sie haben also keine Probleme, wenn Sie die umgekehrte Beziehung eingerichtet haben. Andernfalls müssen Sie die Validierung erzwingen, indem Sie den Code wie folgt schreiben.

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
quelle
9
Dies ist eine großartige Antwort. Vielen Dank, dass Sie klar dargelegt haben, was - aus den Dokumenten und aus allem, was ich gelesen habe - das Hauptargument für Inversen für alles ist, auch wenn die umgekehrte Beziehung nicht menschlich bedeutsam ist. Dies sollte wirklich die akzeptierte Antwort sein. All diese Frage Thread jetzt fehlt , ist eine detailliertere Untersuchung der Gründe , die Sie wählen könnten nicht Umkehrungen haben, berührt in Rose Perrone Antwort (und auch in der Dokumentation, ein wenig).
Mark Amery
46

Ich werde die endgültige Antwort umschreiben, die ich in More iPhone 3 Development von Dave Mark und Jeff LeMarche gefunden habe.

Apple empfiehlt im Allgemeinen, dass Sie immer die Umkehrung erstellen und angeben, auch wenn Sie die Umkehrungsbeziehung in Ihrer App nicht verwenden. Aus diesem Grund werden Sie gewarnt, wenn Sie keine Inverse angeben.

Beziehungen sind nicht erforderlich eine inverse haben, denn es gibt einige Szenarien gibt , in denen die inverse Beziehung Performance schaden könnten. Angenommen, die umgekehrte Beziehung enthält eine extrem große Anzahl von Objekten. Das Entfernen des Inversen erfordert das Durchlaufen des Satzes, der die inverse, schwächende Leistung darstellt.

Aber es sei denn , Sie haben einen bestimmten Grund nicht an, die inverse Modell . Es hilft Core Data dabei, die Datenintegrität sicherzustellen. Wenn Sie auf Leistungsprobleme stoßen, ist es relativ einfach, die umgekehrte Beziehung später zu entfernen.

Rose Perrone
quelle
24

Die bessere Frage ist: "Gibt es einen Grund, keine Umkehrung zu haben?" Core Data ist wirklich ein Framework zur Verwaltung von Objektgraphen, kein Persistenz-Framework. Mit anderen Worten, seine Aufgabe besteht darin, die Beziehungen zwischen Objekten im Objektdiagramm zu verwalten. Inverse Beziehungen machen dies viel einfacher. Aus diesem Grund erwartet Core Data inverse Beziehungen und ist für diesen Anwendungsfall geschrieben. Ohne sie müssen Sie die Konsistenz des Objektdiagramms selbst verwalten. Insbesondere zu viele Beziehungen ohne umgekehrte Beziehung werden sehr wahrscheinlich durch Kerndaten beschädigt, es sei denn, Sie arbeiten sehr hart, um die Dinge am Laufen zu halten. Die Kosten in Bezug auf die Festplattengröße für die inversen Beziehungen sind im Vergleich zu dem Nutzen, den Sie daraus ziehen, wirklich unbedeutend.

Barry Wark
quelle
20

Es gibt mindestens ein Szenario, in dem ein guter Fall für eine Kerndatenbeziehung ohne Umkehrung gemacht werden kann: Wenn bereits eine andere Kerndatenbeziehung zwischen den beiden Objekten besteht, wird das Objektdiagramm beibehalten.

Beispielsweise enthält ein Buch viele Seiten, während sich eine Seite in einem Buch befindet. Dies ist eine wechselseitige Viele-zu-Eins-Beziehung. Durch das Löschen einer Seite wird nur die Beziehung ungültig, während durch das Löschen eines Buches auch die Seite gelöscht wird.

Möglicherweise möchten Sie jedoch auch die aktuell gelesene Seite für jedes Buch verfolgen. Dies könnte mit einer „current“ erfolgen Eigenschaft auf Seite , aber dann müssen Sie eine andere Logik , um sicherzustellen , dass nur eine Seite im Buch wie die aktuelle Seite jederzeit markiert. Wenn Sie stattdessen eine currentPage- Beziehung von Book zu einer einzelnen Seite herstellen, wird sichergestellt, dass immer nur eine aktuelle Seite markiert ist. Außerdem kann mit einfach book.currentPage problemlos auf diese Seite mit einem Verweis auf das Buch zugegriffen werden.

Grafische Darstellung der Kerndatenbeziehung zwischen Büchern und Seiten

Wie wäre die wechselseitige Beziehung in diesem Fall? Etwas weitgehend Unsinniges. "myBook" oder ähnliches könnte wieder in die andere Richtung hinzugefügt werden, enthält jedoch nur die Informationen, die bereits in der "Buch" -Beziehung für die Seite enthalten sind, und schafft so eigene Risiken. Möglicherweise wird in Zukunft die Art und Weise, wie Sie eine dieser Beziehungen verwenden, geändert, was zu Änderungen in Ihrer Kerndatenkonfiguration führt. Wenn page.myBook an einigen Stellen verwendet wurde, an denen page.book im Code hätte verwendet werden sollen, können Probleme auftreten. Eine andere Möglichkeit, dies proaktiv zu vermeiden, besteht darin, myBook nicht in der NSManagedObject-Unterklasse verfügbar zu machen, die für den Zugriff auf die Seite verwendet wird. Es kann jedoch argumentiert werden, dass es einfacher ist, das Inverse überhaupt nicht zu modellieren.

In dem beschriebenen Beispiel sollte die Löschregel für die currentPage-Beziehung auf "No Action" oder "Cascade" gesetzt werden, da keine wechselseitige Beziehung zu "Nullify" besteht. (Cascade impliziert, dass Sie beim Lesen jede Seite aus dem Buch herausreißen. Dies kann jedoch der Fall sein, wenn Sie besonders kalt sind und Kraftstoff benötigen.)

Wenn gezeigt werden kann, dass die Integrität von Objektgraphen nicht gefährdet ist, wie in diesem Beispiel, und die Komplexität und Wartbarkeit des Codes verbessert wird, kann argumentiert werden, dass eine Beziehung ohne Inverse die richtige Entscheidung sein kann.

Duncan Babbage
quelle
Danke Duncan - das kommt einem Anwendungsfall, den ich habe, sehr nahe. Im Wesentlichen muss ich in der Lage sein, die letzte Seite eines Buches zu bekommen. Ich brauche dies als dauerhaften Wert, damit ich ihn in einem Abrufprädikat verwenden kann. Ich hatte eine "bookIAmLastPageOf" -Inverse eingerichtet, aber sie ist ziemlich unsinnig und verursacht andere Probleme (ich verstehe das nicht richtig). Wissen Sie, ob es eine Möglichkeit gibt, die Warnung für einen einzelnen Fall zu deaktivieren?
Benjohn
Gibt es eine Möglichkeit, die Warnungen zu deaktivieren? Ich habe jetzt 2: ... sollte eine inverse und keine Aktion Löschregel haben ... kann zu Beziehungen zu gelöschten Objekten führen . Und kann currentPagemarkiert werden als Optional?
SwiftArchitect
Wäre das Speichern der currentPage als NSManagedObjectID anstelle einer Beziehung ein gültiger Ansatz?
user965972
4

Während die Dokumente keine Umkehrung zu erfordern scheinen, habe ich gerade ein Szenario gelöst, das tatsächlich zu "Datenverlust" führte, indem ich keine Umkehrung hatte. Ich habe ein Berichtsobjekt, das eine Beziehung zu vielen zu meldepflichtigen Objekten hat. Ohne die umgekehrte Beziehung gingen alle Änderungen an der zu vielen Beziehung beim Neustart verloren. Nach der Überprüfung des Core Data-Debugs stellte sich heraus, dass die Aktualisierungen des Objektdiagramms (der Beziehungen) trotz des Speicherns des Berichtsobjekts nie vorgenommen wurden. Ich habe eine Umkehrung hinzugefügt, obwohl ich sie nicht benutze, und voila, sie funktioniert. Es kann also nicht heißen, dass es erforderlich ist, aber Beziehungen ohne Umkehrungen können definitiv seltsame Nebenwirkungen haben.

greg
quelle
0

Inverse werden auch verwendet für Object Integrity(aus anderen Gründen siehe die anderen Antworten):

Der empfohlene Ansatz besteht darin, Beziehungen in beide Richtungen zu modellieren und die inversen Beziehungen entsprechend anzugeben. Core Data verwendet diese Informationen, um die Konsistenz des Objektdiagramms sicherzustellen, wenn eine Änderung vorgenommen wird

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

Der bereitgestellte Link gibt Ihnen Ideen, warum Sie ein inverseSet haben sollten . Ohne sie können Sie Daten / Integrität verlieren. Auch die Wahrscheinlichkeit, dass Sie auf ein Objekt zugreifen, ist nilwahrscheinlicher.

J. Doe
quelle
0

Es besteht im Allgemeinen keine Notwendigkeit für eine umgekehrte Beziehung. Es gibt jedoch nur wenige Macken / Fehler in den Kerndaten, bei denen Sie eine umgekehrte Beziehung benötigen. Es gibt Fälle, in denen Beziehungen / Objekte verloren gehen, obwohl beim Speichern des Kontexts kein Fehler auftritt, wenn eine umgekehrte Beziehung fehlt. Überprüfen Sie dieses Beispiel , das ich erstellt habe, um zu demonstrieren, dass Objekte fehlen und wie Sie das Problem umgehen können, während Sie mit Kerndaten arbeiten

Dhilip
quelle