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

2020-09-18  本文已影响0人  luin4

[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 isa走向-c600

2: isKindOfClass探索

isMemberOfClass图示-c382
上一篇下一篇

猜你喜欢

热点阅读