158

Let's say I have two Entity classes: SocialApp and SocialAppType

In SocialApp I have one Attribute: appURL and one Relationship: type.

In SocialAppType I have three Attributes: baseURL, name and favicon.

The destination of the SocialApp relationship type is a single record in SocialAppType.

As an example, for multiple Flickr accounts, there would be a number of SocialApp records, with each record holding a link to a person's account. There would be one SocialAppType record for the "Flickr" type, that all SocialApp records would point to.

When I build an application with this schema, I get a warning that there is no inverse relationship between SocialAppType and SocialApp.

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

Do I need an inverse, and why?

Alex Reynolds
  • 91,635
  • 50
  • 223
  • 320

8 Answers8

143

Apple documentation has an great example that suggest a situation where you might have problems by not having an inverse relationship. Let's map it into this case.

Assume you modeled it as follows: enter image description here

Note you have a to-one relationship called "type", from SocialApp to SocialAppType. The relationship is non-optional and has a "deny" delete rule.

Now consider the following:

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

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

What we expect is to fail this context save since we have set the delete rule as Deny while relationship is non optional.

But here the save succeeds.

The reason is that we haven't set an inverse relationship. Because of that, the socialApp instance does not get marked as changed when appType is deleted. So no validation happens for socialApp before saving (it assumes no validation needed since no change happened). But actually a change happened. But it doesn't get reflected.

If we recall appType by

SocialAppType *appType = [socialApp socialAppType];

appType is nil.

Weird, isn't it? We get nil for a non-optional attribute?

So you are in no trouble if you have set up the inverse relationship. Otherwise you have to do force validation by writing the code as follows.

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];
sudo make install
  • 5,341
  • 3
  • 26
  • 45
MadNik
  • 7,273
  • 2
  • 33
  • 37
  • 10
    This is a great answer; thanks for spelling out in clear detail what is - from the docs, and from all I've read - the main argument in favor of having inverses for everything, even where the inverse relationship isn't humanly meaningful. This really ought to be the accepted answer. All this question thread is missing now is a more detailed exploration of the reasons you might choose *not* to have inverses, touched on in Rose Perrone's answer (and also in the documentation, a little). – Mark Amery Jul 03 '13 at 22:01
119

In practice, I haven't had any data loss due to not having an inverse - at least that I am aware of. A quick Google suggests you should use them:

An inverse relationship doesn't just make things more tidy, it's actually used by Core Data to maintain data integrity.

-- Cocoa Dev Central

You should typically model relationships in both directions, and specify the inverse relationships appropriately. Core Data uses this information to ensure the consistency of the object graph if a change is made (see “Manipulating Relationships and Object Graph Integrity”). For a discussion of some of the reasons why you might want to not model a relationship in both directions, and some of the problems that might arise if you don’t, see “Unidirectional Relationships.”

-- Core Data Programming Guide

kelin
  • 9,553
  • 6
  • 63
  • 92
Matthew Schinckel
  • 32,344
  • 6
  • 71
  • 109
  • 6
    See MadNik's answer (at the bottom of the page at the moment that I'm writing this) for a more thorough explanation of what the docs mean when they say that inverse relationships help maintain data integrity. – Mark Amery Jul 03 '13 at 22:02
47

I'll paraphrase the definitive answer I found in More iPhone 3 Development by Dave Mark and Jeff LeMarche.

Apple generally recommends that you always create and specify the inverse, even if you don't use the inverse relationship in your app. For this reason, it warns you when you fail to provide an inverse.

Relationships are not required to have an inverse, because there are a few scenarios in which the inverse relationship could hurt performance. For example, suppose the inverse relationship contains an extremely large number of objects. Removing the inverse requires iterating over the set that represents the inverse, weakening performance.

But unless you have a specific reason not to, model the inverse. It helps Core Data ensure data integrity. If you run into performance issues, it's relatively easy to remove the inverse relationship later.

Rose Perrone
  • 55,475
  • 49
  • 196
  • 231
24

The better question is, "is there a reason not to have an inverse"? Core Data is really an object graph management framework, not a persistence framework. In other words, its job is to manage the relationships between objects in the object graph. Inverse relationships make this much easier. For that reason, Core Data expects inverse relationships and is written for that use case. Without them, you will have to manage the object graph consistency yourself. In particular, to-many relationships without an inverse relationship are very likely to be corrupted by Core Data unless you work very hard to keep things working. The cost in terms of disk size for the inverse relationships really is insignificant in comparison to the benefit it gains you.

Barry Wark
  • 105,416
  • 24
  • 177
  • 204
21

There is at least one scenario where a good case can be made for a core data relationship without an inverse: when there is another core data relationship between the two objects already, which will handle maintaining the object graph.

For instance, a book contains many pages, while a page is in one book. This is a two-way many-to-one relationship. Deleting a page just nullifies the relationship, whereas deleting a book will also delete the page.

However, you may also wish to track the current page being read for each book. This could be done with a "currentPage" property on Page, but then you need other logic to ensure that only one page in the book is marked as the current page at any time. Instead, making a currentPage relationship from Book to a single page will ensure that there will always only be one current page marked, and furthermore that this page can be accessed easily with a reference to the book with simply book.currentPage.

Graphical presentation of core data relationship between books and pages

What would the reciprocal relationship be in this case? Something largely nonsensical. "myBook" or similar could be added back in the other direction, but it contains only the information already contained in the "book" relationship for the page, and so creates its own risks. Perhaps in the future, the way you are using one of these relationships is changed, resulting in changes in your core data configuration. If page.myBook has been used in some places where page.book should have been used in the code, there could be problems. Another way to proactively avoid this would also be to not expose myBook in the NSManagedObject subclass that is used to access page. However, it can be argued that it is simpler to not model the inverse in the first place.

In the example outlined, the delete rule for the currentPage relationship should be set to "No Action" or "Cascade", since there is no reciprocal relationship to "Nullify". (Cascade implies you are ripping every page out of the book as you read it, but that might be true if you're particularly cold and need fuel.)

When it can be demonstrated that object graph integrity is not at risk, as in this example, and code complexity and maintainability is improved, it can be argued that a relationship without an inverse may be the correct decision.


An alternative solution, as discussed in the comments, is to create your own UUID property on the target (in the example here, every Page would have an id that is a UUID), store that as a property (currentPage just stores a UUID as an Attribute in Book, rather than being a relationship), and then write a method to fetch the Page with the matching UUID when needed. This is probably a better approach than using a relationship without an inverse, not the least because it avoids the warning messages discussed.

Duncan Babbage
  • 18,889
  • 3
  • 50
  • 91
  • Thanks Duncan – this is extremely close to a use case I have. Essentially, I need to be able to get the **last page** of a book. I need this to be a persisted value so that I can use it in a fetch predicate. I had set up a "bookIAmLastPageOf" inverse, but it's pretty nonsensical and is causing other problems (I don't properly understand). Do you know if there is a way to disable the warning for a single case? – Benjohn Apr 09 '15 at 10:01
  • Is there a way to disable the warnings? I now have 2: *...should have an inverse* and *No Action Delete Rule ... may result in relationships to deleted objects*. And can `currentPage` be marked as `Optional`? – SwiftArchitect Oct 04 '15 at 04:30
  • Would storing the currentPage as a NSManagedObjectID instead of a relationship be a valid approach? – user965972 Feb 24 '16 at 08:29
  • My understanding (happy to be corrected) is that you can't rely on an NSManagedObjectID to necessarily remain the same across things like app restarts and iCloud/CloudKit syncing. In particular, I believe the NSManagedObjectID is different when the object is first created, but before an object is first inserted in a save operation into the managed store... An alternative solution is to create your own UUID property on the target, store that as a property, and then fetch the target with the matching UUID. This is probably a better approach than using a relationship without an inverse. – Duncan Babbage Aug 23 '20 at 21:29
4

While the docs don't seem to require an inverse, I just resolved a scenario that did in fact result in "data loss" by not having an inverse. I have a report object that has a to-many relationship on reportable objects. Without the inverse relationship, any changes to the to-many relationship were lost upon relaunch. After inspecting the Core Data debug it was apparent that even though I was saving the report object, the updates to the object graph (relationships) were never being made. I added an inverse, even though I don't use it, and voila, it works. So it might not say it's required but relationships without inverses can definitely have strange side effects.

greg
  • 1,876
  • 16
  • 26
1

There is no need for inverse relationship generally. But there are few quirks/bugs in Core data where you need an inverse relationship. There are cases where relationships/objects go missing , even though there is no error while saving the context, if there are missing inverse relationship. Check this example, which I created to demonstrate objects missing and how to workaround, while working with Core data

Dhilip
  • 498
  • 4
  • 18
0

Inverses are also used for Object Integrity (for other reasons, see the other answers):

The recommended approach is to model relationships in both directions and specify the inverse relationships appropriately. Core Data uses this information to ensure the consistency of the object graph if a change is made

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

The provided link gives you ideas why you should have an inverse set. Without it, you can lose data/integrety. Also, the chance that you access an object which is nil is more likely.

J. Doe
  • 9,757
  • 4
  • 42
  • 83