Vsevolod Kukhelny
Vsevolod Kukhelny

Reputation: 317

Objective C init method and inheritance

I have 2 classes A and B,B is inherited from A,A is inherited from NSObject.Class A has a function -(void)init:

- (void)init {
if (self = [super init]) {

NSLog(@"A");    

}
return self;
} 

B has:

- (id)init {
if (self = [super init]) {

NSLog(@"B");    

}
return self;
} 

After compilation I have "A" and "B" in my console,despite of condition in B "if (self = [super init])" can't be true,cause its superclass init method returns void.Any ideas?

P.S. I know that init method must return id,I'm interested why this works while it shouldn't.

Upvotes: 3

Views: 868

Answers (2)

Chuck
Chuck

Reputation: 237110

You're invoking undefined behavior by assigning the result of a void function call. Undefined behavior means pretty much anything can happen. This falls under the header of "anything," so it can happen.

As for why the compiler isn't noticing your mistake: You probably neglected to declare the override in your header, so it's assuming that your method has the same signature as the nearest declaration it can find, NSObject's.

Upvotes: 5

nall
nall

Reputation: 16149

Listen to Chuck. He's smart :)

I'll try to add a bit more detail.

One way to think about this is "If both were declared -(id)init how would A return its value to B"?

It goes something like this. The return self statement would take the pointer to A and put it in an agreed upon location. This location would be defined in the ABI. On a OSX/x86-64 machine, it's the RAX register (IIRC). So return self really says "write the pointer to self into RAX".

Then when B regains control it knows the return value is in RAX and can use it however it likes. Note you didn't remove the return self statement from A's implementation, so self was probably still written to RAX where B found it and used it. Or maybe RAX still had a pointer from the NSObject init implementation. You'd have to disassemble it to know for sure.

Now let's say you didn't keep the return self in A's implementation. Now there's some random junk in RAX which B is going to try to use like it's a pointer to an A. This is where the undefined behavior Chuck mentions comes in. If you're lucky it will just crash.

I'm guessing your code above gave some compiler error/warning that you had to suppress?

Upvotes: 5

Related Questions