消息查找流程

iOS中方法的查找分为两部分:

  • 快速查找,在缓存中直接找到发送的消息
  • 慢速查找,在缓存中如果没找到就到了慢速查找,先找当前类,再找父类以及父类的父类,如果找到就执行,如果找不到就执行动态方法解析和消息转发

1.代码分析方法查找流程

三者关系如图所示:


图片.png

子类中对象方法的层层调用

LGStudent *student = [[LGStudent alloc] init];
        // 对象方法
        // 自己有 - 返回自己
        [student sayHello];
        // 自己没有 - 老爸有 -
        [student sayNB];
        // 自己没有 - 老爸没有 - NSObject
        [student sayMaster];
        // 自己没有 - 老爸没有 - NSObject 没有
        // unrecognized selector sent to instance 0x103000450
       [student performSelector:@selector(saySomething)];

打印结果:


图片.png

结论:只要是继承关系,子类可以调用父类中的所有对象方法,如果一直追究到NSObject中也没有的对象方法saySomething,则会报出一个最经典最常见的错误信息:unrecognized selector sent to instance 0x100562f70

子类中类方法的层层调用

图片.png

打印结果:


图片.png

结论:同上,只不过这里论证的是类方法。即,几个类中不存在sayLove方法,所以会报经典错误。

子类调用NSObject中的对象方法

图片.png

结论:完全遵守isa的走位流程图(https://www.jianshu.com/p/cf18f3cc1306)

2.源码分析消息查找流程

主要涉及方法:_class_lookupMethodAndLoadCache3

图片.png
IMP lookUpImpOrForward(Class cls, SEL sel, id inst, bool initialize, bool cache, bool resolver) 

参数解读

  • cls 当前类
  • sel 当前方法的名字
  • inst 当前类对象
  • initialize 是否初始化
  • cache 是否缓存
  • resolver 是否在执行动态方法解析

2.如果缓存命中 直接返回imp


图片.png
if (cache) {
        imp = cache_getImp(cls, sel);
        if (imp) return imp;
    }

3.realizeClass 方法实现

static Class realizeClass(Class cls)
{
   runtimeLock.assertLocked();

   const class_ro_t *ro;  // 方法列表 属性列表 等的存储结构题
   class_rw_t *rw; // 存储 class_ro_t的结构题
   Class supercls; // 父类
   Class metacls; // 元类
   bool isMeta; // 当前类是否是元类
   ro = (const class_ro_t *)cls->data();  // 给ro赋值
   isMeta = ro->flags & RO_META; // 是否是元类判断
   supercls = realizeClass(remapClass(cls->superclass)); // 初始化父类
   metacls = realizeClass(remapClass(cls->ISA())); // 初始化元类
   cls->superclass = supercls; 找到当前类的 父类
   cls->initClassIsa(metacls); 初始化当前类的元类
   if (supercls) { // 如果有父类就把当前类关联到父类的子类列表中
       addSubclass(supercls, cls);
   } else {
       addRootClass(cls);
   }

4.如果类没有初始化就执行初始化 _class_initialize 的代码就不放了 主要作用就是 没有初始化的类进行初始化 如果有父类并且父类也没有初始化就连 父类也初始化掉 如果初始化完了就什么也不做 如果正在初始化 就等待

if (initialize  &&  !cls->isInitialized()) {
       runtimeLock.unlock();
       _class_initialize (_class_getNonMetaClass(cls, inst));
       runtimeLock.lock();
       // If sel == initialize, _class_initialize will send +initialize and 
       // then the messenger will send +initialize again after this 
       // procedure finishes. Of course, if this is not being called 
       // from the messenger then it won't happen. 2778172
   }

5.开始找方法和存储缓存

{
       Method meth = getMethodNoSuper_nolock(cls, sel); // 一个for循环 从方法列表中找方法
       if (meth) { // 如果找到方法 开始缓存 
           log_and_fill_cache(cls, meth->imp, sel, inst, cls); // 这里会调用 cache_fill 上一篇已经写过
           imp = meth->imp;
           goto done; //  找到直接返回
       }
   }

6.如果当前类没找到,就查找父类以及父类的父类,并且执行缓存,如果找到直接返回

for (Class curClass = cls->superclass;
            curClass != nil;
            curClass = curClass->superclass)

7.如果以上的过程都没有找到方法,那么就执行动态方法解析,也就是 if (resolver && !triedResolver) {这个判断体中的内容}

8. _class_resolveMethod 方法实现

void _class_resolveMethod(Class cls, SEL sel, id inst)
{
   // 如果不是元类 说明是类 此时类中已经没有方法 直接执行  _class_resolveInstanceMethod
   if (! cls->isMetaClass()) {
       // try [cls resolveInstanceMethod:sel]

       _class_resolveInstanceMethod(cls, sel, inst); //  执行 + (BOOL)resolveInstanceMethod:(SEL)sel方法
   }
   else { 
       // try [nonMetaClass resolveClassMethod:sel]
       // and [cls resolveInstanceMethod:sel]
       _class_resolveClassMethod(cls, sel, inst);
       if (!lookUpImpOrNil(cls, sel, inst, 
                           NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) 
       {
           _class_resolveInstanceMethod(cls, sel, inst); // 
       }
   }
}

上述代码当是元类的时候: cls 就是元类 inst 类对象 这时会做两次尝试 resolveClassMethod _class_resolveInstanceMethod

9.最后动态方法解析会走到 _class_resolveInstanceMethod, 如果类没有实现resolveInstanceMethod就直接返回 如果类实现了 resolveInstanceMethod 就发送 SEL_resolveInstanceMethod消息

static void _class_resolveInstanceMethod(Class cls, SEL sel, id inst)
{
    if (! lookUpImpOrNil(cls->ISA(), SEL_resolveInstanceMethod, cls, 
                         NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) 
    {
        // Resolver not implemented.
        return;
    }

    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    bool resolved = msg(cls, SEL_resolveInstanceMethod, sel);

    // Cache the result (good or bad) so the resolver doesn't fire next time.
    // +resolveInstanceMethod adds to self a.k.a. cls
    IMP imp = lookUpImpOrNil(cls, sel, inst, 
                             NO/*initialize*/, YES/*cache*/, NO/*resolver*/);
}


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

推荐阅读更多精彩内容