为什么Objective-C方法名称的最后一部分必须带一个参数(当有多个部分时)?


90

在Objective-C中,您不能在最后一个组件不带参数的地方声明方法名称。例如,以下内容是非法的。

-(void)take:(id)theMoney andRun;
-(void)take:(id)yourMedicine andDontComplain;

为什么用Objective-C这样设计?仅仅是Smalltalk的产物,没有人看到有需要摆脱的必要吗?

此限制在Smalltalk中是有意义的,因为Smalltalk在消息调用周围没有定界符,因此最终组件将被解释为最后一个参数的一元消息。例如,BillyAndBobby take:'$100' andRun将解析为BillyAndBobby take:('$100' andRun)。在Objective-C中,这不需要括号,这无关紧要。

支持无参数选择器组件不会像用程序员选择的方法名称(例如,runWith:而不是语言)那样以所有常用的语言度量方法给我们带来很多好处。take:andRun)不会影响程序的功能语义,也不会影响语言的表现力。确实,具有无参数组件的程序与没有该组件的程序等效。因此,我对不需要此功能的答案不感兴趣(除非这是Objective-C设计师的陈述原因;是否有人偶然知道Brad Cox或Tom Love?他们在这里吗?)或那句话如何编写方法名称,以便不需要该功能。主要的好处是可读性和可写性(这与可读性一样,只是...您知道),因为这意味着您可以编写与自然语言句子更加相似的方法名称。这样的人-(BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)theApplication(马特·加拉格尔指出的“可可的爱”-(BOOL)application:(NSApplication*)theApplication shouldTerminateAfterLastWindowClosed,因此将参数直接放在适当名词的旁边。

苹果的Objective-C运行时(例如)完全能够处理这类选择器,那么为什么不编译呢?为什么不同时在方法名称中支持它们呢?

#import <Foundation/Foundation.h>
#import <objc/runtime.h>

@interface Potrzebie : NSObject
-(void)take:(id)thing;
@end

@implementation Potrzebie
+(void)initialize {
    SEL take_andRun = NSSelectorFromString(@"take:andRun");
    IMP take_ = class_getMethodImplementation(self, @selector(take:));
    if (take_) {
        if (NO == class_addMethod(self, take_andRun, take_, "@@:@")) {
            NSLog(@"Couldn't add selector '%@' to class %s.", 
                  NSStringFromSelector(take_andRun), 
                  class_getName(self));
        }
    } else {
        NSLog(@"Couldn't find method 'take:'.");
    }
}

-(void)take:(id)thing {
    NSLog(@"-take: (actually %@) %@",NSStringFromSelector(_cmd), thing);
}
@end

int main() {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    Potrzebie *axolotl=[[Potrzebie alloc] init];
    [axolotl take:@"paradichloroaminobenzaldehyde"];
    [axolotl performSelector:NSSelectorFromString(@"take:andRun") 
                  withObject:@"$100"];
    [axolotl release];

    [pool release];
    return 0;
}

1
无法解释无法实现的原因,但是为什么可以实现呢?苹果会将方法命名为“ takeAndRun:”和“ takeAndDontComplain:”;)

takeAndRunWith:(id)theMoneytakeAndDon'tComplainAbout:(id)yourMedicine。当然,语法上很尴尬。
马修·弗雷德里克

13
感谢您提出这个问题-这是一个非常有趣的问题。我会问周围。
bbum 2010年

5
偶尔的强迫语法尴尬使我想要该功能。
outis 2010年

1
好问题。顺便说一句,编译器对像这样的方法没有问题:- (void) :(id)theMoney;- (void) :(id)obj1 :(id)obj2;。因此,仅包含冒号的选择器就可以了。;-)
Ole Begemann 2010年

Answers:


111

这是布拉德·考克斯(Brad Cox)。我原来的答案误解了这个问题。我以为trueFast是一个硬编码的扩展,可以触发更快的消息传递,而不是一种语法糖。真正的答案是Smalltalk不支持它,可能是因为它的解析器无法处理(假定的)歧义。尽管OC的方括号可以消除任何歧义,但我只是没有想到脱离Smalltalk的关键字结构。


谢谢,布拉德 我对您答案的解释是相同的;没有考虑离开SmallTalk。
bbum 2010年

42

对Objective-C进行21年的编程,这个问题我从未想到。给定语言设计,编译器是正确的,运行时函数是错误的()。

带有方法名称的交错参数的概念始终意味着,如果至少有一个参数,则最后一个参数始终是方法调用语法的最后一部分。

如果不经过很多深入的思考,我敢打赌有些语法缺陷会导致不强制使用当前模式。至少,这会使编译器更难编写,因为任何将可选元素与表达式交织在一起的语法都将始终较难解析。甚至可能会出现边缘保护套,无法对其进行防护。当然,Obj-C ++会使它更具挑战性,但是直到基本语法已经定型数年之后,它才与该语言集成。

至于为什么以这种方式设计Objective-C,我怀疑答案是该语言的原始设计者只是不考虑允许交错语法超出最后一个参数。

那是最好的猜测。我会问'em之一,并在发现更多信息时更新答案。


我向布拉德·考克斯(Brad Cox)询问了这一点,他非常慷慨地详细回答(谢谢布拉德!):

那时,我专注于在C中尽可能多地复制Smalltalk,并尽可能高效地执行该操作。任何空闲的周期都会使普通的消息传递变得快速。没有想到专门的消息传递选项(“ reallyFast?” [ bbum:我以'doSomething:withSomething:reallyFast'为例进行询问),)],因为普通消息已经尽可能快了。这涉及到手动调整C原始消息传递器的汇编程序输出,这是一个可移植的噩梦,以至于后来将其中的一些(如果不是全部的话)拿走了。我确实记得,手工入侵的消息传递程序非常快;关于两个函数调用的成本;一个进入消息传递程序逻辑,其余的则在那里进行方法查找。
后来,Steve Naroff和其他人在Smalltalk的纯动态键入的基础上添加了静态键入增强功能。我对此的参与有限。

去读布拉德的答案!


语法错误使我非常感兴趣。
outis 2010年

1
在我看来,如果您有一大堆没有冒号的方法名,则会遇到解析问题,因为您无法确定它是否打算作为方法名的一部分。
NSResponder 2010年

2
我第一次为代表设计协议时就没想到,这距从Objective-C开始不到21年。正常模式是提供发送委托消息的对象作为第一个参数。例如-myObjectWithDelegate: (id) foo wantsYouToDoSomethingWith: (id) bar。如果您在协议中有不需要其他参数的方法,则这会导致方法名称中的不一致问题。有关示例,请参见NSTableViewDataSource。一种方法没有遵循其他方法的整洁模式。
JeremyP 2010年

21

仅作为参考,运行时实际上并不关心选择器,任何C字符串都是有效的,您也可以像这样创建选择器:“ == + === + ---__-- ¨^ ::::::“”“不带任何参数的运行时将接受它,编译器无法接受,否则将无法解析。对于选择器,绝对没有健全性检查。


7
+1我以为您提出这个建议完全疯了,但您是对的。对于那些不相信的人:pastie.org/1388361
Dave DeLong 2010年

6

我认为它们在Objective-C中不受支持,因为它们在Smalltalk中也不可用。但这与您认为的原因不同:不需要它们。需要的是对带有0、1、2、3,...参数的方法的支持。对于每种参数,已经有一种有效的语法可以调用它们。添加任何其他语法只会导致不必要的混乱。

如果您想使用多字无参数选择器,为什么还要再添加一个单字呢?然后有人可能会问

 [axolotl perform selector: Y with object: Y]

也被支持(即选择器是一个单词序列,有些带有冒号和参数,有些则没有)。尽管这是可能的,但我认为没有人认为这值得。


1
我曾经想到过“不需要它们”的论点,但是我对此并不感兴趣。更新的问题使这一点更加清楚。允许任意选择器组件不采用参数的方式在可写性方面提供了更少的空间,因为空格和驼峰大小写之间的差异小于最终的无参数组件与重写自然语言语句和重新定位参数之间的差异(两者都必须完成)与ObjC保持一致)。
outis 2010年

另外,允许使用任意无参数的组件会增加关键字冲突的可能性,并使名称解析复杂化,尤其是在混合使用C ++和ObjC时(and并且or会成为特定的绊脚石)。如果只允许方法名称的最后一个组成部分不带参数,则发生的情况会少得多,因为它倾向于由多个单词组成。
outis 2010年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.