经过上一篇的铺垫,现在真正进入消息的发送,即剖析objc_msgsend函数如何把id和SEL这两个参数变为函数指针并执行。
真正的任务从现在开始,为了将消息发送给正确的接收者,经过一下几个步骤:
第一步:检查Selector是否需要执行。比如当程序运行在有垃圾回收机制的环境中,将会忽略retain和release消息,例如桌面的MAC系统。
第二步:检查receiver是否为nil。不像其他语言,nil在objective-C中是完全合法的(这个对程序员比较好,至少我们省去了给一个对象发送消息前检查对象是否为空的操作)。如果receiver为空,则会将 selector也设置为空,并且直接返回到消息调用的地方。如果对象非空,就继续下一步。
第三步:接下来会根据SEL到当前类中查找对应的IMP,首先会在cache中检索它,如果找到了就根据函数指针跳转到这个函数执行,否则进行下一步。
第四步:检索当前类对象中的方法表(method list),如果找到了,加入cache中,并且就跳转到这个函数之行,否则进行下一步。
第五步:从父类中寻找,直到根类:NSObject类。找到了就将方法加入对应类的cache表中,如果仍为找到,则要进入后文介绍的内容:动态方法决议。
第六步:如果动态方法决议仍不能解决问题,只能进行最后一次尝试,进入消息转发流程。
第七步:如果还不行,去死吧。
在第一步执行之前,编译器会根据情况在objc_msgSend
, objc_msgSend_stret
, objc_msgSendSuper
, 或objc_msgSendSuper_stret
四个方法中选择一个来调用。如果消息是传递给超类,那么会调用名字带有”Super”的函数;如果消息返回值是数据结构而不是简单值时,那么会调用名字带有”stret”的函数。排列组合正好四个方法。值得一提的是在 i386 平台处理返回类型为浮点数的消息时,需要用到objc_msgSend_fpret
函数来进行处理,这是因为返回类型为浮点数的函数对应的 ABI(Application Binary Interface) 与返回整型的函数的 ABI 不兼容。此时objc_msgSend
不再适用,于是objc_msgSend_fpret
被派上用场,它会对浮点数寄存器做特殊处理。不过在 PPC 或 PPC64 平台是不需要麻烦它的。PS:有木有发现这些函数的命名规律哦?带“Super”的是消息传递给超类;“stret”可分为“st”+“ret”两部分,分别代表“struct”和“return”;“fpret”就是“fp”+“ret”,分别代表“floating-point”和“return”。
三,四,五步其过程就是先从id指向的类中找,找不到再从父类中找,一直找到NSobject。
到此我们绝大部分方法就可以确定,即消息SEL已经找到了正确的接收者,可以执行了。
如果此时还消息还没找到正确的接收者,那么就要启动六,七步中的动态决议过程和消息转发过程。Cache中寻找方法和这两个过程在下一篇中讲。