1、问题思考

创建Person类和Person的子类Student,在Student内部添加如下代码,观察两组打印结果:

  1. NSLog(@"第一组----------");
  2. NSLog(@"[self class] %@", [self class]);
  3. NSLog(@"[self superclass] %@", [self superclass]);
  4. NSLog(@"第二组----------");
  5. NSLog(@"[super class] %@", [super class]);
  6. NSLog(@"[super superclass] %@", [super superclass]);

打印结果:

  1. ~: 第一组----------
  2. ~: [self class] Student
  3. ~: [self superclass] Person
  4. ~: 第二组----------
  5. ~: [super class] Student
  6. ~: [super superclass] Person

如果super代表的是父类,那么为什么self和super调用class的结果相同?

2、super的结构

为Person和Student都添加run方法,在Student执行run方法时调用父类run方法:

  1. - (void)run {
  2. [super run];
  3. }

转成C++代码:

  1. ((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Student"))}, sel_registerName("run"));

整理一下:

  1. struct objc_super sp = {
  2. self,
  3. [Person class],
  4. };
  5. objc_msgSendSuper(sp, @selector(run));

可以看到使用super关键字调用方法是,底层使用的是objc_msgSendSuper方法,且传入参数为objc_super结构体和SEL,objc_super的结构如下,包含消息接收者和消息接收者的父类两个成员:

  1. struct __rw_objc_super {
  2. struct objc_object *object; // 消息接收者
  3. struct objc_object *superClass; // 消息接收者的父类
  4. };

通过 objc源码 message.h中objc_msgSendSuper方法注释可知,当调用方法时会先在superClass中开始搜索方法实现。

3、class、superclass方法

class方法是在NSObject内部实现的,源码:

  1. - (Class)class {
  2. return object_getClass(self);
  3. }
  4. - (Class)superclass {
  5. return [self class]->getSuperclass();
  6. }

返回的是方法接收者receiver的class(isa指针)。

4、总结

super调用方法底层实际上是通过objc_msgSendSuper方法实现,传入的参数是objc_super结构体,而objc_super结构体的第一个参数是self,所以最后在调用class方法时的receiver还是self,[super message]总结如下:
1、消息接收者仍然是子类对象
2、从父类开始查找方法的实现
所以打印[super class]结果仍然是Student。

5、补充

将super调用转成C++代码后,显示的底层实现是objc_msgSendSuper方法,但真实的底层方法是objc_msgSendSuper2,可以通过汇编代码查看(参考OC代码转换):

  1. 0x100003f2e <+46>: callq 0x100003f4c ; symbol stub for: objc_msgSendSuper2
  1. callq _objc_msgSendSuper2

由此可见,super真实底层实现是_objc_msgSendSuper2方法。