美文网首页
[OC]self与super调用本质分析

[OC]self与super调用本质分析

作者: 陌问MW | 来源:发表于2019-12-05 11:45 被阅读0次

一、self 基本说明

在日常开发中,我们经常使用到self关键字,比如,访问属性,调用实例方法等。那么self到底指的是什么?我们来看一下官方文档的解释和定义:

Returns the receiver. //返回接收器

在看一下NSObject中关于self的一些说法:

Rather than using [[XYZPerson alloc] init] in the class factory method, instead try using [[self alloc] init].

Using self in a class factory method means that you’re referring to the class itself.

在工厂方法中使用self意味着引用类本身,意思就是说self就是当前类,那么再来看一下源码中对self的定义,代码(1-1),如下:

+ (id)self {
    return (id)self;
}
//-----------------------------分割线--------------------------
- (id)self {
    return self;
}

可以看到self只是返回本身,也就是当前调用者。\color{red}{(PS:调用者并不一定是当前类,继续看下文可明白)}

因为self是消息机制中的第一个隐藏参数,在消息传递的过程中,本赋值为调用者,也就是消息接收者。第二个隐藏参数为_cmd,也就是SEL,调用的方法。

二、super 基本说明

super关键字,\color{red}{(PS:不知为何大部分文章中都说super不是关键字,关键字的概念就是保留字,很明显super是保留字)}也是我们经常看到和使用的。代码(2-1),如下:

- (void)viewDidLoad {
    [super viewDidLoad];
}
//-----------------------------分割线--------------------------
- (instancetype)init {
    self = [super init];
    if(self){
        //...
    }
    return self;
}

关于super的说明,本人并没有在官方文档中看到,如果大家有发现请关注本文末尾的公众号,告知本人,将不胜感激。只是在NSObject中有部分说明如下:

There’s another important 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.

意思是说,super是一个重要的关键字\color{red}{(这里也有说super是关键字,所以网上很多说法是错误的,大家要自己甄别!)},向super发送消息是调用继承链上的超类方法。super最常见的用法就是覆盖一个方法。

三、探究selfsuper

首先创建一个基于 Single View App的模板项目,添加PersonStudent类,Person类继承于NSObject类,Student类继承于Person类,以备后面使用。下面我们在项目默认创建的ViewController.m类中添加代码(3-1),如下:

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"self:%@", NSStringFromClass([self class]));
    NSLog(@"super:%@", NSStringFromClass([super class]));
}

查看输出结果如下:

demo1[2534:11854005] self:ViewController
demo1[2534:11854005] super:ViewController

我们发现selfsuper的输出同样是ViewController,也就是当前类。然后我们继续试验,在PersonStudent类中分别添加eat方法,代码(3-2),如下:

#import "Person.h"

@implementation Person

- (void)eat {
    NSLog(@"%@ eat!", self);
}

@end
//-----------------------------分割线--------------------------
#import "Student.h"

@implementation Student

- (void)eat {
    [super eat];
    NSLog(@"%@ eat!", self);
}

@end

然后在ViewController.mViewDidLoad方法中,创建Student类并调用eat方法,代码(3-3),如下:

Student * student = [[Student alloc] init];
[student eat];

运行,输出结果如下:

demo1[2962:11906123] <Student: 0x600003fc8100> eat!
demo1[2962:11906123] <Student: 0x600003fc8100> eat!

我们明明是在Student类中通过super调用的父类Person中的eat方法,为何Person中的eat方法输出的也是Student eat呢?关于这一部分的解释,网上有很多说明解释,总感觉不够详细彻底,下面我们来分析一下。

我们通过命令行将Student.m文件转成.cpp文件,,cd进入到Student.m文件所在的目录,使用命令如下:

xcrun -sdk iphonesimulator clang -rewrite-objc Student.m

会在当前文件夹中生成Student.cpp文件,打开找到以下代码(3-4),

static void _I_Student_eat(Student * self, SEL _cmd) {
    ((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Student"))}, sel_registerName("eat"));
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_fm_9d3dmfcj49598v7_hghf3qjc0000gn_T_Student_370c84_mi_0, self);
}

找到关于super调用的部分代码(3-5),如下:

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

我们发现,当super调用时,其实是调用的objc_msgSendSuper方法进行的消息发送,我们看下官方文档对于此方法的解释:

Sends a message with a simple return value to the superclass of an instance of a class.

向超类发送消息。

通过代码(3-5),可以得知,我们这里有两个参数,一个是结构体指针类型__rw_objc_super,一个是返回SEL类型方法编号的sel_registerName。而__rw_objc_super在我们编译的Student.cpp文件中可以找到原型,代码(3-6),如下:

struct __rw_objc_super { 
    struct objc_object *object; 
    struct objc_object *superClass; 
    __rw_objc_super(struct objc_object *o, struct objc_object *s) : object(o), superClass(s) {} 
};

结合官方文档对objc_msgSendSuper方法参数的说明我们得知,__rw_objc_super,也就是objc_super。官方文档对此参数的说明如下:

A pointer to an objc_super data structure. Pass values identifying the context the message was sent to, including the instance of the class that is to receive the message and the superclass at which to start searching for the method implementation.

指向objc_super的结构体指针,传递标识了上下文的消息,包含了消息接收者和要在超类中搜索的方法实现。我们再来看一下objc_super结构体的源码(3-7),如下:

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

结合我们编译的代码(3-5),我们得知,消息的接收者在这里传递的就是当前对象self,即Student类的实例对象,方法即eat

通过上面的分析我们得知,在我们使用super方法调用父类方法时,其实传递了隐藏参数。我们上面说过,self调用返回方法本身,并不一定是你看到的当前调用者。这里可以说明,当我们在父类中使用self时,因为是在子类中通过super方法调用的父类方法,在这过程中,通过隐藏结构体参数,消息接收者传递的是当前的self,也就是Student的实例,所以我们在父类中使用self时,其实是使用子类的实例在调用父类的方法,所以也就有了代码(3-1)和(3-3)的输出。

相关文章

网友评论

      本文标题:[OC]self与super调用本质分析

      本文链接:https://www.haomeiwen.com/subject/cmddgctx.html