【iOS】objc_msgSend
Objective-C 中对象调用方法叫做“传递消息”,给某对象发送消息相当于在该对象上调用消息方法。消息有“名称(name)”和“selector”,可以传入参数,也可以有返回值。
首先介绍几个概念:
绑定
绑定是方法的调用与所在的类关联起来。绑定分为静态绑定和动态绑定或称为前期绑定和后期绑定。
静态绑定
前期绑定 在程序执行前方法已被绑定,在编译期就能决定运行时所应调用的函数。
动态绑定
后期绑定 在运行时根据具体对象进行绑定。
动态绑定过程:1,虚拟机提取对象的实际类型的方法表 2,虚拟机搜索方法名;3,调用方法。
在 Objective-C 中,如果向某对象传递消息,那就会使用动态绑定机制来决定需要调用的方法,对象收到消息之后,究竟调用哪个方法完全于运行期决定,甚至可以在程序运行时改变,所以使得Objective-C成为一门真正的动态语言。
消息传递机制中的核心函数:
void objc_msgSend(id self,SEL cmd,...)
第一个参数代表接受者,第二个参数代表选择子(SEL),后续参数就是消息中的那些参数。
给对象发送消息:
id returnValue = objc_msgSend(someObject,@selector(messageName:),parameter);
objc_msgSend函数根据接受者和选择子(SEL)类型调用合适的方法。
在接受者所属的类中搜寻其“方法列表”,若有名称相符的方法就跳转实现代码,若无则沿着继承体继续上找找到合适的方法后跳转,如果最终未找到就执行消息转发操作。
如上所述想要掉一个方法要执行很多步骤,幸好objc_msgsend会将匹配到的方法存入“快速映射表”里,每个类都有一块这样的缓存。
特殊消息的调用
-
objc-msgSend_stret
待发送的消息返回为结构体 -
objc_msgSend_fpret
消息返回的是浮点型。 -
obj_msgSendSuper
给父类发消息[super message: parmeter];
objc_msgSend等函数一旦找到对应方法就会“跳转过去”执行方法,之所以这样做因为OC对象的每个方法都能看做简单的C方法,原形如下:
<return_type> Class_selector(id self,SEL _cmd ,...)
真正的函数名可能与上面不一样,此处用类名(class)与选择子(selector)命名是为了解释其工作原理。每个类里都有一个表格,其中的指针都会指向这中函数,选择子的名称则是查表时所用的“key”,objc_msgSend函数正是通过这种表格寻找应该执行的方法并跳转到其实现的。注:原形和objc_msgSend函数很相似,绝非巧合是为了利用“尾调用优化”,使得“跳转”这一步更简单些。
尾调用优化
如果函数的最后一项操作仅仅是调用另外一个函数,而无返回值时,就可执行尾调用优化技术。执行前一个函数后编译器会生成调转至另一个函数的指令码,而不会像调用堆栈中推入新的“栈帧”。若不这样做每次调用OC方法前,都需为调用objc_msgSend函数准备“栈帧”,会容易出现“栈溢出”现象。
总结要点
- 消息由接受者、选择子及参数构成,给某个对象发送消息就相当于在该对象上调用方法。
- 发给某对象的全部消息都由“动态消息派发系统”来处理,该系统会先找出该对象实际类型的方法表,通过选择子的名字在方法表中找到对应方法,并执行。