前语
这篇文章起源于很陈旧的一个面试题,为什么一个类的 [self class]
和 [super class]
的输出是相同的,今天从源码的视点去剖析一下。
场景再现:
@interface Animal : NSObject
@end
@implementation Animal
@end
@interface Cat : Animal
@end
@implementation Cat
- (instancetype)init {
if (self = [super init]) {
NSLog(@"%@", [self class]);
NSLog(@"%@", [super class]);
}
return self;
}
@end
输出:
Cat
Cat
源码
既然都是调用的 class
办法,咱们就先看一下 class
办法的完成是什么:
- (Class)class {
return object_getClass(self);
}
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
能够发现,class
办法其实便是传入一个目标(实例或类),然后输出这个目标的 isa
,那么关键就在于,[self class]
和 [super class]
所传入的目标分别是什么。
将 Cat
放到一个 .m
文件中,进入到它所在的文件夹,履行 clang -rewrite-objc Cat.m
,得到一个 Cat.cpp
文件,这是 Clang 编译 Cat.m
的输出文件,打开这个文件,直接查找 Cat_init
,就能定位到咱们在 Cat.m
中编写的 init
办法。
去掉干扰项后,[self class]
和 [super class]
的源码如下:
// [self class]
((Class (*)(id, SEL))(void *)objc_msgSend)(
(id)self, // Cat
sel_registerName("class") // class 办法
)
// [super class]
((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)(
(__rw_objc_super) {
(id)self, // Cat
(id)class_getSuperclass(objc_getClass("Cat")) // Animal
},
sel_registerName("class") // class 办法
)
能够看到它们的不同在于,[self class]
调用的是 objc_msgSend
,第一个参数为 (id)self
,而 [super class]
调用的是 objc_msgSendSuper
,第一个参数是 __rw_objc_super
,两者的第二个参数都是相同的,都是办法名,咱们先疏忽,只看不同的部分。
__rw_objc_super
的构造如下:
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_msgSendSuper
的源码(只有头文件),它的界说为:
objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
接收的第一个参数是 objc_super
类型的一个结构体,与咱们编译出来的 __rw_objc_super
其实是同一个东西,咱们看一下源码中的 objc_super
:
struct objc_super {
__unsafe_unretained _Nonnull id receiver;
#if !defined(__cplusplus) && !__OBJC2__
__unsafe_unretained _Nonnull Class class;
#else
__unsafe_unretained _Nonnull Class super_class;
#endif
};
// 因为 #if 中的条件不满足,所以简化下来,它的结构为:
struct objc_super {
id receiver;
Class super_class;
};
这个与 __rw_objc_super
的结构是对应的。
一个 receiver
,一个 super_class
,根据咱们编译后看到的,receiver
传入的是 self
,也便是 Cat
实例目标,super_class
用的是 class_getSuperclass(objc_getClass("Cat"))
得出来的父类,也便是 Animal
。
关键点在于,这里传入的 receiver
,依然是 Cat
的实例目标,在调用 class
办法,也便是 object_getClass
,传入的参数便是 receiver
,其实便是 self
,所以回来的 class
,便是 Cat
类。
也便是,[self class]
和 [super class]
,二者调用 object_getCalss
办法所传入的参数,其实都是 self
,所以它们的回来结果是相同的。
而它们的区别在于查找办法的顺序上,objc_msgSend
是从自己开端,沿着承继链一路往上去查找办法,而 objc_msgSendSuper
是从 self.superclass
,也便是自己的父类开端,沿着承继链一路往上查找办法,但是用于消息发送的 receiver
,接收者仍是自己,只不过不会查找自己的类中的办法列表,而直接从自己的父类中开端查找。
就比如有两个快递,一个是我买的,一个是我爸买的,但是用的都是我的信息,我是快递的签收人,我知道有一个快递是我爸的,我会给他,但是如果有人问,签收人的信息,那就仍是我。
Always me.
其他
runtime 是开源的,能够从 官网 进行下载,或者网上找一份能够编译的版别,我用的是 KCCObjc4。