以下代码的打印结果是什么?为什么?
1 | @interface JQApple : JQFruit |
按照面向对象的思想应该是分别打印JQApple
和JQFruit
然而运行结果却出乎我们的意料,最终均都打印“JQApple”。这是为什么?
因为self
是类的隐藏参数,指向当前调用方法的对象。而super
并不是如我们所想是指向当前对象父类的指针。其实super
是一个编译器标识符,在运行时中与self
相同,指向同一个消息接受者,只是self
会优先在当前类的methodLists
中查找方法,而super
则是优先从父类中查找。验证如下:
在终端运行:
1 | $ clang -rewrite-objc main.m |
可以看到运行时代码如下:
1 | NSLog((NSString *)&__NSConstantStringImpl__var_folders_ld_03322m393b5cyvhz2zhv2c100000gn_T_main_97554f_mi_0, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class")))); |
删除相关无关类型及方法后代码如下:
1 | objc_msgSend((id)self, sel_registerName("class")); |
查看函数定义:
1 | /** |
可知objc_msgSend
函数中的self
参数是指指向接收消息的类的实例的指针,即消息接受者,而op
参数则是指处理该消息的selector
;objc_msgSendSuper
函数中的参数super
则是一个objc_super
结构体,objc_super
结构体定义如下:
1 | /// Specifies the superclass of an instance. |
其中receiver
是指类的实例,super_class
则是指该实例的父类。可以看到在编译后的C++
代码中有个__rw_objc_super
结构体:
1 | struct __rw_objc_super { |
其实即为objc_super
结构体。通过(__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("JQApple"))}
该段代码可知:我们把self
以及JQApple
的父类通过结构体的构造方法构造了一个__rw_objc_super
结构体,也就是objc_super
。因此objc_super
结构体中的receiver
既是self
。所以[self class]
和[super class]
指向的是同一个消息接受者,只是self
会优先从当前类的实现中寻找方法处理消息,而super
则是会优先从objc_super
结构体中的super_class
也就是父类的实现中查找。JQFruit
及JQApple
中均未实现- (Class)class;
方法,因此会逐级向上查找最终调用基类NSObject
的- (Class)class;
方法,通过官方开源的NSObject
的- (Class)class;
方法代码:
1 | - (Class)class{ |
可知,消息接受者是self
,而[self class]
和[super class]
指向的是同一个消息接受者,因此该段代码均打印JQApple
。