views:

1619

answers:

3

I am getting a EXC_BAD_ACCESS (SIGBUS) on this line in my iPhone project:

if (timeoutTimer) [timeoutTimer invalidate];

The thing that has me stumped is that I don't understand how that line could crash, since the if statement is meant to be checking for nil. Am I misunderstanding the way Objective-C works, or do line numbers in crash statements sometime have the wrong line in them?

A: 

They should be the same. Perhaps the line number is in fact incorrect.

Look for other possible errors near that in your code and see if you find anything.

Ben Alpert
+10  A: 

Just because a variable is set to a value other than nil doesn't mean it's pointing to a valid object. For example:

id object = [[NSObject alloc] init];
[object release];
NSLog(@"%@", object); // Not nil, but a deallocated object,
                      // meaning a likely crash

Your timer has probably already been gotten rid of (or possibly hasn't been created at all?) but the variable wasn't set to nil.

Chuck
makes perfect sense, thanks. I realised that I didn't retain my timer, which is something I should do.
rustyshelf
It normally isn't necessary to retain a timer. If you create a timer with initWithFireDate..., it isn't autoreleased. And once a timer is added to a runloop (either by addTimer or one of the scheduledTimer... methods), the runloop retains it until it's finished with it.
Chuck
I created it with NSTimer scheduledTimerWithTimeInterval which I assume is auto released. Is initWithFireDate considered better?
rustyshelf
Nah, scheduledTimer... is the "normal" way to do it. But as the NSTimer Docs say: "Note in particular that run loops retain their timers, so you can release a timer after you have added it to a run loop."
Chuck
Also note that [nil anySelectorYouCareToSend] is a NOP in Objective-C.
Darron
well you learn something new every day. In my day job world null.anySelectorYouCareToSend() brings things to a screaming halt
rustyshelf
@rustyshelf that's one of the great things about Objective-C compared to C# or other languages. When you only care about the final return value, but it takes four or five method calls to get it, not having to check for nil at every step along the way removes a lot of "ugly" lines of code. Remember that this is only true for integers and pointers though, calling methods that return doubles or floats on nil will return garbage.
Marc Charbonneau
+1  A: 

I just ran into a similar issue, so here's another example of what might cause a check such as yours to fail.

In my case, I was getting the value from a dictionary like this:

NSString *text = [dict objectForKey:@"text"];

Later on, I was using the variable like this:

if (test) {
    // do something with "test"
}

This resulted in a EXC_BAD_ACCESS error and program crash.

The problem was that my dictionary used NSNull values in cases where an object had an empty value (it had been deserialized from JSON), since NSDictionary cannot hold nil values. I ended up working around it like this:

NSString *text = [dict objectForKey:@"text"];
if ([[NSNull null] isEqual:text]) {
    text = nil;
}
Mirko Froehlich