阅读 254

iOS小知识之方法的本质,SEL、IMP及两者关系

方法的本质

方法的本质其实就是objc_msgSend消息发送,objc_msgSend的参数有

  • 消息接收者

  • 消息主体(SEL + 参数)

我们可以通过下面的方法验证,在main函数中,写入以下代码:

LGPerson *person = [LGPerson alloc]; 
[person sayNB]; 
[person say:@"NB"];复制代码

生成cpp文件,可以看到底层代码为

LGPerson *person = ((LGPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LGPerson"), sel_registerName("alloc")); 
((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("sayNB")); 
((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)person, sel_registerName("say:"), (NSString *)&__NSConstantStringImpl__var_folders_jl_d06jlfkj2ws74_5g45kms07m0000gn_T_main_d8842a_mi_3);复制代码
  • sel_registerName为@selector()的底层实现

  • 如果子类中没有方法,会向父类查找即调用objc_msgSendSuper,向父类发送消息

发送消息的主要流程如下:

  • 快速查找:objc_msgSend查找cache_t缓存消息

  • 慢速查找:递归自己和父类查找方法lookUpImpOrForward

  • 查找不到消息,进行动态方法解析:resolveInstanceMethod

  • 消息快速转发:forwardingTargetForSeletor

  • 消息慢速转发:消息签名methodSignatureForSelector和分发forwardInvocation

  • 最终仍为找到消息:程序crash,报经典错误消息unrecognized selector sent to instance xxx

SEL、IMP及两者关系

  • SEL是方法编号,即方法名称,在dyld加载镜像时,通过read_image方法加载到内存的表中了。

  • IMP是函数实现指针,找IMP就是找函数的过程

  • 两者关系:sel相当于书本的目录标题,imp就是书本的页码。查找具体的函数就是想看这本书里面具体篇章的内容:

    • 我们首先要找到想看的标题们也就是title-sel

    • 然后根据目录找到对应的页码-imp

    • 找到具体的内容-方法的具体实现

扩展

方法的快速查找流程是用汇编实现的,原因在于

  • 快速查找流程,即:方法缓存查找,目的就是提升效率。使用汇编代码最接近机器语言,可以最大程度优化存储空间与执行时间

  • 汇编代码对于动态参数、可变参数有更好的支持

二分查找法:

  • 查找过程:表中方法编号按升序排列,将表中间位置记录的方法编号与将要查找的方法编号比较,如果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,如果查找的方法编号大于中间位置记录的方法编号,则进一步查找后一子表,否则进一步查找前一子表。重复以上过程,直到找到满足条件的记录,此时查找成功。或直到子表不存在为止,此时查找不成功。


作者:叶秋主
链接:https://juejin.cn/post/7020667539155845156


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐