iOS刨根问底栏目组iOS 深入理解

ios 中 [self class] 和 [super clas

2018-02-12  本文已影响15人  神的旨意

问题?

网上很多关于[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
第二步 我们可以看一下代码真实调用情况....

看第二步之前先了解一下selfsuper的区别

  1. self 是当前方法的调用者,是方法的隐藏参数,方法的隐藏参数还有一个_cmd参数,可以在调试的时候看到。
    image.png

如果是类方法:代表当前类
如果是对象方法:代表当前类的对象

  1. super 是编译器指令

把上述代码写到一个文件中,命名为Parent.m文件,放到桌面的clang文件夹中


image.png

打开终端,执行命令

  1. 先cd到Parent.m目录中
  2. 在执行clang -rewrite-objc Parent.m命令
    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"))方式


说的在明白一点就是:
首先你要知道 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 <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;
}

上述代码验证了

  1. 最终都会执行objc_msgSend((id)self, sel_registerName("run")) 且self都是Son对象
  2. 查找的位置不同,[self run]执行了Son类的run方法并输出内容, [super run]执行了Parent类的run方法并输出内容
1518422017310.jpg

以上内容如果你感觉有问题的话,可以评论交流...

上一篇下一篇

猜你喜欢

热点阅读