Core Data中的“悬挂对无效对象的引用”错误,具有看似有效的对象

时间:2010-10-21 01:11:04

标签: cocoa core-data

使用Core Data我遇到了一个非常棘手的问题。在我的Core Data存储中,对于现有的Core Data对象,我正在检查是否存在关系,如果不存在,我会像这样创建对象(这是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和AFStoredTrack都是核心数据对象。 AFFetchedTagSet和AFStoredTrack位于磁盘上的Core Data存储中,而AFTrack位于单独的内存核心数据存储中。

请注意,由于AFStoredTrack对象位于单独的商店中,我需要像这样获取它(这是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;

由于这是AFTrack上的一个方法,它只是检索AFTrack自己的托管对象上下文,因此第一个摘录中的代码应始终对所有操作使用完全相同的托管对象上下文。

但是,在第一个摘录中,在使用新对象调用setFetchedTagSet:并尝试保存Core Data存储之后,我收到此错误:

"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})";

但是AFFetchedTagSet和AFStoredTrack对象似乎都是有效的,因为它们的id匹配,并且这些对象的对象上下文仍然存在并由AFFingerprintGeneratorOperation对象保留。

我已经看到了这个CoreData: "Dangling reference to an invalid object." error和这个http://lists.apple.com/archives/cocoa-dev/2009/Nov/msg00190.html,但这两个链接似乎都没有帮助。前者似乎说关系是坏的(我认为不是这样),而后者则说要避免改变awakeFromFetch中的关系,据我所知,我不会这样做。

任何帮助?

3 个答案:

答案 0 :(得分:2)

在这个例子中,Core Data抛出的错误代码实际上是非常正确的,但是很难弄清楚发生了什么。

AFTrack对象上的“fetchedTagSet”属性只是AFStoredTrack上“fetchedTagSet”属性的一个方便的只读属性,这意味着为了检索AFFetchedTagSet类,我必须首先检索AFStoredTrack对象。

AFTrack上获取AFStoredTrack方法的方法是使用objectRegisteredForID:方法。如果没有向您正在调用objectRegisteredForID:方法的托管对象上下文注册,则不会获取对象。所以,我正在检索不存在的AFStoredTrack故障,并在它们上设置属性。

(注意:“未在托管对象上下文中注册”并不意味着它们不存在于Core Data存储中。它只是意味着特定的托管对象上下文不存在知道那个对象,因为你还没有插入它或在那个上下文中获取它。)

出于某种原因,objectRegisteredForID:返回空白 AFStoredTrack,而不是文档所述的nil。结果是我认为我有一个有效的AFStoredTrack对象,但实际上它只是一个占位符,所有值都是零。尝试保存对象使上下文意识到AFStoredTrack无效,产生错误消息。

修复方法是更改​​AFTrack(检索AFStoredTrack对象的方法)上的方法,以使用 objectWithID:而不是 objectRegisteredForID:。正如文档所述,objectWithID:获取一个对象,如果上下文尚未知道它。那返回了一个有效的对象,然后一切都很顺利。

答案 1 :(得分:1)

使用此代码解决了我的问题:

[[CustomManagedObject managedObjectContext] performBlockAndWait:^{
        NSError *error;
        if (![[CustomManagedObject managedObjectContext] save:&error])
        {
            NSLog(@"Error in Saving: %@", [error.userInfo description]);
        }
    }];

答案 2 :(得分:0)

由于您在这里处理分散在两个不同存储中的对象,并且不允许跨越不同存储的关系,您可能需要确保将要创建的AFFetchedTagSet分配给与之相同的持久存储。您正在关联的AFStoredTrack对象。我不确定在创建新对象时默认使用哪个存储Core Data,但是当您尝试保存跨越两个存储的关系设置的上下文时,我可以看到Core Data正好适用,因此设置存储显然肯定不会受伤。您可以在创建提取的标记集后调用-[NSManagedObjectContext assignObject:toPersistentStore:]来执行此操作。不是100%肯定这是问题,但这是我先尝试的。

作为旁注,如果在AFFetchTagSet和AFStoredTrack之间的Core Data模型中设置了反向关系,则只需要-setFetchedTagSet:和-setStoredTrack:的两个调用中的一个,并且Core Data应该小心另一个是自动化的。