示例
@interface Person : NSObject
@end
@implementation Person
- (instancetype)init{
self = [super init];
if (self) {
NSLog(@"%@",NSStringFromClass([self class]));
NSLog(@"%@",NSStringFromClass([super class]));
}
return self;
}
@end
打印结果都:Person
解答
首先看看class
的实现
- (Class)class {
return object_getClass(self);
}
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
- 返回的是调用者的
isa
然后我们看看self
和super
分别调用class
方法有什么区别。
通过终端命令查看c++代码(注意命令行要进入文件Person.m所在的路径)
xcrun -sdk iphonesimulator clang -rewrite-objc Person.m
打开生成的Person.cpp
文件,在里面查找init
的实现
static instancetype _I_Person_init(Person * self, SEL _cmd) {
self = ((Person *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Person"))}, sel_registerName("init"));
if (self) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_60_8_r2m72n2795wn0bkj4xfvwh0000gn_T_Person_257638_mi_0,NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class"))));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_60_8_r2m72n2795wn0bkj4xfvwh0000gn_T_Person_257638_mi_1,NSStringFromClass(((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Person"))}, sel_registerName("class"))));
}
return self;
}
- self调用class,转换为c++中是通过
objc_msgSend
发送class
消息。((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class"))
- super调用class,在c++中是通过
objc_msgSendSuper
发送class
消息。((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Person"))}, sel_registerName("class"))
-
objc_msgSendSuper
的第一个参数是一个结构体,而objc_msgSend
第一个参数是id
类型
我们回到objc
开源代码中,看看objc_msgSendSuper
是如何定义的
找不到定义,但是找到了声明,通过注释了解到是struct objc_super
类型,那么我们更换搜索目标
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 */
};
- 一个有两个参数:分别是id类型的receiver变量和Class类型的父类变量。通过c++代码看到receiver变量赋值的还是
self
- 注意最后一行注释:使用
super_class
在查找方法的时候更快。就是跳过子类的方法流程,直接在父类中查找
此时就清楚原因了:
- self调用class方法时,先在当前类中查找方法,找不到就去父类中找。最后到NSObject中。self调用时,编译期间调用的是objc_msgSend函数,函数中有两个默认参数第一个是self,第二个是sel。调用class返回的是self的isa。
- super是直接在父类中查找,最后也是找到NSObject中。super时,编译期间调用的是objc_msgSendSuper函数,也有两个默认参数,第一个是struct objc_super * 类型,第二个是sel。而结构体中第一个属性接受者,也是self。这样调用class返回的也是self的isa。
- ==因此打印的都是Student==
华丽的分割区域,请接着往下看。
上面的分析是有小小的瑕疵
上面的结果是对的,但是分析中有小小的瑕疵。我们使用的是编译期间的代码,但是OC可是动态的。
在[super class]
代码处打断点,打开汇编调试:
- 在运行时调用的是
objc_msgSendSuper2
,我们在objc
源码中看看它的实现
/********************************************************************
* id objc_msgSendSuper2(struct objc_super *super, SEL op, ...)
*
* struct objc_super {
* id receiver;
* Class cls; // SUBCLASS of the class to search
* }
********************************************************************/
ENTRY _objc_msgSendSuper2
ldr r9, [r0, #CLASS] // class = struct super->class
ldr r9, [r9, #SUPERCLASS] // class = class->superclass
CacheLookup NORMAL, _objc_msgSendSuper2
// cache hit, IMP in r12, eq already set for nonstret forwarding
ldr r0, [r0, #RECEIVER] // load real receiver
bx r12 // call imp
CacheLookup2 NORMAL, _objc_msgSendSuper2
// cache miss
ldr r9, [r0, #CLASS] // class = struct super->class
ldr r9, [r9, #SUPERCLASS] // class = class->superclass
ldr r0, [r0, #RECEIVER] // load real receiver
b __objc_msgSend_uncached
END_ENTRY _objc_msgSendSuper2
- 注释中解释了,
objc_super
的第二个属性是当前类 - 汇编代码还是获取
super class
进行查找,和我们上面的分析还是一样的。
总结
clang
和xcrun
命令只是编译期的一个中间产物,也只是一个参考。运行起来后才是真正的情况。
就想这道题,通过运行后,我们发现super真正底层调用的是_objc_msgSendSuper2
。虽然结果是一样的,但是以后进行底层分析的时候建议还是要以运行时的为准。