Does the new LLVM compiler in Xcode4 make class inheritance not work properly?
EDIT: Problem solved! After cleaning and rebooting it just disappeared! I don't know what caused this!
This has caused me headaches for a full day now:
In Xcode 3.2 everything worked excellent. Then I switched to 4.2 and suddenly a class inheritance does not work anymore.
I have a class TheSuperclass
and TheSubclass : TheSuperclass
. To simplify testing, I really created them like this. There's no more code than what you can see here:
// TheSuperclass.h
@interface TheSuperclass : NSObject {
}
// subclasses must override this method
- (id)returnSomethingUseful;
@end
// TheSuperclass.m
#import "TheSuperclass.h"
@implementation TheSuperclass
- (id)returnSomethingUseful {
NSLog(@"Dude, you have to override -returnSomethingUseful");
return nil; // subclasses override this method!
}
- (id)init {
if ((self = [super init])) {
obj = [self returnSomethingUseful]; // TEST
NSLog(@"TheSuperclass initialized: %@", obj);
}
return self;
}
@end
// TheSubclass.h
#import "TheSuperclass.h"
@interface TheSubclass : TheSuperclass {
}
@end
// TheSubclass.h
#import "TheSubclass.h"
- (id)returnSomethingUseful {
NSLog(@"Correct method called!");
return usefulObject;
}
TheSubclass *foo = [[TheSubclass alloc] init]; // remember: init of superclass calls the method
// Getting the NSLog: "Dude, you have to override -returnSomethingUseful"
id bar = [foo returnSomethingUseful]; // now call it directly on foo
// Getting the NSLog: "Correct method called!"
TheSuperclass
declares a template method, that is, a method which just does nothing, returns nil and is intended for subclassing:
- (id)returnSomethingUseful {
NSLog(@"Dude, you have to override -returnSomethingUseful");
return nil; // subclasses override this method!
}
In TheSubclass
, I simply override that template method. And of course I COPIED the implementation out of TheSuperclass
to get it 100% right. No typo. That's a checked fact. Looks like this:
- (id)returnSomethingUseful {
NSLog(@"Correct method called!");
return usefulObject;
}
In the implementation of TheSuperclass
a piece of code calls [self returnSomethingUseful]
in order to get that object from the template method. It's a great pattern and I have used it a lot. It always worked exactly like this.
But now it appears that even though I create an instance of TheSubclass
, it always calls the WRONG method. The one from TheSuperclass, instead of the one it should (which is the overwriding method of course)
I've checked that at least 100 times! Seriously, it is an instance of TheSubclass
. It calls the init method of TheSuperclass
giving me NSLogs.
Now the really strange part: When I call that method on my TheSubclass object "from outside", it works:
The开发者_运维百科Subclass *foo = [[TheSubclass alloc] init];
id bar = [foo returnSomethingUseful];
// Getting the NSLog: "Correct method called!"
So to emphasize it: When I call [self returnSomethingUseful]
from within the implementation of TheSuperclass, it calls the WRONG implementation (which is the one of the superclass, rather than the overwritten one in the subclass.
So how can I work around this? Where does this possibly come from? Is this a problem in the runtime, maybe caused by an error in the compiler?
LLVM clearly is able to implement simple subclassing correctly. Thousands of programs use it every day, so this is not a problem to be worked around if you are writing standard code (not trying anything tricky or undocumented). Here are the things you should do:
- Reboot. Xcode pretends that you don't have to reboot after install. You do in my experience. It solves 90% of problems after upgrading Xcode.
- Clean and rebuild. This is less likely to fix it because going from 3.2 to 4.2 puts the derived files in completely different places anyway. But it's worth trying.
- Check
[self class]
to make sure you really are what you think you are. - Verify that you never mess with the
isa
pointer (easy to find; search for->isa
in your code). - Check your
init
. This is the most likely place to have done something over-tricky. Make sure you're following the simple patterns. - Derive the simplest form of the problem and post some code that demonstrates it. From your description, this should be possible to do in very few lines of code. Attempting to create this simplified version will more often than not show you where your mistake is.
If the compiler screwed up something so basic, the system wouldn't boot.
Assert the class in your init
method because either you have an instance of TheSuperclass or something is misspelled.
The term is "override", not "overwrite".
You haven't shown us enough of your code to know what the problem is. Show us the -init methods for both classes, and show us where your instance of the subclass is being created.
精彩评论