Is there a way to instantiate a NSManagedObject without inserting it?

IphoneCore DataNsfetchedresultscontrollerNsmanagedobject

Iphone Problem Overview


I have a user interface to insert a Transaction. once the user clicks on a plus he gets the screen and i want to instantiate my Core Data NSManagedObject entity let the user work on it. Then when the user clicks on the Save button i will call the save function.

so down to code:

transaction = (Transaction *)[NSEntityDescription insertNewObjectForEntityForName:@"Transaction" inManagedObjectContext:self.managedObjectContext];
//even if i dont call save: its going to show up on my table
[self.managedObjectContext save:&error]

P.S i am using an NSFetchedResultsController on that table and I see that the NSFetchedResultsController is inserting a section and an object to the table.

My thought is if there is a way to instantiate the Transaction NSManagedObject i could update it with out saving untill the client choses to.

Iphone Solutions


Solution 1 - Iphone

For what it's worth, Marcus Zarra seems to be promoting the nil context approach, claiming that it's expensive to create a new context. For more details, see this answer to a similar question.

Update

I'm currently using the nil context approach and have encountered something that might be of interest to others. To create a managed object without a context, you use the initWithEntity:insertIntoManagedObjectContext: method of NSManagedObject. According to Apple's documentation for this method:

> If context is not nil, this method > invokes [context insertObject:self] > (which causes awakeFromInsert to be > invoked).

The implication here is important. Using a nil context when creating a managed object will prevent insertObject: from being called and therefore prevent awakeFromInsert from being called. Consequently, any object initialization or setting of default property values done in awakeFromInsert will not happen automatically when using a nil context.

Bottom line: When using a managed object without a context, awakeFromInsert will not be called automatically and you may need extra code to compensate.

Solution 2 - Iphone

here is how i worked it out:

On load, where we know we are dealing with a new transaction, i created an out of context one.

NSEntityDescription *entity = [NSEntityDescription entityForName:@"Transaction" inManagedObjectContext:self.managedObjectContext];
		transaction = (Transaction *)[[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];
	

then when it came to establishing a relation ship i did this:

if( transaction.managedObjectContext == nil){
		NSEntityDescription *entity = [NSEntityDescription entityForName:@"Category" inManagedObjectContext:self.managedObjectContext];
		Category *category = (Category *)[[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:nil];
		category.title = ((Category *)obj).title;
		transaction.category = category;
		[category release];
	}
	else {
		transaction.category = (Category *)obj;
	}

and at the end to save:

if (transaction.managedObjectContext == nil) {
		[self.managedObjectContext insertObject:transaction.category];
		[self.managedObjectContext insertObject:transaction];
	}
	//NSLog(@"\n saving transaction\n%@", self.transaction);
	
	NSError *error;
	if (![self.managedObjectContext save:&error]) {
		// Update to handle the error appropriately.
		NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
		exit(-1);  // Fail
	}

Solution 3 - Iphone

There's a fundamental problem with using a nil MOC: Objects in different MOCs aren't supposed to reference each other — this presumably also applies when one side of a relationship has a nil MOC. What happens if you save? (What happens when another part of your app saves?)

If your object doesn't have relationships, then there are plenty of things you can do (like NSCoding).

You might be able to use -[NSManagedObject isInserted] in NSPredicate (presumably it's YES between inserting and successfully saving). Alternatively, you can use a transient property with the same behaviour (set it to YES in awakeFromInsert and NO in willSave). Both of these may be problematic if a different part of your app saves.

Using a second MOC is how CoreData is "supposed" to be used, though; it handles conflict detection and resolution for you automatically. Of course, you don't want to create a new MOC each time there's a change; it might be vaguely sensible to have one MOC for unsaved changes by the slow "user thread" if you don't mind some parts of the UI seeing unsaved changes in other parts (the overhead of inter-MOC communication is negligible).

Solution 4 - Iphone

You can insert an NSManagedObjectContext with the -[NSManagedObject initWithEntity:insertIntoManagedObjectContext:], passing nil for the managed object context. You must, of course, assign it to a context (using -[NSManageObjectContext insertObject:] before saving. This is, as far as I know, not really the intended pattern in Core Data, however (but see @mzarra's answer here). There are some tricky ordering issues (i.e. making sure the instance gets assigned to a context before it expects to have one, etc.). The more standard pattern is to create a new managed object context and insert your new object into that context. When the user saves, save the context, and handle the NSManagedObjectDidSaveNotification to merge the changes into your 'main' context. If the user cancels the transaction, you just blow away the context and go on with your business.

Solution 5 - Iphone

An NSManagedObject can be created using the nil as the context, but if there other NSManagedObjects it must link to it will result in an error. The way I do it I pass the context into the destination screen and create a NSManagedObject in that screen. Make all the changes link other NSManagedObjects. If the user taps the cancel button I delete the NSManagedObject and save the context. If the user taps the the save button I update the data in the NSManagedObject, save it to the context, and release the screen. In the source screen I update the table with a reload.

Deleting the NSManagedObject in the destination screen gives core data time to update the file. This is usually enough time for you not to see the change in the tableview. In the iPhone Calendar app you have a delay from the time it saves to the time it shows up in the tableview. This could be considered a good thing from a UI stand point that your user will focus on the row that was just added. I hope this helps.

Solution 6 - Iphone

transaction = (Transaction *)[NSEntityDescription insertNewObjectForEntityForName:@"Transaction" inManagedObjectContext:nil];

if the last param is nil, it will return a NSManagedObject without save to db

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionEdward AshakView Question on Stackoverflow
Solution 1 - IphoneJames HuddlestonView Answer on Stackoverflow
Solution 2 - IphoneEdward AshakView Answer on Stackoverflow
Solution 3 - Iphonetc.View Answer on Stackoverflow
Solution 4 - IphoneBarry WarkView Answer on Stackoverflow
Solution 5 - IphoneejkujanView Answer on Stackoverflow
Solution 6 - Iphoneuser501836View Answer on Stackoverflow