How to copy or move NSManagedObject from one context to another?

I have what I suppose is a pretty standard setup, with one MOC record that is never saved (contains a bunch of objects downloaded from the Internet) and another persistent MOC that stores the objects. When a user selects an object from scratchMOC to add to their library, I want to either 1) remove the object from scratchMOC and paste it into PermanentMOC, or 2) copy the object to PermanentMOC. The master data FAQ says that I can copy an object as follows:

NSManagedObjectID *objectID = [managedObject objectID]; NSManagedObject *copy = [context2 objectWithID:objectID]; 

(In this case, context2 will be a constant MOC.) However, when I do this, the copied object fails; data is not initially resolved. When it is resolved, later all values ​​are zero; None of the data (attributes or relationships) from the original managed entity is copied or referenced. Therefore, I see no difference between using this objectWithID method and simply inserts a brand new object into PermanentMOC using insertNewObjectForEntityForName :.

I understand that I can create a new object in PermanentMOC and manually copy each pair of key values ​​from the old object, but I'm not very happy with this solution. (I have several different managed objects for which I have this problem, so I don’t want to write and update copy methods: for all of them as development continues.) Is there a better way?

+48
cocoa-touch cocoa core-data nsmanagedobject nsmanagedobjectcontext
Jun 08 2018-10-06T10:
source share
4 answers

First, having more than one NSManagedObjectContext in a single thread is not a standard configuration. In 99% of cases, you need only one context, and this will solve this situation for you.

Why do you feel you need more than one NSManagedObjectContext ?

Update

This is truly one of the few use cases I've seen where it makes sense. To do this, you need to make a recursive copy of the object from one context to another. The workflow will be as follows:

  • Create a new object in a constant context
  • get the attribute dictionary from the source object (use -dictionaryWithValuesForKeys and -[NSEntityDescription attributesByName] for this.
  • set a dictionary of values ​​to the target object (using -setValuesForKeysWithDictionary )
  • If you have a relationship, you will need to make this copy recursively and go through the relationship either hardcoded (to avoid some cyclic logic) or using -[NSEntityDescription relationshipsByName]

As already mentioned, you can download a sample code from my book from the “Main Map of Programmers” and see one solution to this problem. Of course, in the book I discuss it in more detail :)

+49
Jun 09 '10 at 17:54
source share
— -

The documentation is misleading and incomplete. The objectID methods themselves do not copy objects, they just guarantee that you get the specific object that you need.

context2 in the example is actually the source context, not the destination. You get zero because the destination context does not have an object with this identifier.

Copying managed objects is quite difficult due to the complexity of the object graph and how the context controls the graph. In the new context, you need to recreate the copied object in detail.

Here is an example of code that I have disabled from the example code Basic data for a pragmatic programmer: Apple API for saving data in Mac OS X. (You may be able to download all of the project code without buying a book on the Pragmatic website.) It should provide you with a rough idea of ​​how to make a copy of the object between context.

You can create basic code that copies objects, but the data of each relationship between the object graph usually means that you have to customize for each data model.

+9
Jun 08 '10 at 16:41
source share

The very same problem found this article on created disabled entities, which can later be added to the context: http://locassa.com/temporary-storage-in-apples-coredata/

The idea is that you have an NSManagedObject because you are going to store objects in a database. My obstacle was that many of these objects are loaded via the HTTP API, and I want to throw most of them out at the end of the session. Think about the user message flow, and I want to save only those that were selected or saved as a draft.

I create all my posts using

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

and then messages are inserted into the context of the local managed entity when they are preferred

 + (BOOL)favoritePost:(Post *)post isFavorite:(BOOL)isFavorite { // Set the post 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; } 

Hope this helps.

+6
Jul 12 2018-12-12T00:
source share

You need to make sure that you keep the context in which managedObject lives. To get the same object in a different context, it must be present in persistent storage.

According to the documentation , objectWithID: always returns an object. Thus, the fact that an object solves the error, all nil values ​​mean that it does not find your object in persistent storage.

+1
Jun 08 '10 at 15:27
source share



All Articles