iOS[super class]和[self class]

作者: 大兵布莱恩特 | 来源:发表于2018-07-07 09:56 被阅读126次

    最近小编所在公司招 iOS 开发职位,小编也出了几道面试题考察下候选人的 iOS 开发水平,其中有一道题如下:

    @implementation Student : Person
    
    - (instancetype)init
    {
        self = [super init];
        if (self) {
        
        
            id obj1 = [self class];
            id obj2 = [super class];
            NSLog(@"%@",obj1);
            NSLog(@"%@",obj2);
    
        }
        return self;
    }
    @end
    

    大部分候选人回答的 [self class ]输出 Student , [Super class]输出 Person, 只有少部分候选人回答都是输出 Student ,当然至于为什么输出结果都是 Student, 很少有能回答出来的.

    接下来小编通过将Student.m 转换成 Cpp 文件,带大家一块去看看 [self class] 和 [super class] 背后究竟做了那些事情.

    通过终端讲Student.m 转成Student.cpp 文件


    image.png

    2 找到 Student 的 init 方法 分析代码

    static instancetype _I_Student_init(Student * self, SEL _cmd) {
     self = ((Student *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Student"))}, sel_registerName("init"));
     if (self) {
    
    
      id obj1 = ((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class"));
      id obj2 = ((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Student"))}, sel_registerName("class"));
      NSLog((NSString *)&__NSConstantStringImpl__var_folders_hj_pwgsq9614nb0vq4zd315tcx80000gn_T_Student_e7cbc1_mi_0,obj1);
      NSLog((NSString *)&__NSConstantStringImpl__var_folders_hj_pwgsq9614nb0vq4zd315tcx80000gn_T_Student_e7cbc1_mi_1,obj2);
    
     }
     return self;
    }
    
    

    由此可见 当我们调用[self class] 时候实际上编译器最终给我们转成
    objc_msgSend(self,@selector(class)) , 消息的接收者是当前所在类的实例对象 , 这个时候就会去 self 所在类 Student 去查找 class 方法 ,
    如果当前类 Student 没有 class 会向Student 父类 Person 类找 class 方法,
    如果 Person 类也没有找到 class 方法,最终会找到最顶级父类 NSObject 的 class 方法,
    最终找到 NSObject 的 class 方法 ,并调用了object_getClass(self) ,由于消息接收者是 self 当前类实例对象,
    所以最终 [self class]输出 Student

    那么为什么明明调用了 super 这个关键字 返回的[super class] 还是 Student 呢 ?

    通过上边代码可知 , [super class] 最终编译器转化成了 objc_msgSendSuper(struct objc_super *,SEL) ,其中

    /// Specifies the superclass of an instance. 
    struct objc_super {
        /// Specifies an instance of a class.
        __unsafe_unretained _Nonnull id receiver;
    
        /// Specifies the particular superclass of the instance to message. 
    #if !defined(__cplusplus)  &&  !__OBJC2__
        /* For compatibility with old objc-runtime.h header */
        __unsafe_unretained _Nonnull Class class;
    #else
        __unsafe_unretained _Nonnull Class super_class;
    #endif
        /* super_class is the first class to search */
    };
    #endif
    
    

    objc_super 是一个结构体,内部有一个 receiver 实例,和一 个 Class super_class,指向了当前类的父类 Class ,通过这个父类可以直接的从父类里边开始查找方法,由于消息接收者还是当前类的实例对象 self, 最终如果在父类中没有找到class 这个方法,会在 Person 类的父类 NSObject 中去查找 class 方法,由于 NSObject 的 class 方法,调用的是 object_getClass(self) ,所以最终消息接收者为 student 实例对象,所以返回的还是 Student .

    当然我们知道了[ super class] 最终编译后代码样子,也可以自己手动去调用 objc_msgSendSuper 方法

    
    - (instancetype)init
    {
        self = [super init];
        if (self) {
        
        
            id obj1 = [self class];
            id obj2 = [super class];
            NSLog(@"%@",obj1);
            NSLog(@"%@",obj2);
    
            struct objc_super1 {
                __unsafe_unretained id receiver;
                Class superClass;
            };
            struct objc_super1 obj_super = {self,class_getSuperclass(object_getClass(self))};
            id obj3 = objc_msgSendSuper(&obj_super,@selector(class));
    
        }
        return self;
    }
    
    

    obj3输出的结果和直接调用 [super class]结果是一模一样的,刚才我们假设的情况都是 super Person 没有 class 方法,如果 Person 重写了 class 方法呢,将会怎么样?

    image.png

    当我们在 Student init 方法中调用 [super class] 时候 ,它首先会到 Person 类中查找 class 方法 ,当它发现了 Person 实现了 class 方法,就会调用[person class] 方法, object_getClass 这个时候 object_getClass(self), 这个 self 是 Student 的实例对象,就是消息接收者,所以即使重写了 Person 的 class 方法 ,依然返回的还是 Student ,除非来个极端的 把 class 方法 实现 返回个 nil , 这样最终调用[super class]结果才会返回nil

    KVO巧妙的利用了子类 重写了 class 方法 ,让我们误以为 [person class] 还是当前类 Person ,而不是动态创建的子类 NSKVONotifily_Person 这个子类 , 就是让子类返回了父类的 Class ,所以调用 [person class] 返回的还是 Person 从而对开发者隐藏了 NSKVONotifily_Person子类的存在

    - (Class)class {
        return class_getSuperclass(object_getClass(self));
    }
    

    好了,我是大兵布莱恩特,欢迎加入博主技术交流群,iOS 开发交流群

    QQ20180712-0.png

    相关文章

      网友评论

      • cfced65b253f:你好!我们是程序员大咖旗下专注于程序员生态的公众号程序员大咖(微信号 CodePush)。我们很赞赏你的文章,希望能获得转载授权。授权后,你的文章将会在公众号程序员大咖、程序员共读、源码共读、Java编程精选、iOS开发等渠道发布。我们会注明来源和作者姓名。
        非常感谢~~~
        cfced65b253f:@大兵布莱恩特 非常感谢
      • SHyH5:这题目已经被考烂了
        大兵布莱恩特:@SHyH5 如果我是刚步入 iOS 开发 可能我不知道这道面试题最终会输出什么,但是已经有3年以上 iOS 开发经验了 所以不能再知其然不知其所以然 要不面试时候 总会被面试官说 感觉你什么都能答出了些 但是答的都不够深入 . 跟其他人回答的差不多 没有回答到点子上.
        SHyH5:@大兵布莱恩特 确实是,这么多年一直都在问这些!也蛮经典的
        大兵布莱恩特:啊 考察面试者对 super 的理解 objc_msgSend objc_msgSendSuper的理解 从我14年开始 iOS 时候开始 面试的都是内存管理 消息机制 多线程 这些 从来没觉得过时
      • Dimon_Hu:66666
      • 大兵布莱恩特:英语不是很好 有些单词拼写有误 敬请原谅

      本文标题:iOS[super class]和[self class]

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