1.0 翻譯:marsprince Lenhoon(微博) 校對(duì):numbbbbb, stanzhai
2.0 翻譯+校對(duì):Lenhoon, BridgeQ
2.1 翻譯:mmoaay, shanks 校對(duì):shanks
2.2 翻譯:星夜暮晨
3.0 翻譯:chenmingjia
4.1 翻譯+校對(duì):mylittleswift
本頁(yè)包含內(nèi)容:
聲明(declaration) 用以向程序里引入新的名字或者結(jié)構(gòu)。舉例來(lái)說(shuō),可以使用聲明來(lái)引入函數(shù)和方法,變量和常量,或者定義新的具有命名的枚舉、結(jié)構(gòu)、類和協(xié)議類型。還可以使用聲明來(lái)擴(kuò)展一個(gè)既有的具有命名的類型的行為,或者在程序里引入在其它地方聲明的符號(hào)。
在 Swift 中,大多數(shù)聲明在某種意義上講也是定義,因?yàn)槁暶魍殡S著實(shí)現(xiàn)或初始化。由于協(xié)議并不提供實(shí)現(xiàn),大多數(shù)協(xié)議成員僅僅只是聲明而已。為了方便起見(jiàn),也是因?yàn)檫@些區(qū)別在 Swift 中并不是很重要,“聲明”這個(gè)術(shù)語(yǔ)同時(shí)包含了聲明和定義兩種含義。
聲明語(yǔ)法 聲明 → 導(dǎo)入聲明 聲明 → 常量聲明 聲明 → 變量聲明 聲明 → 類型別名聲明 聲明 → 函數(shù)聲明 聲明 → 枚舉聲明 聲明 → 結(jié)構(gòu)體聲明 聲明 → 類聲明 聲明 → 協(xié)議聲明 聲明 → 構(gòu)造器聲明 聲明 → 析構(gòu)器聲明 聲明 → 擴(kuò)展聲明 聲明 → 下標(biāo)聲明 聲明 → 運(yùn)算符聲明 多條聲明 → 聲明 多條聲明可選
Swift 的源文件中的頂級(jí)代碼(top-level code)由零個(gè)或多個(gè)語(yǔ)句、聲明和表達(dá)式組成。默認(rèn)情況下,在一個(gè)源文件的頂層聲明的變量,常量和其他具有命名的聲明可以被同模塊中的每一個(gè)源文件中的代碼訪問(wèn)。可以使用一個(gè)訪問(wèn)級(jí)別修飾符來(lái)標(biāo)記聲明來(lái)覆蓋這種默認(rèn)行為,請(qǐng)參閱 訪問(wèn)控制級(jí)別。
頂級(jí)聲明語(yǔ)法
頂級(jí)聲明 → 多條語(yǔ)句可選
代碼塊(code block) 可以將一些聲明和控制結(jié)構(gòu)組織在一起。它有如下的形式:
{
語(yǔ)句
}
代碼塊中的“語(yǔ)句”包括聲明、表達(dá)式和各種其他類型的語(yǔ)句,它們按照在源碼中的出現(xiàn)順序被依次執(zhí)行。
代碼塊語(yǔ)法
代碼塊 → { 多條語(yǔ)句可選 }
導(dǎo)入聲明(import declaration) 讓你可以使用在其他文件中聲明的內(nèi)容。導(dǎo)入語(yǔ)句的基本形式是導(dǎo)入整個(gè)模塊,它由 import 關(guān)鍵字和緊隨其后的模塊名組成:
import 模塊
可以對(duì)導(dǎo)入操作提供更細(xì)致的控制,如指定一個(gè)特殊的子模塊或者指定一個(gè)模塊或子模塊中的某個(gè)聲明。提供了這些限制后,在當(dāng)前作用域中,只有被導(dǎo)入的符號(hào)是可用的,而不是整個(gè)模塊中的所有聲明。
import 導(dǎo)入類型 模塊.符號(hào)名
import 模塊.子模塊
導(dǎo)入聲明語(yǔ)法
導(dǎo)入聲明 → 特性列表可選 import 導(dǎo)入類型可選 導(dǎo)入路徑
導(dǎo)入類型 → typealias | struct | class | enum | protocol | let | var | func 導(dǎo)入路徑 → 導(dǎo)入路徑標(biāo)識(shí)符 | 導(dǎo)入路徑標(biāo)識(shí)符 . 導(dǎo)入路徑 導(dǎo)入路徑標(biāo)識(shí)符 → 標(biāo)識(shí)符 | 運(yùn)算符
常量聲明(constant declaration) 可以在程序中引入一個(gè)具有命名的常量。常量以關(guān)鍵字 let 來(lái)聲明,遵循如下格式:
let 常量名稱: 類型 = 表達(dá)式
常量聲明在“常量名稱”和用于初始化的“表達(dá)式”的值之間定義了一種不可變的綁定關(guān)系;當(dāng)常量的值被設(shè)定之后,它就無(wú)法被更改。這意味著,如果常量以類對(duì)象來(lái)初始化,對(duì)象本身的內(nèi)容是可以改變的,但是常量和該對(duì)象之間的綁定關(guān)系是不能改變的。
當(dāng)一個(gè)常量被聲明為全局常量時(shí),它必須擁有一個(gè)初始值。在類或者結(jié)構(gòu)中聲明一個(gè)常量時(shí),它將作為常量屬性(constant property)。常量聲明不能是計(jì)算型屬性,因此也沒(méi)有存取方法。
如果常量名稱是元組形式,元組中每一項(xiàng)的名稱都會(huì)和初始化表達(dá)式中對(duì)應(yīng)的值進(jìn)行綁定。
let (firstNumber, secondNumber) = (10, 42)
在上例中,firstNumber 是一個(gè)值為 10 的常量,secnodeName 是一個(gè)值為 42 的常量。所有常量都可以獨(dú)立地使用:
print("The first number is \(firstNumber).")
// 打印 “The first number is 10.”
print("The second number is \(secondNumber).")
// 打印 “The second number is 42.”
當(dāng)常量名稱的類型(: 類型)可以被推斷出時(shí),類型標(biāo)注在常量聲明中是可選的,正如 類型推斷 中所描述的。
聲明一個(gè)常量類型屬性要使用 static 聲明修飾符。類型屬性在 類型屬性中有介紹。
如果還想獲得更多關(guān)于常量的信息或者想在使用中獲得幫助,請(qǐng)參閱 常量和變量 和 存儲(chǔ)屬性。
常量聲明語(yǔ)法
常量聲明 → 特性列表可選 聲明修飾符列表可選 let 模式構(gòu)造器列表 模式構(gòu)造器列表 → 模式構(gòu)造器 | 模式構(gòu)造器 , 模式構(gòu)造器列表 模式構(gòu)造器 → 模式 構(gòu)造器可選 構(gòu)造器 → = 表達(dá)式
變量聲明(variable declaration) 可以在程序中引入一個(gè)具有命名的變量,它以關(guān)鍵字 var 來(lái)聲明。
變量聲明有幾種不同的形式,可以聲明不同種類的命名值和可變值,如存儲(chǔ)型和計(jì)算型變量和屬性,屬性觀察器,以及靜態(tài)變量屬性。所使用的聲明形式取決于變量聲明的適用范圍和打算聲明的變量類型。
注意
也可以在協(xié)議聲明中聲明屬性,詳情請(qǐng)參閱 協(xié)議屬性聲明。
可以在子類中重寫(xiě)繼承來(lái)的變量屬性,使用 override 聲明修飾符標(biāo)記屬性的聲明即可,詳情請(qǐng)參閱 重寫(xiě)。
使用如下形式聲明一個(gè)存儲(chǔ)型變量或存儲(chǔ)型變量屬性:
var 變量名稱: 類型 = 表達(dá)式
可以在全局范圍,函數(shù)內(nèi)部,或者在類和結(jié)構(gòu)的聲明中使用這種形式來(lái)聲明一個(gè)變量。當(dāng)變量以這種形式在全局范圍或者函數(shù)內(nèi)部被聲明時(shí),它代表一個(gè)存儲(chǔ)型變量。當(dāng)它在類或者結(jié)構(gòu)中被聲明時(shí),它代表一個(gè)存儲(chǔ)型變量屬性(stored variable property)。
用于初始化的表達(dá)式不可以在協(xié)議的聲明中出現(xiàn),在其他情況下,該表達(dá)式是可選的。如果沒(méi)有初始化表達(dá)式,那么變量聲明必須包含類型標(biāo)注(: type)。
如同常量聲明,如果變量名稱是元組形式,元組中每一項(xiàng)的名稱都會(huì)和初始化表達(dá)式中對(duì)應(yīng)的值進(jìn)行綁定。
正如名字所示,存儲(chǔ)型變量和存儲(chǔ)型變量屬性的值會(huì)存儲(chǔ)在內(nèi)存中。
使用如下形式聲明一個(gè)計(jì)算型變量或計(jì)算型屬性:
var 變量名稱: 類型 {
get {
語(yǔ)句
}
set(setter 名稱) {
語(yǔ)句
}
}
可以在全局范圍、函數(shù)內(nèi)部,以及類、結(jié)構(gòu)、枚舉、擴(kuò)展的聲明中使用這種形式的聲明。當(dāng)變量以這種形式在全局范圍或者函數(shù)內(nèi)部被聲明時(shí),它表示一個(gè)計(jì)算型變量。當(dāng)它在類、結(jié)構(gòu)、枚舉、擴(kuò)展聲明的上下文中被聲明時(shí),它表示一個(gè)計(jì)算型屬性(computed property)。
getter 用來(lái)讀取變量值,setter 用來(lái)寫(xiě)入變量值。setter 子句是可選的,getter 子句是必須的。不過(guò)也可以將這些子句都省略,直接返回請(qǐng)求的值,正如在 只讀計(jì)算型屬性 中描述的那樣。但是如果提供了一個(gè) setter 子句,就必須也提供一個(gè) getter 子句。
setter 的圓括號(hào)以及 setter 名稱是可選的。如果提供了 setter 名稱,它就會(huì)作為 setter 的參數(shù)名稱使用。如果不提供 setter 名稱,setter 的參數(shù)的默認(rèn)名稱為 newValue,正如在 便捷 setter 聲明 中描述的那樣。
與存儲(chǔ)型變量和存儲(chǔ)型屬性不同,計(jì)算型變量和計(jì)算型屬性的值不存儲(chǔ)在內(nèi)存中。
要獲得更多關(guān)于計(jì)算型屬性的信息和例子,請(qǐng)參閱 計(jì)算型屬性。
可以在聲明存儲(chǔ)型變量或?qū)傩詴r(shí)提供 willSet 和 didSet 觀察器。一個(gè)包含觀察器的存儲(chǔ)型變量或?qū)傩砸匀缦滦问铰暶鳎?/p>
var 變量名稱: 類型 = 表達(dá)式 {
willSet(setter 名稱) {
語(yǔ)句
}
didSet(setter 名稱) {
語(yǔ)句
}
}
可以在全局范圍、函數(shù)內(nèi)部,或者類、結(jié)構(gòu)的聲明中使用這種形式的聲明。當(dāng)變量以這種形式在全局范圍或者函數(shù)內(nèi)部被聲明時(shí),觀察器表示一個(gè)存儲(chǔ)型變量觀察器。當(dāng)它在類和結(jié)構(gòu)的聲明中被聲明時(shí),觀察器表示一個(gè)屬性觀察器。
可以為任何存儲(chǔ)型屬性添加觀察器。也可以通過(guò)重寫(xiě)父類屬性的方式為任何繼承的屬性(無(wú)論是存儲(chǔ)型還是計(jì)算型的)添加觀察器 ,正如 重寫(xiě)屬性觀察器 中所描述的。
用于初始化的表達(dá)式在類或者結(jié)構(gòu)的聲明中是可選的,但是在其他聲明中則是必須的。如果可以從初始化表達(dá)式中推斷出類型信息,那么可以不提供類型標(biāo)注。
當(dāng)變量或?qū)傩缘闹当桓淖儠r(shí),willSet 和 didSet 觀察器提供了一種觀察方法。觀察器會(huì)在變量的值被改變時(shí)調(diào)用,但不會(huì)在初始化時(shí)被調(diào)用。
willSet 觀察器只在變量或?qū)傩缘闹当桓淖冎罢{(diào)用。新的值作為一個(gè)常量傳入 willSet 觀察器,因此不可以在 willSet 中改變它。didSet 觀察器在變量或?qū)傩缘闹当桓淖兒罅⒓凑{(diào)用。和 willSet 觀察器相反,為了方便獲取舊值,舊值會(huì)傳入 didSet 觀察器。這意味著,如果在變量或?qū)傩缘?didiset 觀察器中設(shè)置值,設(shè)置的新值會(huì)取代剛剛在 willSet 觀察器中傳入的那個(gè)值。
在 willSet 和 didSet 中,圓括號(hào)以及其中的 setter 名稱是可選的。如果提供了一個(gè) setter 名稱,它就會(huì)作為 willSet 和 didSet 的參數(shù)被使用。如果不提供 setter 名稱,willSet 觀察器的默認(rèn)參數(shù)名為 newValue,didSet 觀察器的默認(rèn)參數(shù)名為 oldValue。
提供了 willSet 時(shí),didSet 是可選的。同樣的,提供了 didSet 時(shí),willSet 則是可選的。
要獲得更多信息以及查看如何使用屬性觀察器的例子,請(qǐng)參閱 屬性觀察器。
要聲明一個(gè)類型變量屬性,用 static 聲明修飾符標(biāo)記該聲明。類可以改用 class 聲明修飾符標(biāo)記類的類型計(jì)算型屬性從而允許子類重寫(xiě)超類的實(shí)現(xiàn)。類型屬性在 類型屬性 章節(jié)有詳細(xì)討論。
注意
在一個(gè)類聲明中,使用關(guān)鍵字
static與同時(shí)使用class和final去標(biāo)記一個(gè)聲明的效果相同。
變量聲明語(yǔ)法
變量聲明 → 變量聲明頭 模式構(gòu)造器列表 變量聲明 → 變量聲明頭 變量名稱 類型標(biāo)注 代碼塊 變量聲明 → 變量聲明頭 變量名稱 類型標(biāo)注 getter-setter 代碼塊 變量聲明 → 變量聲明頭 變量名稱 類型標(biāo)注 getter-setter 關(guān)鍵字代碼塊 變量聲明 → 變量聲明頭 變量名稱 構(gòu)造器 willSet-didSet 代碼塊 變量聲明 → 變量聲明頭 變量名稱 類型標(biāo)注 構(gòu)造器可選 willSet-didSet 代碼塊
變量聲明頭 → 特性列表可選 聲明修飾符列表可選 var 變量名稱 → 標(biāo)識(shí)符
getter-setter 代碼塊 → 代碼塊 getter-setter 代碼塊 → { getter 子句 setter 子句可選 } getter-setter 代碼塊 → { setter 子句 getter 子句 } getter 子句 → 特性列表可選 get 代碼塊 setter 子句 → 特性列表可選 set setter 名稱可選 代碼塊 setter 名稱 → ( 標(biāo)識(shí)符 )
getter-setter 關(guān)鍵字代碼塊 → { getter 關(guān)鍵字子句 setter 關(guān)鍵字子句可選 } getter-setter 關(guān)鍵字代碼塊 → { setter 關(guān)鍵字子句 getter 關(guān)鍵字子句 } getter 關(guān)鍵字子句 → 特性列表可選 get setter 關(guān)鍵字子句 → 特性列表可選 set
willSet-didSet 代碼塊 → { willSet 子句 didSet 子句可選 } willSet-didSet 代碼塊 → { didSet 子句 willSet 子句可選 } willSet 子句 → 特性列表可選 willSet setter 名稱可選 代碼塊 didSet 子句 → 特性列表可選 didSet setter 名稱可選 代碼塊
類型別名(type alias) 聲明可以在程序中為一個(gè)既有類型聲明一個(gè)別名。類型別名聲明語(yǔ)句使用關(guān)鍵字 typealias 聲明,遵循如下的形式:
typealias 類型別名 = 現(xiàn)存類型
當(dāng)聲明一個(gè)類型的別名后,可以在程序的任何地方使用“別名”來(lái)代替現(xiàn)有類型?,F(xiàn)有類型可以是具有命名的類型或者混合類型。類型別名不產(chǎn)生新的類型,它只是使用別名來(lái)引用現(xiàn)有類型。 類型別名聲明可以通過(guò)泛型參數(shù)來(lái)給一個(gè)現(xiàn)有泛型類型提供名稱。類型別名為現(xiàn)有類型的一部分或者全部泛型參數(shù)提供具體類型。例如:
typealias StringDictionary<Value> = Dictionary<String, Value>
// 下列兩個(gè)字典擁有同樣的類型
var dictionary1: StringDictionary<Int> = [:]
var dictionary2: Dictionary<String, Int> = [:]
當(dāng)一個(gè)類型別名帶著泛型參數(shù)一起被聲明時(shí),這些參數(shù)的約束必須與現(xiàn)有參數(shù)的約束完全匹配。例如:
typealias DictionaryOfInts<Key: Hashable> = Dictionary<Key, Int>
因?yàn)轭愋蛣e名可以和現(xiàn)有類型相互交換使用,類型別名不可以引入額外的類型約束。 在協(xié)議聲明中,類型別名可以為那些經(jīng)常使用的類型提供一個(gè)更短更方便的名稱,例如:
protocol Sequence {
associatedtype Iterator: IteratorProtocol
typealias Element = Iterator.Element
}
func sum<T: Sequence>(_ sequence: T) -> Int where T.Element == Int {
// ...
}
假如沒(méi)有類型別名,sum 函數(shù)將必須引用關(guān)聯(lián)類型通過(guò) T.Iterator.Element 的形式來(lái)替代 T.Element。
另請(qǐng)參閱 協(xié)議關(guān)聯(lián)類型聲明。
類型別名聲明語(yǔ)法
類型別名聲明 → 類型別名頭 類型別名賦值 類型別名頭 → 特性列表可選 訪問(wèn)級(jí)別修飾符可選 typealias 類型別名名稱 類型別名名稱 → 標(biāo)識(shí)符 類型別名賦值 → = 類型
使用函數(shù)聲明(function declaration) 在程序中引入新的函數(shù)或者方法。在類、結(jié)構(gòu)體、枚舉,或者協(xié)議中聲明的函數(shù)會(huì)作為方法。函數(shù)聲明使用關(guān)鍵字 func,遵循如下的形式:
func 函數(shù)名稱(參數(shù)列表) -> 返回類型 {
語(yǔ)句
}
如果函數(shù)返回 Void 類型,返回類型可以省略,如下所示:
func 函數(shù)名稱(參數(shù)列表) {
語(yǔ)句
}
每個(gè)參數(shù)的類型都要標(biāo)明,因?yàn)樗鼈儾荒鼙煌茢喑鰜?lái)。如果您在某個(gè)參數(shù)類型前面加上了 inout,那么這個(gè)參數(shù)就可以在這個(gè)函數(shù)作用域當(dāng)中被修改。更多關(guān)于 inout 參數(shù)的討論,請(qǐng)參閱 輸入輸出參數(shù)。
函數(shù)可以使用元組類型作為返回類型來(lái)返回多個(gè)值。
函數(shù)定義可以出現(xiàn)在另一個(gè)函數(shù)聲明內(nèi)。這種函數(shù)被稱作嵌套函數(shù)(nested function)。更多關(guān)于嵌套函數(shù)的討論,請(qǐng)參閱 嵌套函數(shù)。
函數(shù)的參數(shù)列表由一個(gè)或多個(gè)函數(shù)參數(shù)組成,參數(shù)間以逗號(hào)分隔。函數(shù)調(diào)用時(shí)的參數(shù)順序必須和函數(shù)聲明時(shí)的參數(shù)順序一致。最簡(jiǎn)單的參數(shù)列表有著如下的形式:
參數(shù)名稱: 參數(shù)類型
一個(gè)參數(shù)有一個(gè)內(nèi)部名稱,這個(gè)內(nèi)部名稱可以在函數(shù)體內(nèi)被使用。還有一個(gè)外部名稱,當(dāng)調(diào)用函數(shù)時(shí)這個(gè)外部名稱被作為實(shí)參的標(biāo)簽來(lái)使用。默認(rèn)情況下,第一個(gè)參數(shù)的外部名稱會(huì)被省略,第二個(gè)和之后的參數(shù)使用它們的內(nèi)部名稱作為它們的外部名稱。例如:
func f(x: Int, y: Int) -> Int { return x + y }
f(1, y: 2) // 參數(shù) y 有標(biāo)簽,參數(shù) x 則沒(méi)有
可以按照如下兩種形式之一,重寫(xiě)參數(shù)名稱的默認(rèn)行為:
外部參數(shù)名稱 內(nèi)部參數(shù)名稱: 參數(shù)類型
_ 內(nèi)部參數(shù)名稱: 參數(shù)類型
在內(nèi)部參數(shù)名稱前的名稱會(huì)作為這個(gè)參數(shù)的外部名稱,這個(gè)名稱可以和內(nèi)部參數(shù)的名稱不同。外部參數(shù)名稱在函數(shù)被調(diào)用時(shí)必須被使用,即對(duì)應(yīng)的參數(shù)在方法或函數(shù)被調(diào)用時(shí)必須有外部名。
內(nèi)部參數(shù)名稱前的下劃線(_)可使該參數(shù)在函數(shù)被調(diào)用時(shí)沒(méi)有名稱。在函數(shù)或方法調(diào)用時(shí),對(duì)應(yīng)的參數(shù)必須沒(méi)有名字。
func f(x x: Int, withY y: Int, _ z: Int) -> Int { return x + y + z }
f(x: 1, withY: 2, 3) // 參數(shù) x 和 y 是有標(biāo)簽的,參數(shù) z 則沒(méi)有
輸入輸出參數(shù)被傳遞時(shí)遵循如下規(guī)則:
這種行為被稱為拷入拷出(copy-in copy-out) 或值結(jié)果調(diào)用(call by value result)。例如,當(dāng)一個(gè)計(jì)算型屬性或者一個(gè)具有屬性觀察器的屬性被用作函數(shù)的輸入輸出參數(shù)時(shí),其 getter 會(huì)在函數(shù)調(diào)用時(shí)被調(diào)用,而其 setter 會(huì)在函數(shù)返回時(shí)被調(diào)用。
作為一種優(yōu)化手段,當(dāng)參數(shù)值存儲(chǔ)在內(nèi)存中的物理地址時(shí),在函數(shù)體內(nèi)部和外部均會(huì)使用同一內(nèi)存位置。這種優(yōu)化行為被稱為引用調(diào)用(call by reference),它滿足了拷入拷出模式的所有要求,且消除了復(fù)制帶來(lái)的開(kāi)銷。在代碼中,要規(guī)范使用拷入拷出模式,不要依賴于引用調(diào)用。
不要使用傳遞給輸入輸出參數(shù)的值,即使原始值在當(dāng)前作用域中依然可用。當(dāng)函數(shù)返回時(shí),你對(duì)原始值所做的更改會(huì)被拷貝的值所覆蓋。不要依賴于引用調(diào)用的優(yōu)化機(jī)制來(lái)試圖避免這種覆蓋。
不能將同一個(gè)值傳遞給多個(gè)輸入輸出參數(shù),因?yàn)檫@種情況下的拷貝與覆蓋行為的順序是不確定的,因此原始值的最終值也將無(wú)法確定。例如:
var x = 10
func f(inout a: Int, inout _ b: Int) {
a += 1
b += 10
}
f(&x, &x) // 編譯報(bào)錯(cuò) error: inout arguments are not allowed to alias each other
如果嵌套函數(shù)在外層函數(shù)返回后才調(diào)用,嵌套函數(shù)對(duì)輸入輸出參數(shù)造成的任何改變將不會(huì)影響到原始值。例如:
func outer(inout a: Int) -> () -> Void {
func inner() {
a += 1
}
return inner
}
var x = 10
let f = outer(&x)
f()
print(x)
// 打印 “10”
調(diào)用嵌套函數(shù) inner() 對(duì) a 遞增后,x 的值并未發(fā)生改變,因?yàn)?inner() 在外層函數(shù) outer() 返回后才被調(diào)用。若要改變 x 的值,必須在 outer() 返回前調(diào)用 inner()。
關(guān)于輸入輸出參數(shù)的詳細(xì)討論,請(qǐng)參閱 輸入輸出參數(shù)。
參數(shù)可以被忽略,數(shù)量可以不固定,還可以為其提供默認(rèn)值,使用形式如下:
_ : 參數(shù)類型
參數(shù)名稱: 參數(shù)類型...
參數(shù)名稱: 參數(shù)類型 = 默認(rèn)參數(shù)值
以下劃線(_)命名的參數(shù)會(huì)被顯式忽略,無(wú)法在函數(shù)體內(nèi)使用。
一個(gè)參數(shù)的基本類型名稱如果緊跟著三個(gè)點(diǎn)(...),會(huì)被視為可變參數(shù)。一個(gè)函數(shù)至多可以擁有一個(gè)可變參數(shù),且必須是最后一個(gè)參數(shù)??勺儏?shù)會(huì)作為包含該參數(shù)類型元素的數(shù)組處理。舉例來(lái)講,可變參數(shù) Int... 會(huì)作為 [Int] 來(lái)處理。關(guān)于使用可變參數(shù)的例子,請(qǐng)參閱 可變參數(shù)。
如果在參數(shù)類型后面有一個(gè)以等號(hào)(=)連接的表達(dá)式,該參數(shù)會(huì)擁有默認(rèn)值,即給定表達(dá)式的值。當(dāng)函數(shù)被調(diào)用時(shí),給定的表達(dá)式會(huì)被求值。如果參數(shù)在函數(shù)調(diào)用時(shí)被省略了,就會(huì)使用其默認(rèn)值。
func f(x: Int = 42) -> Int { return x }
f() // 有效,使用默認(rèn)值
f(7) // 有效,提供了值
f(x: 7) // 無(wú)效,該參數(shù)沒(méi)有外部名稱
枚舉或結(jié)構(gòu)體的方法如果會(huì)修改 self,則必須以 mutating 聲明修飾符標(biāo)記。
子類重寫(xiě)超類中的方法必須以 override 聲明修飾符標(biāo)記。重寫(xiě)方法時(shí)不使用 override 修飾符,或者被 override 修飾符修飾的方法并未對(duì)超類方法構(gòu)成重寫(xiě),都會(huì)導(dǎo)致編譯錯(cuò)誤。
枚舉或者結(jié)構(gòu)體中的類型方法,要以 static 聲明修飾符標(biāo)記,而對(duì)于類中的類型方法,除了使用 static,還可使用 class 聲明修飾符標(biāo)記。
可以拋出錯(cuò)誤的函數(shù)或方法必須使用 throws 關(guān)鍵字標(biāo)記。這類函數(shù)和方法被稱為拋出函數(shù)和拋出方法。它們有著下面的形式:
func 函數(shù)名稱(參數(shù)列表) throws -> 返回類型 {
語(yǔ)句
}
拋出函數(shù)或拋出方法的調(diào)用必須包裹在 try 或者 try! 表達(dá)式中(也就是說(shuō),在作用域內(nèi)使用 try 或者 try! 運(yùn)算符)。
throws 關(guān)鍵字是函數(shù)的類型的一部分,非拋出函數(shù)是拋出函數(shù)的子類型。所以,可以在使用拋出函數(shù)的地方使用非拋出函數(shù)。
不能僅基于函數(shù)能否拋出錯(cuò)誤來(lái)進(jìn)行函數(shù)重載。也就是說(shuō),可以基于函數(shù)的函數(shù)類型的參數(shù)能否拋出錯(cuò)誤來(lái)進(jìn)行函數(shù)重載。
拋出方法不能重寫(xiě)非拋出方法,而且拋出方法不能滿足協(xié)議對(duì)于非拋出方法的要求。也就是說(shuō),非拋出方法可以重寫(xiě)拋出方法,而且非拋出方法可以滿足協(xié)議對(duì)于拋出方法的要求。
函數(shù)或方法可以使用 rethrows 關(guān)鍵字來(lái)聲明,從而表明僅當(dāng)該函數(shù)或方法的一個(gè)函數(shù)類型的參數(shù)拋出錯(cuò)誤時(shí),該函數(shù)或方法才拋出錯(cuò)誤。這類函數(shù)和方法被稱為重拋函數(shù)和重拋方法。重新拋出錯(cuò)誤的函數(shù)或方法必須至少有一個(gè)參數(shù)的類型為拋出函數(shù)。
func someFunction(callback: () throws -> Void) rethrows {
try callback()
}
重拋函數(shù)或者方法不能夠從自身直接拋出任何錯(cuò)誤,這意味著它不能夠包含 throw 語(yǔ)句。它只能夠傳遞作為參數(shù)的拋出函數(shù)所拋出的錯(cuò)誤。例如,在 do-catch 代碼塊中調(diào)用拋出函數(shù),并在 catch 子句中拋出其它錯(cuò)誤都是不允許的。
拋出方法不能重寫(xiě)重拋方法,而且拋出方法不能滿足協(xié)議對(duì)于重拋方法的要求。也就是說(shuō),重拋方法可以重寫(xiě)拋出方法,而且重拋方法可以滿足協(xié)議對(duì)于拋出方法的要求。
Swift 定義了 Never 類型,它表示函數(shù)或者方法不會(huì)返回給它的調(diào)用者。Never 返回類型的函數(shù)或方法可以稱為不歸,不歸函數(shù)、方法要么引發(fā)不可恢復(fù)的錯(cuò)誤,要么永遠(yuǎn)不停地運(yùn)作,這會(huì)使調(diào)用后本應(yīng)執(zhí)行得代碼就不再執(zhí)行了。但即使是不歸函數(shù)、方法,拋錯(cuò)函數(shù)和重拋出函數(shù)也可以將程序控制轉(zhuǎn)移到合適的 catch 代碼塊。
不歸函數(shù)、方法可以在 guard 語(yǔ)句的 else 字句中調(diào)用,具體討論在Guard 語(yǔ)句。 你可以重載一個(gè)不歸方法,但是新的方法必須保持原有的返回類型和沒(méi)有返回的行為。
函數(shù)聲明語(yǔ)法
函數(shù)頭 → 特性列表可選 聲明修飾符列表可選 func 函數(shù)名 → 標(biāo)識(shí)符 | 運(yùn)算符
函數(shù)簽名 → 參數(shù)子句列表 throws可選 函數(shù)結(jié)果可選 函數(shù)簽名 → 參數(shù)子句列表 rethrows 函數(shù)結(jié)果可選 函數(shù)結(jié)果 → -> 特性列表可選 類型 函數(shù)體 → 代碼塊
參數(shù)子句列表 → 參數(shù)子句 參數(shù)子句列表可選 參數(shù)子句 → ( ) | ( 參數(shù)列表 ) 參數(shù)列表 → 參數(shù) | 參數(shù) , 參數(shù)列表 參數(shù) → let可選 外部參數(shù)名可選 內(nèi)部參數(shù)名 類型標(biāo)注 默認(rèn)參數(shù)子句可選 參數(shù) → inout 外部參數(shù)名可選 內(nèi)部參數(shù)名 類型標(biāo)注 參數(shù) → 外部參數(shù)名可選 內(nèi)部參數(shù)名 類型標(biāo)注 ... 外部參數(shù)名 → 標(biāo)識(shí)符 | _ 內(nèi)部參數(shù)名 → 標(biāo)識(shí)符 | _ 默認(rèn)參數(shù)子句 → = 表達(dá)式
在程序中使用枚舉聲明(enumeration declaration) 來(lái)引入一個(gè)枚舉類型。
枚舉聲明有兩種基本形式,使用關(guān)鍵字 enum 來(lái)聲明。枚舉聲明體包含零個(gè)或多個(gè)值,稱為枚舉用例,還可包含任意數(shù)量的聲明,包括計(jì)算型屬性、實(shí)例方法、類型方法、構(gòu)造器、類型別名,甚至其他枚舉、結(jié)構(gòu)體和類。枚舉聲明不能包含析構(gòu)器或者協(xié)議聲明。
枚舉類型可以采納任意數(shù)量的協(xié)議,但是枚舉不能從類、結(jié)構(gòu)體和其他枚舉繼承。
不同于類或者結(jié)構(gòu)體,枚舉類型并不隱式提供默認(rèn)構(gòu)造器,所有構(gòu)造器必須顯式聲明。一個(gè)構(gòu)造器可以委托給枚舉中的其他構(gòu)造器,但是構(gòu)造過(guò)程僅當(dāng)構(gòu)造器將一個(gè)枚舉用例賦值給 self 后才算完成。
和結(jié)構(gòu)體類似但是和類不同,枚舉是值類型。枚舉實(shí)例在被賦值到變量或常量時(shí),或者傳遞給函數(shù)作為參數(shù)時(shí)會(huì)被復(fù)制。更多關(guān)于值類型的信息,請(qǐng)參閱 結(jié)構(gòu)體和枚舉是值類型。
可以擴(kuò)展枚舉類型,正如在 擴(kuò)展聲明 中討論的一樣。
如下的形式聲明了一個(gè)包含任意類型枚舉用例的枚舉變量:
enum 枚舉名稱: 采納的協(xié)議 {
case 枚舉用例1
case 枚舉用例2(關(guān)聯(lián)值類型)
}
這種形式的枚舉聲明在其他語(yǔ)言中有時(shí)被叫做可識(shí)別聯(lián)合。
在這種形式中,每個(gè)用例塊由關(guān)鍵字 case 開(kāi)始,后面緊接一個(gè)或多個(gè)以逗號(hào)分隔的枚舉用例。每個(gè)用例名必須是獨(dú)一無(wú)二的。每個(gè)用例也可以指定它所存儲(chǔ)的指定類型的值,這些類型在關(guān)聯(lián)值類型的元組中被指定,緊跟用例名之后。
具有關(guān)聯(lián)值的枚舉用例可以像函數(shù)一樣使用,通過(guò)指定的關(guān)聯(lián)值創(chuàng)建枚舉實(shí)例。和真正的函數(shù)一樣,你可以獲取枚舉用例的引用,然后在后續(xù)代碼中調(diào)用它。
enum Number {
case Integer(Int)
case Real(Double)
}
// f 的類型為 (Int) -> Number
let f = Number.Integer
// 利用 f 把一個(gè)整數(shù)數(shù)組轉(zhuǎn)成 Number 數(shù)組
let evenInts: [Number] = [0, 2, 4, 6].map(f)
要獲得更多關(guān)于具有關(guān)聯(lián)值的枚舉用例的信息和例子,請(qǐng)參閱 關(guān)聯(lián)值。
枚舉類型可以具有遞歸結(jié)構(gòu),就是說(shuō),枚舉用例的關(guān)聯(lián)值類型可以是枚舉類型自身。然而,枚舉類型的實(shí)例具有值語(yǔ)義,這意味著它們?cè)趦?nèi)存中有固定布局。為了支持遞歸,編譯器必須插入一個(gè)間接層。
要讓某個(gè)枚舉用例支持遞歸,使用 indirect 聲明修飾符標(biāo)記該用例。
enum Tree<T> {
case Empty
indirect case Node(value: T, left: Tree, right:Tree)
}
要讓一個(gè)枚舉類型的所有用例都支持遞歸,使用 indirect 修飾符標(biāo)記整個(gè)枚舉類型,當(dāng)枚舉有多個(gè)用例且每個(gè)用例都需要使用 indirect 修飾符標(biāo)記的時(shí)候這將非常便利。
被 indirect 修飾符標(biāo)記的枚舉用例必須有一個(gè)關(guān)聯(lián)值。使用 indirect 修飾符標(biāo)記的枚舉類型可以既包含有關(guān)聯(lián)值的用例,同時(shí)還可包含沒(méi)有關(guān)聯(lián)值的用例。但是,它不能再單獨(dú)使用 indirect 修飾符來(lái)標(biāo)記某個(gè)用例。
以下形式聲明了一種枚舉類型,其中各個(gè)枚舉用例的類型均為同一種基本類型:
enum 枚舉名稱: 原始值類型, 采納的協(xié)議 {
case 枚舉用例1 = 原始值1
case 枚舉用例2 = 原始值2
}
在這種形式中,每一個(gè)用例塊由 case 關(guān)鍵字開(kāi)始,后面緊跟一個(gè)或多個(gè)以逗號(hào)分隔的枚舉用例。和第一種形式的枚舉用例不同,這種形式的枚舉用例包含一個(gè)基礎(chǔ)值,叫做原始值,各個(gè)枚舉用例的原始值的類型必須相同。這些原始值的類型通過(guò)原始值類型指定,必須表示一個(gè)整數(shù)、浮點(diǎn)數(shù)、字符串或者字符。原始值類型必須符合 Equatable 協(xié)議和下列字面量轉(zhuǎn)換協(xié)議中的一種:整型字面量需符合 IntergerLiteralConvertible 協(xié)議,浮點(diǎn)型字面量需符合 FloatingPointLiteralConvertible 協(xié)議,包含任意數(shù)量字符的字符串型字面量需符合 StringLiteralConvertible 協(xié)議,僅包含一個(gè)單一字符的字符串型字面量需符合 ExtendedGraphemeClusterLiteralConvertible 協(xié)議。每一個(gè)用例的名字和原始值必須唯一。
如果原始值類型被指定為 Int,則不必為用例顯式地指定原始值,它們會(huì)隱式地被賦值 0、1、2 等。每個(gè)未被賦值的 Int 類型的用例會(huì)被隱式地賦值,其值為上一個(gè)用例的原始值加 1。
enum ExampleEnum: Int {
case A, B, C = 5, D
}
在上面的例子中,ExampleEnum.A 的原始值是 0,ExampleEnum.B 的原始值是 1。因?yàn)?ExampleEnum.C 的原始值被顯式地設(shè)定為 5,因此 ExampleEnum.D 的原始值會(huì)自動(dòng)增長(zhǎng)為 6。
如果原始值類型被指定為 String 類型,你不用明確地為用例指定原始值,每個(gè)沒(méi)有指定原始值的用例會(huì)隱式地將用例名字作為原始值。
enum WeekendDay: String {
case Saturday, Sunday
}
在上面這個(gè)例子中,WeekendDay.Saturday 的原始值是 "Saturday",WeekendDay.Sunday 的原始值是 "Sunday"。
枚舉用例具有原始值的枚舉類型隱式地符合定義在 Swift 標(biāo)準(zhǔn)庫(kù)中的 RawRepres