XJones
XJones

Reputation: 21967

How do I set a computed NSManagedObject property automatically before it is saved?

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

Answers (1)

Sherman Lo
Sherman Lo

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

Related Questions