当前位置: 首页 > wzjs >正文

先进网站重庆森林经典台词截图

先进网站,重庆森林经典台词截图,wordpress日文主题,wordpress设置插件目录 前言 方法的本质 向不同对象发送消息 发送实例方法 发送类方法 对象调用方法 实际执行是父类 向父类发送类方法 消息查找流程 开始查找 快速查找流程 慢速查找流程 动态方法决议 应用场景 优化方案 消息转发机制 快速转发流程 应用场景 慢速转发流程 应…

目录

前言

方法的本质

向不同对象发送消息

发送实例方法

发送类方法

对象调用方法 实际执行是父类

向父类发送类方法

消息查找流程

开始查找

快速查找流程

慢速查找流程

动态方法决议

应用场景

优化方案

消息转发机制

快速转发流程

应用场景

慢速转发流程

应用场景


前言

在OC底层中,方法的调用实质上是通过消息的发送实现的,这篇文章我们来看一看消息的发送是怎么样的

方法的本质

方法的本质就是通过objc_msgSend发送消息,有两个参数,第一个是id类型,表示消息接受者,第二个,表示方法编号。

向不同对象发送消息

发送实例方法

消息接收者是实例对象

发送类方法

本质上是向类对象发送消息

objc_getClass得到的是类对象

对象调用方法 实际执行是父类

Runtime中提供了一个接口处理这种情况:父类中实现了该方法,而子类没有实现该方法,子类对象调用方法,会执行父类中实现(符合继承的特性)

这个接口是objc_msgSendSuper,使用时还需要用到objc_super结构体,并给结构体赋值(receiver、super_class)

该结构体中receiver表示接收消息的实例对象,super_class表示父类类对象,根据这个赋值

可以看到这两种方式都是执行父类的实现,因此可以推断:方法调用首先在类中查找,如果找不到就到父类中查找

向父类发送类方法

上面向父类发送实例方法时,receiver表示实例对象,super_class表示父类类对象。而如果向父类发送类方法,reciever表示类对象,super_class表示父类元类对象

消息查找流程

消息查找的流程就是通过上层的sel发送消息objc_msgSend找到底层具体imp的实现的过程,objc_msgSend是用汇编写的而不是用C语言

开始查找

在开始objc_msgSend之后

  1. 首先会判断消息接受者是否为空,为空就直接返回

  2. 然后会判断是否为小对象,也就是是否为tagged_pointers

  3. 之后取对象中的isa存到寄存器p13中,根据isa进行mask地址偏移来得到对应的上级对象(类、元类)

取得了上级对象之后,就可以开始快速查找流程了,也就是在缓存中找imp的过程

快速查找流程

  1. 首先通过类的首地址偏移16字节找到cache的地址(cache离首地址16字节,isa占8字节,superclass占8字节),cache高16位存mask,低48位存buckets

  2. 然后从cache中分别取出buckets和mask,根据mask通过哈希算法算出哈希下标,根据哈希下标和bukets首地址来得到对应的bucket,bucket中存放着imp和sel

  3. 那么怎么确定找到的imp和sel就是要找的那个呢?主要是通过两层循环:

    1. 第一层循环:比较bucket中的sel和objc_msgSend中第二个参数_cmd是否相等:如果相等,就直接跳转到CacheHit,即缓存命中,返回imp;如果不相等,有三种情况:

      1. 一种是一直找不到,就直接跳转到CheckMiss,因为参数$0是normal,会跳转到__objc_msgSend_uncached,看英文就能明白意思就是没找到,这时就会进入慢速查找流程

      2. 第二种是如果获取到的bucket是第一个元素,那么就手动把它设置为最后一个元素,然后进行第二层循环

      3. 如果当前bucket不是第一个元素,那就继续当前的循环

    2. 第二层循环:和第一层循环基本相同,只是如果bucket还是等于buckets中第一个元素,就直接跳转到JumpMiss,此时也会跳转到没找到__objc_msgSend_uncached,进入慢速查找

慢速查找流程

慢速查找的过程分为汇编和C两个部分,这里我们不纠结汇编部分,汇编最后调用的是lookUpImpOrForward,这是一个C实现的函数

NEVER_INLINE
IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{// 定义的消息转发const IMP forward_imp = (IMP)_objc_msgForward_impcache;IMP imp = nil;Class curClass;
​runtimeLock.assertUnlocked();
​if (slowpath(!cls->isInitialized())) {// The first message sent to a class is often +new or +alloc, or +self// which goes through objc_opt_* or various optimized entry points.//// However, the class isn't realized/initialized yet at this point,// and the optimized entry points fall down through objc_msgSend,// which ends up here.//// We really want to avoid caching these, as it can cause IMP caches// to be made with a single entry forever.//// Note that this check is racy as several threads might try to// message a given class for the first time at the same time,// in which case we might cache anyway.behavior |= LOOKUP_NOCACHE;}
​// 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.lock();
​// We don't want people to be able to craft a binary blob that looks like// a class but really isn't one and do a CFI attack.//// To make these harder we want to make sure this is a class that was// either built into the binary or legitimately registered through// objc_duplicateClass, objc_initializeClassPair or objc_allocateClassPair.//判断是否是一个已知的类:判断当前类是否是已经被认可的类,即已经加载的类checkIsKnownClass(cls);
​//判断类是否实现,如果没有,需要先实现,此时的目的是为了确定父类链,方法后续的循环cls = realizeAndInitializeIfNeeded_locked(inst, cls, behavior & LOOKUP_INITIALIZE);// runtimeLock may have been dropped but is now locked againruntimeLock.assertLocked();curClass = cls;
​// The code used to lookup the class's cache again right after// we take the lock but for the vast majority of the cases// evidence shows this is a miss most of the time, hence a time loss.//// The only codepath calling into this without having performed some// kind of cache lookup is class_getInstanceMethod().//----查找类的缓存// unreasonableClassCount -- 表示类的迭代的上限//(猜测这里递归的原因是attempts在第一次循环时作了减一操作,然后再次循环时,仍在上限的范围内,所以可以继续递归)for (unsigned attempts = unreasonableClassCount();;) {if (curClass->cache.isConstantOptimizedCache(/* strict */true)) {
#if CONFIG_USE_PREOPT_CACHESimp = cache_getImp(curClass, sel);if (imp) goto done_unlock;curClass = curClass->cache.preoptFallbackClass();
#endif} else {// curClass method list.//---当前类方法列表(采用二分查找算法),如果找到,则返回,将方法缓存到cache中Method meth = getMethodNoSuper_nolock(curClass, sel);if (meth) {imp = meth->imp(false);goto done;}//当前类 = 当前类的父类,并判断父类是否为nilif (slowpath((curClass = curClass->getSuperclass()) == nil)) {// No implementation found, and method resolver didn't help.// Use forwarding.//--未找到方法实现,方法解析器也不行,使用转发imp = forward_imp;break;}}
​// Halt if there is a cycle in the superclass chain.// 如果父类链中存在循环,则停止if (slowpath(--attempts == 0)) {_objc_fatal("Memory corruption in class list.");}
​// Superclass cache.// --父类缓存imp = cache_getImp(curClass, sel);if (slowpath(imp == forward_imp)) {// Found a forward:: entry in a superclass.// Stop searching, but don't cache yet; call method// resolver for this class first.// 如果在父类中找到了forward,则停止查找,且不缓存,首先调用此类的方法解析器break;}if (fastpath(imp)) {// Found the method in a superclass. Cache it in this class.//如果在父类中,找到了此方法,将其存储到cache中goto done;}}
​// No implementation found. Try method resolver once.//没有找到方法实现,尝试一次方法解析
​if (slowpath(behavior & LOOKUP_RESOLVER)) {//动态方法决议的控制条件,表示流程只走一次behavior ^= LOOKUP_RESOLVER;return resolveMethod_locked(inst, sel, cls, behavior);}
​done:if (fastpath((behavior & LOOKUP_NOCACHE) == 0)) {
#if CONFIG_USE_PREOPT_CACHESwhile (cls->cache.isConstantOptimizedCache(/* strict */true)) {cls = cls->cache.preoptFallbackClass();}
#endif//存储到缓存log_and_fill_cache(cls, imp, sel, inst, curClass);}done_unlock://解锁runtimeLock.unlock();if (slowpath((behavior & LOOKUP_NIL) && imp == forward_imp)) {return nil;}return imp;
}

上面是慢速查找的源码,用自然语言来表述就是:

  1. 首先进行一次快速查找,也就是在cache缓存中查找,找到就直接返回imp,没找到就继续

  2. 先判断cls是否是已知类,如果不是就报错;再判断类是否实现,如果没实现需要先实现,这个时候实现的目的是为了确定它的父类链,ro以及rw等,方便之后数据读取和查找;还要判断是否初始化,没有就初始化

  3. 接下来进入for循环,沿着类或元类的继承链进行查找:

    1. 对于当前cls,在方法列表中使用二分查找进行查找,如果找到就进入cache写入流程并返回imp,如果没找到就返回nil

    2. 当前cls赋值为父类,如果父类为nil,imp = 消息转发,并终止递归,开始判断是否执行过动态方法解析

    3. 如果父类链中存在循环就报错

    4. 在父类中查找时,会先在父类缓存中查找,再在方法列表中查找

  4. 判断是否执行过动态方法解析,如果没有就执行动态方法解析,执行过一次的话就走消息转发流程

在二分查找过程中,如果找到的与key的value值相等,需要先排除分类方法

在进行完快速查找和慢速查找的流程之后,会进入动态方法决议和消息转发流程

动态方法决议

在查找流程没找到方法时,有一次机会补救就是动态方法决议,以实例方法为例,程序会走到resolveInstanceMethod方法:

用自然语言描述如下:

  1. 在发送resolveInstanceMethod消息前,先查找cls中有没有这个方法的实现,也就是通过lookUpImpOrNil方法进入lookUpImpOrForward慢速查找流程找这个方法:

    1. 如果没找到就直接返回

    2. 如果找到了就发送resolveInstanceMethod消息

  2. 再慢速查找实例方法的实现,又进行一次慢速查找

应用场景

使用动态方法决议可以解决一些方法未实现的报错,重写resolveInstanceMethod类方法并在其中将其指向其他方法的实现,比如有一个say666没实现,但是实现了sayMaster方法

类方法同理,将方法名改为resolveClassMethod即可

优化方案

在上面的场景中,我们需要对每一个类的方法进行重写,并且我们又知道慢速方法查找路径最后都会走到根类,因此我们可以为NSObjct添加分类来统一处理

消息转发机制

如果前面的过程都没找到该方法,那我也是没招了(bushi),那就会进行消息转发流程,消息转发流程分为快速转发和慢速转发,如果方法没有实现而崩溃报错,在崩溃之前会调用两遍动态方法决议,两遍快速转发,两遍慢速转发

快速转发流程

forwardingTargetForSelector在源码中只有声明,但是我们可以从帮助文档中看到有关于它的解释:

  • 该方法的返回对象是执行sel的新对象,也就是自己处理不了会将消息转发给别的对象进行相关方法的处理,但是不能返回self,否则会一直找不到

  • 该方法的效率较高,如果不实现,会走到forwardInvocation:方法进行处理

  • 底层会调用objc_msgSend(forwardingTarget, sel, ...);来实现消息的发送

  • 被转发消息的接受者参数、返回值等应和原方法相同

应用场景

比如TCJPerson没实现的方法,转发给实现了的TCJStudent

也可以直接调用父类的该方法,如果没找到的话会直接报错

慢速转发流程

methodSignatureForSelector慢速查找流程同样在帮助文档中寻找,可以发现forwardInvocationmethodSignatureForSelector必须同时存在

底层会通过方法签名生成一个NSInvocation,作为参数传递使用,接着查找可以响应NSInvocation中编码的消息的对象,找到后使用anInvocation将消息发送给该对象,并且anInvocation保存结果,运行时系统将提取结果并传递给原始发送者

应用场景

慢速转发的流程就是methodSignatureForSelector提供一个方法签名,然后forwardInvocation通过NSInvocation来实现消息的转发

无论在forwardInvocation方法中是否处理invocation事务,程序都不会崩溃

方法和消息的流程就到这里了,在上面的过程中你有没有注意到动态方法决议进行了两遍这个问题?它为什么会执行两遍呢?

其实第二次动态方法决议是在methodSignatureForSelectorforwardInvocation方法之间,是开始进行慢速消息转发之前再给的一次机会

http://www.dtcms.com/wzjs/322670.html

相关文章:

  • 海外网站推广方案短视频seo询盘系统
  • 东莞知名网站网页设计效果图及代码
  • 美国服务器购买网站关键词排名优化方法
  • 设计部联盟网站疫情最新消息
  • 安徽网站建设整体策划方案百度人工客服24小时电话
  • 山东淄博网站建设武汉做seo
  • 最早做网购的网站小红书seo是什么意思
  • 中国电信备案网站百度关键词首页排名
  • python 网站开发神器百度网盘资源分享
  • 凡科免费建站平台裤子seo标题优化关键词
  • 微博营销策划方案范文保定百度seo排名
  • 凡科网可以免费做网站吗北京seo课程培训
  • 大理网站建设独立站seo是什么意思
  • 旅游网站开发选题背景网页制作模板
  • 欧洲美女网站武汉网络推广网络营销
  • 自助建站网站的宣传手册网络营销是以什么为中心
  • ai网站大全小说排行榜
  • 南阳政府做网站推广吗网络优化大师
  • wps上怎么做网站点击分析表如何设置淘宝友情链接
  • 网站快速备案多少钱网络推广公司简介模板
  • 女生做网站开发武汉网络推广优化
  • 赣州网站建设优化服务东莞营销网站建设直播
  • wordpress怎样操作嘉兴新站seo外包
  • 香河做网站公司广告网站有哪些
  • 做最好的整站模板下载网站win7优化
  • 兴义哪有做网站外链屏蔽逐步解除
  • 邢台提供网站建设公司哪家好网络营销做得好的产品
  • 镇江网站关键词优化预订百度一下你就知道官网
  • 开发个网站开票名称是什么百度推广怎么看关键词排名
  • 营销型网站 案例百度新闻app