我只是盯着玩Objective C(编写玩具iPhone应用程序),我很好奇用于发送消息的底层机制。我非常了解C ++中的虚函数通常是如何实现的,以及相对于静态或非虚方法调用的成本是多少,但我没有任何Obj-C背景知道如何发送消息。浏览我发现this松散的基准测试,它提到IMP缓存的消息比虚函数调用更快,而虚拟函数调用反过来比标准消息发送更快。
我不是要优化任何事情,只是更深入地了解消息的分派方式。
我知道其中一些问题可能是“依赖于实现的”,但实际上只有一个实现非常重要。
答案 0 :(得分:90)
如何调度Obj-C消息?
使用运行时的objc_msgSend()
函数调度Objective-C消息。在Apple docs中显示,该函数至少需要2个参数:
类的实例有一个isa
指针,它是指向其类对象的指针。每个对象中的方法选择器存储在类对象的“表”中,objc_msgSend()
函数跟随指向类对象的isa
指针,查找此表,并检查是否方法在类的表中。如果找不到它,它会在类的超类表中查找该方法。如果未找到,它将继续向上对象树,直到它找到方法或到达根对象(NSObject
)。此时,抛出异常。
实例方法指针如何被缓存,您(通常)可以通过阅读代码来判断消息是否会被缓存?
来自Apple的Messaging上的Objective-C运行时指南:
为了加快消息传递过程,运行时系统会在使用时缓存方法的选择器和地址。每个类都有一个单独的缓存,它可以包含继承方法的选择器以及类中定义的方法。在搜索调度表之前,消息传递例程首先检查接收对象类的高速缓存(理论上可能会再次使用一次使用的方法)。如果方法选择器位于缓存中,则消息传递仅比函数调用稍慢。一旦程序运行了足够长的时间来“预热”其缓存,它发送的几乎所有消息都会找到一个缓存的方法。随着程序的运行,缓存会动态增长以容纳新消息。
如上所述,一旦程序运行,缓存就开始发生,并且在程序运行足够长时间之后,大多数方法调用将通过缓存方法运行。正如它所说的那样,缓存是在使用方法时发生的,所以只有在使用它时才会缓存消息。
类方法与C函数(或C ++中的静态类方法)基本相同,还是对它们有更多的东西?
类对象以类似于类实例的方式处理方法发送。每个类对象都有一个对象,它在一个名为metaclass
的对象中存储自己的类方法。类对象有自己的isa
指向其元类对象的指针,该对象又具有超级元类对象,它可以从中继承类对象。对类方法的方法调度如下:
isa
指向元类对象的指针答案 1 :(得分:19)
我还在我的博客上的x86_64上通过objc_msgSend()的指令编写了一条指令,如果有人想深入探讨:
http://www.friday.com/bbum/2009/12/18/objc_msgsend-part-1-the-road-map/