How to Deal with Temporary NSManagedObject instanc

2019-01-02 19:31发布

I need to create NSManagedObject instances, do some stuff with them and then trash them or store to sqlite db. The problem is, I cannot create instances of NSManagedObject unconnected to NSManagedObjectContext and this means I have to clear up somehow after I decide that I don't need some of the objects in my db.

To deal with it, I have created an in-memory store using the same coordinator and I'm placing temporary objects there by using assignObject:toPersistentStore. Now, how do I ensure that these temporary objects don't get to the data, which I fetch from the common to both stores context? Or do I have to create separate contexts for such a task?


UPD:

Now I'm thinking about making separate context for in-memory store. How do I move objects from one context to another? Just using [context insertObject:]? Will it work OK in this setup? If I insert one object from the graph of objects, does the whole graph also get inserted into context?

8条回答
泛滥B
2楼-- · 2019-01-02 19:48

Depending on your use of the temporary object there are some caveats to the above recommendations. My use case is that I want to create a temporary object and bind it to views. When the user opts to save this object, I want to setup relationships to existing object(s) and save. I want to do this to avoid creating a temporary object to hold those values. (Yes, I could just wait until the user saves and then grab the view contents but I'm putting these views inside of a table and the logic to do this is less elegant.)

The options for temporary objects are:

1) (Preferred) Create the temporary object in a child context. This won't work because I'm binding the object to the UI and I can't guarantee the object accessors are called on the child context. (I have found no documentation that states otherwise so I have to assume.)

2) Create the temporary object with nil object context. This doesn't work and results in data loss/corruption.

My Solution: I solved this by creating the temporary object with nil object context but when I save the object, rather than inserting it as #2, I copy all of it's attributes into a new object that I create in the main context. I created a supporting method in my NSManagedObject subclass called cloneInto: that lets me copy attributes and relationships easily for any object.

查看更多
十年一品温如言
3楼-- · 2019-01-02 19:48

For me Marcus's answer didn't work. Here's what worked for me:

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

then, if I decide to save it:

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

We must also not forget to release it

[unassociatedObject release]
查看更多
爱死公子算了
4楼-- · 2019-01-02 19:52

iOS5 provides a simpler alternative to Mike Weller's answer. Instead use a child NSManagedObjectContext. It removes the need to trampoline through NSNotificationCenter

To create a child context:

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

Then create your objects using the child context:

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

The changes are only applied when the child context is saved. So to discard the changes just do not save.

There is still a limitation on relationships. ie You can't create relationships to objects in other contexts. To get around this use objectID's, to get the object from the child context. eg.

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

Note, saving the child context applies the changes to the parent context. Saving the parent context persists the changes.

See wwdc 2012 session 214 for a full explanation.

查看更多
萌妹纸的霸气范
5楼-- · 2019-01-02 19:53

I am rewriting this answer for Swift as all similar questions for swift redirect to this question.

You can declare the object without any ManagedContext using the following code.

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

Later on, to save the object you can insert it into the context and save it.

myContext.insert(unassociatedObject)
// Saving the object
do {
    try self.stack.saveContext()
    } catch {
        print("save unsuccessful")
    }
}
查看更多
梦该遗忘
6楼-- · 2019-01-02 19:54

Creating temporary objects from nil context works fine until you actually try to have a relationship with an object whose context != nil!

make sure your okay with that.

查看更多
时光乱了年华
7楼-- · 2019-01-02 19:55

NOTE: This answer is very old. See comments for full history. My recommendation has since changed and I no longer recommend using unassociated NSManagedObject instances. My current recommendation is to use temporary child NSManagedObjectContext instances.

Original Answer

The easiest way to do this is to create your NSManagedObject instances without an associated NSManagedObjectContext.

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

Then when you want to save it:

[myMOC insertObject:unassociatedObject];
NSError *error = nil;
if (![myMoc save:&error]) {
  //Respond to the error
}
查看更多
登录 后发表回答