Could someone help me understand the primitive accessors with this example : i don’t understand what is automatically set and the order of those methods :
1.after a person is created, is willSave the first method called? (i guess so, because save: is called after we create a person with insertNewObjectForEntityForName )
2.in RootViewController (the second chunk of code), we then call the getter of eyeColor with : person.eyeColor :
a) in eyeColor, we call : [self eyeColorData] ,
b) but setPrimitiveEyeColorData is in willSave, which is accessible only if primitiveEyeColor exists,
c) but setPrimitiveEyeColor is in eyeColor and only called if [self eyeColorData] exists. So, i’m a bit confused with this code, could someone help me?
here’s the code about eyeColor and eyeColorData :
@dynamic eyeColorData;
@dynamic eyeColor;
@interface AWPerson (PrimitiveAccessors)
- (UIColor *)primitiveEyeColor;
- (void)setPrimitiveEyeColor:(UIColor *)value;
- (NSData *)primitiveEyeColorData;
- (void)setPrimitiveEyeColorData:(NSData *)value;
@end
+ (id)personInManagedObjectContext:(NSManagedObjectContext *)moc {
return [NSEntityDescription
insertNewObjectForEntityForName:@"Person"
inManagedObjectContext:moc];
}
+ (id)randomPersonInManagedObjectContext:(NSManagedObjectContext *)moc {
AWPerson *randomPerson = [self personInManagedObjectContext:moc];
//...
randomPerson.eyeColor = [self randomColor]; //setter eyeColor
return randomPerson;
}
+ (UIColor *)randomColor {
static NSArray *colorsArray = nil;
if( !colorsArray ) {
colorsArray = [[NSArray alloc] initWithObjects:
[UIColor lightGrayColor],
[UIColor blueColor],
[UIColor greenColor], nil];
}
int randomIndex = arc4random() % [colorsArray count];
return [colorsArray objectAtIndex:randomIndex];
}
- (void)willSave {
UIColor *color = [self primitiveEyeColor];
if( color ) {
[self setPrimitiveEyeColorData:
[NSKeyedArchiver archivedDataWithRootObject:color]];
} else {
[self setPrimitiveEyeColorData:nil];
}
[super willSave];
}
- (UIColor *)eyeColor {
[self willAccessValueForKey:@"eyeColor"];
UIColor *tmpValue = [self primitiveEyeColor];
[self didAccessValueForKey:@"eyeColor"];
if( tmpValue ) return tmpValue;
NSData *colorData = [self eyeColorData];
if( !colorData ) return nil;
tmpValue = [NSKeyedUnarchiver unarchiveObjectWithData:colorData];
[self setPrimitiveEyeColor:tmpValue];
return tmpValue;
}
in RootViewController :
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
AWPerson *person = [[self fetchedResultsController] objectAtIndexPath:indexPath];
[cell setBackgroundColor:person.eyeColor];
}
Thanks
EDIT – Added info on
willSaveTo answer your first question, willSave is called whenever the object is saved (using the
savemethod). So the first method called will be one of the class methods (used to create the object) or init and then, since you said that the object is saved just after it is created, willSave gets called.I think the key to understanding this is to realize that
eyeColor,primitiveEyeColor, and their setters are all ultimately interacting with the same variable in memory (the iVar named eyeColor). The difference is whether or not the code in the setter/getter (in this case the- (UIColor *)eyeColor {function) is called.There are just a few different ways to interact with it:
[self primitiveEyeColor];– This reads the value of the iVar directly.[self setPrimitiveEyeColor:tmpValue];– This sets the value of the iVar directly.[self eyeColor]– This calls the- (UIColor *)eyeColormethod in your class (which should ultimately retrieve the iVar or a representation of it).[self setEyeColor:value]– This calls the- (void)setEyeColor:(UIColor *)newColormethod in your class. Note that in this case it doesn’t exist so it simply calls the primitive method (and does the KVO magic).In this particular code, they are using a “non-standard persistent attribute” because NSManagedObject does not support UIColor’s. Read about it here.
EDIT 2
To answer your other questions:
Yes, once eyeColor is set (either via the
setEyeColormethod or thesetPrimitiveEyeColormethod), you can read it fromprimitiveEyeColorand it will return the same value.Note that once it is set,
eyeColorandprimitiveEyeColorreturn the same value and can be called from anywhere in your class (not justwillSave).This method only looks at
eyeColorData(which was stored during the last call towillSave) ifeyeColoris nil. This is an optimization because we could skip all of this and just unarchiveeyeColorDataevery time if we wanted to. In this case, once a value is unarchived or set to a new value, it always stores that value and returns it so that we don’t have to call unarchive again.Also, there is really what I believe to be an error here (although it could be by design). Let’s say that we perform the following steps:
eyeColorto a random color (let’s say blue).eyeColorto nilNow, if you check the color using
[self eyeColor]it will see that primitiveEyeColor isniland unarchiveeyeColorDataagain, therefore returning the blue color that was stored previously. You should probably be over-riding the set function so that it setseyeColorDatatonilwheneyeColoris set tonil. That way, checking the value ofeyeColorafter setting it tonilwill returnnilas expected.