Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

what does the compiler when it finds [super msg];

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'?

like image 530
Yurii Romanchenko Avatar asked Jun 30 '13 17:06

Yurii Romanchenko


2 Answers

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 SecondCLassm 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 ;-))

like image 68
CRD Avatar answered Nov 03 '22 14:11

CRD


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.

like image 45
bbum Avatar answered Nov 03 '22 12:11

bbum