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

2023-12-20

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


正如@user1118321上面所说,objc_msgSend基本上是Objective-C的消息调度的实现。基本上,当您发送诸如[foo bar], objc_msgSend被调用,它本质上是这样做的:

  1. 弄清楚是什么班级foo is.

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

  3. 从步骤 2 调用实现。

如果你想深入了解内部运作的本质objc_msgSend,这篇文章非常棒:https://www.mikeash.com/pyblog/friday-qa-2012-11-16-lets-build-objc_msgsend.html 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");
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

什么是 objc_msgSend 以及为什么它占用如此多的处理时间? 的相关文章

随机推荐