cfi*_*her 21 cocoa objective-c objective-c-runtime
Apple的Objective C Runtime Guide指出,不要在自己的代码中使用objc_msgSend(),而是建议使用methodForSelector:.但是,它没有提供任何理由.
在代码中调用objc_msgSend()有什么危险?
小智 37
objc_msgSend()
当遇到Objective-C消息传递表达式时,编译器会自动生成对其(或其某些变体)的调用.如果您知道要在编译时发送的类和选择器,则没有理由编写
id obj = objc_msgSend(objc_msgSend([NSObject class], @selector(alloc)), @selector(init));
Run Code Online (Sandbox Code Playgroud)
代替
id obj = [[NSObject alloc] init];
Run Code Online (Sandbox Code Playgroud)
即使你不知道类或选择器(甚至两者),它仍然更安全(至少编译器有机会警告你,如果你正在做一些可能讨厌/错误的事情)来获得正确类型的函数指针实现本身并使用该函数指针代替:
const char *(*fptr)(NSString *, SEL) = [NSString instanceMethodForSelector:@selector(UTF8String)];
const char *cstr = fptr(@"Foo");
Run Code Online (Sandbox Code Playgroud)
当方法的参数类型对默认促销敏感时尤其如此 - 如果它们是,那么您不希望通过可变参数objc_msgSend()
获取传递它们,因为您的程序将快速调用未定义的行为.
注意#1中的"或其某些变体"部分.并非所有消息发送都使用该objc_msgSend()
功能.由于ABI中的复杂性和要求(特别是在函数的调用约定中),存在用于返回(例如,浮点值或结构)的单独函数.例如,在执行某种搜索(子串等)的方法的情况下,它返回一个NSRange
结构,根据平台,可能需要使用messenger函数的结构返回版本:
NSRange retval;
objc_msgSend_stret(&retval, @"FooBar", @selector(rangeOfString:), @"Bar");
Run Code Online (Sandbox Code Playgroud)
如果你弄错了(例如你使用了不合适的信使功能,你将指针混合到返回值self
等等),你的程序可能会表现不正确和/或崩溃.(而且你很可能会弄错,因为它甚至不那么简单 - 并非所有方法都返回struct
使用这个变体,因为小结构将适合一个或两个处理器寄存器,从而无需使用堆栈作为返回值.这就是为什么 - 除非你是一个硬核ABI黑客 - 你宁愿让编译器完成它的工作,或者有龙.)
CRD*_*CRD 10
你问"危险是什么?" 并且@ H2CO3列出了一些结尾,"除非你是一个核心的ABI黑客"......
与许多规则一样,也有例外(在ARC下可能还有一些例外).因此,您使用的推理msgSend
应该遵循以下方面:
[ 1] I think I should use msgSend
- don't.
[2] But I've a case here... - you probably haven't, keep looking for another solution.
...
[10] I really think I should use it here - think again.
...
[100] Really, this looks like a case for msgSend
, I can't see any other solution! OK, go read Document.m
in the TextEdit code sample from Apple. Do you know why they used msgSend
? Are you sure... think again...
...
[1000] I understand why Apple used it, and my case is similar... You've found and understood the exception that proves the rule and your case matches, use it!
HTH
归档时间: |
|
查看次数: |
14180 次 |
最近记录: |