什么是objc_msgSend以及它为什么占用这么多处理时间?

时间:2017-12-25 01:43:58

标签: swift sprite-kit

我一直在分析我的回合制游戏应用程序,我遇到了一个有趣的(可能)问题。根据下面的图片,似乎objc_msgSend占用了我应用的运行时间差不多一分钟。这是什么,它是一些写得不好的代码的标志?谢谢!

enter image description here

2 个答案:

答案 0 :(得分:4)

正如@ user1118321所述,objc_msgSend基本上是Objective-C的消息调度的实现。基本上,当您发送[foo bar]之类的消息时,会调用objc_msgSend,并且它基本上会执行此操作:

  1. 找出foo类。

  2. bar消息(转换为基于字符串的选择器)发送到foo,并获得一个实现(基本上是一个带foo的C函数和选择器作为它的前两个参数)。这可以在运行时拦截并以很多粗糙的方式进行操作,这非常酷但也会产生性能成本。此外,选择器操作涉及字符串操作,这会产生自己的性能成本。

  3. 从第2步调用实现。

  4. 如果你想深入了解objc_msgSend内部运作的细节,那么这篇文章非常棒:https://www.mikeash.com/pyblog/friday-qa-2012-11-16-lets-build-objc_msgsend.html

    无论如何,所有这一切显然不会像直接的C函数调用一样快,但对于大多数情况来说,objc_msgSend的开销不足以成为主要问题(函数本身)是用一些非常好的核心手工优化汇编程序编写的,并使用了一堆缓存技术,所以它可能非常接近它所能提供的最佳性能。但是,有些情况下objc_msgSend的性能可能会受到关注,对于这些情况,您可以尝试尽量减少使用Objective-C调用,如@ user1118321所述。或者,如果您可以缩小导致问题的特定Objective-C方法调用,则可以使用称为IMP缓存的技术,通过该技术可以查找一次方法并将其实现保存为C函数指针,然后,您可以根据需要重复调​​用,将对象和选择器作为前两个参数发送。

    例如,如果您有此对象:

    @interface Foo: NSObject
    
    - (id)doSomethingWithString:(NSString *)bar;
    
    @end
    

    你可以像IMP一样得到它:

    Foo *foo = ...
    SEL selector = @selector(doSomethingWithString:);
    IMP imp = [foo methodForSelector:selector];
    id (*funcVersion)(Foo *, SEL, NSString *) = (id (*)(Foo *, SEL, NSString *))imp;
    

    您可以将funcVersion和选择器存储在某处,然后像这样调用它。这样做不会产生objc_msgSend的费用,因为您将有效地跳过上面列表中的前两个步骤。

    id returnValue = funcVersion(foo, selector, @"Baz");
    

答案 1 :(得分:3)

objc_msgSend是将选择器转换为地址并在每次调用Objective-C中的方法时跳转到它的函数。它并不表示编程本身很差。但如果它占用了您计划的大部分时间,您可能需要考虑重构代码,这样您就不需要调用这么多方法来完成您的工作了。这样做。如果没有关于您的应用程序的任何更多信息,就无法告诉您如何继续。

我以前打过这个。就我而言,我的应用程序正在调用一个方法来检索NSDictionary。但事实证明,该词典在整个应用程序的生命周期中都是静态的。每次调用它时,该方法都是从头开始创建的。因此,我不是反复调用创建字典的方法,而是在开始时调用它一次并保存(保留)结果,从而消除将来对方法的所有调用。