iOS之方法的慢速查找流程总结

134 阅读3分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

慢速查找流程:

  • 判断cls是否已注册
    • 已注册,继续代码流程
    • 未注册,在checkIsKnownClass函数中报错
  • 判断cls的实现
    • 实现类的isa走位和父类链
  • 判断cls的初始化
    • 准备rorw
    • 初始化类的父类及元类
    • 递归操作,初始化父类链中的所有类,直到NSObject的父类为nil
    • 目的:用于查找方法,当子类没有该方法,在父类中继续查找
  • 查找imp
    • 死循环,符合条件,通过gotobreak跳出循环
  • 共享缓存中查找
    • 由于多线程写入方法,此时可能会找到之前未缓存的方法
  • 当前类中查找
    • 在当前类的方法列表中查找,使用二分查找法
    • 找到imp,跳转done流程
  • 判断父类是否存在
    • 如果父类为空,imp赋值为forward_imp,使用break停止循环,进入动态方法决议流程
  • 在父类中查找imp
    • 此时curClassSuperclass
    • 执行父类的快速查找流程 
    • 在父类的缓存中查找,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,然后返回