引出问题
现在我们创建两个类:Parent和Son。
#import <Foundation/Foundation.h>
@interface Parent : NSObject
@end
@implementation Parent
@end
@interface Son : Parent
@end
@implementation Son
- (instancetype)init{
if (self = [super init]) {
NSLog(@"self: %@",[self class]);
NSLog(@"super: %@",[super class]);
}
return self;
}
@end
当在外界调用Son类的init后,输出如下:
2019-11-12 11:11:19.984281 testProject[434:111502] self: Son
2019-11-12 11:11:19.984387 testProject[434:111502] super: Son
按照我们的常规理解,super对应的应该打印Parent啊,为啥最后打印的是Son呢?
代码的真实调用情况
先来了解一下self和super的区别
1,self是当前方法的调用者,它是方法的隐藏参数。方法的隐藏参数还有一个,是_cmd参数,我们可以在调试的时候看到。
如果是在实例方法中,self代表的是当前类的实例对象;
如果是在类方法中,self代表的就是当前的类对象。
2,super是编译器指令。
接下来,我们将上述代码写在一个文件中,命名为Person.m文件,放在桌面的clang文件夹中。
打开终端,cd到Person.m目录(即clang文件夹)下,然后执行clang -rewrite-objc Person.m命令,这样在clang目录中可以看到多了一个Person.cpp文件。Person.cpp是clang命令编译Person.m文件的输出(相当于我们Xcode的编译操作)。
打开Person.cpp文件,我们看到有两段代码是需要我们关注的:
......省略的代码......
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) {}
};
......省略的代码......
//这个地方就是我们Son类的init方法
static instancetype _I_Son_init(Son * self, SEL _cmd) {
if (self = ((Son *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Son"))}, sel_registerName("init"))) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_fz_rpw560ws5gxgsnqc6l8c6mqh0000gn_T_Parent_12a896_mi_0,((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class")));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_fz_rpw560ws5gxgsnqc6l8c6mqh0000gn_T_Parent_12a896_mi_1,((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Son"))}, sel_registerName("class")));
}
return self;
}
......省略的代码......
接下来我们来分析一下这段代码
我们只看[self class]和[super class]相关的代码:
// [self class] 等价于下面代码
((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class"))
// [super class] 等价于下面代码
((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Son"))}, sel_registerName("class"))
// [self class] 和 [super class] 代码声明区别如下:
//[self class]
objc_msgSend(id, SEL)
//[super class]
objc_msgSendSuper(__rw_objc_super *, SEL)
解释一下区别:
1. [self class]代码底层是objc_msgSend(id, SEL),
[super class]代码底层是objc_msgSendSuper(__rw_objc_super *, SEL)
2. SEL 是方法选择器 都是 - (Class)class 方法
3. 它们的方法名字不同
4. 第一个参数不同,objc_msgSend的第一个参数是id,
objc_msgSendSuper的第一个参数是 __rw_objc_super 是一个结构体,如下:
struct __rw_objc_super {
struct objc_object *object; //当前对象,是self即Son类
struct objc_object *superClass; //当前对象的父类对象,即Parent
__rw_objc_super(struct objc_object *o, struct objc_object *s) : object(o), superClass(s) {}
};
// [self class] 和 [super class] 代码实现区别如下
//[self class]
objc_msgSend(
(id)self, //当前类,即Son类
sel_registerName("class")//从self类中开始查找class方法
)
//[super class]
objc_msgSendSuper(
(__rw_objc_super){
(id)self, //还是这个self,这个和上面objc_msgSend中的self是一样的,都是Son这个类
(id)class_getSuperclass(objc_getClass("Son"))//从class_getSuperclass(objc_getClass("Son")类(即Parent类)中开始查找class方法
},
sel_registerName("class")//查找的方法
)
从上述分析可以看出,[self class]和[super class]的区别就是objc_msgSend(id, SEL)和objc_msgSendSuper(__rw_objc_super *, SEL)的区别:
所以,二者只是查找class的起始位置不同,其最终调用方式是完全一致的,这是第一个关键点。
上面我们已经知道了,[self class]和[super class]的区别在于二者对class方法的查找起始位置不同,接下来我们就来聊聊他们的具体查找流程。
首先你要知道Son继承自Parent,Parent继承自NSObject。
[self class]调用Son类中的class方法,找到class方法后调用即可;
如果没有找到class方法,就开始向父类即Parent类中查找;
如果还没有找到的话,开始向NSObject类中找。
当然,class方法在Son类中是没有实现的,所以此处调用的是NSObject类中的class方法
因此,class的查找顺序是Son -> Parent -> NSObject 中的class方法
[super class]调用Parent类中的class方法,找到class方法后调用即可;
如果没有找到class方法,就开始向父类即NSObject类中查找。
当然,class方法在Parent类也没有实现,所以此处调用的也是NSObject类中的class方法
因此,class的查找顺序是 Parent -> NSObject 中的class方法
那么现在问题来了,既然都是在NSObject中找到的class方法,那么为什么输出的都是Son呢?且听我慢慢分析。
在NSObject中,class方法的实现是这样的:
-(Class)class {
return object_getClass(self); //这个self是Son类的对象
}
[self class]和[super class]最终都是执行objc_msgSend((id)self, sel_registerName("class")),也就是说,他们最终都是给self发送一个名为class的消息。
也就是说,二者最终都会执行到这个object_getClass(self)函数,而这个函数中的参数self都是指的Son类的实例对象,因此,[self class]和[super class]都是指的Son类。
总结
1,[self method]和[super method]的不同点是开始查找method方法的起始位置不同;
2,它们最终都是执行objc_msgSend((id)self, sel_registerName("method")),只是本例中有一个特殊点,在NSObject类的class方法中需要一个参数self,这个self就是调用这个方法的对象。
-(Class)class {
return object_getClass(self); //这个self是Son类的对象
}
Demo
#import <Foundation/Foundation.h>
@interface Parent : NSObject
- (void)run;
@end
@implementation Parent
- (void)run{
NSLog(@"Parent: run,当前self是:%@", NSStringFromClass([self class]));
}
@end
@interface Son : Parent
- (void)run;
@end
@implementation Son
- (void)run{
NSLog(@"Son: run,当前self是:%@", NSStringFromClass([self class]));
}
- (instancetype)init{
if (self = [super init]) {
[self run];
[super run];
}
return self;
}
@end
最终输出结果:
2018-02-12 14:56:23.916168 testProject[451:136266] Son: run,当前self是:Son
2018-02-12 14:56:23.916250 testProject[451:136266] Parent: run,当前self是:Son
对上述代码执行clang命令,可以看到编译后的源码:
// Parent.m 类的run编译
static void _I_Parent_run(Parent * self, SEL _cmd) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_fz_rpw560ws5gxgsnqc6l8c6mqh0000gn_T_Parent_e1bd12_mi_0, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class"))));
}
// Son.m 类的run编译
static void _I_Son_run(Son * self, SEL _cmd) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_fz_rpw560ws5gxgsnqc6l8c6mqh0000gn_T_Parent_e1bd12_mi_1, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class"))));
}
// Son.m 类的init编译
static instancetype _I_Son_init(Son * self, SEL _cmd) {
if (self = ((Son *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Son"))}, sel_registerName("init"))) {
((void (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("run"));
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Son"))}, sel_registerName("run"));
}
return self;
}
下面我们来分析。
[self run]最终会找到Son中的run方法,执行下述代码:
NSLog(@"Son: run,当前self是:%@", NSStringFromClass([self class]));
这里的self指的是Son的实例对象,没有异议,所以说这里的[self class]就是Son,没有问题。
[super run]编译之后是这样的:
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Son"))}, sel_registerName("run"));
也就是说,它不是通过调用objc_msgSend给对象发送消息,而是调用了objc_msgSendSuper:
objc_msgSendSuper((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Son"))}, sel_registerName("run"));
objc_msgSendSuper的第一个参数是 __rw_objc_super 是一个结构体,如下:
struct __rw_objc_super {
struct objc_object *object; //当前对象,是self即Son类
struct objc_object *superClass; //当前对象的父类对象,即Parent
__rw_objc_super(struct objc_object *o, struct objc_object *s) : object(o), superClass(s) {}
};
objc_msgSendSuper的作用就是定义查找run方法的起始位置在Son类的父类Parent类中,而其最终还是会调用到objc_msgSend((id)self, sel_registerName("run"));。这里的self就是objc_msgSendSuper的第一个参数__rw_objc_super结构体中的object,它表示的是当前方法的调用者。
也就是说,[super run]最终会执行objc_msgSend((id)self, sel_registerName("run"));,这里的self就是run方法的真正调用者,即run消息的真正接收者,也就是Son的实例对象自身。
所以在Parent类的run方法里面:
NSLog(@"Parent: run,当前self是:%@", NSStringFromClass([self class]));
这个self就是Son的实例对象,因此这里的[self class]表示的就是Son。
以上。