self 和 super的区别

iOS 开发中经常用到或者看到 [self class][super class] ,很多人都会用但是没有研究过这两者究竟有什么区别,今天就本文就讲解一下 selfsuper 两者的区别

self 和 super 的区别

self 是一个隐藏参数变量,指向当前调用方法的对象,还有一个隐藏参数是 _cmd,代表当前方法 selector,在 runtime 时会调用 objc_msgSend() 方法

super 并不是隐藏参数,它只是编译器的指令符号,它和 self 指向的是相同的消息接收者,在 runtime 时调用 objc_msgSendSuper() 方法

官方文档里面 self 和 super 的解释:

Whenever you’re writing a method implementation, you have access to an important hidden value, self. Conceptually, self is a way to refer to “the object that’s received this message.” It’s a pointer, just like the greeting value above, and can be used to call a method on the current receiving object.

There’s anotherimportant keyword available to you in Objective-C, called super. Sending a message to super is a way to call through to a method implementation defined by a superclass further up the inheritance chain. The most common use of super is when overriding a method.

接下来我们先看看下面的代码,代码里定义了一个 Parents 的父类,定义了一个 继承自 Parents 的子类 Children,在 Parents 的 .h 文件里定一个了一个 eat 方法,在 Children 的 .m 文件里重写了父类的eat 方法

Parents 类的实现:

1
2
3
4
5
6
7
8
9
10
// .h
#import <Foundation/Foundation.h>
@interface Parents : NSObject
- (void)eat;
@end
// .m
#import "Parents.h"
@implementation Parents
@end

Children 类的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// .h
#import "Parents.h"
@interface Children : Parents
@end
// .m
#import "Children.h"
@implementation Children
- (void)eat{
// class:获取当前调用者的类
// superclass:获取当前调用者的父类
// super: 是一个编译指示器,编译器看到这个标志,就会让当前对象去调用父类的方法,本质还是当前对象在调用方法
NSLog(@"%@ %@ %@ %@",[self class], [self superclass], [super class], [super superclass]);
}
@end

然后在控制器里实例化一个 Children 对象并调用 eat 方法

1
2
3
4
5
- (void)viewDidLoad {
[super viewDidLoad];
Children *child = [[Children alloc] init];
[child eat];
}

得到下面的打印结果:

1
2017-04-24 15:38:38.789 selfandsuper[2228:134855] Children Parents Children Parents

解释一下打印结果:

[self class]: 的调用者是 Children
[self superclass]self 的父类是 Parents
[super class]: 当前调用者还是 self 也就是 Children,只不过有 super 之后编译器会去调用父类的 class 方法,而不是本类里的
[super superclass]:当前调用者是 self 也就是 Children,它的 superclassParents,只不过有 super 之后编译器会去调用父类的 superclass 方法,而不是本类里的

self 和 super 底层的实现原理

OC 的内部实现是运行时(Runtime)机制,方法的调用实际上消息发送机制(msgSend),先看一下 objc_msgSend() 的函数定义:

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

这个方法的第一个参数是消息接收者,第二个参数是调用的具体类方法的 selector,后面是 selector 方法的可变参数,先不管这个可变参数

以上面 [self methonName] 为例,编译器会替换成下面的代码:

1
objc_msgSend(child, sel_registerName("methonName"));

这个 selector 是从当前 self 的方法列表开始找,当找到后把对应的 selector 传递过去

而当使用 [super methonName] 调用时,会使用objc_msgSendSuper() 函数,编译器会替换成下面的代码:

1
objc_msgSendSuper({self, class_getSuperclass(objc_getClass("Parents"))}, sel_registerName("methonName"));

看一下 objc_msgSendSuper()的函数定义:

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

第一个参数是个 objc_super 的结构体,第二个参数还是类似上面的类方法的 selector,先看下 objc_super 这个结构体的组成:

1
2
3
4
struct objc_super {
__unsafe_unretained id receiver;
__unsafe_unretained Class super_class;
};

这个结构体包含了两个成员,一个是 receiver,这个类似上面 objc_msgSend 的第一个参数,第二个成员是记录这个类的父类是什么

以上面的 [super methonName] 为例,当编译器遇到 Children 里的方法里的 [super methonName]时,开始做这几个事:

  1. 构建 objc_super 的结构体,此时这个结构体的第一个成员变量receiver 就是 Children,和 self 相同,而第二个成员变量super_Class 就是指类 Parents,因为 Children 的父类就是 Parents
  2. 调用 objc_msgSendSuper() 的方法,将第一步构建的这个结构体和 methonName 的 selector 传递过去,函数里面在做的事情类似这样:从 objc_super 结构体指向的 super_Class 的方法列表开始找 methonName 的 selector,找到后再以 objc_super->receiver 去调用这个 selector,可能也会使用 objc_msgSend() 这个函数,不过此时的第一个参数就是objc_super->receiver,第二个参数是从 objc_super->super_Class中找到的 selector

里面的调用机制大体就是这样了,通过以上面的分析,回过头来看开始的代码,当输出 [self class][super class]时,是个怎样的过程:

当调用 [self class] 时,这时的 selfChildren,在使用objc_msgSend() 时,第一个参数是 self,也是 Children *child 这个实例,第二个参数,要先找到 class 这个方法的selector,先从 Children 这个类开始找,没有找到,然后到Children 的父类 Parents 中去找,也没有,再去 Parents 的父类 NSObject 去找,一层一层向上找之后,在 NSObject 的类中发现这个 class方法,而 NSObject 的这个 class 方法的返回值就是 self 的类别,所以这里打印 Children

当使用 [super class]时,这时要转换成objc_msgSendSuper()的方法,先构造 objc_super的结构体,第一个成员变量就是 self, 第二个成员变量是 Parents,然后要找 class 这个 selector,先去 super_Class 也就是 Parents 中去找,没有,然后去 Parents 的父类中去找,结果还是在 NSObject 中找到了,然后内部使用函数 objc_msgSend(objc_super->receiver, @selector(class)) 去调用,此时已经和我们用 [self class] 调用时相同了,此时的 receiver 就是 Children *child 这个实例,所以这里返回的也是 Children

以上就是在 OC 中的类实现中经常看到这两个关键字 selfsuper 的区别

参考文章:
iOS经典讲解之[self class]和[super class]的区别

本人刚开始写博客,主要是为了给自己的知识点做一个笔记,方便自己以后查阅,如果能让别人有所启发也是荣幸之至!如有错误,欢迎指正!