Czy każda relacja danych podstawowych musi mieć odwrotność?

150

Powiedzmy, że mam dwie klasy Entity: SocialAppiSocialAppType

W SocialAppmam jeden atrybut: appURLi jedno Stan: type.

W SocialAppTypeMam trzy atrybuty: baseURL, namei favicon.

Miejsce docelowe SocialApprelacji typeto pojedynczy rekord w SocialAppType.

Na przykład w przypadku wielu kont Flickr byłoby kilka SocialApprekordów, z których każdy zawiera łącze do konta danej osoby. Byłby jeden SocialAppTyperekord typu „Flickr”, na który SocialAppwskazywałyby wszystkie rekordy.

Kiedy tworzę aplikację z tym schematem, otrzymuję ostrzeżenie, że nie ma odwrotnej relacji między SocialAppTypei SocialApp.

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

Czy potrzebuję odwrotności i dlaczego?

Alex Reynolds
źródło

Odpowiedzi:

117

W praktyce nie miałem żadnej utraty danych z powodu braku odwrotności - przynajmniej tego jestem świadomy. Szybki Google sugeruje, że powinieneś z nich skorzystać:

Odwrotna zależność nie tylko sprawia, że ​​wszystko jest bardziej uporządkowane, ale w rzeczywistości jest wykorzystywane przez dane podstawowe do zachowania integralności danych.

- Cocoa Dev Central

Zazwyczaj należy modelować relacje w obu kierunkach i odpowiednio określać relacje odwrotne. Dane podstawowe wykorzystują te informacje, aby zapewnić spójność wykresu obiektu w przypadku wprowadzenia zmiany (patrz „Manipulowanie relacjami i integralnością wykresu obiektów”). Omówienie niektórych powodów, dla których możesz chcieć nie modelować relacji w obu kierunkach, oraz niektórych problemów, które mogą się pojawić, jeśli tego nie zrobisz, znajdziesz w sekcji „Relacje jednokierunkowe”.

- Podręcznik programowania podstawowych danych

Matthew Schinckel
źródło
5
Zobacz odpowiedź MadNika (na dole strony w chwili, gdy to piszę), aby dokładniej wyjaśnić, co mają na myśli doktorzy, gdy mówią, że odwrotne relacje pomagają zachować integralność danych.
Mark Amery,
135

Dokumentacja Apple zawiera świetny przykład, który sugeruje sytuację, w której możesz mieć problemy, nie mając odwrotnej zależności. Zmapujmy to w tym przypadku.

Załóżmy, że zaprojektowałeś to w następujący sposób: wprowadź opis obrazu tutaj

Zauważ, że istnieje relacja do jednego zwana „ typem ”, od SocialAppdo SocialAppType. Relacja nie jest opcjonalna i ma regułę usuwania „odmów” .

Rozważmy teraz następujące kwestie:

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

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

Oczekujemy, że ten kontekst nie zostanie zapisany, ponieważ ustawiliśmy regułę usuwania jako Odmów, podczas gdy relacja nie jest opcjonalna.

Ale tutaj zapis się udaje.

Powodem jest to, że nie ustawiliśmy odwrotnej zależności . Z tego powodu instancja aplikacji społecznościowej nie jest oznaczana jako zmieniona po usunięciu appType. Dlatego przed zapisaniem aplikacji społecznościowej nie jest przeprowadzana weryfikacja (zakłada ona, że ​​nie jest wymagana weryfikacja, ponieważ nie nastąpiła żadna zmiana). Ale tak naprawdę nastąpiła zmiana. Ale to się nie odbija.

Jeśli przypomnimy sobie appType według

SocialAppType *appType = [socialApp socialAppType];

appType ma wartość nil.

Dziwne, prawda? Otrzymujemy zero dla atrybutu nie opcjonalnego?

Więc nie masz kłopotów, jeśli ustawiłeś odwrotną zależność. W przeciwnym razie musisz wymusić walidację, pisząc kod w następujący sposób.

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
źródło
9
To świetna odpowiedź; dziękuję za jasne, szczegółowe wyjaśnienie, co jest - z dokumentów i ze wszystkiego, co przeczytałem - głównym argumentem przemawiającym za posiadaniem odwrotności do wszystkiego, nawet jeśli odwrotna relacja nie ma ludzkiego znaczenia. To naprawdę powinna być akceptowana odpowiedź. Cały ten wątek pytań, którego teraz brakuje, to bardziej szczegółowe badanie powodów, dla których możesz zdecydować się nie mieć odwrotności, poruszonych w odpowiedzi Rose Perrone (a także trochę w dokumentacji).
Mark Amery,
46

Sparafrazuję ostateczną odpowiedź, którą znalazłem w More iPhone 3 Development autorstwa Dave'a Marka i Jeffa LeMarche.

Firma Apple ogólnie zaleca, aby zawsze tworzyć i określać odwrotność, nawet jeśli nie używasz odwrotnej relacji w swojej aplikacji. Z tego powodu ostrzega Cię, gdy nie podasz odwrotności.

Relacje nie muszą mieć odwrotności, ponieważ istnieje kilka scenariuszy, w których odwrotna zależność może zaszkodzić wydajności. Na przykład załóżmy, że odwrotna zależność zawiera niezwykle dużą liczbę obiektów. Usunięcie odwrotności wymaga iteracji po zestawie, który reprezentuje odwrotność, osłabiającą wydajność.

Ale jeśli nie masz konkretnego powodu, aby tego nie robić, modeluj odwrotnie . Pomaga Core Data w zapewnieniu integralności danych. Jeśli napotkasz problemy z wydajnością, stosunkowo łatwo jest później usunąć odwrotną zależność.

Rose Perrone
źródło
24

Lepsze pytanie brzmi: „czy istnieje powód, aby nie mieć odwrotności”? Dane podstawowe to w rzeczywistości struktura zarządzania grafami obiektów, a nie struktura trwałości. Innymi słowy, jego zadaniem jest zarządzanie relacjami między obiektami na grafie obiektów. Odwrotne relacje znacznie to ułatwiają. Z tego powodu Core Data oczekuje odwrotnych relacji i jest napisany dla tego przypadku użycia. Bez nich będziesz musiał samodzielnie zarządzać spójnością grafu obiektów. W szczególności relacje typu „to-many” bez relacji odwrotnej są bardzo prawdopodobne, że zostaną uszkodzone przez dane podstawowe, chyba że będziesz bardzo ciężko pracować, aby wszystko działało. Koszt pod względem rozmiaru dysku dla odwrotnych relacji jest naprawdę nieistotny w porównaniu z korzyściami, jakie przynosi.

Barry Wark
źródło
20

Istnieje co najmniej jeden scenariusz, w którym można przedstawić dobre argumenty za podstawową relacją danych bez odwrotności: gdy istnieje już inna podstawowa relacja danych między dwoma obiektami, która zajmie się utrzymaniem grafu obiektu.

Na przykład książka zawiera wiele stron, podczas gdy strona znajduje się w jednej książce. Jest to dwukierunkowa relacja wiele do jednego. Usunięcie strony po prostu unieważnia relację, podczas gdy usunięcie książki spowoduje również usunięcie strony.

Możesz jednak również chcieć śledzić aktualnie czytaną stronę dla każdej książki. Można to zrobić za pomocą właściwości „currentPage” na stronie , ale wtedy potrzebna jest inna logika, aby zapewnić, że tylko jedna strona w książce jest oznaczona jako bieżąca strona w dowolnym momencie. Zamiast tego, utworzenie relacji currentPage z Book na pojedynczą stronę zapewni, że zawsze będzie zaznaczona tylko jedna bieżąca strona, a ponadto, że ta strona będzie łatwo dostępna z odniesieniem do książki po prostu book.currentPage.

Graficzna prezentacja podstawowych relacji danych między książkami i stronami

Jaka byłaby w tym przypadku wzajemna relacja? Coś w dużej mierze bezsensownego. „myBook” lub coś podobnego można dodać z powrotem w innym kierunku, ale zawiera on tylko informacje zawarte już w relacji „książka” dla strony, co stwarza własne ryzyko. Być może w przyszłości sposób korzystania z jednej z tych relacji ulegnie zmianie, co spowoduje zmiany w konfiguracji podstawowych danych. Jeśli page.myBook został użyty w niektórych miejscach, w których w kodzie powinien zostać użyty page.book, mogą wystąpić problemy. Innym sposobem proaktywnego uniknięcia tego byłoby również nie ujawnianie myBook w podklasie NSManagedObject, która jest używana do uzyskiwania dostępu do strony. Można jednak argumentować, że w pierwszej kolejności łatwiej jest nie modelować odwrotności.

W przedstawionym przykładzie reguła usuwania dla relacji currentPage powinna być ustawiona na „No Action” lub „Cascade”, ponieważ nie ma wzajemnego związku z „Nullify”. (Kaskada sugeruje, że podczas czytania wyrywasz każdą stronę z książki, ale może to być prawda, jeśli jesteś szczególnie zimny i potrzebujesz paliwa).

Kiedy można wykazać, że integralność grafu obiektowego nie jest zagrożona, jak w tym przykładzie, a złożoność kodu i łatwość konserwacji są poprawione, można argumentować, że relacja bez odwrotności może być właściwą decyzją.

Duncan Babbage
źródło
Dzięki Duncan - jest to bardzo zbliżone do przypadku użycia, który mam. Zasadniczo muszę mieć możliwość uzyskania ostatniej strony książki. Potrzebuję, aby była to trwała wartość, aby można było jej użyć w predykacie pobierania. Ustawiłem odwrotność "bookIAmLastPageOf", ale jest to całkiem bezsensowne i powoduje inne problemy (nie rozumiem). Czy wiesz, czy istnieje sposób na wyłączenie ostrzeżenia dla pojedynczego przypadku?
Benjohn,
Czy istnieje sposób na wyłączenie ostrzeżeń? Mam teraz 2: ... powinien mieć odwrotność, a zasada usuwania braku akcji ... może skutkować powiązaniami z usuniętymi obiektami . I można currentPageoznaczyć jako Optional?
SwiftArchitect
Czy przechowywanie currentPage jako NSManagedObjectID zamiast relacji byłoby prawidłowym podejściem?
user965972
4

Chociaż dokumenty nie wydają się wymagać odwrotności, właśnie rozwiązałem scenariusz, który w rzeczywistości spowodował „utratę danych”, ponieważ nie miał odwrotności. Mam obiekt raportu, który ma relację do wielu na obiektach raportowanych. Bez relacji odwrotnej wszelkie zmiany w relacji „wiele do wielu” zostały utracone po ponownym uruchomieniu. Po sprawdzeniu debugowania danych podstawowych okazało się, że mimo zapisywania obiektu raportu aktualizacje wykresu obiektu (relacji) nigdy nie były wykonywane. Dodałem odwrotność, mimo że go nie używam i voila, działa. Może więc nie mówić, że jest to wymagane, ale relacje bez odwrotności mogą z pewnością mieć dziwne skutki uboczne.

greg
źródło
0

Odwrotności są również używane do Object Integrity(z innych powodów zobacz inne odpowiedzi):

Zalecanym podejściem jest modelowanie relacji w obu kierunkach i odpowiednie określenie relacji odwrotnych. Dane podstawowe wykorzystują te informacje, aby zapewnić spójność grafu obiektów w przypadku wprowadzenia zmiany

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

Podany link daje pomysły, dlaczego warto mieć inversezestaw. Bez tego możesz stracić dane / integralność. Ponadto prawdopodobieństwo uzyskania dostępu do obiektu niljest bardziej prawdopodobne.

J. Doe
źródło
0

Generalnie nie ma potrzeby stosowania odwrotnej zależności. Ale jest kilka dziwactw / błędów w danych podstawowych, w których potrzebujesz odwrotnej relacji. Istnieją przypadki, w których brakuje relacji / obiektów, nawet jeśli nie ma błędu podczas zapisywania kontekstu, jeśli brakuje odwrotnej relacji. Sprawdź ten przykład , który utworzyłem, aby zademonstrować brakujące obiekty i sposoby obejścia tego problemu podczas pracy z danymi podstawowymi

Dhilip
źródło