Swift 2.2学习笔记之『Function Parameter Names浅谈』

Swift的func参数名规则很有意思,它的完整形式如下:

func foo(firstExternalParamName firstInternalParamName: Int,
        secondExternalParamName secondInternalParamName: Int) -> Int {
    return firstInternalParamName + secondInternalParamName
}

foo(firstExternalParamName: 1, secondExternalParamName: 2)  //3

每一个参数的参数名,都由两部分组成:external parameter name+internal parameter name
external parameter name是供调用者使用的,而internal parameter name是供函数体使用的。

调用的时候,需要按照参数的顺序,依次使用这些外部参数名,看起来长得有点像Python,但顺序不能颠倒:

foo(firstExternalParamName: 1, secondExternalParamName: 2)

跟C/C++中的函数不同,Swift这样的规则,使得参数名也参与了函数语义的表达。
比如上例,改一下参数名,使语义更明确:

func add(firstValue first: Int,
        secondValue second: Int) -> Int {
    return first + second
}

add(firstValue: 1, secondValue: 2)

有时候,仅凭函数名部分,其实已经足够表达语义,调用者要是可以省略external parameter name部分就好了。显然,上例中的add()函数,就属于这种情况:

func add(_ first: Int,
        _ second: Int) -> Int {
    return first + second
}

add(1, 2)

可以看到,把参数的external parameter name部分都写成_(下划线)后,调用时就无需提供参数名了。此时回到了C/C++时代。(如果你非要闲得蛋疼调用时提供参数名的话也是可以的:add(_: 1, _: 2)

函数定义时,每个参数名都要写外部名和内部名实在太烦了,能不能偷懒只写一个呢?必须可以:

func add(first: Int,
        second: Int) -> Int {
    return first + second
}

每个参数名由两部分改成了一个,这也是最“Swiftic”的写法(下文会详述),对于这样的形式,编译器会自动帮你按照固定的规则补全为:

func add(_ first: Int,
        second second: Int) -> Int {
    return first + second
}

规则显而易见:第一个参数,自动帮你添加名为_的外部参数名,之后的参数,外部参数名和内部参数名都长一个样。
于是调用者需要这样调用:

add(1, second: 2)

要是你铁了心想要C/C++风格的调用,那就勉为其难折中一下:

func add(first: Int,
        _ second: Int) -> Int {
    return first + second
}
add(1, 2)

这下皆大欢喜了。

至此,应该明白Swift函数参数名的简写规则了吧,我们再次重温下以加深记忆:

本需要两部分组成的参数名,如果只提供单独的一个,对于第一个参数,自动帮你添加名为_的外部参数名,第二个及后,自动生成同样的外部名和内部名

Swift对第一个参数进行特殊对待,显然是Objective-C的历史原因。上面的add函数用Objective-C会怎么写?

- (void) add:(int)first with:(int)second;

这个方法的签名为:add:with:,每一个参数,都对应一个方法名,所有方法名组合起来表达整个方法的语义。
到了Swift,对于第一个参数,它有函数名(add)+ 参数名(first)两部分来辅佐;对于第二个及以后的参数,仅有参数名(second)可供扶持,于是,Swift需要对第一个参数特殊处理,仅使用函数名来辅佐它就足够了,所以才会在补全规则中对第一个参数的外部参数名进行了省略。

至此,可以理解为何下面的形式才是最“Swiftic”的写法了吧:

func add(first: Int,
        second: Int) -> Int {
    return first + second
}
add(1, second: 2)

函数名add对应第一个参数1,参数名second,对应第二个参数2,看起来跟Objective-C神似有木有:[obj add: 1 with: 2],Apple对于Objective-C到底是有多钟爱……

至此,似乎圆满了?慢着!有特例!
突然间杀出了个搅局者——init方法(欲知此货的详情,请猛击此处

init方法带着哭腔说道:尼玛,其他函数的名字都能各种改,我特么只能固定叫init,你特么让我咋辅佐第一个参数啊,臣妾真的做不到!

Apple想了一下说:我擦,(为了老情人Objective-C)前面设计得这么精妙,我自己都快被自己聪明哭了,不能单单为了你就推翻这一切啊,嗯,那啥,你就委屈一下下好了,作个特例吧。

于是对于init方法的参数名展开规则,就变成了所有参数自动生成同样的外部名和内部名

class Class {
    var a: Int
    var b: Int
    init(a: Int, b: Int) { //编译器展开为 init(a a: Int, b b: Int)
        self.a = a
        self.b = b
    }
}
var obj = Class(a: 1, b: 2) //创建一个Class类型的instance

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据