在 Objective-C 运行时,为什么 method_getNumberOfArguments 返回的结果比选择器暗示的多两个结果?
例如,为什么@selector(initWithPrice:color:) 返回 4?
在 Objective-C 运行时,为什么 method_getNumberOfArguments 返回的结果比选择器暗示的多两个结果?
例如,为什么@selector(initWithPrice:color:) 返回 4?
好的。只是为了澄清事实,是的,任何objective-c方法的前两个参数都是self
and _cmd
,总是按照这个顺序。
然而,更有趣的主题是为什么会出现这种情况。为此,我们必须先回顾一下 objc 的历史。事不宜迟,让我们开始吧。
早在 1983 年,objective-c 的“上帝”Brad Cox 想要在 C之上创建一种面向对象的基于运行时的语言,以实现跨平台的良好性能和灵活性。结果,最初的 Objective-C“编译器”只是将 Objective-C 源代码转换为 C 运行时等效项的简单预处理器,然后使用特定于平台的 C 编译器工具进行编译。
然而,C 并不是为对象而设计的,这是 Objective-C 必须克服的最基本的东西。虽然 C 是一种健壮且灵活的语言,但运行时支持是它的关键缺陷之一。
在 Objective-C 的早期设计阶段,决定对象将是一个纯粹的基于堆的指针设计,这样它们就可以在任何函数之间传递而没有奇怪的复制语义等(这在 Obj-C++ 中有所改变和 ARC,但这对于这篇文章来说范围太广了),并且每个方法都应该是自我意识的(实际上,正如 bbum 所指出的,这是使用与原始函数调用相同的堆栈帧的优化),所以理论上,您可以将多个方法名称映射到同一个选择器,如下所示:
// this is a completely valid objc 1.0 method declaration
void *nameOrAge(id self, SEL _cmd) {
if (_cmd == @selector(name)) {
return "Richard";
}
if (_cmd == @selector(age)) {
return (void *) (intptr_t) 16;
}
return NULL;
}
那么这个函数理论上可以映射到两个选择器,name
和age
,并根据调用哪个选择器执行条件代码。在一般的 Objective-C 代码中,这并不是什么大不了的事,因为现在 ARC 很难将函数映射到选择器,由于强制转换等,但是从那时起语言已经发展了很多。
希望这可以帮助您理解Objective-C 方法的两个“不可见”参数背后的原因,第一个是被调用的对象,第二个是在该对象上调用的方法。
前两个参数是隐藏参数 self 和 _cmd。