Objective-C property - getter behaviour

13,560

Solution 1

In your description method, you aren't using the property, you're accessing the ivar.

-(NSString*) description
{
    return [NSString stringWithFormat:@"Event< timestamp:%d, start:%d >", 
             timestamp, 
             startTime]; // <-- This is accessing the instance variable, not the property.
}

This will work for you:

-(NSString*) description
{
    return [NSString stringWithFormat:@"Event< timestamp:%d, start:%d >", 
             timestamp, 
             self.startTime]; // <-- This is using the property accessor.
}

The property-vs-ivar thing messes people up all the time, so excuse me while I ramble on about it for a minute. :) If you already know all of this, skip ahead.

When you create and synthesize a property, as you did above, two things happen:

  1. an ivar is created of the proper type.
  2. a getter function is created, which returns that ivar.

The important part about point 2 is that, by default, the ivar and the getter function (and therefore, the property) have the same names.

So this:

@interface Event
@property(nonatomic, assign) NSUInteger timestamp;
@property(nonatomic, readonly, getter = timestamp) NSUInteger startTime;
@end

@implementation Event
@synthesize timestamp, startTime;
@end

...turns into this:

@interface Event {
    NSUInteger timestamp;
    NSUInteger startTime;
}
@end

@implementation Event
- (NSUInteger) timestamp {
    return timestamp
}

- (void) setTimestamp:(NSUInteger) ts {
    timestamp = ts;
}

- (NSUInteger) startTime {
    return [self timestamp];
}
@end

How dot syntax works is that this:

NSUInteger foo = myEvent.startTime;

really does

NSUInteger foo = [myEvent startTime];

All of that to say that when you access an ivar, you're... well, accessing an ivar. When you use a property, you're calling a function which returns a value. More importantly, it's exceedingly easy to do one thing when you meant the other, because the syntax is so very similar. It's for this reason that many people routinely synthesize their ivars with leading underscores, so that it's harder to mess up.

@property(nonatomic, assign) NSUInteger timestamp;
@property(nonatomic, readonly, getter = timestamp) NSUInteger startTime;

@synthesize timestamp = _timestamp;
@synthesize startTime = _startTime;

NSLog( @"startTime = %d", _startTime );  // OK, accessing the ivar.
NSLog( @"startTime = %d", self.startTime );  // OK, using the property.
NSLog( @"startTime = %d", startTime );  // NO, that'll cause a compile error, and 
                                        // you'll say "whoops", and then change it 
                                        // to one of the above, thereby avoiding
                                        // potentially hours of head-scratching.  :)

Solution 2

Make sure you synthesize in the correct order so the getter exists for startTime.

//implementation
@synthesize timestamp;
@synthesize statTime;
Share:
13,560
Daniel
Author by

Daniel

iOS developer

Updated on June 27, 2022

Comments

  • Daniel
    Daniel over 1 year

    What is technically wrong with the following:

    @property(nonatomic, assign) NSUInteger timestamp;
    @property(nonatomic, readonly, getter = timestamp) NSUInteger startTime;
    @property(nonatomic, assign) NSUInteger endTime;
    

    I am sure I can find a better way to organise this, but this is what I ended up with at one point in my project and I noticed that accessing the startTime property always returned 0, even when the timestamp property was set to a correct timestamp.

    It seems having set the getter of startTime to an existing property (timestamp), it is not forwarding the value of timestamp when I do:

    event.startTime => 0
    event.timestamp => 1340920893
    

    All these are timestamps by the way.

    Just a reminder, I know the above should have happened in my project but I don't understand why accessing startTime doesn't forward onto timestamp property.

    UPDATE

    In my implementation I am synthesising all of these properties:

    @synthesize timestamp, endTime, startTime;
    

    Please check an example object to use that demonstrates this at my gist on GitHub: https://gist.github.com/3013951