ios|ios 中 [self class] 和 [super class] 区别

问题? 【ios|ios 中 [self class] 和 [super class] 区别】网上很多关于[self class][super class] 的讨论,讨论问题的焦点是为什么[self class][super class] 输出的结果是一样的,即都在Son类中输出的话,都输出Son。网上大多都很雷同,点都说到了但没有指明关键点,怎么自己一步一步的去找到问题的根源,只是填鸭式的告诉读者。感觉很...
解决方案(一步步找到问题) 第一步 创建两个类Person和Son(情景重现)

#import @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

第二步 我们可以看一下代码真实调用情况.... 看第二步之前先了解一下selfsuper的区别
  1. self 是当前方法的调用者,是方法的隐藏参数,方法的隐藏参数还有一个_cmd参数,可以在调试的时候看到。
    ios|ios 中 [self class] 和 [super class] 区别
    文章图片
    image.png
如果是类方法:代表当前类
如果是对象方法:代表当前类的对象
  1. super 是编译器指令
把上述代码写到一个文件中,命名为Parent.m文件,放到桌面的clang文件夹中

ios|ios 中 [self class] 和 [super class] 区别
文章图片
image.png
打开终端,执行命令
  1. 先cd到Parent.m目录中
  2. 在执行clang -rewrite-objc Parent.m命令
    ios|ios 中 [self class] 和 [super class] 区别
    文章图片
    image.png
    这样在clang目录中可以看到多了一个Parent.cpp文件,*.cpp文件是clang命令编译Parent.m文件的输出(相当于我们Xcode的编译操作)。
  3. 打开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
总结:
  1. [self method][super method] 的不同点是开始查找method的方法的起始位置不同.
  2. 最终都是执行objc_msgSend( (id)self, sel_registerName("class"))这个,只是本例中有一个特殊点即NSObject类的方法class需要一个参数,该参数就是调用这个方法的对象(即 self)。
-(Class)class { return object_getClass(self); //这个self是Son类的对象 }

可以写一个demo 加以验证,代码如下:
#import @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; }

上述代码验证了
  1. 最终都会执行objc_msgSend((id)self, sel_registerName("run")) 且self都是Son对象
  2. 查找的位置不同,[self run]执行了Son类的run方法并输出内容, [super run]执行了Parent类的run方法并输出内容
ios|ios 中 [self class] 和 [super class] 区别
文章图片
1518422017310.jpg 以上内容如果你感觉有问题的话,可以评论交流...

    推荐阅读