27 Apr 14

pop quiz: why are my parameters zero when I pass them in with non-zero values

This one had me stuck for a number of minutes. It’s not hard, but it got me. See if you can spot it:

say you create a new class that is inited like so:

@interface YoDogWhatsWrong
@property (nonatomic) float aValue;
@implementation YoDogWhatsWrong
- (id)initWithAValue:(float)aFloat anotherValue:(float)someOtherValue
    if ((self = [super init])) {
        self.aValue = aFloat;
    return self;

and you call it somewhere with

    YoDogWhatsWrong *notCreatedProperly = [[YoDogWhatsWrong alloc] initWithAValue:180 anotherValue: 0];
    NSLog(@"aValue is %f", notCreatedProperly.aValue);

and you find that to your dismay that ‘aValue is 0.0’. There’s something wrong here, and it is worse than your lack of creative variable names.

Did you spot it?

Did you?

Well, Obj-C doesn’t have a compilation error if you don’t have a method declared in the interface and you still call it, but it does give you a warning. If you’re anything like me, you’ve probably abused this before when you’re hacking away at the implementation, with the intent of adding the methods that are used publicly. Well, most of the time you can get away with this, because many of the times the parameters are objective-C objects. In this case they were floats, and unfortunately, either the obj-c spec the compiler assumes a default calling convention that was of type ‘id’ instead of float, and sizeof ‘id’ was larger than sizeof(float) here. The rest of the story should be clear now: the first parameter in the init method got the upper half of the bits of ‘180.f’ which is just zeros, and the next parameter got the lower half.

This weird behavior will also happen if you forget to include a header for a method that uses (float) or some other primitive type. A lot of bad stuff can be avoided by keeping a zero warning count, or at least, looking at your warnings regularly.

So to fix the problem, simply add the method declaration to the interface, like you (I) should have been doing all along:

@interface YoDogWhatsWrong
@property (nonatomic) float aValue;
- (id)initWithAValue:(float)aFloat anotherValue:(float)someOtherValue;

FYI this was on the iOS simulator 7.0 4 inch (not 64 bit) and (disclaimer) I haven’t compiled any of the code on this post.