Eli
Eli

Reputation: 4932

EXC_BAD_ACCESS on iPhone when using "obj != nil

I've got a very simple line of code in Objective-C:

if ((selectedEntity != nil) && [selectedEntity isKindOfClass:[MobileEntity class]])

Occasionally and for no reason I can tell, the game crashes on this line of code with an EXC-BAD-ACCESS. It usually seems to be about the time when something gets removed from the playing field, so I'm guessing that what was the selectedEntity gets dealloc'd, then this results. Aside from being impossible to select exiting Entities (but who knows, maybe this isn't actually true in my code...), the fact that I am specifically checking to see if there is a selectedEntity before I access it means that I shouldn't be having any problems here. Objective-C is supposed to support Boolean short-citcuiting, but it appears to not be EDIT: looks like short-circuiting has nothing to do with the problem.

Also, I put a @try/@catch around this code block because I knew it was exploding every once in a while, but that appears to be ignored (I'm guessing EXC-BAD-ACCESS can't be caught).

So basically I'm wondering if anybody either knows a way I can catch this and throw it out (because I don't care about this error as long as it doesn't make the game crash) or can explain why it might be happening. I know Objective-C does weird things with the "nil" value, so I'm guessing it's pointing to some weird space that is neither an object pointer or nil.

EDIT: Just to clarify, I know the below code is wrong, it's what I was guessing was happening in my program. I was asking if that would cause a problem - which it indeed does. :-)

EDIT: Looks like there is a fringe case that allows you to select an Entity before it gets erased. So, it appears the progression of the code goes like this:

selectedEntity = obj;
NSAutoreleasePool *pool = ...;
[obj release];
if (selectedEntity != nil && etc...) {}
[pool release];

So I'm guessing that because the Autorelease pool has not yet been released, the object is not nil but its retain count is at 0 so it's not allowed to be accessed anyway... or something along those lines?

Also, my game is single-threaded, so this isn't a threading issue.

EDIT: I fixed the problem, in two ways. First, I didn't allow selection of the entity in that fringe case. Second, instead of just calling [entities removeObjectAtIndex:i] (the code to remove any entities that will be deleted), I changed it to:

//Deselect it if it has been selected.
if (entity == selectedEntity)
{
    selectedEntity = nil;
}

[entities removeObjectAtIndex:i];

Just make sure you are assigning nil to the variable at the same time you release it, as jib suggested.

Upvotes: 1

Views: 5251

Answers (5)

bbum
bbum

Reputation: 162722

This has nothing to do with short circuiting. Objective-C eats messages to nil, so the check for selectedEntity != nil isn't necessary (since messages-to-nil will return NO for BOOL return types).

EXC_BAD_ACCESS is not a catchable exception. It is a catastrophic failure generally caused by trying to follow in invalid pointer.

More likely than not, whatever object selectedEntity points to has been released before the code is executed. Thus, it is neither nil nor a valid object.

Turn on NSZombies and try again.

If your app is threaded, are you synchronizing selectedEntity across threads properly (keeping in mind that, in general, diddling the UI from secondary threads is not supported)?


Your post was edited to indicate that the fix is:

//Deselect it if it has been selected.
if (entity == selectedEntity)
{
    selectedEntity = nil;
}

[entities removeObjectAtIndex:i];

This fixes the issue because the NSMutableArray will -release objects upon removal. If the retain count falls to zero, the object is deallocated and selectedEntity would then point to a deallocated object.

Upvotes: 9

Russell Zornes
Russell Zornes

Reputation: 706

 

selectedEntity = obj;
NSAutoreleasePool *pool = ...;
[obj release];
if (selectedEntity != nil && etc...) {}
[pool release];

 

You have a dangling pointer or zombie here. The selectedEntity is pointing at obj which gets releases right before you reference selectedEntity. This makes selectedEntity non-nil but an invalid object so any dereference of it will crash.

You wanted to autorelease the obj rather than release it.

Upvotes: 0

unknown
unknown

Reputation:

if an object (selectedEntity) has been released and dealloc'd it is not == nil. It is a pointer to an arbitrary piece of memory, and deferencing it ( if(selectedEntity!=nil ) is a Programming Error (EXC_BAD_ACCESS).

Hence the common obj-c paradigm:-

[selectedEntity release]; selectedEntity = nil;

Upvotes: 6

Marcus S. Zarra
Marcus S. Zarra

Reputation: 46718

Put a breakpoint on OBJC_EXCEPTION_THROW and see where it is really being thrown. That line should never throw a EXC_BAD_ACCESS on its own.

Are you perhaps doing something within the IF block that could cause the exception?

Upvotes: 0

Nir Levy
Nir Levy

Reputation: 4740

i just read this http://developer.apple.com/mac/library/qa/qa2004/qa1367.html which indicated that the error you are getting is a result of over-releasing the object. this means that altough selectedEntity is nill, you released it to many times and it just not yours to use anymore..

Upvotes: 0

Related Questions