primitive accessors in this example

Go To StackoverFlow.com

0

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

2012-04-05 22:27
by Paul


1

EDIT - Added info on willSave
To answer your first question, willSave is called whenever the object is saved (using the save method). 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:

  1. [self primitiveEyeColor]; - This reads the value of the iVar directly.
  2. [self setPrimitiveEyeColor:tmpValue]; - This sets the value of the iVar directly.
  3. [self eyeColor] - This calls the - (UIColor *)eyeColor method in your class (which should ultimately retrieve the iVar or a representation of it).
  4. [self setEyeColor:value] - This calls the - (void)setEyeColor:(UIColor *)newColor method 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:

a) The color in randomPerson.eyeColor = [self randomColor] is accessible with [self primitiveEyeColor] (in willSave)?

Yes, once eyeColor is set (either via the setEyeColor method or the setPrimitiveEyeColor method), you can read it from primitiveEyeColor and it will return the same value. Note that once it is set, eyeColor and primitiveEyeColor return the same value and can be called from anywhere in your class (not just willSave).

b) So if [self primitiveEyeColor] != nil : in eyeColor, the line : if( tmpValue ) return tmpValue; should therefore always be true... when can we unarchive eyeColorData if UIColor *tmpValue = [self primitiveEyeColor] is always returned in -(UIColor *)eyeColor?

This method only looks at eyeColorData (which was stored during the last call to willSave) if eyeColor is nil. This is an optimization because we could skip all of this and just unarchive eyeColorData every 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:

  1. Set eyeColor to a random color (let's say blue).
  2. save the object.
  3. Set eyeColor to nil

Now, if you check the color using [self eyeColor] it will see that primitiveEyeColor is nil and unarchive eyeColorData again, therefore returning the blue color that was stored previously. You should probably be over-riding the set function so that it sets eyeColorData to nil when eyeColor is set to nil. That way, checking the value of eyeColor after setting it to nil will return nil as expected.

2012-04-05 22:38
by lnafziger
thanks lnafziger, a) the color in randomPerson.eyeColor = [self randomColor] is accessible with [self primitiveEyeColor] (in willSave)? b) so if [self primitiveEyeColor] != nil : in eyeColor, the line : if( tmpValue ) return tmpValue; should therefore always be true... when can we unarchive eyeColorData if UIColor *tmpValue = [self primitiveEyeColor] is always returned in -(UIColor *)eyeColor? thanks agai - Paul 2012-04-06 00:01
I updated my answer to address these questions so that I would have more room. : - lnafziger 2012-04-06 03:41
thanks! about your n°3 : where do we set eyeColor to nil after we save the object - Paul 2012-04-06 11:09
This code doesn't. I was saying IF you set it to nil after saving then this will be a problem - lnafziger 2012-04-06 13:13
thanks, ok, sorry but i'm still misunderstanding something: the first time we want to retrieve/unarchive the value eyeColorData : if primitiveEyeColor does exist, it returns the value of type UIColor in - (UIColor *)eyeColor, and we don't unarchive the data from colorData ... afterwards, i get what you say : "so that we don't have to call unarchive again", but what about the first time - (UIColor *)eyeColor { is called? it seems to me that we stop at if( tmpValue ) return tmpValue; because [self primitiveEyeColor] returns the value from ...= [self randomColor]Paul 2012-04-06 14:47
The first time, eyeColor will be nil so it sets tmpValue to nil. Then the line if (tmpValue) return tmpValue; means "if tmpValue is not nil return that value otherwise continue" - lnafziger 2012-04-06 14:54
sorry i'm confused, isn't it in this order : 1.randomPerson.eyeColor = [self randomColor]; , then 2.-(void)willSave { UIColor *color = [self primitiveEyeColor]; returns the color set in 1), then in RootVC 3.[cell setBackgroundColor:person.eyeColor];, then if( tmpValue ) return tmpValue; ? in this order, tmpValue always contains a value, isn't it? Thanks again for your help, i really appreciat - Paul 2012-04-06 15:04
Yes, in this case. However if you don't set the eye color (in step 1) first, it will check to see if you have previously stored the value - lnafziger 2012-04-06 15:15
alright ;) i got it, thank you very much for staying in this conversation ;) i appreciated, thanks - Paul 2012-04-06 17:02
Ads