In layman's language Runtime (D): The essence of super

Runtime series

Layman Runtime (a): acquaintance
layman Runtime (2): The data structure
layman Runtime (III): messaging
layman Runtime (IV): The nature of super
layman Runtime (e): a particular application
layman Runtime (VI): interview questions related

1. objc_super 与 objc_msgSendSuper

Let's look at two data structures objc_superand objc_super2.
They can see that the difference between the two is that the second member:
objc_super: // super_class ReceiverClass parent
objc_super2: current_class // receiverClass (class object message recipient)

// message.h(objc4)
struct objc_super {
    __unsafe_unretained _Nonnull id 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;  // receiverClass 的父类
#endif
    /* super_class is the first class to search */
};

// objc_runtime_new.h(objc4)
struct objc_super2 {
    id receiver;  // 消息接收者
    Class current_class;  // receiverClass(消息接收者的class对象)
};

Let's look at two functions objc_msgSendSuper()and objc_msgSendSuper2().
From the source point of view, two function parameters received no difference,
but we can surmise from official comments, objc_msgSendSuper2()the first argument should be received objc_super2instead 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, ...)

2. self and super

self

  • OC implicit methods with two parameters: (id)selfand (SEL)_cmd;
  • the object is a self pointer to the current method caller / recipient of the message;
    if instance method, it is the object points to the current instance of the class;
    if it is a class method, which points to the class object is the current class.
  • When using self calling a method, the bottom will be converted to objc_msgSend()the calling function, can know through the article, the function will search from the current implementation of the message recipient class.

super

  • It is a super compiler directives;
  • When using super call method, the underlying converted to objc_msgSendSuper2()the calling function, the function will search from the current implementation of the message receiver parent class.

3. super nature

We clang convert the OC code for the C ++ code:

    [super viewDidLoad];
    // 转换为 C++
    ((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
    // 简化
    struct objc_super arg = {
        self,
        class_getSuperclass(objc_getClass("ViewController"))
    };
    objc_msgSendSuper(arg, sel_registerName("viewDidLoad"));

You can see, Runtime will be superconverted to a objc_msgSendSuper()function call, parameters objc_superand SEL.

Intermediate code LLVM &

So why said earlier superconverted to objc_msgSendSuper2()call the function of it?
Because the real implementation and the underlying turn into C ++ implementation is different,
LLVMthe compiler will "-OC Code" to turn into "intermediate code (.ll)" then into "assembler, machine code", the intermediate code is non- C / C ++.
You can use the following command line instruction generating intermediate code: clang -emit-llvm -S main.m
details, refer to the official documentation LLVM , do not make too much introduction.

By compiling verification

ViewController.m converting files into assembly code to verify:

see line 18 of code that is [super viewDidLoad]converted into assembly code


You can see above, [super viewDidLoad]the underlying fact is converted into objc_msgSendSuper2()function calls instead objc_msgSendSuper().

super nature

  • When using super call method, the underlying converted to objc_msgSendSuper2()call the function, the function takes two parameters struct objc_super2and SEL.
struct objc_super2 {
    id receiver;  // 消息接收者
    Class current_class;  // receiverClass
};
id _Nullable objc_msgSendSuper2(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
  • objc_msgSendSuper2()Internally by the function current_classof the superclasspointer to get its parent class, from the beginning to find the parent class implementation of the method. Ignore "Find method from receiverClass procedure", the corresponding figure is started from step 5.
    objc_msgSendSuper2()执行流程
  • 要注意receiver消息接收者还是子类对象,而不是父类对象,只是查找方法实现的范围变了。

4. 相关面试题

Q:调用以下 init 方法的打印结果是什么?

@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

[self class] = HTStudent
[super class] = HTStudent
[self superclass] = HTPerson
[super superclass] = HTPerson

classsuperclass方法的实现在 NSObject 类中,可以看到它们的返回值取决于receiver

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

[self class]是从receiverClass开始查找方法的实现,如果没有重写的情况,则会一直找到基类 NSObject,然后调用。
[super class]是从receiverClass->superclass开始查找方法的实现,如果没有重写的情况,则会一直找到基类 NSObject,然后调用。
由于receiver相同,所以它们的返回值是一样的。

发布了12 篇原创文章 · 获赞 0 · 访问量 192

Guess you like

Origin blog.csdn.net/weixin_42350379/article/details/104504616