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

[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: 通过与查找自身的类型, 并与自身进行对比
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容