问题?
网上很多关于[self class]
和 [super class]
的讨论,讨论问题的焦点是为什么[self class]
和 [super class]
输出的结果是一样的,即都在Son类中输出的话,都输出Son
。网上大多都很雷同,点都说到了但没有指明关键点,怎么自己一步一步的去找到问题的根源,只是填鸭式的告诉读者。感觉很😭...
解决方案(一步步找到问题)
第一步 创建两个类Person和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
方法后,输出如下
2018-02-12 11:11:19.984281 testProject[434:111502] self: Son
2018-02-12 11:11:19.984387 testProject[434:111502] super: Son
第二步 我们可以看一下代码真实调用情况....
看第二步之前先了解一下self
和super
的区别
-
self
是当前方法的调用者,是方法的隐藏参数,方法的隐藏参数还有一个_cmd
参数,可以在调试的时候看到。
如果是类方法:代表当前类
如果是对象方法:代表当前类的对象
-
super
是编译器指令
把上述代码写到一个文件中,命名为Parent.m文件,放到桌面的clang文件夹中
打开终端,执行命令
- 先cd到
Parent.m
目录中 - 在执行
clang -rewrite-objc Parent.m
命令
这样在clang目录中可以看到多了一个Parent.cpp
文件,*.cpp文件是clang命令编译Parent.m
文件的输出(相当于我们Xcode的编译操作)。 - 打开
Parent.cpp
文件,我们看到有2段代码是我们关注的
......省略的代码......
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")类(即Person类)中开始查找class方法
},
sel_registerName("class")//查找的方法
)
从上述分析中可以看出
[self class] 和 [super class] 的区别也就是
objc_msgSend(id, SEL) 和 objc_msgSendSuper(__rw_objc_super *, SEL)的区别:
a. objc_msgSend查找class方法的起始位置是当前类即Son。
b. objc_msgSendSuper查找class方法的起始位置是class_getSuperclass(objc_getClass("Son"))即Person类,
最终objc_msgSendSuper也会转为objc_msgSend( (id)self, sel_registerName("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中找到的方法,那为什么输出的都是Son呢?
网上的其他文章看了半天还是不明白为什么输出的都是Son,其他文章只说了上面的一个关键点即【只是他们查找方法的起始位置不同,最终调用方式是一样的】
最终都是执行objc_msgSend( (id)self, sel_registerName("class"))
还有一个关键点在NSObject的class的方法的实现,如下
-(Class)class {
return object_getClass(self); //这个self是Son类的对象
}
[self class]
和 [super class]
最终都执行
objc_msgSend( (id)self, sel_registerName("class")) 发送消息
其中self对象都是(即Son类),NSObject中的class方法需要一个参数self,而此时的self就是Son类,
所以[self class]
和 [super class]
输出的都是Son
总结:
-
[self method]
和[super method]
的不同点是开始查找method的方法的起始位置不同. - 最终都是执行
objc_msgSend( (id)self, sel_registerName("class"))
这个,只是本例中有一个特殊点即NSObject类的方法class需要一个参数,该参数就是调用这个方法的对象(即 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;
}
上述代码验证了
- 最终都会执行objc_msgSend((id)self, sel_registerName("run")) 且self都是Son对象
- 查找的位置不同,
[self run]
执行了Son类的run方法并输出内容,[super run]
执行了Parent类的run方法并输出内容
以上内容如果你感觉有问题的话,可以评论交流...