面试题

1
2
3
4
5
6
7
8
9
10
@implementation Son : Father
- (id)init {
self = [super init];
if (self) {
NSLog(@"%@", NSStringFromClass([self class]));
NSLog(@"%@", NSStringFromClass([super class]));
}
return self;
}
@end
1
2
// 运行结果:
2016-04-17 14:21:36.060 Test[8566:568584] NSStringFromClass([self class]) = Son 2016-04-17 14:21:36.061 Test[8566:568584] NSStringFromClass([super class]) = Son

self 是类的隐藏参数,指向当前调用方法的这个类的实例。
super 是一个 Magic Keyword, 它本质是一个编译器标示符,和 self 是指向的同一个消息接受者。而不同的是,super 是告诉编译器,调用 class 这个方法时,要去父类的方法,而不是本类里的。

上面的demo中不管调用 [self class] 还是 [super class],最终的接受消息的对象都是当前 Son 这个对象。

当使用 self 调用方法时,会从当前类的方法列表中开始找,如果没有,就从父类中再找;
当使用 super 时,则从父类的方法列表中开始找。

通过clang命令验证:

1
clang -rewrite-objc test.m

image.png

从上面的代码中,我们可以发现当在调用 [self class] 时,会转化成 objc_msgSend 方法。看下方法定义:

1
id objc_msgSend(id self, SEL op, ...)

而在调用 [super class] 时,会转化成 objc_msgSendSuper 方法。看下方法定义:

1
id objc_msgSendSuper(struct objc_super *super, SEL op, ...)

查看 objc_super 结构体发现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
OBJC_EXPORT void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ )


/// Specifies the superclass of an instance.
struct objc_super {
/// Specifies an instance of a class.
__unsafe_unretained 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 Class class;
#else
__unsafe_unretained Class super_class;
#endif
/* super_class is the first class to search */
};

objc_super 结构体有两个成员:

  • 第一个成员是接收消息的 receiver, 类似于上面的 objc_msgSend 方法第一个参数 self
  • 第二个成员是当前类的父类 super_class

所以,当调用 [self class] 时,实际先调用的是 objc_msgSend 方法,第一个参数是接收消息的 receiver 也就是 Son 当前的这个实例,然后在 Son 类的方法列表开始查找 selector,如果没有,则去父类的方法列表开始查找 selector,如果父类里面也没有,则会在 NSObject 查找方法列表开始查找 selector,找到后以 self 去调用父类的这个 selector

objc Runtime 开源代码对 - (Class)class 方法的实现:

1
2
3
- (Class)class {
return object_getClass(self);
}

- (Class)class 的实现就是返回 self 自己,故上述输出结果为 Son

而当调用 [super class] 时,会转换成 objc_msgSendSuper 方法:

  • objc_super 结构体指向的 superClass 父类的方法列表开始查找 selector,找到后以 objc->receiver 去调用父类的这个 selector
  • 由于找到了父类 NSObject 里面的 class 方法的 IMP ,又因为传入的入参 objc_super->receiver 指向 selfself 也就是 Son 当前的这个实例,所以父类的方法 class 执行 IMP 之后,输出还是 Son
    最后输出两个都一样,都是输出 Son

Reference

http://blog.sunnyxx.com/2014/11/06/runtime-nuts/