Objective-C 中 self、super 关键字

628 阅读4分钟

1. self

self是一个特殊的指针,只在 @implementation 的方法中才有。self不能离开类, 离开类之后没有任何意义。

使用场合:

  • 只能用在方法中(对象方法\类方法),不能用在函数中。可以用于在对象方法之间进行相互调用,也可以用于在类方法之间进行相互调用。
  • 用于区分成员变量和局部变量同名的情况。

每次调用方法的时候,系统会自动创建self指针。

self 是如何获取当前方法的对象呢?

objc_msgSend 找到方法对应实现时,它将直接调用该方法实现,并将消息中所有参数都传递给方法实现,同时,它还将传递两个隐藏参数:

  • 接受消息的对象(self 所指向的内容,当前方法的对象指针)
  • 方法选择器(_cmd 指向的内容,当前方法的 SEL 指针)

self指针指向的是方法调用者:

  • 在类方法中,self指针指向的是(调用当前类方法的)类。
  • 在对象方法中,self指针指向的是(当前对象方法的)对象。

注意:

  • self自动区分类方法和对象方法, 如果在类方法中使用self调用对象方法, 那么会直接报错
  • 不能在对象方法或者类方法中利用self调用当前self所在的方法(死循环)

2. super

super是预编译指令,编译器的指令符号,

使用super关键字发送消息会被编译器转化为调用objc_msgSendSuper以及相关函数(由返回值决定)。

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

super指代的是struct objc_super结构体指针。该结构体需要包含接收消息的实例以及一开始寻找方法实现的父类

struct objc_super {
    /// Specifies an instance of a class.
    __unsafe_unretained id receiver;

    /// Specifies the particular superclass of the instance to message. 
    __unsafe_unretained Class super_class;
    /* super_class is the first class to search */
};

3. superself 区别

  1. self调用自己方法,super调用父类方法

  2. self类(指针),super是预编译指令

  3. [self class] 和 [super class] 输出是一样的.这是由于 class 这个方法, 子类和父类都是没有实现的. 真正实现的是 NSObject, 所以不管是用self从当前类的方法列表中开始查找还是用super从父类的方法列表中开始查找都没有区别.

4. [super init];

一个子类从父类继承,那么也要实现父类的所有功能,所以在子类的初始化方法中,必须首先调用父类的初始化方法,以实现父类相关资源的初始化。

例如我们在初始化狗这一对象时,必须先初始化哺乳动物这一对象,并把结果赋予狗,以使狗满足属于哺乳动物这一特征,[super init]selfsuper中调用init, super调用 superSuperinit。直到根类NSObject中的initNSObjectinit方法很简单,就是return self),根类中init负责初始化 内存区域 向里面添加 一些必要的属性,返回内存指针, 这样延着继承链初始化的内存指针被从上到下传递,在不同的子类中向块内存添加 子类必要的属性,直到当前类中得到内存指针,即self不为nil的情况下,就可以开始做子类的初始化。在if (slef){//添加当前类的属性 }

- (instancetype)init {
  //1.必须首先调用父类的初始化方法,以实现父类相关资源的初始化
  //2.必须判断 self 不为 nil,才可以开始做子类的初始化
    if (self = [super init]) {
        // Initialize self
    }
    return self;//3.返回当前对象的地址
}

5. selfsuper底层实现原理

当使用self关键字发送消息会被编译器转化为调用objc_msgSend函数:id objc_msgSend(id theReceiver, SEL theSelector, ...)。第 一个参数是消息接收者,第二个参数是调用的具体类方法的selector,后面是selector方法的可变参数。

[self setName:]为例,编译器会替换成调用objc_msgSend的函数调用,其中theReceiverselftheSelector@selector(setName:),这个selector是从当前selfclass的方法列表开始找的setName,当找到后把对应的selector 传递过去。

当使用super键字发送消息会被编译器转化为调用objc_msgSendSuper函数:id objc_msgSendSuper(struct objc_super *super, SEL op, ...),第二个参数还是类似上面的类方法的selector.第一个参数是个objc_super的结构体,

struct objc_super {
    id receiver;
    Class superClass;
 };

当编译器遇到[super setName:]时,开始做这几个事:

构建objc_super的结构体,此时这个结构体的第一个成员变量receiver就是当前类,和self相同。而第二个成员变量 superClass就是指父类调用objc_msgSendSuper的方法,将这个结构体和setNamesel传递过去。

函数里面在做的事情类似这样:从objc_super结构体指向的superClass的方法列表开始找setName的 selector,找到后再以 objc_super->receiver 去调用这个 selector