Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Objective-C property - getter behaviour

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

like image 369
Daniel Avatar asked Jun 28 '12 20:06

Daniel


2 Answers

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.  :)
like image 150
zpasternack Avatar answered Oct 17 '22 21:10

zpasternack


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

//implementation
@synthesize timestamp;
@synthesize statTime;
like image 30
Joe Avatar answered Oct 17 '22 21:10

Joe