获取PermanentID后,Core Data无法满足对象的错误

时间:2012-07-04 02:35:31

标签: iphone objective-c ios core-data

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

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

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

所以我想我可能会尝试

[backgroundMOC obtainPermanentIDsForObjects:backgroundMOC.registeredObjects.allObjects error:nil];
在保存之前

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

  

CoreData无法解决...

的错误

如果您有任何可能引导我朝正确方向发展的提示,请分享。感谢

编辑:确定所以最初我在backgroundMOC上调用了acquirePermanentIDsForObjects,后者是mainMOC的子节点,它是masterMOC的子节点。我切换它以便我获得mainMOC上的ID,它解决了我所有的问题(现在)。我是不是应该在子语境中调用obtainPermIds?

2 个答案:

答案 0 :(得分:19)

这是一个已知的错误(嵌套的上下文在保存新对象时没有获得永久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,但它并没有那么多,而且它发生在后台线程中,每次下降都很短...... / p>

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

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

不幸的是,你不能让一个单独的MOC只生成persistentID请求并传递ObjectID,尽管你可以在DB中有一个单独的MOC制作原型对象,并为你提供ObjectID。

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

修改

回应斯文......

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

老实说,这一切都是为了解决当前存在的错误,这些错误值得解决中小型更新。当修复错误时,你的代码将是相同的(没有获得)。所以,我建议这种方法用于较小的进口。

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

最后,关于永久ID的数据库影响。丢弃MOC是可以的。磁盘被命中,元数据被更改,但对象不会被保留。

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

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

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

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

答案 1 :(得分:0)

在前台线程和后台线程之间工作时,我遇到过Core Data的各种挫败感。在搜索我的一个问题的解决方案时,我遇到了

Magical Record

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

您可能想要查看它。