为什么不在Objective C中使用objc_msgSend()?

时间:2013-06-23 17:26:01

标签: objective-c cocoa objective-c-runtime

Apple的Objective C Runtime Guide指出,不要在自己的代码中使用objc_msgSend(),而是建议使用methodForSelector:而不是。但是,它没有提供任何理由。

在代码中调用objc_msgSend()会有什么危险?

3 个答案:

答案 0 :(得分:36)

原因#1:样式不好 - 这是多余的,不可读的。

当遇到Objective-C消息传递表达式时,编译器会自动生成对objc_msgSend()(或其某些变体)的调用。如果您知道要在编译时发送的类和选择器,则没有理由编写

id obj = objc_msgSend(objc_msgSend([NSObject class], @selector(alloc)), @selector(init));

而不是

id obj = [[NSObject alloc] init];

即使你不知道类或选择器(甚至两者),它仍然更安全(至少编译器有机会警告你,如果你正在做一些可能讨厌/错误的事情)来获得正确的类型函数指向实现本身并改为使用该函数指针:

const char *(*fptr)(NSString *, SEL) = [NSString instanceMethodForSelector:@selector(UTF8String)];
const char *cstr = fptr(@"Foo");

当方法的参数类型对默认促销敏感时尤其如此 - 如果是,那么您不希望通过可变参数objc_msgSend()传递它们,因为您的程序将快速调用未定义的行为。

原因#2:危险且容易出错。

注意#1中的“或其某些变体”部分。并非所有消息发送都使用objc_msgSend()函数本身。由于ABI中的复杂性和要求(特别是在函数的调用约定中),存在用于返回(例如,浮点值或结构)的单独函数。例如,在执行某种搜索(子串等)的方法的情况下,它返回NSRange结构,根据平台,可能需要使用结构返回版本信使功能:

NSRange retval;
objc_msgSend_stret(&retval, @"FooBar", @selector(rangeOfString:), @"Bar");

如果你弄错了(例如,你使用了不适当的信使函数,你将指针混合到返回值和self等),你的程序可能会表现不正确和/或崩溃。 (你很可能会弄错,因为它甚至不那么简单 - 并非所有返回struct的方法都使用这种变体,因为小结构将适合一个或两个处理器寄存器,不需要使用堆栈作为返回值的位置。这就是为什么 - 除非你是一个硬核ABI黑客 - 你宁愿让编译器完成它的工作,或者有龙。)

答案 1 :(得分:10)

你问“危险是什么?”并且@ H2CO3列出了一些结尾,“除非你是一个核心的ABI黑客”......

与许多规则一样,也有例外(在ARC下可能还有一些例外)。因此,使用msgSend的原因应该是:

[1]我想我应该使用msgSend - 不要

[2]但我在这里有一个案例...... - 你可能没有,继续寻找其他解决方案

...

[10]我真的认为我应该在这里使用它 - 再想一想

...

[100]真的,这看起来像msgSend的情况,我看不到任何其他解决方案!好的,请在TextEdit code sample from Apple中阅读Document.m。您知道他们使用msgSend的原因吗?你确定...... 再想一想......

...

[1000]我理解为什么Apple使用它,我的情况类似......你发现理解证明规则与你的案例匹配的例外使用它

HTH

答案 2 :(得分:3)

我可以提出一个案子。我们在跨平台项目(Windows,Mac和Linux)中使用msgSend in one of our C++ files。我们使用它来重新计算后台(共享代码)中的引用,该引用稍后用于从前端到后端,反之亦然。非常特殊,无可否认。