Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Core Data unique constraints - "Dangling reference to an invalid object" on save

I'm introducing Unique Constraints to an existing app, in a new model version.

The model has entity Person which has an optional one-to-one relationship with entity Pet. Person's delete rule is cascade, and Pet's inverse relationship delete rule is nullify. There's another entity, Job, which Person also has a to-one relationship. But that deletion rule, and the inverse deletion rule, are both nullify.

Person has a "personID" property which I've made unique.

In a main queue context I've got an instance of Person, with ID xxx, that has no Pet set. Let's call that person "Charlie". I create a Job as well, and set that as "iOS Dev", and assign it to Charlie's job property.

Then in a child private queue context, I insert a new "Charlie" also with ID xxx (the unique constraint) and create a Pet and set it. I don't set a Job. I save the child context with no issue.

Immediately after, I attempt to save the Main Queue context. But just before that, I inspect the insertedObjects property of the Main Queue context and see, as I expect to, an instance of Person with ID xxx. What I'm expecting is that this second instance will be handled by unique constraints and be "merged" with the original instance of Person from the prior paragraph. But, when I actually call the MOC's save: method, it throws an exception and I inspect the NSError object:

Error Domain=NSCocoaErrorDomain Code=1550 "The operation couldn’t be completed. (Cocoa error 1550.)" UserInfo={Dangling reference to an invalid object.=null

Any ideas on how to avoid the exception? I'm used to "Dangling references" in Core Data meaning a relationship is misconfigured, but I've gone over it a number of times and it looks fine, so I'm not sure what else to troubleshoot. I have all contexts involved using the NSMergeByPropertyObjectTrumpMergePolicy merge policy.

Obviously this is a little hard to follow, so I threw a sample project up on Github in case anybody is interested: https://github.com/bpapa/core-data-unique-bug

like image 860
bpapa Avatar asked Apr 15 '16 14:04

bpapa


2 Answers

I wound up burning a Code-Level Support ticket on this, and an Apple engineer confirmed that there is a bug. As a workaround, it was recommended to write my own merge policy that calls super and then manually makes sure the relationship is set on both ends.

like image 197
bpapa Avatar answered Oct 05 '22 12:10

bpapa


If you want to employ the new iOS 9 Unique Constrains strategy, you have to make sure that both contexts have the same merge policy set.

Note that this will not help you if you have a scenario where the newer instance of the object has an attribute/relationship that is nil and your intention is to always "augment" the object, i.e. fill any missing attributes and update existing ones.

Otherwise, for pre-iOS9, the following is valid.

Perhaps there is some misunderstanding about the meaning of the merge policy you employ (NSMergeByPropertyObjectTrumpMergePolicy). It refers to a situation where you have one version of an object in memory and another in the persistent store, not two versions in memory in different contexts. From the header files:

This singleton policy merges conflicts between the persistent store's version of the object and the current in memory version.

Thus, your process of creating new object is not the correct approach.

Instead, you should fetch the Person in the child context using the unique ID.

Alternatively, you could make the person's objectID (an opaque Core Data artifact) available to the child context to get a reference to the same object in the object graph using objectWithID:.

like image 34
Mundi Avatar answered Oct 05 '22 11:10

Mundi