什么时候在Swift中需要参数标签?

在回答这个问题时,需要调用init参数标签。 这在Swift中是正常的。

 class Foo { init(one: Int, two: String) { } } let foo = Foo(42, "Hello world") // Missing argument labels 'one:two:' in call 

然而,陌生的势力正在发挥作用:

 extension Foo { func run(one: String, two: [Int]) { } } foo.run(one: "Goodbye", two: []) // Extraneous argument label 'one:' in call 

要在这里使用参数标签,必须明确声明。

我还没有在文档中看到很详尽的解释。 哪些类/实例/全局函数是需要参数标签的? Obj-C方法总是导出和导入参数标签?

从Swift 3.0开始,这又一次发生了变化:所有的方法,函数和初始值设定项都需要所有参数的参数标签,除非你明确地select了使用外部名字_ 。 这意味着像addChildViewController(_:)这样的方法现在写成这样:

 func addChildViewController(_ childController: UIViewController) 

这是作为Swift Evolutionstream程的一部分提出并批准的 ,并在SR-961中实施。

所有init方法都需要参数名称:

 var view = NSView(frame: NSRect(x: 10, y: 10, width: 50, height: 50)) class Foo { init(one: Int, two: String) { } } let foo = Foo(one: 42, two: "Hello world") 

在一个对象上调用的所有方法都使用参数名称,除了第一个参数:

 extension Foo { func run(one: String, two: [Int]) { } } foo.run("Goodbye", two: []) 

所有包括Swift和Objective-C中的类函数都遵循相同的模式。 您也可以显式添加外部名称。

 extension Foo{ class func baz(one: Int, two: String){} class func other(exOne one: Int, exTwo two: String){} } Foo.baz(10, two:"str") Foo.other(exOne: 20, exTwo:"str") 

不是类函数的Swift函数不需要参数名称,但是您仍然可以明确地添加它们:

 func bar(one: Int, two: String){} bar(1, "hello") 

正如Bryan所说,当调用方法签名中具有参数名称的objective-c方法时,Swift方法调用是有意义的。 Init方法包含第一个参数,因为Swift将init方法从initWith:…中的objective-c更改为Class(),所以方法名称中不再包含第一个参数名称。

Swift 3.0

在Swift 3.0中, 预计2016年下半年发布 ,默认行为很简单:

  • 所有方法的所有参数默认都有外部标签。

您可以在Swift APIdevise指南中最简洁地find这些规则。 在SE-0056中提出了这种最新的行为,“在所有参数包括第一标签上build立一致的标签行为”,并在SR-961中实施。 在“覆盖默认行为”中,可以如下所述更改默认行为。

Swift 2.2

在Swift 2.2中,语言的外部参数标签的默认值已经改变,现在更简单了。 默认行为可以总结如下:

  • 方法和函数的第一个参数不应该有外部参数标签。
  • 方法和函数的其他参数应该有外部参数标签。
  • 所有初始化参数都应该有外部参数标签。

在“覆盖默认行为”中,可以如下所述更改默认行为。

一个例子

这些规则最好用一个例子来说明:

 func printAnimal(animal: String, legCount: Int) { let legNoun = legCount == 1 ? "leg" : "legs" print("\(animal) has \(legCount) \(legNoun)") } struct Player { let name: String let lives: Int init(name: String, lives: Int) { self.name = name self.lives = lives } func printCurrentScore(currentScore: Int, highScore: Int) { print("\(name)'s score is \(currentScore). Their high score is \(highScore)") } } // SWIFT 3.0 // In Swift 3.0, all argument labels must be included printAnimal(animal: "Dog", legCount: 4) let p = Player(name: "Riley", lives: 3) p.printCurrentScore(currentScore: 50, highScore: 110) // SWIFT 2.2 // In Swift 2.2, argument labels must be included or omitted in exactly the following way // given the definition of the various objects. printAnimal("Dog", legCount: 4) let p = Player(name: "Riley", lives: 3) p.printCurrentScore(50, highScore: 110) // In Swift 2.2, none of the following will work printAnimal(animal: "Dog", legCount: 4) // Extraneous argument label 'animal:' in call let q = Player("Riley", lives: 3) // Missing argument label 'name:' in call p.printCurrentScore(50, 110) // Missing argument label 'highScore:' in call 

覆盖默认行为

对于任何方法或函数的任何参数,您都可能会偏离语言的默认值,虽然风格指南正确地警告您不要这样做,除非有充分的理由。

要添加一个外部参数标签,通常不会有一个 – 仅适用于Swift 2.2,因为Swift 3.0默认为每个参数分配外部标签 – 或者更改外部参数标签 – 适用于两个版本 – 写入所需的外部参数本地参数标签之前的标签:

 func printAnimal(theAnimal animal: String, legCount: Int) { let legNoun = legCount == 1 ? "leg" : "legs" print("\(animal) has \(legCount) \(legNoun)") } printAnimal(theAnimal: "Dog", legCount: 4) 

要删除外部参数标签(通常为1),请使用特殊的外部参数标签_

 func printAnimal(animal: String, _ legCount: Int) { let legNoun = legCount == 1 ? "leg" : "legs" print("\(animal) has \(legCount) \(legNoun)") } // SWIFT 3.0 printAnimal(theAnimal: "Dog", 4) // SWIFT 2.2 printAnimal("Dog", 4) 

这些“默认覆盖”将适用于任何方法或function,包括初始化。

下面是我通过阅读(相当稀less的)文档以及通过简单的实验得到的结果:

  • 初始方法总是需要它们的标签 。 初始化方法,如标签,因为他们明确了什么init方法,确切地说,你想调用。 否则,这个:

     FooBar(foos: 5) 

    和这个:

     FooBar(bars: 5) 

    看起来完全一样:

     FooBar(5) 

    这里没有其他的地方 – init方法是Swift中唯一的地方,它们都有相同的名字,但是可能有不同的参数。 这就是为什么…

  • 函数 ,方法等( 不是 init方法的东西)都有第一个标签被省略 – 这是为了样式,并减less无聊的重复性。 代替

     aDictionary.removeValueForKey(key: "four") 

    我们有这个:

     aDictionary.removeValueForKey("four") 

    对于具有两个参数的函数,仍然有相当不明确且易于阅读的参数。 所以,而不是

     anArray.insert("zebras", 9) 

    我们有一个更容易理解的阅读forms:

     anArray.insert("zebras", atIndex: 9) 

看起来好多了 当我在WWDC的时候,这被认为是Swift的一个特性:Java风格的现代的,简短的论点,而不会牺牲可读性。 正如Bryan Chen的回答所显示的那样,这也简化了Objective-C的过渡。

您可以在标签之前使用#作为调用方法所需的参数标签。

例如:

 func addLocation(latitude : Double, longitude : Double) { /*...*/ } addLocation(125.0, -34.1) // Not clear 

可以这样改进:

 func addLocation(#latitude : Double, #longitude : Double) { /*...*/ } addLocation(latitude: 125.0, longitude: -34.1) // Better 

(从WWDC 2014 – 416 – build设现代框架 ,15分钟)

这只是使Swift中的ObjC方法看起来不错。

文档

实例方法

方法的本地和外部参数名称

具体来说,Swift默认在方法中给第一个参数名称一个本地参数名称 ,并且默认给出第二个和后续参数名称本地和外部参数名称。 这个约定匹配你在编写Objective-C方法时熟悉的典型的命名和调用约定,并且不需要限定你的参数名就可以进行expression式的方法调用。

上面描述的默认行为意味着Swift中的方法定义是用与Objective-C相同的语法风格编写的,并且以自然的,expression的方式进行调用。

自定义初始化

本地和外部参数名称

但是,初始化函数和方法在括号之前没有标识函数的名称。 因此,初始化器参数的名称和types在识别应该调用哪个初始化器时起着特别重要的作用。 正因为如此,Swift为初始化程序中的每个参数提供了一个自动外部名称,如果您自己没有提供外部名称的话。

例如对于这个ObjC类

 @interface Counter : NSObject @property int count; - (void)incrementBy:(int)amount numberOfTimes:(int)numberOfTimes; @end 

它写在斯威夫特

 class Counter { var count: Int = 0 func incrementBy(amount: Int, numberOfTimes: Int) { count += amount * numberOfTimes } } 

调用ObjC版本

 [counter incrementBy:10 numberOfTimes:2]; 

和Swift版本

 counter.incrementBy(10, numberOfTimes:2) 

你可以看到他们几乎是一样的