Runtime Series

A simple and profound explanation of the super Runtime (1) A simple and profound explanation of the super Runtime (2) A simple and profound explanation of the data structure Runtime (3) A simple and profound explanation of the message mechanism Runtime (4) A simple and profound explanation of the super

1. Objc_super objc_msgSendSuper

Let’s start with two data structures, objc_super and objc_super2. The difference is in the second member: objc_super: super_class // Parent of receiverClass objc_super2: current_class // receiverClass (class object for message recipients)

// message.h (objc4)
struct objc_super {
    __unsafe_unretained _Nonnull id receiver;  // Message receiver
#if !defined(__cplusplus)  &&  !__OBJC2__
    /* For compatibility with old objc-runtime.h header */
    __unsafe_unretained _Nonnull Class class;
#else
    __unsafe_unretained _Nonnull Class super_class;  // Parent class of receiverClass
#endif
    /* super_class is the first class to search */
};

// objc_runtime_new.h (objc4)
struct objc_super2 {
    id receiver;  // Message receiver
    Class current_class;  // receiverClass (class object for message receiver)
};
Copy the code

Look at two more functions, objc_msgSendSuper() and objc_msgSendSuper2(). From the source code, the arguments received by the two functions are the same, but we can infer from the official comments that the first argument received by the objc_msgSendSuper2() function should be objc_super2, not objc_super.

// message.h (objc4)
void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... * / )

// objc-abi.h (objc4)
// objc_msgSendSuper2() takes the current search class, not its superclass.
id _Nullable objc_msgSendSuper2(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
Copy the code

2. The self and super

self

  • Each OC method takes two implicit parameters:(id)selfand(SEL)_cmd;
  • Self is an object pointer to the caller/message receiver of the current method; If it’s an instance method, it’s an instance object that points to the current class. If it is a class method, it is a class object that points to the current class.
  • When calling a method with self, the underlying conversion isobjc_msgSend()Function, as you can see from the previous article, which is called fromCurrent message receiver classTo find the implementation of the method.

super

  • Super is a compiler directive;
  • When a method is called using super, the underlying conversion isobjc_msgSendSuper2()Function that will be called fromThe parent class of the current message receiver classTo find the implementation of the method.

3. The super nature

We converted the following OC code to C++ code using clang:

    [super viewDidLoad];
Copy the code
    // convert to C++
    ((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
    / / to simplify
    struct objc_super arg = {
        self,
        class_getSuperclass(objc_getClass("ViewController"))}; objc_msgSendSuper(arg, sel_registerName("viewDidLoad"));
Copy the code

As you can see, Runtime converts super to a call to the objc_msgSendSuper() function, taking objc_super and SEL.

LLVM & intermediate code

So why does super translate to a call to objc_msgSendSuper2()? The LLVM compiler converts “OC code” to “intermediate code (.ll)” and then to “assembly, machine code”, which is not C/C++. You can use the following command line command to generate the intermediate code: clang-eme-llvm -s main.m For details, see the official LLVM document.

Assembly verification

Convert the viewController.m file to assembly code for validation:

[super viewDidLoad]

[super viewDidLoad]
objc_msgSendSuper2()
objc_msgSendSuper()

Super nature

  • When a method is called using super, the underlying conversion isobjc_msgSendSuper2()Function that takes two argumentsstruct objc_super2andSEL.
struct objc_super2 {
    id receiver;  // Message receiver
    Class current_class;  // receiverClass
};
id _Nullable objc_msgSendSuper2(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
Copy the code
  • objc_msgSendSuper2()The inside of the function will passcurrent_classthesuperclassThe pointer gets its parent class, and starts with the parent class to find the method implementation. Ignore the “Procedure for finding a method from receiverClass” and go straight to step 5.
  • Pay attention toreceiverThe message receiver is still a subclass object, not a parent object, but the scope of the lookup method implementation has changed.

Relevant interview questions

Q: What is the print result of calling the following init method?

@interface HTPerson : NSObject
@end

@interface HTStudent : HTPerson
@end

@implementation HTStudent
- (instancetype)init
{
    if (self = [super init]) {
        
        NSLog(@"[self class] = %@"[self class]);
        NSLog(@"[super class] = %@"[super class]);
        NSLog(@"[self superclass] = %@"[self superclass]);
        NSLog(@"[super superclass] = %@"[super superclass]);
        
    }
    return self;
}
@end
Copy the code

[self class] = HTStudent

[super class] = HTStudent

[self superclass] = HTPerson

[super superclass] = HTPerson

The class and superClass methods are implemented in the NSObject class, and you can see that their return values depend on the Receiver.

+ (Class)class {
    return self;
}
- (Class)class {
    return object_getClass(self);
}
+ (Class)superclass {
    return self->superclass;
}
- (Class)superclass {
    return [self class]->superclass;
}
Copy the code

[self Class] starts with receiverClass and looks for the implementation of the method. If there is no override, it goes all the way to the base NSObject and calls it. [Super Class] starts with receiverClass-> SuperClass and looks for the method implementation. If there is no overwrite, it will find the base class NSObject and then call it. Since the receiver is the same, their return value is the same.