I am having issues with my custom accessor method for a core data property on an iPhone app. I want to do a little more than just pull out the primitive type and return it. I have an NSSet of possible activityTypes and if there is no primitiveType for the object then I want to look in that set and return the activityType that matches another variable on my class.
The following is the code that I came up with for doing this. The issue with the code is that my call to [self willChangeValueForKey:@"type"]
leads to an infinite loop where the program keeps spitting out the NSLog "Setting type to ..." - probably because I am calling the willChange from within the accessor?
If I take out the [self willChangeValueForKey:@"type"]
I do not get a loop and the program runs fine and the next time I try to access the type on the object the primitive type is correctly recalled. However, when I tell my ManagedObjectContext that it is time to save it tells me that there are no changes to any of the objects - so since I am not calling willChangeValueForKey - the change to the primitiveType is never persisted for the object.
- (ActivityType *)type {
[self willAccessValueForKey:@"type"];
ActivityType *myType = [self primitiveType];
[self didAccessValueForKey:@"type"];
if (myType != nil) {
NSLog(@"Use primitive (%@)", [myType myDescription]);
} else {
// 1) find type
NSSet *types = self.myActivityTypes;
NSSet *foundTypes = [types objectsPassingTest:^BOOL(id obj, BOOL *stop) {
ActivityType *object = (ActivityType *) obj;
return ([object.typeName isEqualToString:self.activityTypeName]);
}];
myType = [foundTypes allObjects][0];
NSLog(@"Setting type to %@", [myType myDescription]);
// in this case we should not alert anyone since we are inside the getter?
[self willChangeValueForKey:@"type"];
[self setPrimitiveType:myType];
[self didChangeValueForKey:@"type"];
}
return myType;
}
The managed object property observing mechanism will be using the accessors to check new and old values. Since you are changing the value within the getter, the getter will be called again, so your infinite loop begins.
This sort of lazy loading doesn't really work with managed objects. You can either set the activity type elsewhere, such as awakeFromFetch or when the activityTypeName is set, or if that isn't appropriate, implement a seperate accessor:
-(ActivityType*)calculatedActivityType
{
ActivityType *myType = self.activityType;
if (myType) return myType;
NSSet *types = self.myActivityTypes;
NSSet *foundTypes = [types objectsPassingTest:^BOOL(id obj, BOOL *stop) {
ActivityType *object = (ActivityType *) obj;
return ([object.typeName isEqualToString:self.activityTypeName]);
}];
myType = [foundTypes allObjects][0];
self.activityType = myType;
return myType;
}
This won't cause any loops because you're not within any of the accessors.