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