重新认识下 self super,及msgSend msgSendSuper
先看几行行老生常谈的代码。。。
1 | id obj1 = self.class; |
其输出结果如图所示:(其中 runtimeTest 继承NSObject,我们在runtimeTest类中 测试如上代码)

利用clang 命令查看其C++实现 ,其中XXXX 为目标文件
1 | clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk XXXX.m |
其编译结果为1
2
3
4
5
6
7
8
9id obj1 = ((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class"));
id obj2 = ((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("runtimeTest"))}, sel_registerName("class"));
id obj3 = ((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("superclass"));
id obj4 = ((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("runtimeTest"))}, sel_registerName("superclass"));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_ng_8ncwcfkj69n6sh2hxlsfzz300000gn_T_runtimeTest_929dba_mi_0,obj1);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_ng_8ncwcfkj69n6sh2hxlsfzz300000gn_T_runtimeTest_929dba_mi_1,obj2);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_ng_8ncwcfkj69n6sh2hxlsfzz300000gn_T_runtimeTest_929dba_mi_2,obj3);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_ng_8ncwcfkj69n6sh2hxlsfzz300000gn_T_runtimeTest_929dba_mi_3,obj4);
从objc/message.h中 objc_msgSend 的参数说明可以看出其第一个参数为消息的接收者,
而objc_msgSendSuper 参数的说明中,我们需要注意一点,objc_msgSendSuper方法是直接从父类的方法列表中找实现,所以当我们使用super调用
方法时,运行时会将[super callFunction]转换为objc_msgSendSuper并且父类的方法中寻找实现,以此来实现调用父类方法,如下图
objc_msgSendSuper第一个参数为一个名为objc_super 的结构体,其构成如下
从上面的obj1和obj2 的示例里可以看出,obj1的接受者参数为self,obj2在objc_super的结构体中,receiver的值同样是self.
NSObject的class方法实现源码地址

在class的实现中,我们看到其返回值为isa,同时我们也注意到,在发起消息时传入的接收者都为self(obj1和obj2的示例中,即为runtimeTest类的实例),
所以在其父类的class方法实现中,此时isa 也都为runtimeTest类,所以我们最终obj1和obj2的打印结果都为runtimeTest,
但是在obj3和obj4的实例中,superclass方法返回的是((struct objc_class *)self)->super_class。所以结果为runtimeTest的父类即:NSObject 。
2019.05.13更新
这里面有一个错误,就是上面截图的class方法 是类方法,并不是实例方法。下面补充实例方法的实现
总结
self与super在调用方法时,在运行时的变现有所不同,self为objc_msgSend,super为objc_msgSendSuper。objc_msgSend会优先从当前类中找实现。objc_msgSendSuper直接从父类中寻找实现。- 之所以上面4个示例的输出结果为runtimeTest,runtimeTest,NSObject,NSObject,是由消息的接收者与父类的方法共同决定的。
