在获取永久 ID 后,核心数据无法为对象完成故障

Posted

技术标签:

【中文标题】在获取永久 ID 后,核心数据无法为对象完成故障【英文标题】:Core Data could not fulfill fault for object after obtainPermanentIDs 【发布时间】:2012-07-04 02:35:31 【问题描述】:

我正在从网络服务器获取数据,并在名为 backgroundMOC 的子私有背景上下文中对其进行处理。它是链接到主 UI 的 mainMOC 的子对象,因此保存在 backgroundMOC 上会触发 UI 更改。 mainMOCmasterMOC 的子代,masterMOC 是绑定到持久存储的私有后台队列,因此保存在主服务器上会保存到磁盘。

我现在做的是接收数据,在backgroundMOC上创建新对象,然后保存backgroundMOC(以便UI更新),保存mainMOC,(这样我几乎可以保存到磁盘),然后保存masterMOC(这样我终于可以写入磁盘了)。问题是当对象通过获取的结果控制器出现在 UI 中时,objectId 仍然是一个临时对象。

这会导致重复行问题,如果我从服务器收到相同的数据(偶然),我的backgroundMOC 不知道这个对象已经存在,因为它没有被分配一个永久 id,所以它创建另一个对象。当我重新启动应用程序时,重复的对象消失了,所以我知道这只是 id 映射的问题。

所以我想我可以试试

[backgroundMOC obtainPermanentIDsForObjects:backgroundMOC.registeredObjects.allObjects error:nil];

完全保存之前(我也尝试过保存后)。但是,由于某种原因,调用此行会引发异常:

CoreData 无法满足...

如果您有任何提示可以引导我走向正确的方向,请分享。谢谢

编辑:好的,所以最初我在 backgroundMOC 上调用了 gainPermanentIDsForObjects,它是 mainMOC 的子对象,它是 masterMOC 的子对象。我切换它以便获得 mainMOC 上的 id,它解决了我所有的问题(目前)。我不应该在子上下文中调用 gainPermIds 吗?

【问题讨论】:

@Sven 你为什么要为此悬赏?你有类似的问题吗? 是的,这与我面临的问题几乎完全相同。 我的“解决方案”是摆脱主/主/子设置,只使用主/子,以便持久存储链接到主队列而不是后台队列。 今天在这个问题上花了大约 5 个小时后,我最终也这样做了。我会在这个问题上花更多时间,但我们已经接近最后期限,所以我有更重要的事情要做。不过,对于未来的项目,我真的很想找到一个解决方案。 【参考方案1】:

这是一个已知的错误(保存新对象时嵌套上下文未获得永久 ID)可能会在即将发布的版本中修复...

虽然您应该能够要求提供永久 ID,但您应该只在已插入的对象上要求它们。

[moc obtainPermanentIDsForObjects:moc.insertedObjects.allObjects error:0];

您必须在保存 MOC 之前执行此操作,因为如果您在未获得永久 ID 的情况下保存,临时 ID 会传播到父上下文。例如,在您保存到 mainMoc,然后获取 IDS 的情况下,backgroundMOC 仍然具有临时 ID,因此将来从中保存会创建重复数据。

请注意,获取永久 ID 会一直到数据库,但如果您在主 MOC 的子 MOC 中执行此操作,则在发生这种情况时您根本不应该阻塞主线程。

因此,在您从最低级别的 MOC 中保存时,您应该有效地拥有这样的东西(当然,要进行适当的错误处理)...

[backgroundMoc performBlock:^
    [backgroundMoc obtainPermanentIDsForObjects:backgroundMoc.insertedObjects.allObjects error:0];
    [backgroundMoc save:0];
    [mainMoc performBlock:^
       [mainMoc save:0];
        [masterMoc performBlock:^
            [masterMoc save:0];
        ];
    ];
];

如果您愿意,还可以玩一些其他游戏。

在 NSManagedObject 上提供一个与此类似的类别...

@implementation NSManagedObject (initWithPermanentID)
- (id)initWithEntity:(NSEntityDescription *)entity insertWithPermanentIDIntoManagedObjectContext:(NSManagedObjectContext *)context 
    if (self = [self initWithEntity:entity insertIntoManagedObjectContext:context]) 
        NSError *error = nil;
        if (![context obtainPermanentIDsForObjects:@[self] error:&error]) 
            @throw [NSException exceptionWithName:@"CoreData Error" reason:error.localizedDescription userInfo:error.userInfo];
        
    
    return self;


+ (NSArray*)createMultipleObjects:(NSUInteger)count withEntity:(NSEntityDescription *)entity inManagedObjectContext:(NSManagedObjectContext *)context 
    NSMutableArray *array = [NSMutableArray arrayWithCapacity:count];
    for (NSUInteger i = 0; i < count; ++i) 
        [array addObject:[[self alloc] initWithEntity:entity insertIntoManagedObjectContext:context]];
    
    NSError *error = nil;
    if (![context obtainPermanentIDsForObjects:array error:&error]) 
        @throw [NSException exceptionWithName:@"CoreData Error" reason:error.localizedDescription userInfo:error.userInfo];
    
    return array;

@end

现在,在第一个中,您需要付费进入数据库并为创建的每个实体创建一个 ID,但这并不多,而且它发生在后台线程中,并且每次下降都很短...

哦,好吧,它不是最好的,但它提供了有用的。此外,第二个创建多个相同对象,并同时获取它们的永久 ID。

您也可以使用直接连接到 PSC 的 MOC,并监视 DidChange 事件,但这与旧方法相同。

不幸的是,您不能有一个单独的 MOC 只发出 persistentID 请求并传递 ObjectID,尽管您可以有一个单独的 MOC 在数据库中制作原型对象,并为您提供它们的 ObjectID。

原型工厂是一种相当普遍的模式,如果你走这条路,那么当最终的错误修复到达这里时,很容易做出微小的改变。

编辑

回应斯文……

如果您要创建新的复杂图表,则需要在创建后立即获取永久 ID。要减少对商店的点击次数,您应该全部创建它们,然后立即获取 ID,然后开始连接它们。

老实说,所有这些都是为了解决当前存在的错误,这些错误对于中小型更新来说是值得解决的。修复错误后,您的代码将是相同的(无需获取)。因此,我建议将这种方法用于较小的导入。

如果您要进行大规模更新,我建议使用“旧”方法。创建一个直接连接到 PSC 的新 MOC。在那里进行所有更改,并让您的“实时”上下文从那些 DidSave 通知中合并。

最后,关于永久 ID 对数据库的影响。可以丢弃 MOC。磁盘被击中,元数据被改变,但对象没有被持久化。

老实说,不过,我并没有进行大型测试来查看是否有任何空白空间,因此您可能想这样做并与我联系。

查看磁盘上的实际数据库文件大小,然后创建10000个对象,然后获取持久ID,释放MOC,再查看大小。

如果有影响,您可以尝试删除对象,或在大更新后对数据库运行清理以查看是否有效。

如果您要创建大量可能会丢弃的对象,则无需访问数据库。您可能只想直接附加到 PSC 并使用旧的忠实通知。

【讨论】:

这听起来不错。我试图使用obtainPermanentIDsForObjects:error: 来解决这个实际上有效的问题,但后来导致了无法访问对象的异常。我想这是因为我在获得永久 ID 之前将新对象(带有临时 ID)添加到关系中。您的示例建议在创建新对象后立即获取永久 ID。如果这对我有用,我会尝试。如果从未保存新对象的上下文会发生什么,您是否有任何信息?【参考方案2】:

在前台线程和后台线程之间工作时,我对 Core Data 感到很沮丧。在为我遇到的一个问题寻找解决方案时

Magical Record

我花了一些时间阅读文档和方法,我可以说它确实使使用 Core Data 变得更加容易。具体来说,它还将帮助您管理多个上下文和线程。

您可能想检查一下。

【讨论】:

以上是关于在获取永久 ID 后,核心数据无法为对象完成故障的主要内容,如果未能解决你的问题,请参考以下文章

核心数据故障

核心数据对象id格式

核心数据:我想要获取的对象中的 pk

vSAN永久磁盘故障的处理过程

核心数据谓词根据关系获取数据,错误为无法解析格式字符串

Consul集群故障恢复