美文网首页
05-探索方法的归属和isa的走向

05-探索方法的归属和isa的走向

作者: luin4 | 来源:发表于2020-09-18 10:43 被阅读0次

    [toc]

    探索1: 方法的归属

    通过上一节, 我们学习到了

    1. 通过lldb和内存地址, 从类以及元类里查找我们声明的(class_ro_t *)(成员变量列表), property_array_t(属性列表) ,method_array_t(方法列表), protocol_array_t(protocol_列表)
    2. isa和类继承关系走位
      那这一节我们主要来通过上节学到的知识点

    1: 探索class_getInstanceMethod

    我们先来看代码, 看完代码问题也就出现了, 他们分别会输出什么呢?
    接下来请大家带着目的跟着我一起探索思考吧:

    
    初始化类对象方式:
    1: Class pClass = LLMethodClass.class;
    or
    2: Class pClass = object_getClass(person);
    Class metaClass = objc_getMetaClass(className);
    // - (void)sayHello;
    // + (void)sayHappy;
    Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
    Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));
    
    Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
    Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));
    
    
    1. class_getInstanceMethod作用: 寻找实例方法
    图1: class_getInstanceMethod方法实现-c749
    1. 我们在objc源码中查看它的源码:


      源码-c545
    2. 这里我们分别对lookUpImpOrForward_class_getMethod进行进一步探索, 发现大部分都看不懂, 但有一个方法让我看到了一丝丝熟悉的地方, 请看下图:

      图2: getMethodNoSuper_nolock方法实现-c749

    图中标识的地方, 让我想到了 objc_class结构体, 以及我们上一节找到bits(平移32位内存)取到它里面存储的成员变量(ro), 属性(properties), 方法(methods)等等, 当然图只截了一小部分, 感兴趣的可以自己去看下:

    图3: objc_class结构体回顾-c346
    1. 那么联想到这里, 我们就能大概猜到, 当我们调用class_getInstanceMethod方法时, 它在源码里会根据传入的cls, 来查找它的结构体里有没有我们要获取的方法.
    2. 那我们在回过头来看class_getInstanceMethod(pClass, @selector(sayHello));这段代码, 就是要让我们根据cls类的bits里, 来查找是否有sel这个实例方法, 验证(平移cls内存地址获取bits, 打印methods)后, 发现是有这个方法的.
    3. Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));步骤5相同的验证方法, 发现元类里是没有sayHello方法的, 但是我们找到了sayHappy的方法, 说到这里要拓展一个知识点在源码的世界里, 只有实例方法, 是没有类方法这个说法的(OC层面才会有这些说法), 因为每个类每个元类, 他们都是对象, 他们的方法都存储在他们的父级里.
    4. 接下来继续从他的超类里查找, 也是没有的.
    5. 接下来, 我们也就知道method4和method5的结果, 在类里找类方法肯定没有, 在元类里找类方法, 那一找一个准.
    6. 最后我们来输出验证下:


      最终输出结果-c749

    2: 探索class_getClassMethod

    
    初始化类对象方式:
    1: Class pClass = LLMethodClass.class;
    or
    2: Class pClass = object_getClass(person);
    Class metaClass = objc_getMetaClass(className);
    // - (void)sayHello;
    // + (void)sayHappy;
    Method method1 = class_getClassMethod(pClass, @selector(sayHello));
    Method method2 = class_getClassMethod(metaClass, @selector(sayHello));
    
    Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
    Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
    
    
    1. 苹果官方文档说明


      -c563
    1. 顾名思义, 该方法肯定是从cls里查找是否有sel类方法,查找它的源码
      class_getClassMethod方法实现-c563

    这是说, 会从我们传进来的类的元类中查找元类的实例方法, 变相的也佐证了我们上面说过的一句话:在底层, 没有类方法, 都是实例方法.

    1. 这里有个坑点->cls->getMeta(), 我们来看它的实现:
      -c563

    这里是说当前cls是元类时, 直接返回自身, 否则会调用它的isa获取它的元类.
    这里是因为isa元类走向, 最终会指向根元类并一直循环指向根元类, 这么写就是为了防止死循环.

    1. 看到这里, 一步一步来分析, 很容易就得出结果:

    method1: 类 -> 元类 -> 元类(isMetaClass返回自身)中没有sayHello实例方法.
    method2: 元类-> 元类(isMetaClass返回自身)中没有sayHello实例方法.
    method3: 类 -> 元类 -> 元类(isMetaClass返回自身)中有sayHappy类方法
    method4: 元类 -> 元类(isMetaClass返回自身)中有sayHappy类方法

    1. 打印验证


      -c677

    3: 探索class_getMethodImplementation

    初始化类对象方式:
    1: Class pClass = LLMethodClass.class;
    or
    2: Class pClass = object_getClass(person);
    Class metaClass = objc_getMetaClass(className);
    // - (void)sayHello;
    // + (void)sayHappy;
    Method method1 = class_getMethodImplementation(pClass, @selector(sayHello));
    Method method2 = class_getMethodImplementation(metaClass, @selector(sayHello));
    
    Method method3 = class_getMethodImplementation(pClass, @selector(sayHappy));
    Method method4 = class_getMethodImplementation(metaClass, @selector(sayHappy));
    
    class_getMethodImplementation实现-c749
    ***********************************************************************
    * lookUpImpOrForward.
    * //标准的IMP查找。
    * The standard IMP lookup. 
    * //没有LOOKUP_INITIALIZE:试图避免+initialize(但有时失败)
    * Without LOOKUP_INITIALIZE: tries to avoid +initialize (but sometimes fails)
    * //没有LOOKUP_CACHE:跳过乐观解锁查找(但在其他地方使用缓存)
    * Without LOOKUP_CACHE: skips optimistic unlocked lookup (but uses cache elsewhere)
    * //大多数调用者应该使用LOOKUP_INITIALIZE和LOOKUP_CACHE
    * Most callers should use LOOKUP_INITIALIZE and LOOKUP_CACHE
    ****** //inst是cls的实例或子类,如果不知道,则为nil。
    ****** inst is an instance of cls or a subclass thereof, or nil if none is known. 
    * //如果cls是一个未初始化的元类,那么一个非nil inst会更快
    * If cls is an un-initialized metaclass then a non-nil inst is faster.
    * //可能返回_objc_msgForward_impcache。外用IMPs
    * May return _objc_msgForward_impcache. IMPs destined for external use 
    * //必须转换为_objc_msgForward或_objc_msgForward_stret。
    * must be converted to _objc_msgForward or _objc_msgForward_stret.
    * //如果你根本不想转发,使用LOOKUP_NIL
    * If you don't want forwarding at all, use LOOKUP_NIL.
    **********************************************************************/
    
    lookUpImpOrForward实现-c749
    1. class_getMethodImplementation实际是从cls中的cache_t缓存中查找sel的实现, 查找到返回imp的地址, 没有查找到就会返回一个msg_forward_name的地址(objc中没有,在llvm中查找到了msg_forward_name, 后续看不懂, 有时间再看)

    2. 看到这里, 一步一步来分析, 很容易就得出结果:

      method1: 类中有sayHello实例方法, 所以也查找到它的imp
      method2: 元类中没有sayHello实例方法, 返回msg_forward_name地址
      method3: 类中没有sayHappy类方法, 返回msg_forward_name地址
      method4: 元类有sayHappy类方法, 所以也查找到它的imp

    3. 打印验证

      class_getMethodImplementation验证-c747
      imp2imp3的地址是一样的, 都是消息转发后的imp实现, 具体实现的是, 暂不得而知.

    2: 探索isKindOfClassisMemberOfClass

    1: isKindOfClass探索

    isKindOfClass图示1-c600 isKindOfClass图示2-c600
    • 图示1两个方法都是伪实现,断点并不走这里, 全局搜索下, 只有这个实现, 断点在这里验证

    • 图示2是最终走向(类方法和实例方法), 断点会停到这里, 当环境为 OBJC2时进入断点处, 正常环境则会进入msg_send图示1.

    • 为什么会走到里: 因为编译时就确定的原因, 所以去llvm中查找, 发现和alloc的消息转发定义在了一起, so 骚操作, 你懂得...

    • 作用: 通过查找obj的元类, 并递归循环元类的superclass与otherClass进行对比

      该方法中, 需要注意isa也就是元类的继承关系, 是的元类也存在继承关系:
      元类 -> 根元类(metaClass) -> 根类(NSObject) -> nil

    isa走向-c600

    2: isKindOfClass探索

    isMemberOfClass图示-c382
    • 作用:
      • +isMemberOfClass: 通过与查找自身的元类, 并与自身进行对比
      • -isMemberOfClass: 通过与查找自身的类型, 并与自身进行对比

    相关文章

      网友评论

          本文标题:05-探索方法的归属和isa的走向

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