什么是objc_msgSend,为什么它占用了这么多处理时间?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了什么是objc_msgSend,为什么它占用了这么多处理时间?相关的知识,希望对你有一定的参考价值。

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

enter image description here

答案

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

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

另一答案

正如@ user1118321所述,objc_msgSend基本上是Objective-C的消息调度的实现。基本上,当你发送[foo bar]这样的消息时,会调用objc_msgSend,它基本上会这样做:

  1. 弄清楚foo是什么类。
  2. bar消息(转换为基于字符串的选择器)发送到foo,并获得一个实现(它基本上是一个C函数,它将foo和选择器作为前两个参数)。这可以在运行时拦截并以很多粗糙的方式进行操作,这非常酷但也会产生性能成本。此外,选择器操作涉及字符串操作,这会导致其自身的性能成本。
  3. 从第2步调用实现。

如果你想深入了解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");

以上是关于什么是objc_msgSend,为什么它占用了这么多处理时间?的主要内容,如果未能解决你的问题,请参考以下文章

为什么objc_msgSend必须用汇编实现

objc_msgSend 报错

为啥 Kernel#require 占用了我的应用程序资源的这么大块?

Pytorch:为啥`tensor`变量占用的内存这么小?

JSON是什么,为什么这么流行?

HTML:为啥元素占用这么多宽度?