Reputation: 21967
Say I have an NSManagedObject
subclass called Item
. Whenever an item
instance is saved I want to update a property based on a computed value from a transient property. I know I can update the property whenever the transient property changes but for this question, assume there's a good reason I don't want to do that.
I tried to do this in the willSave
method as follows:
- (void)willSave
{
self.computedProperty = [self computedValueFromTransientProperty];
}
This causes a crash when saving the context. If I move the code out of willSave
and set the property explicitly before invoking save it works fine. The apple docs say you should avoid changing managed object properties in willSave
.
QUESTION: is there a good way to build functionality into a NSManagedObject
subclass so a property can be updated just before save w/o having to explicitly set the property from outside the class and w/o setting the property every time the transient property changes?
Upvotes: 2
Views: 1861
Reputation: 2759
You're able to set persistent properties from willSave
, you just have to be more careful about it.
From the willSave
docs:
This method can have “side effects” on persistent values. You can use it to, for example, compute persistent values from other transient or scratchpad values.
If you want to update a persistent property value, you should typically test for equality of any new value with the existing value before making a change. If you change property values using standard accessor methods, Core Data will observe the resultant change notification and so invoke willSave again before saving the object’s managed object context. If you continue to modify a value in willSave, willSave will continue to be called until your program crashes.
So, what's happening is you're changing computedProperty
, which is causing willSave
to be called again, which changes computedProperty
which calls willSave
again, until your program crashes.
To fix this you need to check whether or not computedProperty
needs to be set again:
- (void)willSave
{
id computed = [self computedValueFromTransientProperty];
if (![self.computedProperty isEqual:computed])
{
self.computedProperty = computed;
}
}
This will mean that computedValueFromTransientProperty
will get called twice, so you might not want to do this if the method is computationally expensive.
Another option is to use the primitive set method which will mean willSave
won't get called twice, but might have side affects depending on how your app interacts with Core Data:
- (void)willSave
{
self.primitiveComputedProperty = [self computedValueFromTransientProperty];
}
Upvotes: 7