本文的切入点是2014年的一场线下分享会,也就是sunnyxx分享的objc runtime。很惭愧,这么多年了才完整的看了一下这个分享会视频。当时他出了一份试题,并戏称精神病院objc runtime入院考试。
我们今天的这篇文章就是从这个试题中的题目入手,来深入的学习runtime。
源码版本objc4-750
第一题
@implementation Son : Father
- (id)init {
self = [super init];
if (self) {
NSLog(@"%@", NSStringFromClass([self class]));
NSLog(@"%@", NSStringFromClass([super class]));
}
return self;
}
@end
第一行的[self class]
应该是没有疑问的,肯定是Son
,问题就出在这个[super class]
。
大家都知道,我们OC的方法在底层会编译为一个objc_msgSend
的方法(消息发送),[self class]
符合这个情况,因为self是类的一个隐藏参数。但是super
并不是一个参数,它是一个关键字,实际上是一个“编译器标示符”,所以这就有点不一样了,经查阅资料,在调用[super class]
的时候,runtime调用的是objc_msgSendSuper
方法,而不是objc_msgSend
。
首先要做的是验证一下是否是调用了objc_msgSendSuper
。这里用到了clang这个工具,我们可以把OC的代码转成C/C++。
@implementation Son
- (void)test {
[super class];
}
@end
在终端运行clang -rewrite-objc Son.m
生成一个Son.cpp文件。
在这个.cpp文件的底部我们可以找到这么一部分代码
// @implementation Son
static void _I_Son_test(Son * self, SEL _cmd) {
((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Son"))}, sel_registerName("class"));
}
// @end
看起来乱七八糟,有很多强制类型转换的代码,不用理它,我们只要看到了我们想要的objc_msgSendSuper
就好。
去源码中看一下这个方法(具体实现好像是汇编,看不懂)
OBJC_EXPORT void
objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ )
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
可以看出来这个方法第一个参数是一个objc_super
类型的结构体,第二个是一个我们常见的SEL,后面的...代表还有扩展参数。
再看一下这个objc_super
结构体。
/// Specifies the superclass of an instance.
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 */
};
第一个参数是接收消息的receiver,第二个是super_class(见名知意~ 😆)。我们和上面提到的.cpp中的代码对应一下就会发现重点了,receiver是self。
所以,这个[super class]
的工作原理是,从objc_super
结构体的super_class
指向类的方法列表开始查找class
方法,找到这个方法之后使用receiver
来调用。
所以,调用class
方法的其实还是self
,结果也就是打印Son
。
(未完待续)
网友评论