消息传递机制
objc_msgSend函数
在OC语言里面,消息在运行时会绑定为(C语言)方法。编译器会转化一个消息表达式:
[receiver selector];
任何在消息中传递的参数也会被objc_msgSend函数处理:
objc_msgSend(receiver, selector, arg1, arg2, ...);
消息传递函数为动态绑定提供所有必要的内容:
- 首先,它找到选择器调用的过程(方法实现)。由于同一个方法在不同的类中可能有不同的实现,这个精确的调用过程依赖于接收者所属于的类。
- 然后,它会调用这个过程,传递接收者对象(一个指向其数据的指针),以及消息中定义的那些参数。
- 最后,它传递过程调用的返回值作为它自身的返回值。
消息传递函数所使用的关键字隐藏在编译器编译生成的每个OC类和对象的结构体中。每一个类结构都包含如下两个本质的元素:
- 一个指向父对象的指针。
- 一个类消息分发表。这个表拥有把方法选择器和类中方法的内存地址联系起来的入口。例如,setOrigin::方法的的选择器是和该方法setOrigin::实现的入口地址进行关联的,display方法的选择器是和display方法的地址关联的,等等。
一个新对象一旦被创建,就会被分配内存,并且他的实例变量会被初始化。这些对象变量中的第一个就是指向该对象对应的类结构的指针。这个指针,叫做isa,给予这个对象访问其类信息,然后通过这个类,访问所有它继承自类的类信息。
当一个消息被传递到一个对象,消息传递函数会沿着对象的isa指针找到其类结构以查找方法选择器以及消息分发表。如果不能找到方法选择器,消息传递函数回继续查找其父类的指针查找方法选择器以及消息分发表,这个过程知道查找到继承树的根为止。一旦查找到对应的选择器,这个函数就会调用分发表里对应的方法地址,传递飞接收者对象的数据结构。
为了加速这个消息调用的过程。运行时系统会缓存它们使用过的选择器以及起对应的方法地址。这些缓存是针对每个类单独缓存的,当然,这其中包含它自己定义的方法以及继承自父类的方法。在搜索分发表之前,消息传递路由会先检查接收消息接收类的缓存(基于如果干过一次就很可能还会干下一次的理论)。如果消息选择器存在在缓存中,消息传递只比直接的函数调用慢那么一点点。一旦一个程序已经跑了足够长的时间以“预热”他的缓存,几乎所有发送的消息就都可能被缓存了。缓存是根据程序运行时新消息的调用动态增长的。