如何处理临时NSManagedObject实例?

时间:2010-07-15 13:56:27

标签: objective-c iphone core-data

我需要创建NSManagedObject个实例,用它们做一些事情,然后将它们丢弃或存储到sqlite db。问题是,我无法创建NSManagedObject未连接到NSManagedObjectContext的实例,这意味着在我决定不需要我的数据库中的某些对象后,我必须以某种方式清理。

为了处理它,我使用相同的协调器创建了一个内存存储,我使用assignObject:toPersistentStore.将临时对象放在那里现在,我如何确保这些临时对象无法访问我从两个商店上下文中获取的数据?或者我是否必须为此类任务创建单独的上下文?


UPD:

现在我正在考虑为内存存储创建单独的上下文。如何将对象从一个上下文移动到另一个上下文?只是使用[context insertObject:]?它在这个设置中可以正常工作吗?如果我从对象图中插入一个对象,整个图形是否也会插入到上下文中?

8 个答案:

答案 0 :(得分:144)

注意:此答案非常旧。查看完整历史记录的评论。我的建议后来发生了变化,我不再建议使用无关联的NSManagedObject个实例。我目前的建议是使用临时子NSManagedObjectContext实例。

原始答案

最简单的方法是创建没有关联NSManagedObject的{​​{1}}个实例。

NSManagedObjectContext

然后当你想保存它时:

NSEntityDescription *entity = [NSEntityDescription entityForName:@"MyEntity" inManagedObjectContext:myMOC];
NSManagedObject *unassociatedObject = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];

答案 1 :(得分:39)

iOS5提供了一个更简单的替代Mike Weller的答案。而是使用 NSManagedObjectContext。它消除了通过NSNotificationCenter蹦床的需要

创建子上下文:

NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
childContext.parentContext = myMangedObjectContext;

然后使用子上下文创建对象:

NSManagedObject *o = [NSEntityDescription insertNewObjectForEntityForName:@"MyObject" inManagedObjectContext:childContext];

仅在保存子上下文时应用更改。所以要放弃更改只是不保存。

关系仍有限制。即,您无法在其他上下文中创建与对象的关系。为了解决这个问题,请使用objectID来从子上下文中获取对象。例如

NSManagedObjectID *mid = [myManagedObject objectID];
MyManagedObject *mySafeManagedObject = [childContext objectWithID:mid];
object.relationship=mySafeManagedObject;

注意,保存子上下文会将更改应用于父上下文。保存父上下文会保留更改。

有关完整说明,请参阅wwdc 2012 session 214

答案 2 :(得分:9)

实现此类事情的正确方法是使用新的托管对象上下文。您使用相同的持久存储创建托管对象上下文:

NSManagedObjectContext *tempContext = [[[NSManagedObjectContext alloc] init] autorelease];
[tempContext setPersistentStore:[originalContext persistentStore]];

然后你添加新对象,改变它们等等。

当需要保存时,需要在tempContext上调用[tempContext save:...],并处理保存通知以将其合并到原始上下文中。要丢弃对象,只需释放此临时上下文并忘记它。

因此,当您保存临时上下文时,更改将持久保存到商店,您只需将这些更改恢复到主要上下文中:

/* Called when the temp context is saved */
- (void)tempContextSaved:(NSNotification *)notification {
    /* Merge the changes into the original managed object context */
    [originalContext mergeChangesFromContextDidSaveNotification:notification];
}

// Here's where we do the save itself

// Add the notification handler
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(tempContextSaved:)
                                             name:NSManagedObjectContextDidSaveNotification
                                           object:tempContext];

// Save
[tempContext save:NULL];
// Remove the handler again
[[NSNotificationCenter defaultCenter] removeObserver:self
                                                name:NSManagedObjectContextDidSaveNotification
                                              object:tempContext];

这也是您应该处理多线程核心数据操作的方式。每个线程一个上下文。

如果您需要从这个临时上下文访问现有对象(添加关系等),那么您需要使用对象的ID来获取这样的新实例:

NSManagedObject *objectInOriginalContext = ...;
NSManagedObject *objectInTemporaryContext = [tempContext objectWithID:[objectInOriginalContext objectID]];

如果您尝试在错误的上下文中使用NSManagedObject,则在保存时会出现异常。

答案 3 :(得分:9)

从nil上下文创建临时对象可以正常工作,直到您真正尝试与上下文的对象建立关系!= nil!

确保你没问题。

答案 4 :(得分:8)

您所描述的正是NSManagedObjectContext的用途。

来自Core Data Programming Guide: Core Data Basics

  

您可以将托管对象上下文视为智能便笺簿。从持久性存储中获取对象时,将临时副本放在便笺簿上,它们形成对象图(或对象图的集合)。然后,您可以根据需要修改这些对象。但是,除非您实际保存这些更改,否则持久存储将保持不变。

Core Data Programming Guide: Managed Object Validation

  

这也支持了表示“暂存区”的托管对象上下文的想法 - 通常,您可以将托管对象带到暂存区并根据需要进行编辑,然后最终提交更改或丢弃它们。

NSManagedObjectContext s设计为轻量级。您可以随意创建和丢弃它们 - 它是持久性存储协调器,它的依赖性很“重”。单个持久性存储协调器可以具有与之关联的许多上下文。在旧的,过时的线程限制模型下,这意味着在每个上下文中设置相同的持久性存储协调器。今天,它意味着将嵌套上下文连接到与持久性存储协调器关联的根上下文。

在该上下文中创建上下文,创建和修改托管对象。如果要保留它们并传达这些更改,请保存上下文。否则丢弃它。

尝试创建独立于NSManagedObjectContext的托管对象会遇到麻烦。请记住,核心数据最终是对象图的变更跟踪机制。因此,托管对象实际上是part of the managed object context。上下文observes their life cyclewithout the context并非所有托管对象功能都能正常工作。

答案 5 :(得分:6)

根据您对临时对象的使用情况,上述建议有一些注意事项。我的用例是我想创建一个临时对象并将其绑定到视图。当用户选择保存此对象时,我想设置与现有对象的关系并保存。我想这样做是为了避免创建一个临时对象来保存这些值。 (是的,我可以等到用户保存然后抓取视图内容,但我将这些视图放在一个表中,并且执行此操作的逻辑不太优雅。)

临时对象的选项是:

1)(首选)在子上下文中创建临时对象。 这不起作用,因为我将对象绑定到UI,我无法保证在子上下文中调用对象访问器。 (我没有找到其他文件,所以我不得不假设。)

2)使用nil对象上下文创建临时对象。 这不起作用,导致数据丢失/损坏。

我的解决方案: 我通过使用nil对象上下文创建临时对象来解决这个问题,但是当我保存对象时,而不是将其作为#2插入,我将其所有属性复制到我在主上下文中创建的新对象中。我在我的NSManagedObject子类中创建了一个名为cloneInto的支持方法:它允许我轻松地为任何对象复制属性和关系。

答案 6 :(得分:1)

对我来说,马库斯的回答没有用。这对我有用:

NSEntityDescription entityForName:@"MyEntity" inManagedObjectContext:myMOC];
NSManagedObject *unassociatedObject = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];

然后,如果我决定保存它:

[myMOC insertObject:unassociatedObjet];
NSError *error = nil;
[myMoc save:&error];
//Check the error!

我们也必须忘记发布它

[unassociatedObject release]

答案 7 :(得分:0)

我正在为Swift重写这个answer,因为所有类似的问题都可以快速重定向到这个问题。

您可以使用以下代码声明没有任何ManagedContext的对象。

let entity = NSEntityDescription.entity(forEntityName: "EntityName", in: myContext)
let unassociatedObject = NSManagedObject.init(entity: entity!, insertInto: nil)

稍后,要保存对象,您可以将其插入上下文并保存。

myContext.insert(unassociatedObject)
// Saving the object
do {
    try self.stack.saveContext()
    } catch {
        print("save unsuccessful")
    }
}