为什么 Objective-C 方法名称的最后一部分必须带有参数(当有多个部分时)?
在 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
) 不影响程序的功能语义,也不影响语言的表达能力。事实上,带有无参数组件的程序与没有无参数组件的程序在 alpha 版本上是等价的。因此,我对那些声称不需要这样的功能的答案不感兴趣(除非这是 Objective-C 设计师所陈述的原因;有人碰巧认识 Brad Cox 或 Tom Love?他们在这里吗?)或者说如何编写方法名称以便不需要该功能。主要好处是可读性和可写性(这就像可读性,只是……你知道),因为这意味着你可以编写更类似于自然语言句子的方法名称。类似 -(BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)theApplication
(Matt Gallagher 指出 在“Cocoa With Love” 上,当您删除形式参数时会有点混乱)可以命名为 -(BOOL) application:(NSApplication*)theApplication shouldTerminateAfterLastWindowClosed
,因此将参数紧挨着适当的名词放置。
Apple 的 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;
}
In Objective-C, you can't declare method names where the last component doesn't take an argument. For example, the following is illegal.
-(void)take:(id)theMoney andRun;
-(void)take:(id)yourMedicine andDontComplain;
Why was Objective-C designed this way? Was it just an artifact of Smalltalk that no one saw a need to be rid of?
This limitation makes sense in Smalltalk, since Smalltalk doesn't have delimiters around message invocation, so the final component would be interpreted as a unary message to the last argument. For example, BillyAndBobby take:'$100' andRun
would be parsed as BillyAndBobby take:('$100' andRun)
. This doesn't matter in Objective-C where square brackets are required.
Supporting parameterless selector components wouldn't gain us much in all the usual ways a language is measured, as the method name a programmer picks (e.g. runWith:
rather than take:andRun
) doesn't affect the functional semantics of a program, nor the expressiveness of the language. Indeed, a program with parameterless components is alpha equivalent to one without. I'm thus not interested in answers that state such a feature isn't necessary (unless that was the stated reasons of the Objective-C designers; does anyone happen to know Brad Cox or Tom Love? Are they here?) or that say how to write method names so the feature isn't needed. The primary benefit is readability and writability (which is like readability, only... you know), as it would mean you could write method names that even more closely resemble natural language sentences. The likes of -(BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)theApplication
(which Matt Gallagher points out on "Cocoa With Love" is a little bit confusing when you drop the formal parameter) could be named -(BOOL)application:(NSApplication*)theApplication shouldTerminateAfterLastWindowClosed
, thus placing the parameter immediately next to the appropriate noun.
Apple's Objective-C runtime (for example) is perfectly capable of handling these kind of selectors, so why not the compiler? Why not support them in method names as well?
#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;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
这是布拉德·考克斯。我原来的回答误解了这个问题。我认为 trueFast 是一个硬编码扩展,用于触发更快的消息传递,而不是一种语法糖。真正的答案是 Smalltalk 不支持它,也许是因为它的解析器无法处理(假设的)歧义。尽管 OC 的方括号可以消除任何歧义,但我只是没有想到要背离 Smalltalk 的关键字结构。
This is Brad Cox. My original answer misunderstood the question. I assumed reallyFast was a hardcoded extension to trigger faster messaging, not a kind of syntactic sugar. The real answer is that Smalltalk didn't support it, perhaps because its parser couldn't deal with the (assumed) ambiguity. Although OC's square brackets would remove any ambiguity, I simply didn't think of departing from Smalltalk's keyword structure.
在我 21 年的 Objective-C 编程经验中,这个问题从未在我的脑海中闪过。鉴于语言设计,编译器是正确的,而运行时函数是错误的()。
参数与方法名称交错的概念始终意味着,如果至少有一个参数,则最后一个参数始终是方法调用语法的最后部分。
没有经过深思熟虑,我敢打赌,存在一些语法错误,不强制执行当前的模式。至少,它会使编译器更难编写,因为任何具有与表达式交错的可选元素的语法总是更难解析。甚至可能存在一种极端情况,完全阻止了它。当然,Obj-C++ 会让它变得更具挑战性,但直到基本语法已经确定多年后才与该语言集成。
至于为什么 Objective-C 是这样设计的,我怀疑答案是该语言的原始设计者只是没有考虑允许交错语法超出最后一个参数。
这是最好的猜测。我会询问其中一位,并在了解更多信息后更新我的答案。
我向布拉德·考克斯询问了此事,他非常慷慨地详细回答了(谢谢布拉德!!):
去阅读布拉德的回答!
21 years of programming Objective-C and this question has never crossed my mind. Given the language design, the compiler is right and the runtime functions are wrong ().
The notion of interleaved arguments with method names has always meant that, if there is at least one argument, the last argument is always the last part of the method invocation syntax.
Without thinking it through terribly much, I'd bet there are some syntactic bugaboos with not enforcing the current pattern. At the least, it would make the compiler harder to write in that any syntax which has optional elements interleaved with expressions is always harder to parse. There might even be an edge case that flat out prevents it. Certainly, Obj-C++ would make it more challenging, but that wasn't integrated with the language until years after the base syntax was already set in stone.
As far as why Objective-C was designed this way, I'd suspect the answer is that the original designers of the language just didn't consider allowing the interleaved syntax to go beyond that last argument.
That is a best guess. I'll ask one of 'em and update my answer when I find out more.
I asked Brad Cox about this and he was very generous in responding in detail (Thanks, Brad!!):
Go read Brad's answer!
仅供您参考,运行时实际上并不关心选择器,任何 C 字符串都是有效的,您也可以创建这样的选择器:“==+===+---__--¡ ¡^::::::" 如果没有参数,运行时会接受它,但编译器不能接受,否则无法解析。对于选择器来说绝对没有健全性检查。
Just for your information, the runtime doesn't actually care about the selectors, any C string is valid, you could as well make a selector like that: "==+===+---__--¨¨¨¨¨^::::::" with no argument the runtime will accept it, the compiler just can't or else it's impossible to parse. There are absolutely no sanity check when it comes to selectors.
我认为 Objective-C 不支持它们,因为 Smalltalk 也不支持它们。但这有一个与您想象的不同的原因:不需要它们。需要的是支持具有 0、1、2、3、... 参数的方法。对于每个参数,已经有一个有效的语法来调用它们。添加任何其他语法只会导致不必要的混乱。
如果您想要多字无参数选择器,为什么要停止使用一个额外的字呢?有人可能会问,这
也得到支持(即选择器是一系列单词,有些带有冒号和参数,有些则没有)。虽然这是可能的,但我认为没有人认为这是值得的。
I assume they are not supported in Objective-C because they weren't available in Smalltalk, either. But that has a different reason than you think: they are not needed. What is needed is support for methods with 0, 1, 2, 3, ... arguments. For every number of arguments, there is already a working syntax to call them. Adding any other syntax would just cause unnecessary confusion.
If you wanted multi-word parameterless selectors, why stop with a single extra word? One might then ask that
also becomes supported (i.e. that a selector is a sequence of words, some with colon and a parameter, and others not). While this would have been possible, I assume that nobody considered it worthwhile.