I've read apple 'messaging' chapter from programming with objective - c and got several questions about self and super.
AFAIK when compiler finds any message it translates it into objc_msgSend with two hidden parameters - receiver, selector and variable arguments for selector.
for example [self test]
will be something like that:
objc_msgSend(self, @selector(test));
if there is no method implementation in receiver's dispatch table then function will try to find implementation in superclasses. super is just a flag for the compiler to start searching method implementation in the superclass of current object, and in documentation apple said that when compiler finds 'super' it translates it in something like that :
struct objc_super mySuperClass = {
self,
[self superclass]
};
objc_msgSendSuper(&mySuperClass, @selector(forwardedMethod));
I've made a project with 3 classes, each one inherits from another.
@interface FirstClass : NSObject
- (void)forwardMethod;
@end
@interface SecondClass : FirstClass
@end
@interface ThirdClass : SecondClass
@end
I created an instance of third class in my root view controller and invoke his method called 'forwardMethod'. The implementation :
//First Class
- (void)forwardMethod {
NSLog(@"Base class reached");
}
//SecondClass imp
- (void)forwardMethod {
NSLog(@"second class");
[super forwardMethod];
}
//ThirdClass imp
- (void)forwardMethod {
NSLog(@"third class");
[super forwardMethod];
}
Everything works fine. But then i decided to interpret compiler:
//First Class
- (void)forwardMethod {
NSLog(@"Base class reached");
}
//SecondClass imp
- (void)forwardMethod {
NSLog(@"second class");
struct objc_super mySuperClass = {
self,
[self superclass]
};
objc_msgSendSuper(&mySuperClass, @selector(forwardMethod));
}
//ThirdClass imp
- (void)forwardMethod {
NSLog(@"third class");
struct objc_super mySuperClass = {
self,
[self superclass]
};
objc_msgSendSuper(&mySuperClass, @selector(forwardMethod));
}
Which results in an recursive call to the second class 'forwardMethod'. I create a struct in 'forwardMethod' at second class using self and [self superclass], but self is thirdclass and
my superclass will be always 'second class'. Maybe I'm doing something wrong, but how i can get to the base class 'forward method'?
Note: For educational purposes only (which is good!), don't use in production code!
You are also most there, just one class out...
To see why you get the recursion consider how the super class can be found using the information available at compile time and at run time.
At runtime the value of self
is a reference to the current object, you can use self
to find the class of the object - in your example self
is an object of type ThirdClass
.
Now the value of self
doesn't change as methods are called, so in your example even in FirstClass
's forwardMethod
the value of self
is a reference to an object of type ThirdClass
. So self
enables you to find the type of the object, but it doesn't tell you where you are currently executing a method in its inheritance chain, so by itself it can't tell you what the next class in that chain is.
So consider compile time. When compiling SecondClass
the compiler knows it super class is FirstClass
, so a call to super
is a call to a method in FirstClass
(apart from a caveat coming below). So the compiler can use self
and [FirstClass class]
to determine the runtime object on which to call the method and the compile time class at which to start the search for the method (as any method lookup is a search starting at a class and proceeding along the inheritance chain until and implementation is found). So in your example code you were just one method out:
@implementation SecondClass
- (void)forwardMethod
{
NSLog(@"second class");
struct objc_super mySuperClass =
{
self,
[FirstClass class]
};
objc_msgSendSuper(&mySuperClass, @selector(forwardMethod));
}
If you use that you code will work. But...
...that caveat mentioned above. Objective-C allows the inheritance chain to be altered at runtime using class swizzling, so the compiler cannot in general rely on the compile time inheritance chain to work out the super class. So what can it use? Well when compiling the source for a particular method it knows the class that method belongs to, so it could compile that methods class into the code to find the super class and use code which starts the search for a method at the next class along the runtime inheritance chain. This is in fact what the compiler will do for your code, just in case you've used class swizzling:
@implementation SecondClass
- (void)forwardMethod
{
NSLog(@"second class");
struct objc_super mySuperClass =
{
self,
[SecondClass class]
};
objc_msgSendSuper2(&mySuperClass, @selector(forwardMethod));
}
Note the compiler passes the compile time current class ([SecondClass class]
) and calls objc_msgSendSuper2
to perform the lookup - which will find the first method in the runtime inheritance chain that is after SecondCLass
m whereas `objc_msgSendSuper
would start the search at SecondClass
itself.
Have fun, but don't use this in general code (unless you have a very, very, very ... very good reason ;-))
In your SecondClass this structure will be populated with exactly the same contents as your ThirdClass:
struct objc_super mySuperClass = {
self,
[self superclass]
};
self
has the same value in both cases and, thus, [self superclass]
will always be SecondClass when called from an instance of ThirdClass (even when the actual implementation -- the code -- resides in SecondClass). There is a bit more magic to what the compiler emits (objc_msgSendSuper is quite straightforward) in that it has to emit a reference to the class such that even things like posing and/or isa pointer manipulation -- bad programmer, no donut -- still work as expected. I haven't looked into the details in long, long while to know exactly how that works.
The source to the runtime and the compiler is available.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With