[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
函数。
它们的不同在于入参不同,一个入参是id
和SEL
,一个是objc_super
和SEL
;
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)self
和class_getSuperclass(objc_getClass("Man"))
。那么在objc_msgSendSuper
中的receiver
就是self
,cls
是父类的class
。
实例、子类、父类、元类关系
父类的参数用来查找是什么意思呢?这就要讲到类与元类对象和消息传递的过程了。
这部分的内容,一般来说是做iOS开发都了解的内容了。这里,贴上经典图:
子类、父类、元类
关系
- 实例通过isa指针关联对应的class类对象,class类对象同样通过isa指针关联元类对象。实际上所谓的实例方法,都是去类对象中找。而类方法都是去元类对象中找。
- 子类通过superclass指针找到父类。当没有父类时该值为nil,元类的最底层父类的指针指向类对象。
- 消息的传递沿着指针往上查找,找不到的话会进入消息转发流程(后面会介绍)。
所以
我们可以看出superClass指针用于查找父类,并在父类中做查询方法等操作。那么,我们可以看出objc_msgSend
和objc_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
。