Core Data 中看似有效的对象出现“悬空引用无效对象”错误

Posted

技术标签:

【中文标题】Core Data 中看似有效的对象出现“悬空引用无效对象”错误【英文标题】:"Dangling reference to an invalid object" error in Core Data with seemingly valid objects 【发布时间】:2010-10-21 01:11:04 【问题描述】:

我在使用 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 都是 Core Data 对象。 AFFetchedTagSet 和 AFStoredTrack 位于磁盘核心数据存储中,而 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: 并尝试保存核心数据存储后,我收到此错误:

"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 中改变关系,据我所知我没有这样做。

有什么帮助吗?

【问题讨论】:

【参考方案1】:

Core Data 抛出的错误代码在这种情况下实际上是完全正确的,但很难弄清楚发生了什么。

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

获取 AFStoredTrack 方法的 AFTrack 方法正在使用 objectRegisteredForID: 方法。如果对象未在您调用 objectRegisteredForID: 方法的托管对象上下文中注册,则不会获取对象。因此,我正在检索不存在的 AFStoredTrack 故障,并为其设置属性。

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

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

解决方法是将 AFTrack(检索 AFStoredTrack 对象的方法)上的方法更改为使用 objectWithID: 而不是 objectRegisteredForID: 。如文档所述,objectWithID: 在上下文还不知道它的情况下获取一个对象。这返回了一个有效的对象,然后一切都很好。

【讨论】:

【参考方案2】:

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

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

【讨论】:

【参考方案3】:

由于您在这里处理的是跨两个不同存储的对象,并且关系不允许跨不同存储,因此您可能需要确保您创建的 AFFetchedTagSet 与您与之关联的 AFStoredTrack 对象。我不确定创建新对象时Core Data默认使用哪个存储,但是当您尝试保存具有跨两个存储设置的关系的上下文时,我可以看到Core Data会出现问题,因此设置存储明确肯定不会受到伤害。您可以在创建获取的标签集后调用-[NSManagedObjectContext assignObject:toPersistentStore:] 来完成此操作。不是 100% 确定这是问题所在,但这是我首先要尝试的。

附带说明,如果您在 Core Data 模型中设置了 AFFetchTagSet 和 AFStoredTrack 之间的反向关系,则应该只需要来自 -setFetchedTagSet: 和 -setStoredTrack: 的两个调用之一,并且 Core Data 应该注意另一个自动。

【讨论】:

在我的 .xcdatamodel 中,我将 AFFetchedTagSet 和 AFStoredTrack 的默认存储都设置为磁盘存储。但无论如何,我尝试在代码中明确执行此操作,但没有任何效果。另外,我之前错误地尝试存储跨存储关系,Core Data已经明确抛出异常。 (另外,关于反向关系,是的,我知道反向是自动设置的,但我添加了这段代码,看看它是否能解决我遇到的问题。)

以上是关于Core Data 中看似有效的对象出现“悬空引用无效对象”错误的主要内容,如果未能解决你的问题,请参考以下文章

垃圾和悬挂参考有什么区别?

使关系有序会导致“对无效对象的悬空引用”。使用上下文限制时出错

Core Data,我如何有效地查找和删除托管对象

Core Data 在看似正常的 NSString 字段上崩溃

既然 objectID 在临时对象和永久对象之间发生变化,如何有效地处理 Core Data 中的临时对象?

NSSortDescriptor 对 Core Data NSSet 对象进行排序的最有效方法