正如@user1118321上面所说,objc_msgSend
基本上是Objective-C的消息调度的实现。基本上,当您发送诸如[foo bar]
, objc_msgSend
被调用,它本质上是这样做的:
弄清楚是什么班级foo
is.
发送bar
消息(转换为基于字符串的选择器)foo
,并获得一个实现(这基本上是一个 C 函数,它需要foo
和选择器作为它的前两个参数)。这可以在运行时拦截并以许多粗糙的方式进行操作,这非常酷,但也会产生性能成本。此外,选择器操作涉及字符串操作,这会带来自身的性能成本。
从步骤 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");