核心数据无法为获取PerPantmanID后的对象填充故障

我从Web服务器获取数据,在名为backgroundMOC的子私人背景上下文中处理数据。 这是一个mainMOC链接到主UI的孩子,所以保存在backgroundMOC触发UI变化。 mainMOCmainMOC的子级,它是一个与持久性存储绑定的私有后台队列,因此在master上保存到磁盘。

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

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

所以我想我可能会尝试

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

保存之前(我已经尝试保存之后)。 但是,由于某种原因,调用这一行将引发exception:

 CoreData could not fulfill a fault for... 

如果您有任何提示可能会导致我正确的方向,请分享。 谢谢

编辑:好的,所以最初我在调用backgroundMOC上的obtainPermanentIDsForObjects,它是mainMOC的子元素,它是masterMOC的子元素。 我把它切换,以便获得mainMOC上的id,并解决了我所有的问题(现在)。 我是不是应该在子上下文中调用obtainPermIds?

这是一个已知的错误(当保存新对象时,嵌套上下文不会获得永久ID),并且应该在即将发布的版本中被修复。

你应该可以要求永久性的身份证,但是你只能要求他们插入的物品。

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

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

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

所以,从最低级别的MOC中保存,你应该有这样的事情(当然有适当的error handling)…

 [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 

现在,第一个,你付出去进入数据库,并创build每个实体创build一个ID,但它不是很多,它发生在一个后台线程,每个下降…

哦,这不是最好的,但它提供了有用的。 另外,第二个创build多个相同的对象,并同时抓取他们的永久ID。

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

不幸的是,你不能拥有一个单独的MOC只是发出persistentID请求并传递ObjectID,虽然你可以有一个独立的MOC在数据库中创build原型对象,并给你它们的ObjectID。

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

编辑

为了回应斯文…

如果您要创build新的复杂graphics,则需要在创build后立即获取永久ID。 为了减less到商店的点击次数,你应该创build它们,然后立即获得ID,然后开始连接它们。

老实说,所有这些都是为了解决当前存在的错误,值得中小型更新。 当错误被修复时,你的代码将会是一样的(没有获得)。 所以,我build议这个方法适用于较小的import。

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

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

老实说,我没有做一个大的testing,看看是否有空的空间,所以你可能想这样做,并回到我身边。

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

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

如果你打算创build大量的对象,那么你可能会丢掉这些对象,那么就不需要打开数据库。 您可能只想直接附加到PSC并使用旧的忠实通知。

在前台线程和后台线程间工作时,Core Data遇到了各种各样的问题。 在为我的一个问题寻找解决scheme时遇到了问题

神奇的logging

我花了一些时间阅读文档和方法,我可以说它确实使Core Data的工作更容易。 具体来说,它也将帮助您pipe理多个上下文和线程。

你可能想看看。