The wrath of the forgotten nil (the sad saga of uninitialized pass-by-reference variables)

I just encountered a hairy yet rather funny bug in Feathers that I thought I'd share with you. It was one of those that leaves you scratching your head even though it's just staring you in the face. (Granted, when you have a large code-base and multiple threads, locating the area of the code where the issue is staring you in the face can be a chore in and of itself.) So, without further ado…

What's wrong with his code?

NSHTTPURLResponse *urlResponse;
NSError *error;
NSData *resultData = [NSURLConnection sendSynchronousRequest:req returningResponse:&urlResponse error:&error];

NSInteger errorCode = [error code];

// Do some error handling...
}

(Said code appears inside an NSOperation instance and hence the use of the synchronous request.)

At first glance, it might appear like there isn't a problem with it but when you run it, you might (it's random, depending on the state of your app's memory at that time) get the dreaded EXC_BAD_ACCESS (if running on the device) or, if you're running in the simulator, the more helpful:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSCFString code]: unrecognized selector sent to instance 0x4b9994'

(To track down the method call that caused the EXC_BAD_ACCESS, type display /s $r1 in the gdb debugger console and you'll see that the method "code" caused it. The simulator error is still more helpful though.)

Wait a second… the only thing we're passing the code message to here is the NSError instance. But the error is telling me that I'm trying to send the selector to a string instance. That's not right. So I logged error, and lo and behold, I got a string ("isExecuting").

Do you see the problem yet?

Look at where I'm declaring the urlResponse and error variables. I forgot to set them to nil. If you declare a pointer, remember that it simply points to some address in memory (and to whatever garbage or otherwise there may be at that address). In this case, the garbage it was pointing to just happened to be a string and this was what caused the crash.

So the code should have read:

NSHTTPURLResponse *urlResponse = nil;
NSError *error = nil;
NSData *resultData = [NSURLConnection sendSynchronousRequest:req returningResponse:&urlResponse error:&error];
// ...

Remember folks, nil is your friend.

Update: As pointed out in the comments, you should really be checking the return value of the call (if (!resultData) {). That would have avoided the bug also.

Comments