小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
慢速查找流程:
- 判断
cls
是否已注册- 已注册,继续代码流程
- 未注册,在
checkIsKnownClass
函数中报错
- 判断
cls
的实现- 实现类的
isa
走位和父类链
- 实现类的
- 判断
cls
的初始化- 准备
ro
和rw
表 - 初始化类的父类及元类
- 递归操作,初始化父类链中的所有类,直到
NSObject
的父类为nil
- 目的:用于查找方法,当子类没有该方法,在父类中继续查找
- 准备
- 查找
imp
- 死循环,符合条件,通过
goto
或break
跳出循环
- 死循环,符合条件,通过
- 共享缓存中查找
- 由于多线程写入方法,此时可能会找到之前未缓存的方法
- 当前类中查找
- 在当前类的方法列表中查找,使用二分查找法
- 找到
imp
,跳转done
流程
- 判断父类是否存在
- 如果父类为空,
imp
赋值为forward_imp
,使用break
停止循环,进入动态方法决议流程
- 如果父类为空,
- 在父类中查找
imp
- 此时
curClass
为Superclass
- 执行父类的快速查找流程
- 在父类的缓存中查找,
cache_getImp
由汇编代码实现 - 找到
imp
,如果是父类的forward_imp
,使用break
停止循环,进入动态方法决议流程。否则,跳转done
流程 - 未找到
imp
,遍历父类继续查找
- 此时
- 动态方法决议
- 当前类和父类中,都找不方法,进入动态方法决议流程
- 判断是否执行过方法动态决议
- 如果没有,执行方法动态决议
- 如果执行过一次方法动态决议,执行消息转发流程
done
流程- 找到
imp
,写入缓存,和cache_t::insert
形成闭环
- 找到
二分查找法:
- 查找过程:表中方法编号按升序排列,将表中间位置记录的方法编号与将要查找的方法编号比较,如果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,如果查找的方法编号大于中间位置记录的方法编号,则进一步查找后一子表,否则进一步查找前一子表。重复以上过程,直到找到满足条件的记录,此时查找成功。或直到子表不存在为止,此时查找不成功。
cache_getImp
:
- 慢速查找流程中,当前类的方法列表中,未找到
imp
,则会执行父类的快速查找流程,调用cache_getImp
,在父类的缓存中查找 cache_getImp
由汇编代码实现- 父类进入快速查找流程,传入的参数略有区别,不会进入
__objc_msgSend_uncached
流程 - 在
CacheLookup
中,未命中缓存,进入LGetImpMissDynamic
流程,将#0
赋值p0
寄存器,相当于返回nil
,然后回到lookUpImpOrForward
函数中,继续for
循环中的代码,进行父类的慢速查找流程 - 在
CacheHit
中,未命中缓存,进入流程9
,执行ret
返回0
。否则,进入AuthAndResignAsIMP
流程,拿到解码后的imp
,然后返回