Runtime Series

Runtime Principle Exploration (I) — An in-depth understanding of ISA (Apple isa optimization)

An in-depth analysis of the Class structure

OC Class cache_t

Runtime principle exploration (4) — To investigate the bottom message mechanism

Exploring the Runtime principle (5) — The nature of Super

Runtime theory exploration (6) — Runtime in the interview question


Start with the interview questions

Look at the following code

* * * * * * * * * * * * * * * * * * * * * * * * * * * *CLPerson.h
@interface CLPerson : NSObject

@end* * * * * * * * * * * * * * * * * * * * * * * * * * * *CLStudent.h
@interface CLStudent : CLPerson

@end* * * * * * * * * * * * * * * * * * * * * * * * * * * *CLStudent.m
@implementation CLStudent
- (instancetype)init
{
    self = [super init];
    if (self) {
        NSLog(@"[self class] = %@"[self class]);
        NSLog(@"[self superclass] = %@"[self superclass]);
        NSLog(@"[super class] = %@"[super class]);
        NSLog(@"[super superclass] = %@"[super superclass]);
    }
    return self;
}
@end

****************************main.m
int main(int argc, char * argv[]) {
    @autoreleasepool{[[CLStudent alloc] init];

        return UIApplicationMain(argc, argv, nil.NSStringFromClass([AppDelegate class])); }}Copy the code

Call [[CLStudent alloc] init]; What will be printed.

CLStudent

[self class] = CLStudent [self superclass] The result should be [self superclass] = CLPerson [super class] — when we override a superclass method, if we need to execute the logic of the superclass method, we usually add [super method name]. Super is not a pointer to the superclass. This should print [super Class] = CLPerson [super superclass] — based on the above inference, this should print CLPerson’s superclass, which is [super superclass] = NSObject

Actual print result

2019- 08- 12 20:59:23.580048+0800 iOS-Runtime[25274:2862267] [self class] = CLStudent
2019- 08- 12 20:59:23.580700+0800 iOS-Runtime[25274:2862267] [self superclass] = CLPerson
2019- 08- 12 20:59:23.580797+0800 iOS-Runtime[25274:2862267] [super class] = CLStudent
2019- 08- 12 20:59:23.580882+0800 iOS-Runtime[25274:2862267] [super superclass] = CLPerson
Copy the code

Through actual code debugging, we see that the print results for [super Class] and [super Superclass] are not what we expected. What’s going on?

What is super

To explain the above question, we have to go back to the essence of the problem. First, let’s simplify the code

* * * * * * * * * * * * * * * * * * * * * * * * * * * *CLPerson.h
@implementation CLStudent
- (void)run;
@end* * * * * * * * * * * * * * * * * * * * * * * * * * * *CLPerson.m
@implementation CLStudent
- (void)run {
    
    NSLog(@"CLPerson Run");
}
@end* * * * * * * * * * * * * * * * * * * * * * * * * * * *CLStudent.m
@implementation CLStudent
- (void)run {
    
    [super run];
    NSLog(@"CLStudent Run");
}
@end

Copy the code

From the command line window, we use the command xcrun-sdk iphoneos clang-arch arm64-rewrite-objc clstudent.m -o clstudent.cpp In clStudent.cpp, you can see the underlying implementation of the run method as follows

static void _I_CLStudent_run(CLStudent * self, SEL _cmd) {

//[super run];
    ((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("CLStudent"))}, sel_registerName("run"));

//NSLog(@"CLStudent Run");NSLog((NSString *)&__NSConstantStringImpl__var_folders_7__p19yp82j0xd2m_1k8fpr77z40000gn_T_CLStudent_5be081_mi_0); } *************🔧🔧🔧 to simplifystatic void _I_CLStudent_run(CLStudent * self, SEL _cmd) {

//[super run];
   objc_msgSendSuper((__rw_objc_super){
            (id)self,   
            (id)class_getSuperclass(objc_getClass("CLStudent"))
           },   
            @selector(run));

//NSLog(@"CLStudent Run"); It doesn't matterNSLog((NSString *)&__NSConstantStringImpl__var_folders_7__p19yp82j0xd2m_1k8fpr77z40000gn_T_CLStudent_5be081_mi_0); } *************♥️♥️♥️static void _I_CLStudent_run(CLStudent * self, SEL _cmd) {
//⚠️⚠️⚠️ structure is extracted separately
struct __rw_objc_super arg = {
            (id)self,   
            (id)class_getSuperclass(objc_getClass("CLStudent"))};/ / 🌞 🌞 🌞 [super run];
   objc_msgSendSuper(arg, @selector(run));
}
Copy the code

From the simplified code, you can see that [super run]; The bottom line is actually calling objc_msgSendSuper(arg, @selector(run)); First, let’s analyze its two parameters. The second argument is a method selector, SEL. Let’s focus on the first argument, which is a struct __rw_objc_super, which is defined in the current intermediate code, or you can find it in the objC source code through objc_super. As shown below.

//♥️♥️♥️C++ intermediate code definition
struct __rw_objc_super { 
	struct objc_object *object; 
	struct objc_object *superClass; 
	__rw_objc_super(struct objc_object *o, struct objc_object *s) : object(o), superClass(s) {} 
};

//⚠️⚠️⚠️objc source code definition
/// Specifies the superclass of an instance. 
struct objc_super {
    /// Specifies an instance of a class.
    __unsafe_unretained _Nonnull id receiver;

    /// Specifies the particular superclass of the instance to message. 
#if! defined(__cplusplus) && ! __OBJC2__
    /* For compatibility with old objc-runtime.h header */
    __unsafe_unretained _Nonnull Class class;
#else
    __unsafe_unretained _Nonnull Class super_class;
#endif
    /* super_class is the first class to search */
};
Copy the code

There are really only two member variables in this structure

  • id receiver;— message receiver, which is actually passed as the argumentself, that is,CLStudentInstance object of
  • Class super_class;Parent class, the assignment code initialized by the structure in the intermediate code(id)class_getSuperclass(objc_getClass("CLStudent")And you can see that the superclass is thetaCLStudentClass object of the parent class[CLPerson class].

Then let’s take a look at what’s going on inside objc_msgSendSuper. According to the function name, we can find the related declaration in message.h

/** * Sends a message with a simple return value to the superclass of an instance of a class. * * @param super A pointer  to an \c objc_super data structure. Pass values identifying the * context the message was sent to, including the instance of the class that is to receive the * message and the superclass at which to start searching for the method implementation. * @param op A pointer of type SEL. Pass the selector of the method that will handle the message. * @param ... * A variable argument list containing the arguments to the method. * * @return The return value of the method identified  by \e op. * * @see objc_msgSend */
OBJC_EXPORT id _Nullable
objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
    OBJC_AVAILABLE(10.0.2.0.9.0.1.0.2.0);
#endif
Copy the code

Read the description of parameters in the comments

  • super— is a pointer to a structurestruct objc_super *, its contents are {Message receiver RECV.Message receiver's superclass object [[recv superClass] class]},objc_msgSendSuperwillThe parent class object of the message recipientAs a starting point for message lookups.
  • opThe message/method that the message recipient receives, that is, the method to look up

To understand, let’s compare the lookup process after sending a normal message to an object

[obj message] -> CLS superclass -> CLS superclass -> CLS superclass -> CLS superclass ->… -> Find method [super Message] in root class object NSObject -> Find method in obj class object CLS (skip this step) -> (start directly from this step) Find method in CLS superclass object [CLS superclass] -> Look for methods in higher parent objects ->… -> Find the method in the root class object NSObject

So for the opening interview question, the real running logic is as follows

summary

NSLog(@"[self class] = %@",[self class]);

  • Message receiver: instance object of CLStudent

  • The final method called: the -(Class) Class method of base Class NSObject

    [self class] = CLStudent [self class] = CLStudentCopy the code

NSLog(@"[super class] = %@",[super class]);

  • Message receiver: is still an instance object of CLStudent

  • The final method called: the -(Class) Class method of base Class NSObject

    [super class] = CLStudent [25274:2862267] [super class] = CLStudentCopy the code

NSLog(@"[self superclass] = %@",[self superclass]);

  • Message receiver: instance object of CLStudent

  • The final method called: the -(Class)superclass method of base Class NSObject

    [self superclass] = CLPerson [self superclass] = CLPersonCopy the code

NSLog(@"[super superclass] = %@",[super superclass]);

  • Message receiver: is still an instance object of CLStudent

  • The final method called: the -(Class)superclass method of base Class NSObject

    2019-08-12 20:580797 +0800 ios-Runtime [25274:2862267] [super superclass] = CLPersonCopy the code

Because the -class and -superclass methods actually use the Runtime API underneath, it simply looks like this

- (Class)class
{
    return object_getClass(self);
}

- (Class)superclass
{
    return class_getSuperclass(object_getClass(self));
}
Copy the code

So once you know who the real message recipient is, trusting the final print is easy to understand.

OK, so that’s the nature of super.


🦋🦋🦋 Portal 🦋 port 🦋

Runtime Principle Exploration (I) — An in-depth understanding of ISA (Apple isa optimization)

An in-depth analysis of the Class structure

OC Class cache_t

Runtime principle exploration (4) — To investigate the bottom message mechanism

Exploring the Runtime principle (5) — The nature of Super

Runtime theory exploration (6) — Runtime in the interview question