一、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
只是返回本身,也就是当前调用者。
因为self
是消息机制中的第一个隐藏参数,在消息传递的过程中,本赋值为调用者,也就是消息接收者。第二个隐藏参数为_cmd
,也就是SEL
,调用的方法。
二、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 tosuper
is a way to call through to a method implementation defined by a superclass further up the inheritance chain. The most common use ofsuper
is when overriding a method.
意思是说,super
是一个重要的关键字,向
super
发送消息是调用继承链上的超类方法。super
最常见的用法就是覆盖一个方法。
三、探究self
和super
首先创建一个基于 Single View App的模板项目,添加Person
和Student
类,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
我们发现self
与super
的输出同样是ViewController
,也就是当前类。然后我们继续试验,在Person
和Student
类中分别添加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.m
的ViewDidLoad
方法中,创建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)的输出。
网友评论