[self class]和[super class]带来的思考

前言

class是很多人经常使用的方法,而【self class】和【super class】的区别也在面试中经常使用到。如下所述:


#import "Person.h"

@interface Man : Person

@end

#import "Man.h"

@implementation Man

- (instancetype)init {
    self = [super init];
    if (self) {
        NSLog(@"%@", [self class]);
        NSLog(@"%@", [super class]);
    }
    return self;
}

@end

那么问题来了,控制台打印的值是什么?

追根溯源

源码实现

想知道这个值是什么的话,最简单的方式当然是跑一下咯。不过,这样做的话,一点都没有灵魂了。所以,我们就用clang编译一下这个文件,看看他在编译器中是怎么做的。命令是 clang -rewrite-objc XXX.m

编译后的后的CPP文件内容炒鸡多,我们找到关键的init方法(秘诀:翻到底部)如下:


static instancetype _I_Man_init(Man * self, SEL _cmd) {
    self = ((Man *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Man"))}, sel_registerName("init"));
    if (self) {
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_jb_tlqj59rx34dfrxpflntjghz80000gn_T_Man_b7c647_mi_0, ((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class")));
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_jb_tlqj59rx34dfrxpflntjghz80000gn_T_Man_b7c647_mi_1, ((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Man"))}, sel_registerName("class")));
    }
    return self;
}

我们抓住主体结构去看的话,重要的代码(去除了类型修饰)如下:


objc_msgSend(self, sel_registerName("class"));
        
objc_msgSendSuper((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Man"))}, sel_registerName("class"))

可以看出,当调用[self class]的时候,是objc_msgSend函数;而[super class]调用objc_msgSendSuper函数。

它们的不同在于入参不同,一个入参是idSEL,一个是objc_superSEL;


OBJC_EXPORT void
objc_msgSend(void /* id self, SEL op, ... */ )
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);

OBJC_EXPORT void
objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ )
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
    
 id objc_msgSendSuper(struct objc_super *super, SEL op, ...)
 
 struct objc_super {
     id receiver;
     Class cls; // the class to search
 }

根据官方注释,可以看出objc_super中的两个参数中,cls用于查找对应的类。objc_super的初始化传入(id)selfclass_getSuperclass(objc_getClass("Man"))。那么在objc_msgSendSuper中的receiver就是selfcls是父类的class

实例、子类、父类、元类关系

父类的参数用来查找是什么意思呢?这就要讲到类与元类对象和消息传递的过程了。
这部分的内容,一般来说是做iOS开发都了解的内容了。这里,贴上经典图:

[self class]和[super class]带来的思考

子类、父类、元类

关系

  • 实例通过isa指针关联对应的class类对象,class类对象同样通过isa指针关联元类对象。实际上所谓的实例方法,都是去类对象中找。而类方法都是去元类对象中找。
  • 子类通过superclass指针找到父类。当没有父类时该值为nil,元类的最底层父类的指针指向类对象。
  • 消息的传递沿着指针往上查找,找不到的话会进入消息转发流程(后面会介绍)。

所以

我们可以看出superClass指针用于查找父类,并在父类中做查询方法等操作。那么,我们可以看出objc_msgSendobjc_msgSendSuper中参数区别的那个superClass的传递,就是让我们在查询方法的时候,从父类进行查询。而不是从当前类的对象开始查询。

但是我们的消息接受者又是谁呢?

[self class]的源码中,我们看到接受者是self,而在[super class]中,我们的消息的接受者是objc_super中的receiver,这个值任然是self


objc_msgSendSuper((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Man"))}, sel_registerName("class"))

结论

[self class][super class]的消息接受者最终都是self。所以,之前那道题的答案也就显而易见了。答案都是Man