什么时候在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)
你可以看到他们几乎是一样的