Runtime底层原理探究(二) --- 消息发送机制(慢速查找)
源码
/***********************************************************************
* 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 lookupif (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
Runtime底层原理探究(二) --- 消息发送机制(慢速查找)相关推荐
- Runtime底层原理总结--反汇编分析消息转发
消息转发:发送一个消息,也就是sel查找imp,当没有找到imp,接下来进入动态方法解析,如果开发者并没有处理,会进入消息转发. 消息转发 前几篇文章介绍了Runtime底层原理和动态方法解析总结 , ...
- Runtime底层原理--Runtime简介、函数注释
Runtime官方文档介绍直通车 扩展:编译时 看到运行时就会想到编译时,编译时主要是将源代码翻译成可识别的机器语言,如果编译时类型检查等翻译过程中发现语法分析之类有错误会给出相应的提示.比如OC,s ...
- KVO-基本使用方法-底层原理探究-自定义KVO-对容器类的监听
书读百变,其义自见! 将KVO形式以代码实现呈现,通俗易懂,更容易掌握 :GitHub -链接如果失效请自动搜索:https://github.com/henusjj/KVO_base 代码中有详 ...
- oc 协议 回调 静态成员_OC底层原理探究:Category、关联对象和block本质
1.分类Category的使用 // 给MJPerson类添加分类 @interface MJPerson : NSObject - (void)run; @end@implementation MJ ...
- iOS底层原理探究 第一探. 事件传递和响应者链
一. 声明: 本文意在探讨, 也参考了几位大神的文章, 在最后我会把链接发出来, 如果有理解错误的地方, 请大神们指正哈! 二. 前言: 最近自己做项目的时候, 用到了UITabbarContro ...
- 服务端Skynet(二)——消息调度机制
服务端Skynet(二)--消息调度机制 文章目录 服务端Skynet(二)--消息调度机制 1.提前了解知识 1.1.互斥锁(mutex lock : **mut**ual **ex**clusio ...
- Unity 消息发送机制 解析
1.思考 消息发送机制,也可以叫做观察者设计模式(应该是这样的). 通俗易懂点讲,就是 一个物体发出消息,另外一个,或者几个物体可以同时接收到这一消息并作出各自不同的行为(反馈,处理). 那么,首先, ...
- Runtime底层原理--动态方法解析、消息转发源码分析
了解了Runtime函数含义,我们就可以直接使用Runtime的API了,那接下来继续探究Runtime的源码,经过源码分析来更加深刻的了解Runtime原理. 开发应用 都知道Runtime很重要, ...
- Kafka系列二——消息发送send方法源码分析
文章目录 一.send使用说明 1.1 客户端代码 1.2 ProducerRecord 二.发送过程 2.1 send 2.2 doSend关键代码 2.2.1 RecordAccumulator原 ...
最新文章
- 在Linux中安装SEP Client
- EOS智能合约授权限制和数据存储
- JAVA 程序执行进行计时,用于验证程序执行的时间
- linux这样去掉文件里高亮字体
- php传递数据给jquery,将值从php传递给jquery
- Spring Shell笔记-help方法及exit及其他方法
- webpack-dev-server 设置反向代理解决跨域问题
- android 圆形相机预览拍照_Android Camera2 Api 实现预览和拍照
- PRD之道:活用Axure绘制高质量的业务流程图
- Linux内核配置选项 (经典学习)
- springboot整合mybatis错误 Invalid bound statement (not found): 解决办法
- python的pyc反编译
- 金融去杠杆环境下,请聆听麦子金服财富投资者给出的答案
- 【GPT4】GPT4 官方报告解读
- 【效率工具】markdown文档自动同步到印象笔记evernote
- zbrush是什么软件
- 简单版的相似图片搜索原理
- 操作系统/LINUX/数据库/算法/设计模式/HR面试题集锦
- 固定收益证券读书笔记(一)
- 在 Kubernetes 上执行 GitHub Actions 流水线作业