views:

117

answers:

3

I'm working on an existing, large-ish codebase, and after upgrading the iOS SDK to 4.1 I am now seeing very strange behaviour. The crux of the matter appears to be a particular class that will no longer alloc - it is throwing a bad access in obj_msgSend, and seems to be the Class object on the stack that objc_msgSend doesn't like - although it is not actually NULL.

The original failing line looked like this:-

tileProjection = [[RMFractalTileProjection alloc] initFromProjection:proj tileSideLength:sideLength maxZoom:18];

I deconstructed this to isolate the problem:-

RMFractalTileProjection *p = [RMFractalTileProjection alloc];  // <- this crashes
p = [p initFromProjection:proj tileSideLength:sideLength maxZoom:18];
tileProjection = p;

I then tried this:-

Class c = NSClassFromString(@"RMFractalTileProjection");
assert(c);
NSLog( @"RMFractalTileProjection class(ptr) %p", c );  // <- prints an address OK
NSLog( @"RMFractalTileProjection class(obj) %@", c );  // <- crashes

In the debugger it looks like the Class object is sensible, but NSLog crashes when it tries to print it.

One thing to note: the class in question is declared as below, and I'm not sure if the protocol is causing a problem. Because this particular part is a large chunk of open source code, it is very difficult to remove this protocol requirement to see if that makes a difference.

@interface RMFractalTileProjection : NSObject<RMMercatorToTileProjection>
{
 ...
}

Any help on this one greatly appreciated - it is a show stopper.

Thanks

+1  A: 

This is not really an answer but some ideas to move forward.

The only causes that leap to mind at the moment are memory corruption and some sort of link issue. Perhaps you are linking two versions of the class somehow.

Assuming this is the class, there doesn't look to be anything wrong to make it crash in alloc. There's no +initialize or anything.

Questions I would be asking myself and trying to answer are:

what happens if I rename the class?

what happens if I create a new identical class with a different name?

the pointer that gets passed to obj_msgSend: is it reasonable? does it point to something that looks like a class?

do you ever subclass the class and do you use initialize on the subclass?

is the pointer always the same? If so you can watch what it points to and see if it changes during execution.

what happens if you send self to the class?

JeremyP
A: 

Thanks for these suggestions JeremyP - it is always good to have fresh suggestions after you've been banging your head against the keyboard all day!

Your suggestion of creating an identical class with the same name appears to have fixed the problem. I have no idea why and I feel I need to understand what's going on here. You're right it sounds like some kind of linker issue, but I still have no idea what could cause such a serious runtime error and not even produce a warning at build time.

Re. the pointer, it does look reasonable, but something inside the class eventually gets dereferenced as a null pointer inside objc_msgSend. Occasionally, after I have changed the code and rebuilt, I get a null pointer instead. This behaviour obviously suggests something nondeterministic like a memory stomp.

I'll post my findings.

Echelon
Justin Niessner
Yeah, I deliberated over that, but it kind of is an answer (because one of the suggestions fixed the problem), and also writing a long comment like that with no newlines is pretty unreadable.
Echelon
A: 

OK, finally found this. As Jeremy suggested, this turned out to be a regular memory stomper.

The difficulty I had finding it was that it wasn't the Class object itself that was getting stomped, but the class' metaclass structure - which is a normal Class object but one level up, referenced by the class 'isa' pointer. That's why the class looked OK to me when I inspected it in the debugger - I need to follow the isa pointer and dump memory at one level up to find this. Luckily for me, the class was only a subclass of NSObject - had it been deeply subclassed, this could have been much harder to find. I got my first clue after biting the bullet, reverse-engineering objc_msgSend, working out exactly what was on the stack frame, and following all the pointers. Yep, the hard way :)

Matt Gallaghar's post (and various others I found by following links) were invaluable in helping me through this maze - thanks guys!

Burned a lot of time on this one, but on the up side I learned a hell of a lot about Objective C internals during the past day and a half :)

Echelon