ios 中 [self class] 和 [super clas

作者: 神的旨意 | 来源:发表于2018-02-12 15:09 被阅读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"))方式

    • 只是查找调用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

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

    相关文章

      网友评论

        本文标题:ios 中 [self class] 和 [super clas

        本文链接:https://www.haomeiwen.com/subject/qzsrtftx.html