为什么method_getNumberOfArguments返回的结果比选择器所暗示的多两个?

Jas*_*ues 1 objective-c objective-c-runtime

在objective-C运行时,为什么method_getNumberOfArguments返回的结果比选择器所暗示的多两个?

例如,为什么@selector(initWithPrice:color :)返回4?

Ric*_*III 6

TL; DR

好的.只是为了直接设置记录,是的,任何objective-c方法的前两个参数都是,self并且_cmd始终按此顺序.

Objective-C的简史

然而,更有趣的主题是这种情况的原因.要做到这一点,我们首先要看看objc的历史.不用多说,让我们开始吧.

早在1983年,Objective Co的"上帝"Brad Cox就想 C 之上创建一个面向对象的基于运行时的语言,以实现跨平台的良好性能和灵活性.因此,第一个Objective-C'编译器'只是将Objective-C源的简单预处理器转换为它们的C-runtime等价物,然后使用特定于平台的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;
}
Run Code Online (Sandbox Code Playgroud)

然后,该函数可以理论上映射到两个选择器,name并且age基于调用哪个来执行条件代码.在一般的Objective-C代码中,这并不算太大,因为现在使用ARC将函数映射到选择器是非常困难的,因为演员等等,但语言从那时起已经发展了很多.

希望这有助于您了解Objective-C方法的两个"不可见"参数背后的原因,第一个是被调用的对象,第二个是在该对象上调用的方法.

  • 几个更正.直到OpenStep API(~1993)才引入引用计数.最初,ObjC内存管理完全像malloc()/ free()w /参考管理一样对待编码器.前两个args(id,SEL)纯粹是因为它允许堆栈帧在调度之间保持不变,从而实现尾调用优化.虽然您可以实现具有一个函数的多个选择器,但正如您所演示的那样,这绝不是将SEL作为第二个参数传递的目标. (2认同)