Objective C消息调度机制

时间:2009-06-11 16:21:20

标签: objective-c performance

我只是盯着玩Objective C(编写玩具iPhone应用程序),我很好奇用于发送消息的底层机制。我非常了解C ++中的虚函数通常是如何实现的,以及相对于静态或非虚方法调用的成本是多少,但我没有任何Obj-C背景知道如何发送消息。浏览我发现this松散的基准测试,它提到IMP缓存的消息比虚函数调用更快,而虚拟函数调用反过来比标准消息发送更快。

我不是要优化任何事情,只是更深入地了解消息的分派方式。

  • 如何调度Obj-C消息?
  • 实例方法指针如何被缓存,您(通常)可以通过阅读代码来判断消息是否会被缓存?
  • 类方法与C函数(或C ++中的静态类方法)基本相同,还是有更多的东西?

我知道其中一些问题可能是“依赖于实现的”,但实际上只有一个实现非常重要。

2 个答案:

答案 0 :(得分:90)

如何调度Obj-C消息?

使用运行时的objc_msgSend()函数调度Objective-C消息。在Apple docs中显示,该函数至少需要2个参数:

  1. 接收对象
  2. 消息的选择器
  3. [正在发送的消息的参数的变量列表。]
  4. 类的实例有一个isa指针,它是指向其类对象的指针。每个对象中的方法选择器存储在类对象的“表”中,objc_msgSend()函数跟随指向类对象的isa指针,查找此表,并检查是否方法在类的表中。如果找不到它,它会在类的超类表中查找该方法。如果未找到,它将继续向上对象树,直到它找到方法或到达根对象(NSObject)。此时,抛出异常。

    实例方法指针如何被缓存,您(通常)可以通过阅读代码来判断消息是否会被缓存?

    来自Apple的Messaging上的Objective-C运行时指南:

      

    为了加快消息传递过程,运行时系统会在使用时缓存方法的选择器和地址。每个类都有一个单独的缓存,它可以包含继承方法的选择器以及类中定义的方法。在搜索调度表之前,消息传递例程首先检查接收对象类的高速缓存(理论上可能会再次使用一次使用的方法)。如果方法选择器位于缓存中,则消息传递仅比函数调用稍慢。一旦程序运行了足够长的时间来“预热”其缓存,它发送的几乎所有消息都会找到一个缓存的方法。随着程序的运行,缓存会动态增长以容纳新消息。

    如上所述,一旦程序运行,缓存就开始发生,并且在程序运行足够长时间之后,大多数方法调用将通过缓存方法运行。正如它所说的那样,缓存是在使用方法时发生的,所以只有在使用它时才会缓存消息。

    类方法与C函数(或C ++中的静态类方法)基本相同,还是对它们有更多的东西?

    类对象以类似于类实例的方式处理方法发送。每个类对象都有一个对象,它在一个名为metaclass的对象中存储自己的方法。类对象有自己的isa指向其元类对象的指针,该对象又具有超级元类对象,它可以从中继承类对象。对类方法的方法调度如下:

    1. 调度系统遵循类对象的isa指向元类对象的指针
    2. 在元类对象的方法表中搜索类方法。
    3. 如果未找到,搜索将继续到元类对象的超类,继续搜索。
    4. 重复此过程,直到找到该方法,或者直到它到达根元类,并抛出异常。

答案 1 :(得分:19)

我还在我的博客上的x86_64上通过objc_msgSend()的指令编写了一条指令,如果有人想深入探讨:

http://www.friday.com/bbum/2009/12/18/objc_msgsend-part-1-the-road-map/