我的问题正如标题所说。显然,第一个参数用于这个指针,有点像c ++。第二个怎么样?你呢。
答案 0 :(得分:7)
objc_msgSend()
的签名是:
id objc_msgSend(id self, SEL op, ...);
每个方法调用都被编译为对此函数的调用。即,如果你打电话:
[anArray objectAtIndex:42];
这将被编译为好像:
objc_msgSend(anArray, @selector(objectAtIndex:), 42);
现在,就你的问题而言,为什么方法会被编译成一个以SEL作为第二个参数的函数。或者,更具体地说,为什么这种方法:
- (id)objectAtIndex:(NSUInteger)index;
完全等同于这个C函数:
id object_at_index(id object, SEL _cmd, NSUInteger index);
答案是速度速度 速度。
具体来说,通过这样做,objc_msgSend()
永远不必重写堆栈帧*,它也可以使用尾调用优化直接跳转到方法调用。这就是为什么你从未在调试器中看到objc_msgSend()
回溯的原因(除了你在信使中实际崩溃/中断的时候)。
objc_msgSend()
使用object
和_cmd
来查找方法的实现,然后,实际上,跳转到该实现。
非常快。堆叠框架未触及。
并且,正如其他人所说的那样,在方法实现中使用_cmd
可能会因各种原因而得心应用。同样,这也意味着信使可以通过NSInvocation等做代理支持等巧妙的技巧。
*重写堆栈帧可能非常复杂和昂贵。有些参数可能会在某些时候出现在寄存器中等等......所有架构都依赖于ABI的肮脏。写imp_implementationWithBlock()
之类的事情面临的最大挑战之一就是弄清楚如何在不触及堆栈的情况下这样做,因为这样做会太慢而且太过膨胀而无法生存。
答案 1 :(得分:2)
使第二个参数包含选择器的目的是启用公共调度机制。因此,方法调度代码总是希望第二个参数是选择器,并根据它调度,或者跟随继承链,甚至创建NSInvocation
并调用forwardInvocation:
。
通常,只有系统级例程使用selector参数,尽管在遇到异常时或者在调试器中试图找出在使用{{1}时遇到困难的例程时,它是相当不错的}
答案 2 :(得分:0)
来自文档:
<强>讨论强>
此数据类型是指向实现该方法的函数的开头的指针。此函数使用为当前CPU体系结构实现的标准C调用约定。第一个参数是指向self的指针(即,此类的特定实例的内存,或者,对于类方法,指向元类的指针)。第二个参数是方法选择器。方法参数如下。
在Objective-C中,当您调用方法时,您需要知道目标,选择器和最终参数。假设您正在尝试手动执行此操作:如果您不知道选择器,如何知道要调用哪种方法?你打电话给一些随机的方法吗?不,您调用正确的方法是因为您知道方法名称。