函數可以外化其參數名。外部名稱要作為實參的標籤出現在對函數的調用中。這麼做是有意義的,原因如下:
·闡明了每個實參的作用;每個實參名都能表示出自己對函數動作的作用。
·將函數區分開來;兩個函數可能有相同的名字和簽名,但卻擁有不同的外化參數名。
·有助於Swift與Objective-C和Cocoa的通信,後者的方法參數幾乎總是有外化名字的。
要想外化參數名,請在函數聲明中將外部名字放在內部參數名之前,中間用空格隔開。外部名字可以與內部名字相同,也可以不同。不過在Swift中,外化參數名已經形成了標準,因此有如下規則:在默認情況下,除了第一個參數,所有參數名都會自動外化。這樣,如果需要外化一個參數名,並且它不是第一個參數,並且希望外化名與內部名相同,那麼你什麼都不需要做,Swift會自動幫你實現。
如下是一個函數聲明,該函數會將一個字符串拼接到自身times次:
func repeatString(s:String, #times:Int) -> String { var result = \"\" for _ in 1...times { result += s } return result }
該函數的第1個參數只有內部名字,不過第2個參數有一個外部名字,它與內部名字相同,都叫作times。下面是調用該函數的代碼:
let s = repeatString(\"hi\", times:3)
如你所見,在調用中,外部名作為一個標籤位於實參之前,中間用冒號隔開。
我之前曾經說過,參數的外部名可以與內部名不同。比如,在repeatString函數中,我們將times作為外部名,將n作為內部名。那麼,函數聲明將會如下所示:
func repeatString(s:String, times n:Int) -> String { var result = \"\" for _ in 1...n { result += s} return result }
函數體中現在已經看不到times了;times只用作外部名,在調用時使用。內部名是n,也是代碼所引用的名字。
外部名的存在並不意味著調用可以使用與聲明不同的參數順序。比如,repeatString接受一個String參數和一個Int參數,順序就是這樣的。雖然標籤可以消除實參對應於哪個參數的歧義,但調用的順序也需要如此(不過稍後我將給出該規則的一個例外情況)。
repeatString函數說明了默認規則,即第1個參數沒有外部名,其他參數則有。為何說這是默認規則呢?一個原因在於第1個參數通常不需要外部名,因為函數名通常能夠清晰表明第1個參數的含義——就像repeatString函數一樣(重複一個字符串,而該字符串應該由第1個參數提供)。另一個原因也是在實際情況中更為重要的,那就是這個約定使得Swift函數能夠與Objective-C方法交互,而後者採取的就是這種方式。
比如,下面是Cocoa NSString方法的Objective-C聲明:
- (NSString *)stringByReplacingOccurrencesOfString:(NSString *)target withString:(NSString *)replacement
該方法接收兩個NSString參數並返回一個NSString。第2個參數的外部名是顯而易見的,即withString。不過第1個參數的名字卻不那麼明顯。一方面,你可以說它是stringByReplacingOccurrencesOfString。另一方面,它實際上並非參數真正的名字;它更像是方法名。實際上,該方法的正式名字是整個字符串stringByReplacingOccurrencesOfString:withString:。不過,Swift函數調用語法通過圓括號將函數名與外部參數名區分開來。因此,如果Swift想要調用這個Objective-C方法,那麼冒號前首先就應該是函數名(位於圓括號之前),然後是第2個實參的標籤(位於圓括號中)。Swift String與Cocoa NSString能夠自動橋接彼此,因此可以在Swift String上調用這個Cocoa方法,如以下代碼所示:
let s = \"hello\" let s2 = s.stringByReplacingOccurrencesOfString(\"ell\", withString:\"ipp\") // s2 is now \"hippo\"
如果函數是你自己定義的(即是你聲明的),並且Objective-C永遠不會調用它(這樣就沒必要遵循Objective-C的要求了),那麼你可以自由改變其默認行為。你可以在自己的函數聲明中做如下事情。
外化第1個參數名
如果想要外化第1個參數名,那麼請將外部名放到內部名之前。這兩個名字可以相同。
修改除第1個參數之外的其他參數名
如果想要修改除第1個參數外的其他參數的外部名,那麼請將所需的外部名放到內部名之前。
防止對除第1個參數外的其他參數進行外化
要想防止對除第1個參數外的其他參數進行外化,請在其前面加上一個下劃線和一個空格:
func say(s:String, _ times:Int) {
現在在調用這個方法時不能對第2個參數加標籤:
let d = Dog d.say(\"woof\", 3)
(這就解釋了本章一開始所作的聲明func sum(x:Int,_y:Int)->Int:這裡阻止了第2個參數名的外化,從而無須提供實參標籤。)
這個函數的名字是什麼?
從技術上來說,一個Swift函數的名字是由圓括號之前的名字加上參數的外部名共同構成的。如果阻止了參數的外部名,那麼可以使用一個下劃線來表示其外部名。結果就是外部參數名會位於圓括號中,後跟一個冒號。比如,函數聲明func say(s:String,times:Int)從技術上來看就是say(_:times:),函數聲明func say(s:String,_times:Int)從技術上來看就是say(_:_:)。這麼表示有點煩瑣,本書也不會這麼使用,不過其優點在於準確和無歧義。