如何将NSManagedObject从一个上下文复制或移动到另一个上下文?

Aeo*_*aut 53 cocoa cocoa-touch core-data nsmanagedobject nsmanagedobjectcontext

我有一个相当标准的设置,一个从未保存的暂存器MOC(包含从Web下载的一堆对象)和另一个永久MOC持久化对象.当用户从scratchMOC中选择一个对象添加到她的库时,我想要1)从scratchMOC中删除对象并插入到permanentMOC中,或者2)将对象复制到permanentMOC中.该核心数据FAQ说,我可以复制的对象是这样的:

NSManagedObjectID *objectID = [managedObject objectID];
NSManagedObject *copy = [context2 objectWithID:objectID];
Run Code Online (Sandbox Code Playgroud)

(In this case, context2 would be permanentMOC.) However, when I do this, the copied object is faulted; the data is initially unresolved. When it does get resolved, later, all of the values are nil; none of the data (attributes or relationships) from the original managedObject are actually copied or referenced. Therefore I can't see any difference between using this objectWithID: method and just inserting an entirely new object into permanentMOC using insertNewObjectForEntityForName:.

I realize I can create a new object in permanentMOC and manually copy each key-value pair from the old object, but I'm not very happy with that solution. (I have a number of different managed objects for which I have this problem, so I don't want to have to write and update copy: methods for all of them as I continue developing.) Is there a better way?

Mar*_*rra 54

首先,将具有多于一个NSManagedObjectContext在单个线程是标准配置.99%的情况下,您只需要一个上下文,这将为您解决这种情况.

为什么你觉得你需要不止一个NSManagedObjectContext

更新

这实际上是我见过的少数几个有用的用例之一.为此,您需要从一个上下文到另一个上下文执行对象的递归复制.工作流程如下:

  1. 在持久化上下文中创建新对象
  2. 从源对象获取属性的字典(使用-dictionaryWithValuesForKeys-[NSEntityDescription attributesByName]执行此操作.
  3. 将值字典设置到目标对象上(使用-setValuesForKeysWithDictionary)
  4. 如果你有关系,你将需要递归地执行此复制并将关系转换为硬编码(以避免某些循环逻辑)或使用 -[NSEntityDescription relationshipsByName]

正如另一个人所提到的,你可以从我的书中从The Pragmatic Programmers Core Data Book下载示例代码,看看这个问题的一个解决方案.当然在书中我更深入地讨论它:)

  • 出于某种原因,我无法获得正确的代码来执行第2步,所以这里是:`NSDictionary*newValues = [oldObject dictionaryWithValuesForKeys:[[objectEntity attributesByName] allKeys]]` (7认同)
  • @Marcus现在对于像亲子MOC这样的CoreData改进仍然有效吗?因为在我看来,使用多个MOC并不仅仅是好的,但推荐使用,即使对于创建新对象等简单情况,也选择取消/保存.Apple示例CoreDataBooks现在使用多个MOC进行此操作. (5认同)
  • 我的应用程序有一个mapView,可以下载数百或数千个对象,以便在应用程序启动时(或用户更改位置)用作注释.我的想法是,将地图对象保存在第二个MOC(scratchMOC)中会更加优雅和高效,而这个MOC永远不会被保存; 按照设计,只要应用程序启动,它就会被抛出并重新加载.然后,用户可以选择一个或多个要添加到其库中的地图对象,然后将这些地图对象复制/移动到持久化的MOC.我认为这将避免迭代通过MOC删除app退出处的数千个地图对象. (3认同)
  • “单线程上的 NSManagedObjectContext 不是标准”——完全不正确 (2认同)

Tec*_*Zen 9

文档具有误导性和不完整性.objectID方法本身不会复制对象,它们只是保证您已经获得了所需的特定对象.

context2在实施例实际上是在源上下文而不是目的地.你得到一个零,因为目标上下文没有该ID的对象.

由于对象图的复杂性以及上下文管理图的方式,复制托管对象是相当复杂的.您必须在新上下文中详细重新创建复制的对象.

下面是一些示例代码,我从The Pragmatic Programmer的核心数据的示例代码中剪切:Apple的Mac OS X上的持久数据API.(您可以下载整个项目代码而无需在Pragmatic站点上购买该书.)它应该为您提供如何在上下文之间复制对象的大致想法.

您可以创建一些复制对象的基本代码,但每个对象图的关系的细节通常意味着您必须为每个数据模型进行自定义.


小智 6

我自己也有同样的问题,发现这篇文章创建了断开连接的实体,以后可以添加到上下文中:http://locassa.com/temporary-storage-in-apples-coredata/

我们的想法是你有一个NSManagedObject,因为你将要在数据库中存储对象.我的障碍是许多这些对象是通过HTTP API下载的,我想在会话结束时抛出大部分对象.想想用户帖子流,我只想保存那些被收藏或保存为草稿的用户帖子.

我使用创建所有帖子

+ (id)newPost {
    NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Post" inManagedObjectContext:self.managedObjectContext];
    Post *post = [[Post alloc] initWithEntity:entityDescription insertIntoManagedObjectContext:nil];
    return post;
}
Run Code Online (Sandbox Code Playgroud)

然后,当帖子被收藏时,这些帖子将被插入到本地托管对象上下文中

+ (BOOL)favoritePost:(Post *)post isFavorite:(BOOL)isFavorite
{
    // Set the post's isFavorite flag
    post.isFavorite = [NSNumber numberWithBool:isFavorite];

    // If the post is being favorited and not yet in the local database, add it
    NSError *error;
    if (isFavorite && [self.managedObjectContext existingObjectWithID:post.objectID error:&error] == nil) {
        [self.managedObjectContext insertObject:post];
    }
    // Else if the post is being un-favorited and is in the local database, delete it
    else if (!isFavorite && [self.managedObjectContext existingObjectWithID:post.objectID error:&error] != nil) {
        [self.managedObjectContext deleteObject:post];
    }

    // If there was an error, output and return NO to indicate a failure
    if (error) {
        NSLog(@"error: %@", error);
        return NO;
    }

    return YES;
}
Run Code Online (Sandbox Code Playgroud)

希望有所帮助.