CoreData: "Dangling reference to an invalid object." error
I'm working on a Cocoa-Touch app, it uses CoreData and has some开发者_StackOverflow中文版 NSPersistentObject subclasses generated by the XCode model editor.
I've noticed that recently, when saving the context I get an error which has as user info the following part:
(gdb) po ui {
"Dangling reference to an invalid object." = <null>;
NSAffectedObjectsErrorKey = <dump #1 of someObject>;
NSLocalizedDescription = "Operation could not be completed. (Cocoa error 1550.)";
NSValidationErrorKey = <someKey pointing to someObject #1>;
NSValidationErrorObject = <dump #2 of someOtherObject which points to the dump #1 object>;
NSValidationErrorValue = { <list of someMoreObjects> };
}
There are on other keys in the user info dictionary.
All the dumps I get are valid objects, I've verified them all.
NSValidationErrorObject
is an object which has an NSSet
which contains objects of types dumped in NSAffectedObjectsErrorKey
and NSValidationErrorValue
. But all these objects are valid.
Furthermore, this happens randomly, sometimes it happens, sometimes not, and sometimes the list dumped in NSValidationErrorValue
is longer, sometimes shorter.
I have no clue why this is happening, or what exactly the error is. Does anyone know what's going on? Any ideas what might cause this bizarre error?
This error usually arises because a relationship is set improperly often when an object is left without a necessary reciprocal relationship. The object is "dangling" because the object graph says it should be in a relationship but it is just hanging off in space unconnected to any other object. The object is still valid in the sense that it is internal consistent but it's not in a valid place in the graph.
This question was asked a while back, but I just ran into it. It was not due in my case to a improperly set relationship technically. It was due to the object being set created in a different context, note not on a different thread just a different context on the same thread.
So look for threading issues if you are doing anything with thread with Core Data.
Let's say you have a table "recipes" and a child table "ingredients". You then create a one-to-many relation from recipe's to ingredients and also create an inverse relationship (one-to-one) from ingredients to recipes. It makes sense to specify a delete rule of "cascade" from the recipes table because if you delete a recipe the ingredient should also be deleted. However, if you specify "no action" in the delete rule on the one-to-one relationship in ingredients you will get the dangling reference error when you try to delete an ingredient. Change the delete rule on the one-to-one relationship to "nullify" and this should correct the problem.
I know it's long after the fact, but I've been fighting this problem on a Core Data Model that has ALL relationships set to Nullify. Kept getting these dangling references until I found a single setPrimitiveValue
instead of setValue
when I was adding to the relationships. Be careful, with relationships, you gotta be sure you do the right thing to let Core Data maintain the relationships for you!
I have another example of how to cause this problem: I have a MOC with a concurrency type of NSMainQueueConcurrencyType
. Somewhere in the code I do this:
__block MyObjectType1 *obj1;
[managedObjectContext performBlockAndWait:^{
obj1 = [NSEntityDescription insertNewObjectForEntityForName:@"Thing" inManagedObjectContext:managedObjectContext];
}];
// some other stuff
[self saveContext];
__block NSManagedObjectID *object1ID;
[managedObjectContext performBlockAndWait:^{
object1ID = [obj1 objectID];
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// do some slow-ish stuff
[managedObjectContext performBlockAndWait:^{
// create new object that has a relationship
NSManagedObject *obj1_copy = [managedObjectContext objectWithID:object1ID];
MyObjectType2 *obj2 = [NSEntityDescription insertNewObjectForEntityForName:@"OtherThing" inManagedObjectContext:managedObjectContext];
obj2.relatedThing = obj1_copy;
}];
[self saveContext];
});
It turns out that sometimes, this fails. I still don't understand why, but forcing to get a non-temporary objectID
seems to do the trick:
[context performBlockAndWait:^{
NSError *error;
[managedObjectContext obtainPermanentIDsForObjects:@[obj1] error:&error];
object1ID = obj1.objectID;
}];
I had the same problem, finally I found the problem was that I was setting a relationship between two different managed object context.
Adding to original answer, there can be couple of reasons for this crash to occur. Read the error description carefully, It In my case i was setting up a relationship with object from another context.
I ran into this issue, and the problem had to do with different (or rather one nil) managed object contexts for entities that had a relationship. In my case, I created a relationship between the entities when both had nil Managed Object Contexts, and then added one of the entities to a MOC, and assumed the other would be added to the MOC as well. I assumed this because of the first two comments to the top answer on this SO thread, but that ended up being wrong.
So lesson learned: if you add an entity to a MOC, other entities that have relationships to it do not get dragged into the MOC along with it. You have to add them to the MOC also or Core Data will not be happy.
My problem was solved using this code:
[[CustomManagedObject managedObjectContext] performBlockAndWait:^{
NSError *error;
if (![[CustomManagedObject managedObjectContext] save:&error])
{
NSLog(@"Error in Saving: %@", [error.userInfo description]);
}
}];
精彩评论