Swift引用中的下划线代表什么?

152
在苹果文档的参考部分中,有很多这样的实例:

func runAction(_action: SKAction!)

Objective-C的“等效”写法是:

- (void)runAction:(SKAction *)action

我认为这里可能很重要的一点是,在Swift参考文档中,在下划线后有一个空格,而且 “action” 是用斜体字书写的。
但我无法理解这是什么意思。所以也许问题是......是否有关于文档中使用的惯例的参考资料?
-- 这是我在引用下划线用法的页面: https://developer.apple.com/documentation/spritekit/sknode#//apple_ref/occ/instm/SKNode/runAction 更新
Swift 3对函数/方法参数名称和参数标签的使用和命名进行了一些更改。这对本问题及其答案有影响。@Rickster 在回答另一个与函数中的_下划线相关的问题时做得非常好,清楚地解决了其中的许多问题,可参见:Why do I need underscores in swift?

3
标记为 [underscore.js] ?? - Martin R
3
文档遵循数学惯例,使用斜体表示变量名。例如:sin² a + cos² a = 1。 - rob mayoff
7个回答

117

两个答案都是正确的,但我想再澄清一点。

_ 用于 修改方法的外部参数名行为

在文档的“方法的本地和外部参数名”章节中,它说:

默认情况下,Swift 会给一个方法的第一个参数名设置一个本地参数名,并且默认情况下会给第二个及之后的参数名同时设置本地和外部参数名

另一方面,默认情况下函数没有外部参数名。

例如,我们在类 Bar 中定义了这个 foo() 方法:

class Bar{
    func foo(s1: String, s2: String) -> String {
        return s1 + s2;
    }
}

当你调用foo()时,它会像这样被调用:bar.foo("Hello", s2: "World").

但是,如果在声明的前面使用_,则可以覆盖此行为。

func foo(s1: String, _ s2: String) -> String{
    return s1 + s2;
}

那么,当你调用foo时,它可以像这样简单地被调用:bar.foo("Hello", "World"),第二个参数的名称不需要。

回到你的情况,runAction是一个方法,因为它与类型SKNode相关联。因此,在参数action前面放置一个_允许您调用runAction而不需要指定外部名称。

Swift 2.0的更新

在本地和外部参数名称声明方面,函数和方法现在以相同的方式工作。

函数现在默认使用外部参数名称进行调用,从第二个参数开始。此规则仅适用于纯Swift代码。

因此,通过在函数前面提供一个_,调用者将无需指定外部参数名称,就像对method所做的一样。


9
如果在第二个参数之前写下划线 _,则你的答案就已经足够清晰明了;但如果在 func runAction(_action: SKAction!) 中,在第一个参数之前加上 _ 或者像这样写代码 func foo(_ s1: String) { // your code },Xcode 会给出警告。但是在参考文献中有很多类似于 func bringSubviewToFront(_ view: UIView) 的代码,为什么呢?请解释一下。 - Will Zhang
10
基本上,它没有任何理由地这样做,只会让每个人感到困惑。太棒了。 - botbot
@Wyatt Zhang,文档中说第一个参数使用“_”是多余的。我可以做出一个假设,即设计师认为这对于你的代码来说显然足够,并且不会增强可读性,相反,它会玷污代码。因此你会收到警告。参考文献的目标恰恰相反:必须尽可能清晰 - 因此在每个方法中都提到了第一个参数的下划线。这就解释了一切。 - Dmitry Gryazin
5
需要注意的是,Swift 3.0 做了一些改变。除非另有规定,否则所有标签都是必需的。因此,在第一个参数之前的“-”不再是多余的。例如:override func viewWillAppear(_ animated: Bool) 表示调用者(Objective-C 代码)将不使用参数标签。 - Mr. T
这个答案应该被更新和/或重写,以反映在函数参数标签和参数名称中指定的新的Swift术语。 - Rob

84

下划线是一种通用的符号,用于表示被丢弃的值。

在这个特定的情况下,它意味着该函数将被调用为runAction(argument)而不是runAction(action:argument)

在其他上下文中,它具有类似的含义,例如:

for _ in 0..<5 { ... }

这意味着我们只想执行块5次,而不关心块内的索引。

在这种情况下:

let (result, _) = someFunctionThatReturnsATuple()

这意味着我们不关心元组的第二个元素,只关心第一个。

那么在这种情况下,参数(一个动作)是一个选项? - Confused
1
不,这会导致此情况下的外部参数名称(action:)为空,因此被省略。参数仍然是必需的,只是没有标记为action:。 - David Berry
4
真正的答案是@dasblinkenlight和我的回答相结合。他更精确地回答了这个具体情况,而我的回答则涉及了更广泛的问题——下划线代表什么? - David Berry
4
您可以使用弃用符号来使非常大的数字更易读,例如 let pi = 3.141_592_653_59。请注意,弃用符号只是为了方便阅读,不会影响数字本身的值。 - SMP
有趣的是,你也可以使用它来使非常大的数字变得不那么易读,例如 let pi = 3.1_4_15_92___63 - David Berry
虽然这些都是事实信息,但并没有回答问题,只会让未来的观众感到困惑。 - Rob

19
自 Swift 3 开始,所有的参数标签都是默认必需的。您可以使用下划线(_)强制 IDE 隐藏参数标签。
func foo(a: String) {
}
    
func foo2(_ a: String) {
}

调用 foo(a: "abc")foo2("abc")

注意:仅当 a 同时是外部的参数标签和内部的变量名称时,才能使用此方法。它相当于 - func foo(a a: String) 将不接受 _

苹果为什么要使用它?

您可以看到苹果正在整个 API 中使用它。 Apple 的库仍然是用 Objective-C 编写的 (如果不是,它们仍然共享设计于 Objective-C 语法的函数名称)

applicationWillResignActive(_ application: UIApplication) 这样的函数将具有一个冗余的参数名称 application,因为已经在其函数名称中存在了 application

您的例子

func runAction(_ action: SKAction!) 将被称为没有其 _ 标记的 runAction(action:)。 参数名action将是 多余的,因为函数名称中已经有一个。这就是该功能的目的和原因。


这是一个非常出色的答案,应该得到比它现在更多的关注。 - Rob

13

参数声明前的标识符定义了一个外部参数名称,这是在调用函数时由调用者提供的名称:

func someFunction(externalParameterName localParameterName: Int)

如果您没有提供默认参数的外部名称,Swift会为您定义的任何默认参数自动提供一个外部名称。如果您在定义参数时使用下划线作为外部参数名,则可以选择退出此行为:

当您定义参数时,使用下划线(_)而不是显式的外部名称可以选择退出此行为。

您可以在这里了解更多关于带有默认值的参数的外部名称的内容


所以...让我看看我的愚蠢是否理解正确。在这个例子中,我应该将我的参数变量/常量命名为“action”,并将其分配给我想要通过此函数运行的SKAction,Swift会自动命名所需的默认参数为“action”。然而,如果我想要自定义命名此操作,我必须在调用函数时使用下划线吗? - Confused
4
我理解的是,SK的设计人员不希望您两次使用action,因为"action"已经是函数名称的一部分。换句话说,他们不希望您编写sprite.runAction(action:moveGroundSpritesForever)。外部参数名称的目的是使您的代码“读起来像一句话”;如果两次使用action,将会破坏这个目的。 - Sergey Kalinichenko
在黑暗中有些灯光亮起来了。我想我明白了。这种语言特性旨在使在Swift中调用函数的方式看起来非常像在Objective-C中带参数调用方法的方式。或许吧。 - Confused
1
然而下划线可以用于某种程度上退出使用这些带有默认值的外部参数名称...只是还没有看到如何实现。会继续寻找。 - Confused
简而言之,它只是在命名参数和序数参数之间切换,因此您可以轻松地切换到foo.bar(param: 'fiddle')foo.bar('fiddle')注意:如果只有一个参数,它确实不明显...但是如果有多个参数,它就变得非常相关:foo.bar(param1: 'fiddle', param2: 'dee')foo.bar('fiddle','dee') - jrypkahauer

10

我认为这迫使了Swift遵循更接近Objective-C的惯例,与Cocoa的惯例更匹配。在Objective-C中,你不需要(外部)命名你的第一个参数。相反,惯例上你通常会将外部名称包含在方法名称的后半部分,像这样:

- (void)myFancyMethodWithFirstName:(NSString *)aFirstName lastName:(NSString *)aLastName;

[someInstance myFancyMethodWithFirstName:@"John" lastName:@"Doe"];
为使Swift的API调用与Objective-C一致,您需要抑制第一个参数的外部参数名。

为使Swift的API调用与Objective-C一致,您需要抑制第一个参数的外部参数名。

func myFancyMethodWithFirstName(_ firstName:String, lastName:String);

someInstance.myFancyMethodWithFirstName("John", lastName:"Doe")

2
我喜欢这个解释。学习了Obj-C之后,你澄清了一些事情。它还强调了在Swift中适当命名方法的重要性,即方法名称的后半部分应描述第一个参数,这通常是Obj-C中的情况。好东西。 - rrrrrraul

5
实际上,用于定义方法的真实代码和苹果文档中的方法声明是有区别的。以 UIControl-addTarget:action:forControlEvents: 方法为例,真实代码如下: enter image description here 但在文档中,它会出现这样的形式(注意 target 前面的 _): enter image description here 在真实代码中,_ 被用来使第二个或后续参数的外部名称在调用方法时不出现,而在文档中,参数本地名称前面的 _ 表示当您调用方法或函数时,不应提供外部名称。 默认情况下,在调用函数时没有外部名称,除非您自己提供或在参数本地名称之前添加 #(无空格),例如,这就是我们使用 dispatch_after 的方式: enter image description here 在文档中,它会出现这样的形式(注意三个 _): enter image description here 函数声明的约定与我描述方法的约定完全相同。

1
只是更加视觉化。

enter image description here

正如您所看到的,_ 只是省略了本地参数名称。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接