深刻浅出 Runtime(一):初识
深刻浅出 Runtime(二):数据结构
深刻浅出 Runtime(三):消息机制
深刻浅出 Runtime(四):super 的本质
深刻浅出 Runtime(五):具体应用
深刻浅出 Runtime(六):相关面试题html
咱们先来看两个数据结构objc_super
和objc_super2
。
能够看到它们两个的区别是在于第二个成员:
objc_super
:super_class // receiverClass 的父类
objc_super2
:current_class // receiverClass(消息接收者的class对象)面试
// message.h(objc4)
struct objc_super {
__unsafe_unretained _Nonnull id receiver; // 消息接收者
#if !defined(__cplusplus) && !__OBJC2__
/* For compatibility with old objc-runtime.h header */
__unsafe_unretained _Nonnull Class class;
#else
__unsafe_unretained _Nonnull Class super_class; // receiverClass 的父类
#endif
/* super_class is the first class to search */
};
// objc_runtime_new.h(objc4)
struct objc_super2 {
id receiver; // 消息接收者
Class current_class; // receiverClass(消息接收者的class对象)
};
复制代码
再来看两个函数objc_msgSendSuper()
和objc_msgSendSuper2()
。
从源码来看,两个函数所接收的参数没有区别,
可是从官方注释咱们能够推测,objc_msgSendSuper2()
函数所接收的第一个参数应该为objc_super2
而非objc_super
。数据结构
// message.h(objc4)
void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ )
// objc-abi.h(objc4)
// objc_msgSendSuper2() takes the current search class, not its superclass.
id _Nullable objc_msgSendSuper2(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
复制代码
(id)self
和(SEL)_cmd
;objc_msgSend()
函数的调用,经过上一篇文章能够知道,该函数会从当前消息接收者类中开始查找方法的实现。objc_msgSendSuper2()
函数的调用,该函数会从当前消息接受者类的父类中开始查找方法的实现。咱们经过 clang 将如下 OC 代码 转换为 C++ 代码:函数
[super viewDidLoad];
复制代码
// 转换为 C++
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
// 简化
struct objc_super arg = {
self,
class_getSuperclass(objc_getClass("ViewController"))
};
objc_msgSendSuper(arg, sel_registerName("viewDidLoad"));
复制代码
能够看到,Runtime 将super
转换为objc_msgSendSuper()
函数的调用,参数为objc_super
和SEL
。post
那么为何前面说super
会转换为objc_msgSendSuper2()
函数的调用呢? 由于转成的 C++ 的实现和真正的底层实现是有差别的,
LLVM
编译器会将“ OC 代码”先转成“中间代码(.ll)”再转成“汇编、机器代码”,该中间代码非 C/C++。 可使用如下命令行指令生成中间代码:clang -emit-llvm -S main.m
具体能够查看官方文档 LLVM,这里不作过多介绍。ui
将 ViewController.m 文件转换成汇编代码进行验证: spa
[super viewDidLoad]
转换成的汇编代码
[super viewDidLoad]
底层其实是转换成了
objc_msgSendSuper2()
函数的调用而非
objc_msgSendSuper()
。
objc_msgSendSuper2()
函数的调用,该函数接收两个参数struct objc_super2
和SEL
。struct objc_super2 {
id receiver; // 消息接收者
Class current_class; // receiverClass
};
id _Nullable objc_msgSendSuper2(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
复制代码
objc_msgSendSuper2()
函数内部会经过current_class
的superclass
指针拿到它的父类,从父类开始查找方法的实现。忽略“从 receiverClass 中查找方法的过程”,对应下图就是直接从第 5 步开始。
receiver
消息接收者仍是子类对象,而不是父类对象,只是查找方法实现的范围变了。@interface HTPerson : NSObject
@end
@interface HTStudent : HTPerson
@end
@implementation HTStudent
- (instancetype)init
{
if (self = [super init]) {
NSLog(@"[self class] = %@",[self class]);
NSLog(@"[super class] = %@",[super class]);
NSLog(@"[self superclass] = %@",[self superclass]);
NSLog(@"[super superclass] = %@",[super superclass]);
}
return self;
}
@end
复制代码
[self class] = HTStudent
[super class] = HTStudent
[self superclass] = HTPerson
[super superclass] = HTPerson命令行
class
和superclass
方法的实如今 NSObject 类中,能够看到它们的返回值取决于receiver
。指针
+ (Class)class {
return self;
}
- (Class)class {
return object_getClass(self);
}
+ (Class)superclass {
return self->superclass;
}
- (Class)superclass {
return [self class]->superclass;
}
复制代码
[self class]
是从receiverClass
开始查找方法的实现,若是没有重写的状况,则会一直找到基类 NSObject,而后调用。
[super class]
是从receiverClass->superclass
开始查找方法的实现,若是没有重写的状况,则会一直找到基类 NSObject,而后调用。
因为receiver
相同,因此它们的返回值是同样的。code