小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
慢速查找流程:
- 判断
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,然后返回