Add an instance of NSManagedObject to NSManagedObjectContext ok, updating the same instance failed
Asked Answered
B

3

4

I am using core data in my iPhone app. I have created a simple class Friend, which derives from NSManagedObject and which uses the following property:

@property (nonatomic, retain) NSString *name;

I am able to add and delete instances of this class to my context and my changes are persistent also.
Now I want to update/modify a Friend-instance and make it persistent again.

But this seems not to work.

Here is piece of code which shows my problem:

//  NSManagedObjectContext *context  = < my managed context> 
//   NSFetchedResultsController *nsfrc= < my fetched result controller>

NSEntityDescription *entity = [nsfrc entity];
NSManagedObject *newManagedObject = [NSEntityDescription 
       insertNewObjectForEntityForName:[entity name]  inManagedObjectContext:context];

Friend *f = (Friend *) newManagedObject;
f.name = @"name1";
//1.  --- here context.hasChanges == 1 ---   ok

NSError *error = nil;
if (![context save:&error]) { ... }
//2.  --- here context.hasChanges == 0 ---   ok

f.name = @"name2";
//3.  --- here context.hasChanges == 0 ---   nok?

if (![context save:&error]) { ... }

At comment 1 everything is fine. I got a new NSManagedObject of the Friend-type and I can change the name property. The context shows me that there is something to save. After saving the context I see context.hasChanges == 0. Note also that the data is persistent after saving the context.

After comment 2 I change the name property. Now I would expect context.hasChanges == 1 and also after the context save I would expect the new name to be persistent. But unfortunately It is not. Starting the application again, loads the Friend instance with the name-property = @"name1".

I cannot find any hint or example inside the core data documentation. So what am I doing wrong? What do I have to do to update/modify an existing Friend-instance and to make it persistent?

The only solution I see is to delete the entry, change it, and add it again. But I don't think that this is the correct way for it.

Thanks!

Barta answered 25/7, 2010 at 11:47 Comment(2)
As an aside, you do not need to cast to your Friend object. Since -insertNewObjectForEntityForName: inManagedObjectContext: returns an id you can simply assign it to Friend*. Casting is unnecessary 99.9% of the time in Objective-C.Winther
Thanks for your hint, Marcus. Of course you're right. It is because I am still not used to objective-c.Barta
B
1

Finally found the solution! For my above question I removed all unnessesary code (unfortunately I also removed my problem). So, to complete this thread:
My Friend-class also has a member:

@property (nonatomic) int duration;

In my test environment I also set the duration (whenever I set the name). This seems to be the reason why the context is not able to recognize any change. If I change the property to

@property (nonatomic, retain) NSNumber duration;

everything worked fine. Note that my xdatamodel has a property duration of type Int32. What I not understand is, why everything worked for the first [context save];. But this is ok for me for now.

To give some good advice here:

Do NOT generate your xdatemodel out of your NSManagedObject-derived class (as I did). Instead create your xdatemodel and then generate your class out of your model (Xcode->Design->DataModel->Copy Obj-C2.0..)!
(Update 03/2011: Generating the Classes out of the Model works like a charm in XCode 4.0!)

I wish I had known this early on this beautiful sunday morning.
Good evening!

Barta answered 25/7, 2010 at 16:10 Comment(0)
S
5

The failure to recognise changes was possibly due to you using @synthesize instead of @dynamic in your subclass implementation. CD provides its own accessors which you will bypass using @synthesize Martin

Sadowski answered 13/3, 2011 at 22:57 Comment(1)
I wish I could upvote this 10 times. The "upvote" should be a "donate with paypal" button.Indecent
S
1

The only thing I can think of is that the context is having trouble observing the Friend object because you've initialized it as a generic NSManagedObject instead an instance of the Friend subclass.

The generic NSManagedObject does not store values in properties like it's subclasses do. Instead, it uses associative storage which is akin to a generic dictionary. In other words, a generic NSManagedObject stores and accesses value in a different location and using a different method of setting and getting than does its subclass. At runtime, this may cause the context confusion.

The first time you check the context, you getting a hasChangess==YES because you've inserted a new object into the object graph. The second time, you're just changing an attribute of an existing object. If the context cannot accurately observe the value of the name key it will not know it has to save the object again on the second pass.

Change:

NSManagedObject *newManagedObject = [NSEntityDescription 
       insertNewObjectForEntityForName:[entity name]  inManagedObjectContext:context];

...to:

Friend *newFriend = [NSEntityDescription 
       insertNewObjectForEntityForName:[entity name]  inManagedObjectContext:context];

... and see if that resolves the problem.

Otherwise, the code looks fine.

Streetcar answered 25/7, 2010 at 13:25 Comment(1)
Thanks! You're probably right, there must be a a problem for the context to observe my friend class (although it worked for the first time..). Unfortunately your change suggestion does not solve the problem, but it was still very helpful. If I work directly on the newManagedObject ([newManagedObject setValue:@"n2" forKey:@"name"] ) everything works fine. So I think there must be a problem with either my Friend-Class or the xcdatamodel. I will dig a bit deeper and see what the problem is.Barta
B
1

Finally found the solution! For my above question I removed all unnessesary code (unfortunately I also removed my problem). So, to complete this thread:
My Friend-class also has a member:

@property (nonatomic) int duration;

In my test environment I also set the duration (whenever I set the name). This seems to be the reason why the context is not able to recognize any change. If I change the property to

@property (nonatomic, retain) NSNumber duration;

everything worked fine. Note that my xdatamodel has a property duration of type Int32. What I not understand is, why everything worked for the first [context save];. But this is ok for me for now.

To give some good advice here:

Do NOT generate your xdatemodel out of your NSManagedObject-derived class (as I did). Instead create your xdatemodel and then generate your class out of your model (Xcode->Design->DataModel->Copy Obj-C2.0..)!
(Update 03/2011: Generating the Classes out of the Model works like a charm in XCode 4.0!)

I wish I had known this early on this beautiful sunday morning.
Good evening!

Barta answered 25/7, 2010 at 16:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.