views:

29

answers:

1

I'm having a really confounding problem using Core Data. In my Core Data store, for an existing Core Data object, I'm checking whether a relationship exists, and if not, I create the object like so (this is a method on AFFingerprintGeneratorOperation):

NSManagedObjectContext *newContext = [[NSManagedObjectContext alloc] init];
[newContext setMergePolicy:NSOverwriteMergePolicy];
NSPersistentStoreCoordinator *sharedStoreCoordinator = [[AFMainController sharedInstance] persistentStoreCoordinator];
[newContext setPersistentStoreCoordinator:sharedStoreCoordinator];
[self setManagedObjectContext:newContext];  // retaining property
[newContext release];

NSEntityDescription *fetchedTagSetEntity = [NSEntityDescription entityForName:@"FetchedTagSet"
                                                       inManagedObjectContext:newContext
                                            ];

AFTrack *retrievedTrack = (AFTrack *)[newContext objectWithID:[self trackObjectID]];
[self setTrack:retrievedTrack];

if (! retrievedTrack.fetchedTagSet) {
    AFFetchedTagSet *newFetchedTagSet = [[AFFetchedTagSet alloc] initWithEntity:fetchedTagSetEntity
                                                 insertIntoManagedObjectContext:newContext];

    [[retrievedTrack storedTrack] setFetchedTagSet:newFetchedTagSet];
    [newFetchedTagSet setStoredTrack:[retrievedTrack storedTrack]];
}

AFTrack, AFFetchedTagSet, and AFStoredTrack are all Core Data objects. AFFetchedTagSet and AFStoredTrack are in an on-disk Core Data store, while AFTrack is in a separate in-memory Core Data store.

Note that since the AFStoredTrack object is in a separate store, I need to fetch it like so (this is a method on AFTrack):

NSManagedObjectContext *objectContext = [self managedObjectContext];
NSPersistentStoreCoordinator *coordinator = [objectContext persistentStoreCoordinator];
NSString *URIString = [self storedTrackObjectIDString];

AFStoredTrack *theStoredTrack = nil;
if (URIString) {
    NSURL *objectURL = [NSURL URLWithString:URIString];
    NSManagedObjectID *storedTrackObjectID = [coordinator managedObjectIDForURIRepresentation:objectURL];
    theStoredTrack = (AFStoredTrack *)[objectContext objectWithID:storedTrackObjectID];
}

return theStoredTrack;

Since this is a method on AFTrack, it simply retrieves the AFTrack's own managed object context, so the code in the first excerpt should always use the exact same managed object context for all operations.

However, in the first excerpt, after calling setFetchedTagSet: with the new object, and attempting to save the Core Data store, I get this error:

"Dangling reference to an invalid object." = "<null>";
NSAffectedObjectsErrorKey =     (
    "<AFStoredTrack: 0x11550eef0> (entity: StoredTrack; id: 0x101eb57d0 <x-coredata:///StoredTrack/tF7F5568E-2959-4786-B73D-B7AC6586F5B9121> ; data: {\n    fetchedTagSet = \"0x11c956b60 <x-coredata:///FetchedTagSet/tF7F5568E-2959-4786-B73D-B7AC6586F5B9124>\";\n    fingerprint = \"ASPtPiNHPC7fGSYXTxtfFboMCg7BCxYQ+gZRCL4FWQdzBD8HPw\";\n    persistentID = nil;\n    status = 3;\n    updateAlbumName = nil;\n    updateArtistName = nil;\n    updateArtwork = nil;\n    updateGenre = nil;\n    updateLyrics = nil;\n    updateReleaseYear = nil;\n    updateTrackName = nil;\n})"
);
NSLocalizedDescription = "storedTrack is not valid.";
NSValidationErrorKey = storedTrack;
NSValidationErrorObject = "<AFFetchedTagSet: 0x11c956ac0> (entity: FetchedTagSet; id: 0x11c956b60 <x-coredata:///FetchedTagSet/tF7F5568E-2959-4786-B73D-B7AC6586F5B9124> ; data: {\n    ampliFindPUID = nil;\n    fingerprint = nil;\n    matchAlbum = nil;\n    matchLyrics = nil;\n    matchTrackName = nil;\n    matchTrackNumber = 0;\n    storedTrack = \"0x101eb57d0 <x-coredata:///StoredTrack/tF7F5568E-2959-4786-B73D-B7AC6586F5B9121>\";\n})";
NSValidationErrorValue = "<AFStoredTrack: 0x11550eef0> (entity: StoredTrack; id: 0x101eb57d0 <x-coredata:///StoredTrack/tF7F5568E-2959-4786-B73D-B7AC6586F5B9121> ; data: {\n    fetchedTagSet = \"0x11c956b60 <x-coredata:///FetchedTagSet/tF7F5568E-2959-4786-B73D-B7AC6586F5B9124>\";\n    fingerprint = \"ASPtPiNHPC7fGSYXTxtfFboMCg7BCxYQ+gZRCL4FWQdzBD8HPw\";\n    persistentID = nil;\n    status = 3;\n    updateAlbumName = nil;\n    updateArtistName = nil;\n    updateArtwork = nil;\n    updateGenre = nil;\n    updateLyrics = nil;\n    updateReleaseYear = nil;\n    updateTrackName = nil;\n})";

But both the AFFetchedTagSet and AFStoredTrack objects seem to be valid, as their ids match up, and the object context for these objects still exists and is retained by the AFFingerprintGeneratorOperation object.

I've seen this http://stackoverflow.com/questions/2003648/coredata-dangling-reference-to-an-invalid-object-error and this http://lists.apple.com/archives/cocoa-dev/2009/Nov/msg00190.html , but neither link seems to help. The former seems to say that a relationship is bad (which I don't think it is), and the latter says to avoid changing relationships in awakeFromFetch, which as far as I can tell I'm not doing.

Any help?

A: 

Since you're dealing with objects split across two different stores here, and relationships are not allow to span across different stores, you might need to make sure that the AFFetchedTagSet you're creating is assigned to the same persistent store as the AFStoredTrack object you're associating it with. I'm not sure which store Core Data defaults to when creating a new object, but I could see Core Data throwing a fit when you try to save a context that has a relationship set up that spans across the two stores, so setting the store explicitly surely couldn't hurt. You can do this by calling -[NSManagedObjectContext assignObject:toPersistentStore:] after creating the fetched tag set. Not 100% sure that's the problem, but that's what I'd try first.

As a side note, if you have inverse relationships set up in your Core Data model between AFFetchTagSet and AFStoredTrack, then only one of the two calls from -setFetchedTagSet: and -setStoredTrack: should be necessary, and Core Data should take care of the other one automagically.

Brian Webster
In my .xcdatamodel, I've set the default store for both AFFetchedTagSet and AFStoredTrack to both be the on-disk store. But in any case, I tried doing this explicitly in code, and it didn't have any effect. Besides, I've erroneously tried to store cross-store relationships before, and Core Data has explicitly thrown an exception.
Simone Manganelli
(Also, regarding inverse relationships, yeah, I knew that the reverse was set automatically, but I added this code in to see if it would solve the problem I'm having.)
Simone Manganelli