博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Runtime底层原理探究(二) --- 消息发送机制(慢速查找)
阅读量:6590 次
发布时间:2019-06-24

本文共 7795 字,大约阅读时间需要 25 分钟。

源码

/************************************************************************ lookUpImpOrForward.* The standard IMP lookup. * initialize==NO tries to avoid +initialize (but sometimes fails)* cache==NO skips optimistic unlocked lookup (but uses cache elsewhere)* Most callers should use initialize==YES and cache==YES.* inst is an instance of cls or a subclass thereof, or nil if none is known. *   If cls is an un-initialized metaclass then a non-nil inst is faster.* May return _objc_msgForward_impcache. IMPs destined for external use *   must be converted to _objc_msgForward or _objc_msgForward_stret.*   If you don't want forwarding at all, use lookUpImpOrNil() instead.**********************************************************************/IMP lookUpImpOrForward(Class cls, SEL sel, id inst,                        bool initialize, bool cache, bool resolver){    IMP imp = nil;    bool triedResolver = NO;    runtimeLock.assertUnlocked();    // Optimistic cache lookup    if (cache) { /// 如果有缓存则从缓存里取        imp = cache_getImp(cls, sel);        if (imp) return imp;    }    // runtimeLock is held during isRealized and isInitialized checking    // to prevent races against concurrent realization.    // runtimeLock is held during method search to make    // method-lookup + cache-fill atomic with respect to method addition.    // Otherwise, a category could be added but ignored indefinitely because    // the cache was re-filled with the old value after the cache flush on    // behalf of the category.    runtimeLock.read();    if (!cls->isRealized()) {        // Drop the read-lock and acquire the write-lock.        // realizeClass() checks isRealized() again to prevent        // a race while the lock is down.        runtimeLock.unlockRead();        runtimeLock.write();        realizeClass(cls);        runtimeLock.unlockWrite();        runtimeLock.read();    }    if (initialize  &&  !cls->isInitialized()) {        runtimeLock.unlockRead();        _class_initialize (_class_getNonMetaClass(cls, inst));        runtimeLock.read();        // 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    }     retry:        runtimeLock.assertReading();    // Try this class's cache.    /// 这里为啥又取了一次imp ///   /// 1. 保证并发  ///  2.remap(cls) -- 重映射    imp = cache_getImp(cls, sel);    if (imp) goto done;    // Try this class's method lists.    {        Method meth = getMethodNoSuper_nolock(cls, sel);        if (meth) {            log_and_fill_cache(cls, meth->imp, sel, inst, cls);            imp = meth->imp;            goto done;        }    }    // Try superclass caches and method lists.    {        unsigned attempts = unreasonableClassCount();        for (Class curClass = cls->superclass;             curClass != nil;             curClass = curClass->superclass)        {            // Halt if there is a cycle in the superclass chain.            if (--attempts == 0) {                _objc_fatal("Memory corruption in class list.");            }                        // Superclass cache.            imp = cache_getImp(curClass, sel);            if (imp) {                if (imp != (IMP)_objc_msgForward_impcache) {                    // Found the method in a superclass. Cache it in this class.                    log_and_fill_cache(cls, imp, sel, inst, curClass);                    goto done;                }                else {                    // Found a forward:: entry in a superclass.                    // Stop searching, but don't cache yet; call method                     // resolver for this class first.                    break;                }            }                        // Superclass method list.            Method meth = getMethodNoSuper_nolock(curClass, sel);            if (meth) {                log_and_fill_cache(cls, meth->imp, sel, inst, curClass);                imp = meth->imp;                goto done;            }        }    }        // No implementation found. Try method resolver once.    if (resolver  &&  !triedResolver) {        runtimeLock.unlockRead();        _class_resolveMethod(cls, sel, inst);        runtimeLock.read();        // Don't cache the result; we don't hold the lock so it may have         // changed already. Re-do the search from scratch instead.        triedResolver = YES;        goto retry;    }    // No implementation found, and method resolver didn't help.     // Use forwarding.    imp = (IMP)_objc_msgForward_impcache;    cache_fill(cls, sel, imp, inst); done:    runtimeLock.unlockRead();    return imp;}复制代码

lookUpImpOrForward

cache

首先声明了一个nil imp,然后判断cache 如果有则调用cache_ getImp,这个cache_ getimp是 是从缓存里去取并非递归,不会执行之前快速查找的代码(慢速查找绝对没有缓存,因为刚刚快速查找调用这个方法传进来的第二个参数是NO,第一个参数的意思是)

checkIsKnowClass

检查类是否是已知类,如果是未知的则抛异常。如果是已知类则判断是否已经实现,如果未实现则进行赋值,然后在判断是否已经初始化。如果类未初始化,对其进行初始化。如果这个消息是initialize,那么直接进行类的初始化

方法查找流程

此时他又执行cache_ getimp(之所以在此这么做1是因为防止并发进行资源抢夺,2是因为remap可能会已经持有就没必要浪费资源再去查找了),如果没有则会执行一个的过程

// Try this class's method lists.    {        Method meth = getMethodNoSuper_nolock(cls, sel);        if (meth) {            log_and_fill_cache(cls, meth->imp, sel, inst, cls);            imp = meth->imp;            goto done;        }    }复制代码

然后传入一个cls和sel,找到对应的imp如果存在则缓存后直接去done,如果没有找到的话则去父类查找

// Try superclass caches and method lists.    {        unsigned attempts = unreasonableClassCount();        for (Class curClass = cls->superclass;             curClass != nil;             curClass = curClass->superclass)        {            // Halt if there is a cycle in the superclass chain.            if (--attempts == 0) {                _objc_fatal("Memory corruption in class list.");            }                        // Superclass cache. ///查找父类方法缓存中取缓存            imp = cache_getImp(curClass, sel);            if (imp) {                if (imp != (IMP)_objc_msgForward_impcache) {                    // Found the method in a superclass. Cache it in this class.                    log_and_fill_cache(cls, imp, sel, inst, curClass);                    goto done;                }                else {                    // Found a forward:: entry in a superclass.                    // Stop searching, but don't cache yet; call method                     // resolver for this class first.                    break;                }            }                        // Superclass method list. ///娶不到的话查找父类方法列表            Method meth = getMethodNoSuper_nolock(curClass, sel);            if (meth) {                log_and_fill_cache(cls, meth->imp, sel, inst, curClass);                imp = meth->imp;                goto done;            }        }    }复制代码

查找父类循环条件是 父类不为nil,则一直进行循环,如果父类方法有缓存则取缓存,然后缓存到子类的方法中,如果父类方法没有缓存,则去方法列表里查找然后进行缓存赋值。

// No implementation found. Try method resolver once.    if (resolver  &&  !triedResolver) {        runtimeLock.unlockRead();        _class_resolveMethod(cls, sel, inst);        runtimeLock.read();        // Don't cache the result; we don't hold the lock so it may have         // changed already. Re-do the search from scratch instead.        triedResolver = YES;        goto retry;    }    // No implementation found, and method resolver didn't help.     // Use forwarding.    imp = (IMP)_objc_msgForward_impcache;    cache_fill(cls, sel, imp, inst);复制代码

如果没有发现实现则会进入。

ps: 2019年04月04日10:33:21 补充:

cache_getImp不会从缓存查找IMP,这个方法不会递归调用因为cache_getImp是一个

他掉用的GETIMP方法当调用CacheHit Checkmiss的时候并没有发送objc_msgSend_uncached

慢速查找总结

当消息发送没有通过查找时则会进入慢速查找,如果未初始化则进行类的初始化,首先查找当前类的方法列表通过Selector找到对应IMP,如果有则返回并缓存,没有的话进行下一步遍历父类直到nil如果父类里存在则返回父类方法。并缓存到当前类,如果都没有则判断reslover参数是否支持消息转发机制,如果支持则进入消息转发流程,如果不支持则抛出异常。

转载于:https://juejin.im/post/5ca1e4e951882543da334d14

你可能感兴趣的文章
在eclipse中如何搭建ssh框架
查看>>
idea文件折叠显示出来配置
查看>>
垃圾回收解析
查看>>
SQLSERVER中的非工作时间不得插入数据的触发器的实现
查看>>
如何写出兼容大部分浏览器的CSS 代码
查看>>
第二阶段冲刺第八天,6月7日。
查看>>
java的左移位(<<)和右移位(>>)和无符号右移(>>>)
查看>>
struts2 action 返回类型分析
查看>>
【原创】FPGA开发手记(三) PS/2键盘
查看>>
linux统计多个文件大小总和
查看>>
java基础-Eclipse开发工具介绍
查看>>
JS常见的字符串操作
查看>>
洛谷P1069 细胞分裂 数学
查看>>
JAVA中的编码分析
查看>>
查看源代码Source not found及在eclipse中配置jdk的src.zip源代码
查看>>
document.all用法
查看>>
uniGUI试用笔记(二)
查看>>
HOG特征-理解篇
查看>>
Microsoft.AlphaImageLoader滤镜解说
查看>>
extjs_02_grid(显示本地数据,显示跨域数据)
查看>>