Swift 什么是objc_msgSend?为什么它会占用这么多处理时间?

Swift 什么是objc_msgSend?为什么它会占用这么多处理时间?,swift,sprite-kit,Swift,Sprite Kit,我一直在分析我基于回合的游戏应用程序,我遇到了一个有趣的(可能)问题。如下图所示,objc\u msgSend似乎占用了我应用程序运行时间的近一分钟。这是什么?这是一些写得不好的代码的标志吗?谢谢 objc\u msgSend是一个函数,它将选择器转换为地址,并在调用Objective-C中的方法时跳转到该地址。它本身并不表示编程不好。但是,如果它占用了大部分程序的时间,您可能需要考虑重构代码,这样您就不需要调用这么多方法来完成您正在做的工作。如果没有关于你的应用程序的更多信息,就不可能告诉你如

我一直在分析我基于回合的游戏应用程序,我遇到了一个有趣的(可能)问题。如下图所示,
objc\u msgSend
似乎占用了我应用程序运行时间的近一分钟。这是什么?这是一些写得不好的代码的标志吗?谢谢


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


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

正如@user1118321所说,
objc\u msgSend
基本上是Objective-C消息分派的实现。基本上,当您发送诸如
[foo bar]
之类的消息时,
objc\u msgSend
会被调用,它基本上是这样做的:

  • 找出什么类
    foo

  • bar
    消息(转换为基于字符串的选择器)发送到
    foo
    ,并获得一个实现(这基本上是一个C函数,将
    foo
    和选择器作为其前两个参数)。这可以在运行时被截获,并以许多粗糙的方式进行操作,这非常酷,但也会带来性能成本。此外,选择器操作还涉及字符串操作,这会导致自身的性能损失

  • 从步骤2调用实现

  • 如果您想深入了解
    objc\u msgSend
    的内部工作原理,本文非常棒:

    无论如何,所有这些显然不会像直接的C函数调用那么快,但是对于大多数情况,来自
    objc\u msgSend
    的开销不足以成为主要问题(该函数本身是用一些相当核心的手工优化汇编程序编写的,并使用了一系列缓存技术,因此它可能非常接近它所能提供的最佳性能)但是,在某些情况下,
    objc\u msgSend
    的性能可能会成为一个问题,对于这些情况,您可以尽量减少使用Objective-C调用,正如@user1118321所说。或者,如果您可以缩小导致问题的特定Objective-C方法调用的范围,您可以使用一种称为IMP缓存的技术,即y,您可以查找一次方法并将其实现保存为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\u msgSend
    的成本,因为您实际上会跳过上面列表中的前两个步骤

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

    谢谢!我可以问一下,什么样的例子会导致objc\u msgSend。我理解你的例子,但不同类的属性访问也会调用它吗?例子:
    self.gameModel!.gameMapModel![Int(self.currenttiletooperation!.y)][Int(self.currenttiletooperation!.x)].tileType=.counter
    和:
    (self.mainBoardMap!.scene as!GameScene).gameCameraAndUI!。显示gamenotification(文本:“计数器不足”,文本颜色:nil,完成:{})
    我相信属性也是以类似的方式实现的,尽管我不是肯定的。您可以阅读有关Objective-C运行时的信息。@Aleksandr我用有关运行时的更多信息更新了我的答案。有关详细信息,请参阅。@Aleksandr抱歉,我没有立即注意到您在评论中的其他问题。属性访问也会被取消通过
    objc_msgSend
    ,它们基本上是Objective-C2.0中引入的语法糖。在旧的1.0版本中,我们只是通过常规的消息发送符号访问属性,即
    [self-someProperty]
    [self-setSomeProperty:newValue]
    而不是
    self.someProperty
    self.someProperty=newValue
    。这两个符号在引擎盖下的作用仍然相同。