鍍金池/ 教程/ iOS/ 聲明(Declarations)
特性(Attributes)
Access Control 權限控制的黑與白
基本運算符(Basic Operators)
基礎部分(The Basics)
閉包(Closures)
擴展
泛型參數(shù)(Generic Parameters and Arguments)
訪問控制和 protected
語句(Statements)
模式(Patterns)
WWDC 里面的那個“大炮打氣球”
關于語言參考(About the Language Reference)
語法總結(Summary of the Grammar)
嵌套類型
類型(Types)
Swift 初見(A Swift Tour)
泛型
枚舉(Enumerations)
高級運算符
繼承
析構過程
關于 Swift(About Swift)
訪問控制
類和結構體
內(nèi)存安全
Swift 與 C 語言指針友好合作
協(xié)議
屬性(Properties)
可選類型完美解決占位問題
錯誤處理
字符串和字符(Strings and Characters)
聲明(Declarations)
自動引用計數(shù)
Swift 里的值類型與引用類型
表達式(Expressions)
Swift 文檔修訂歷史
造個類型不是夢-白話 Swift 類型創(chuàng)建
歡迎使用 Swift
詞法結構(Lexical Structure)
集合類型(Collection Types)
下標
方法(Methods)
可選鏈式調(diào)用
版本兼容性
類型轉(zhuǎn)換
構造過程
The Swift Programming Language 中文版
函數(shù)(Functions)
Swift 教程
控制流(Control Flow)

聲明(Declarations)


1.0 翻譯:marsprince Lenhoon(微博) 校對:numbbbbb, stanzhai

2.0 翻譯+校對:Lenhoon, BridgeQ

2.1 翻譯:mmoaay, shanks 校對:shanks

2.2 翻譯:星夜暮晨

3.0 翻譯:chenmingjia

4.1 翻譯+校對:mylittleswift

本頁包含內(nèi)容:

聲明(declaration) 用以向程序里引入新的名字或者結構。舉例來說,可以使用聲明來引入函數(shù)和方法,變量和常量,或者定義新的具有命名的枚舉、結構、類和協(xié)議類型。還可以使用聲明來擴展一個既有的具有命名的類型的行為,或者在程序里引入在其它地方聲明的符號。

在 Swift 中,大多數(shù)聲明在某種意義上講也是定義,因為聲明往往伴隨著實現(xiàn)或初始化。由于協(xié)議并不提供實現(xiàn),大多數(shù)協(xié)議成員僅僅只是聲明而已。為了方便起見,也是因為這些區(qū)別在 Swift 中并不是很重要,“聲明”這個術語同時包含了聲明和定義兩種含義。

聲明語法 聲明導入聲明 聲明常量聲明 聲明變量聲明 聲明類型別名聲明 聲明函數(shù)聲明 聲明枚舉聲明 聲明結構體聲明 聲明類聲明 聲明協(xié)議聲明 聲明構造器聲明 聲明析構器聲明 聲明擴展聲明 聲明下標聲明 聲明運算符聲明 多條聲明聲明 多條聲明可選

頂級代碼

Swift 的源文件中的頂級代碼(top-level code)由零個或多個語句、聲明和表達式組成。默認情況下,在一個源文件的頂層聲明的變量,常量和其他具有命名的聲明可以被同模塊中的每一個源文件中的代碼訪問。可以使用一個訪問級別修飾符來標記聲明來覆蓋這種默認行為,請參閱 訪問控制級別。

頂級聲明語法

頂級聲明多條語句可選

代碼塊

代碼塊(code block) 可以將一些聲明和控制結構組織在一起。它有如下的形式:

{
    語句
}

代碼塊中的“語句”包括聲明、表達式和各種其他類型的語句,它們按照在源碼中的出現(xiàn)順序被依次執(zhí)行。

代碼塊語法

代碼塊{ 多條語句可選 }

導入聲明

導入聲明(import declaration) 讓你可以使用在其他文件中聲明的內(nèi)容。導入語句的基本形式是導入整個模塊,它由 import 關鍵字和緊隨其后的模塊名組成:

import 模塊

可以對導入操作提供更細致的控制,如指定一個特殊的子模塊或者指定一個模塊或子模塊中的某個聲明。提供了這些限制后,在當前作用域中,只有被導入的符號是可用的,而不是整個模塊中的所有聲明。

import 導入類型 模塊.符號名
import 模塊.子模塊

導入聲明語法

導入聲明特性列表可選 import 導入類型可選 導入路徑

導入類型typealias | struct | class | enum | protocol | let | var | func 導入路徑導入路徑標識符 | 導入路徑標識符 . 導入路徑 導入路徑標識符標識符 | 運算符

常量聲明

常量聲明(constant declaration) 可以在程序中引入一個具有命名的常量。常量以關鍵字 let 來聲明,遵循如下格式:

let 常量名稱: 類型 = 表達式

常量聲明在“常量名稱”和用于初始化的“表達式”的值之間定義了一種不可變的綁定關系;當常量的值被設定之后,它就無法被更改。這意味著,如果常量以類對象來初始化,對象本身的內(nèi)容是可以改變的,但是常量和該對象之間的綁定關系是不能改變的。

當一個常量被聲明為全局常量時,它必須擁有一個初始值。在類或者結構中聲明一個常量時,它將作為常量屬性(constant property)。常量聲明不能是計算型屬性,因此也沒有存取方法。

如果常量名稱是元組形式,元組中每一項的名稱都會和初始化表達式中對應的值進行綁定。

let (firstNumber, secondNumber) = (10, 42)

在上例中,firstNumber 是一個值為 10 的常量,secnodeName 是一個值為 42 的常量。所有常量都可以獨立地使用:

print("The first number is \(firstNumber).")
// 打印 “The first number is 10.”
print("The second number is \(secondNumber).")
// 打印 “The second number is 42.”

當常量名稱的類型(: 類型)可以被推斷出時,類型標注在常量聲明中是可選的,正如 類型推斷 中所描述的。

聲明一個常量類型屬性要使用 static 聲明修飾符。類型屬性在 類型屬性中有介紹。

如果還想獲得更多關于常量的信息或者想在使用中獲得幫助,請參閱 常量和變量存儲屬性。

常量聲明語法

常量聲明特性列表可選 聲明修飾符列表可選 let 模式構造器列表 模式構造器列表模式構造器 | 模式構造器 , 模式構造器列表 模式構造器模式 構造器可選 構造器= 表達式

變量聲明

變量聲明(variable declaration) 可以在程序中引入一個具有命名的變量,它以關鍵字 var 來聲明。

變量聲明有幾種不同的形式,可以聲明不同種類的命名值和可變值,如存儲型和計算型變量和屬性,屬性觀察器,以及靜態(tài)變量屬性。所使用的聲明形式取決于變量聲明的適用范圍和打算聲明的變量類型。

注意

也可以在協(xié)議聲明中聲明屬性,詳情請參閱 協(xié)議屬性聲明。

可以在子類中重寫繼承來的變量屬性,使用 override 聲明修飾符標記屬性的聲明即可,詳情請參閱 重寫。

存儲型變量和存儲型變量屬性

使用如下形式聲明一個存儲型變量或存儲型變量屬性:

var 變量名稱: 類型 = 表達式

可以在全局范圍,函數(shù)內(nèi)部,或者在類和結構的聲明中使用這種形式來聲明一個變量。當變量以這種形式在全局范圍或者函數(shù)內(nèi)部被聲明時,它代表一個存儲型變量。當它在類或者結構中被聲明時,它代表一個存儲型變量屬性(stored variable property)

用于初始化的表達式不可以在協(xié)議的聲明中出現(xiàn),在其他情況下,該表達式是可選的。如果沒有初始化表達式,那么變量聲明必須包含類型標注(: type)。

如同常量聲明,如果變量名稱是元組形式,元組中每一項的名稱都會和初始化表達式中對應的值進行綁定。

正如名字所示,存儲型變量和存儲型變量屬性的值會存儲在內(nèi)存中。

計算型變量和計算型屬性

使用如下形式聲明一個計算型變量或計算型屬性:

var 變量名稱: 類型 {
    get {
        語句
    }
    set(setter 名稱) {
        語句
    }
}

可以在全局范圍、函數(shù)內(nèi)部,以及類、結構、枚舉、擴展的聲明中使用這種形式的聲明。當變量以這種形式在全局范圍或者函數(shù)內(nèi)部被聲明時,它表示一個計算型變量。當它在類、結構、枚舉、擴展聲明的上下文中被聲明時,它表示一個計算型屬性(computed property)。

getter 用來讀取變量值,setter 用來寫入變量值。setter 子句是可選的,getter 子句是必須的。不過也可以將這些子句都省略,直接返回請求的值,正如在 只讀計算型屬性 中描述的那樣。但是如果提供了一個 setter 子句,就必須也提供一個 getter 子句。

setter 的圓括號以及 setter 名稱是可選的。如果提供了 setter 名稱,它就會作為 setter 的參數(shù)名稱使用。如果不提供 setter 名稱,setter 的參數(shù)的默認名稱為 newValue,正如在 便捷 setter 聲明 中描述的那樣。

與存儲型變量和存儲型屬性不同,計算型變量和計算型屬性的值不存儲在內(nèi)存中。

要獲得更多關于計算型屬性的信息和例子,請參閱 計算型屬性。

存儲型變量和屬性的觀察器

可以在聲明存儲型變量或?qū)傩詴r提供 willSetdidSet 觀察器。一個包含觀察器的存儲型變量或?qū)傩砸匀缦滦问铰暶鳎?/p>

var 變量名稱: 類型 = 表達式 {
    willSet(setter 名稱) {
        語句
    }
    didSet(setter 名稱) {
        語句
    }
}

可以在全局范圍、函數(shù)內(nèi)部,或者類、結構的聲明中使用這種形式的聲明。當變量以這種形式在全局范圍或者函數(shù)內(nèi)部被聲明時,觀察器表示一個存儲型變量觀察器。當它在類和結構的聲明中被聲明時,觀察器表示一個屬性觀察器。

可以為任何存儲型屬性添加觀察器。也可以通過重寫父類屬性的方式為任何繼承的屬性(無論是存儲型還是計算型的)添加觀察器 ,正如 重寫屬性觀察器 中所描述的。

用于初始化的表達式在類或者結構的聲明中是可選的,但是在其他聲明中則是必須的。如果可以從初始化表達式中推斷出類型信息,那么可以不提供類型標注。

當變量或?qū)傩缘闹当桓淖儠r,willSetdidSet 觀察器提供了一種觀察方法。觀察器會在變量的值被改變時調(diào)用,但不會在初始化時被調(diào)用。

willSet 觀察器只在變量或?qū)傩缘闹当桓淖冎罢{(diào)用。新的值作為一個常量傳入 willSet 觀察器,因此不可以在 willSet 中改變它。didSet 觀察器在變量或?qū)傩缘闹当桓淖兒罅⒓凑{(diào)用。和 willSet 觀察器相反,為了方便獲取舊值,舊值會傳入 didSet 觀察器。這意味著,如果在變量或?qū)傩缘?didiset 觀察器中設置值,設置的新值會取代剛剛在 willSet 觀察器中傳入的那個值。

willSetdidSet 中,圓括號以及其中的 setter 名稱是可選的。如果提供了一個 setter 名稱,它就會作為 willSetdidSet 的參數(shù)被使用。如果不提供 setter 名稱,willSet 觀察器的默認參數(shù)名為 newValuedidSet 觀察器的默認參數(shù)名為 oldValue。

提供了 willSet 時,didSet 是可選的。同樣的,提供了 didSet 時,willSet 則是可選的。

要獲得更多信息以及查看如何使用屬性觀察器的例子,請參閱 屬性觀察器。

類型變量屬性

要聲明一個類型變量屬性,用 static 聲明修飾符標記該聲明。類可以改用 class 聲明修飾符標記類的類型計算型屬性從而允許子類重寫超類的實現(xiàn)。類型屬性在 類型屬性 章節(jié)有詳細討論。

注意

在一個類聲明中,使用關鍵字 static 與同時使用 classfinal 去標記一個聲明的效果相同。

變量聲明語法

變量聲明變量聲明頭 模式構造器列表 變量聲明變量聲明頭 變量名稱 類型標注 代碼塊 變量聲明變量聲明頭 變量名稱 類型標注 getter-setter 代碼塊 變量聲明變量聲明頭 變量名稱 類型標注 getter-setter 關鍵字代碼塊 變量聲明變量聲明頭 變量名稱 構造器 willSet-didSet 代碼塊 變量聲明變量聲明頭 變量名稱 類型標注 構造器可選 willSet-didSet 代碼塊

變量聲明頭特性列表可選 聲明修飾符列表可選 var 變量名稱標識符

getter-setter 代碼塊代碼塊 getter-setter 代碼塊{ getter 子句 setter 子句可選 } getter-setter 代碼塊{ setter 子句 getter 子句 } getter 子句特性列表可選 get 代碼塊 setter 子句特性列表可選 set setter 名稱可選 代碼塊 setter 名稱( 標識符 )

getter-setter 關鍵字代碼塊{ getter 關鍵字子句 setter 關鍵字子句可選 } getter-setter 關鍵字代碼塊{ setter 關鍵字子句 getter 關鍵字子句 } getter 關鍵字子句特性列表可選 get setter 關鍵字子句特性列表可選 set

willSet-didSet 代碼塊{ willSet 子句 didSet 子句可選 } willSet-didSet 代碼塊{ didSet 子句 willSet 子句可選 } willSet 子句特性列表可選 willSet setter 名稱可選 代碼塊 didSet 子句特性列表可選 didSet setter 名稱可選 代碼塊

類型別名聲明

類型別名(type alias) 聲明可以在程序中為一個既有類型聲明一個別名。類型別名聲明語句使用關鍵字 typealias 聲明,遵循如下的形式:

typealias 類型別名 = 現(xiàn)存類型

當聲明一個類型的別名后,可以在程序的任何地方使用“別名”來代替現(xiàn)有類型?,F(xiàn)有類型可以是具有命名的類型或者混合類型。類型別名不產(chǎn)生新的類型,它只是使用別名來引用現(xiàn)有類型。 類型別名聲明可以通過泛型參數(shù)來給一個現(xiàn)有泛型類型提供名稱。類型別名為現(xiàn)有類型的一部分或者全部泛型參數(shù)提供具體類型。例如:

typealias StringDictionary<Value> = Dictionary<String, Value>

// 下列兩個字典擁有同樣的類型
var dictionary1: StringDictionary<Int> = [:]
var dictionary2: Dictionary<String, Int> = [:]

當一個類型別名帶著泛型參數(shù)一起被聲明時,這些參數(shù)的約束必須與現(xiàn)有參數(shù)的約束完全匹配。例如:

typealias DictionaryOfInts<Key: Hashable> = Dictionary<Key, Int>

因為類型別名可以和現(xiàn)有類型相互交換使用,類型別名不可以引入額外的類型約束。 在協(xié)議聲明中,類型別名可以為那些經(jīng)常使用的類型提供一個更短更方便的名稱,例如:

protocol Sequence {
    associatedtype Iterator: IteratorProtocol
    typealias Element = Iterator.Element
}

func sum<T: Sequence>(_ sequence: T) -> Int where T.Element == Int {
    // ...
}

假如沒有類型別名,sum 函數(shù)將必須引用關聯(lián)類型通過 T.Iterator.Element 的形式來替代 T.Element。

另請參閱 協(xié)議關聯(lián)類型聲明。

類型別名聲明語法

類型別名聲明類型別名頭 類型別名賦值 類型別名頭特性列表可選 訪問級別修飾符可選 typealias 類型別名名稱 類型別名名稱標識符 類型別名賦值= 類型

函數(shù)聲明

使用函數(shù)聲明(function declaration) 在程序中引入新的函數(shù)或者方法。在類、結構體、枚舉,或者協(xié)議中聲明的函數(shù)會作為方法。函數(shù)聲明使用關鍵字 func,遵循如下的形式:

func 函數(shù)名稱(參數(shù)列表) -> 返回類型 {
    語句
}

如果函數(shù)返回 Void 類型,返回類型可以省略,如下所示:

func 函數(shù)名稱(參數(shù)列表) {
    語句
}

每個參數(shù)的類型都要標明,因為它們不能被推斷出來。如果您在某個參數(shù)類型前面加上了 inout,那么這個參數(shù)就可以在這個函數(shù)作用域當中被修改。更多關于 inout 參數(shù)的討論,請參閱 輸入輸出參數(shù)。

函數(shù)可以使用元組類型作為返回類型來返回多個值。

函數(shù)定義可以出現(xiàn)在另一個函數(shù)聲明內(nèi)。這種函數(shù)被稱作嵌套函數(shù)(nested function)。更多關于嵌套函數(shù)的討論,請參閱 嵌套函數(shù)。

參數(shù)名

函數(shù)的參數(shù)列表由一個或多個函數(shù)參數(shù)組成,參數(shù)間以逗號分隔。函數(shù)調(diào)用時的參數(shù)順序必須和函數(shù)聲明時的參數(shù)順序一致。最簡單的參數(shù)列表有著如下的形式:

參數(shù)名稱: 參數(shù)類型

一個參數(shù)有一個內(nèi)部名稱,這個內(nèi)部名稱可以在函數(shù)體內(nèi)被使用。還有一個外部名稱,當調(diào)用函數(shù)時這個外部名稱被作為實參的標簽來使用。默認情況下,第一個參數(shù)的外部名稱會被省略,第二個和之后的參數(shù)使用它們的內(nèi)部名稱作為它們的外部名稱。例如:

func f(x: Int, y: Int) -> Int { return x + y }
f(1, y: 2) // 參數(shù) y 有標簽,參數(shù) x 則沒有

可以按照如下兩種形式之一,重寫參數(shù)名稱的默認行為:

外部參數(shù)名稱 內(nèi)部參數(shù)名稱: 參數(shù)類型 _ 內(nèi)部參數(shù)名稱: 參數(shù)類型

在內(nèi)部參數(shù)名稱前的名稱會作為這個參數(shù)的外部名稱,這個名稱可以和內(nèi)部參數(shù)的名稱不同。外部參數(shù)名稱在函數(shù)被調(diào)用時必須被使用,即對應的參數(shù)在方法或函數(shù)被調(diào)用時必須有外部名。

內(nèi)部參數(shù)名稱前的下劃線(_)可使該參數(shù)在函數(shù)被調(diào)用時沒有名稱。在函數(shù)或方法調(diào)用時,對應的參數(shù)必須沒有名字。

func f(x x: Int, withY y: Int, _ z: Int) -> Int { return x + y + z }
f(x: 1, withY: 2, 3) // 參數(shù) x 和 y 是有標簽的,參數(shù) z 則沒有

輸入輸出參數(shù)

輸入輸出參數(shù)被傳遞時遵循如下規(guī)則:

  1. 函數(shù)調(diào)用時,參數(shù)的值被拷貝。
  2. 函數(shù)體內(nèi)部,拷貝后的值被修改。
  3. 函數(shù)返回后,拷貝后的值被賦值給原參數(shù)。

這種行為被稱為拷入拷出(copy-in copy-out)值結果調(diào)用(call by value result)。例如,當一個計算型屬性或者一個具有屬性觀察器的屬性被用作函數(shù)的輸入輸出參數(shù)時,其 getter 會在函數(shù)調(diào)用時被調(diào)用,而其 setter 會在函數(shù)返回時被調(diào)用。

作為一種優(yōu)化手段,當參數(shù)值存儲在內(nèi)存中的物理地址時,在函數(shù)體內(nèi)部和外部均會使用同一內(nèi)存位置。這種優(yōu)化行為被稱為引用調(diào)用(call by reference),它滿足了拷入拷出模式的所有要求,且消除了復制帶來的開銷。在代碼中,要規(guī)范使用拷入拷出模式,不要依賴于引用調(diào)用。

不要使用傳遞給輸入輸出參數(shù)的值,即使原始值在當前作用域中依然可用。當函數(shù)返回時,你對原始值所做的更改會被拷貝的值所覆蓋。不要依賴于引用調(diào)用的優(yōu)化機制來試圖避免這種覆蓋。

不能將同一個值傳遞給多個輸入輸出參數(shù),因為這種情況下的拷貝與覆蓋行為的順序是不確定的,因此原始值的最終值也將無法確定。例如:

var x = 10
func f(inout a: Int, inout _ b: Int) {
    a += 1
    b += 10
}
f(&x, &x) // 編譯報錯 error: inout arguments are not allowed to alias each other

如果嵌套函數(shù)在外層函數(shù)返回后才調(diào)用,嵌套函數(shù)對輸入輸出參數(shù)造成的任何改變將不會影響到原始值。例如:

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()a 遞增后,x 的值并未發(fā)生改變,因為 inner() 在外層函數(shù) outer() 返回后才被調(diào)用。若要改變 x 的值,必須在 outer() 返回前調(diào)用 inner()。

關于輸入輸出參數(shù)的詳細討論,請參閱 輸入輸出參數(shù)。

特殊參數(shù)

參數(shù)可以被忽略,數(shù)量可以不固定,還可以為其提供默認值,使用形式如下:

_ : 參數(shù)類型
參數(shù)名稱: 參數(shù)類型...
參數(shù)名稱: 參數(shù)類型 = 默認參數(shù)值

以下劃線(_)命名的參數(shù)會被顯式忽略,無法在函數(shù)體內(nèi)使用。

一個參數(shù)的基本類型名稱如果緊跟著三個點(...),會被視為可變參數(shù)。一個函數(shù)至多可以擁有一個可變參數(shù),且必須是最后一個參數(shù)。可變參數(shù)會作為包含該參數(shù)類型元素的數(shù)組處理。舉例來講,可變參數(shù) Int... 會作為 [Int] 來處理。關于使用可變參數(shù)的例子,請參閱 可變參數(shù)。

如果在參數(shù)類型后面有一個以等號(=)連接的表達式,該參數(shù)會擁有默認值,即給定表達式的值。當函數(shù)被調(diào)用時,給定的表達式會被求值。如果參數(shù)在函數(shù)調(diào)用時被省略了,就會使用其默認值。

func f(x: Int = 42) -> Int { return x }
f()     // 有效,使用默認值
f(7)    // 有效,提供了值
f(x: 7) // 無效,該參數(shù)沒有外部名稱

特殊方法

枚舉或結構體的方法如果會修改 self,則必須以 mutating 聲明修飾符標記。

子類重寫超類中的方法必須以 override 聲明修飾符標記。重寫方法時不使用 override 修飾符,或者被 override 修飾符修飾的方法并未對超類方法構成重寫,都會導致編譯錯誤。

枚舉或者結構體中的類型方法,要以 static 聲明修飾符標記,而對于類中的類型方法,除了使用 static,還可使用 class 聲明修飾符標記。

拋出錯誤的函數(shù)和方法

可以拋出錯誤的函數(shù)或方法必須使用 throws 關鍵字標記。這類函數(shù)和方法被稱為拋出函數(shù)和拋出方法。它們有著下面的形式:

func 函數(shù)名稱(參數(shù)列表) throws -> 返回類型 {
    語句
}

拋出函數(shù)或拋出方法的調(diào)用必須包裹在 try 或者 try! 表達式中(也就是說,在作用域內(nèi)使用 try 或者 try! 運算符)。

throws 關鍵字是函數(shù)的類型的一部分,非拋出函數(shù)是拋出函數(shù)的子類型。所以,可以在使用拋出函數(shù)的地方使用非拋出函數(shù)。

不能僅基于函數(shù)能否拋出錯誤來進行函數(shù)重載。也就是說,可以基于函數(shù)的函數(shù)類型的參數(shù)能否拋出錯誤來進行函數(shù)重載。

拋出方法不能重寫非拋出方法,而且拋出方法不能滿足協(xié)議對于非拋出方法的要求。也就是說,非拋出方法可以重寫拋出方法,而且非拋出方法可以滿足協(xié)議對于拋出方法的要求。

重拋錯誤的函數(shù)和方法

函數(shù)或方法可以使用 rethrows 關鍵字來聲明,從而表明僅當該函數(shù)或方法的一個函數(shù)類型的參數(shù)拋出錯誤時,該函數(shù)或方法才拋出錯誤。這類函數(shù)和方法被稱為重拋函數(shù)和重拋方法。重新拋出錯誤的函數(shù)或方法必須至少有一個參數(shù)的類型為拋出函數(shù)。

func someFunction(callback: () throws -> Void) rethrows {
    try callback()
}

重拋函數(shù)或者方法不能夠從自身直接拋出任何錯誤,這意味著它不能夠包含 throw 語句。它只能夠傳遞作為參數(shù)的拋出函數(shù)所拋出的錯誤。例如,在 do-catch 代碼塊中調(diào)用拋出函數(shù),并在 catch 子句中拋出其它錯誤都是不允許的。

拋出方法不能重寫重拋方法,而且拋出方法不能滿足協(xié)議對于重拋方法的要求。也就是說,重拋方法可以重寫拋出方法,而且重拋方法可以滿足協(xié)議對于拋出方法的要求。

永不返回的函數(shù)

Swift 定義了 Never 類型,它表示函數(shù)或者方法不會返回給它的調(diào)用者。Never 返回類型的函數(shù)或方法可以稱為不歸,不歸函數(shù)、方法要么引發(fā)不可恢復的錯誤,要么永遠不停地運作,這會使調(diào)用后本應執(zhí)行得代碼就不再執(zhí)行了。但即使是不歸函數(shù)、方法,拋錯函數(shù)和重拋出函數(shù)也可以將程序控制轉(zhuǎn)移到合適的 catch 代碼塊。

不歸函數(shù)、方法可以在 guard 語句的 else 字句中調(diào)用,具體討論在Guard 語句。 你可以重載一個不歸方法,但是新的方法必須保持原有的返回類型和沒有返回的行為。

函數(shù)聲明語法

函數(shù)聲明函數(shù)頭 函數(shù)名 泛型形參子句可選 函數(shù)簽名 函數(shù)體可選

函數(shù)頭特性列表可選 聲明修飾符列表可選 func 函數(shù)名標識符 | 運算符

函數(shù)簽名參數(shù)子句列表 throws可選 函數(shù)結果可選 函數(shù)簽名參數(shù)子句列表 rethrows 函數(shù)結果可選 函數(shù)結果-> 特性列表可選 類型 函數(shù)體代碼塊

參數(shù)子句列表參數(shù)子句 參數(shù)子句列表可選 參數(shù)子句( ) | ( 參數(shù)列表 ) 參數(shù)列表參數(shù) | 參數(shù) , 參數(shù)列表 參數(shù)let可選 外部參數(shù)名可選 內(nèi)部參數(shù)名 類型標注 默認參數(shù)子句可選 參數(shù)inout 外部參數(shù)名可選 內(nèi)部參數(shù)名 類型標注 參數(shù)外部參數(shù)名可選 內(nèi)部參數(shù)名 類型標注 ... 外部參數(shù)名標識符 | _ 內(nèi)部參數(shù)名標識符 | _ 默認參數(shù)子句= 表達式

枚舉聲明

在程序中使用枚舉聲明(enumeration declaration) 來引入一個枚舉類型。

枚舉聲明有兩種基本形式,使用關鍵字 enum 來聲明。枚舉聲明體包含零個或多個值,稱為枚舉用例,還可包含任意數(shù)量的聲明,包括計算型屬性、實例方法、類型方法、構造器、類型別名,甚至其他枚舉、結構體和類。枚舉聲明不能包含析構器或者協(xié)議聲明。

枚舉類型可以采納任意數(shù)量的協(xié)議,但是枚舉不能從類、結構體和其他枚舉繼承。

不同于類或者結構體,枚舉類型并不隱式提供默認構造器,所有構造器必須顯式聲明。一個構造器可以委托給枚舉中的其他構造器,但是構造過程僅當構造器將一個枚舉用例賦值給 self 后才算完成。

和結構體類似但是和類不同,枚舉是值類型。枚舉實例在被賦值到變量或常量時,或者傳遞給函數(shù)作為參數(shù)時會被復制。更多關于值類型的信息,請參閱 結構體和枚舉是值類型。

可以擴展枚舉類型,正如在 擴展聲明 中討論的一樣。

任意類型的枚舉用例

如下的形式聲明了一個包含任意類型枚舉用例的枚舉變量:

enum 枚舉名稱: 采納的協(xié)議 {
    case 枚舉用例1
    case 枚舉用例2(關聯(lián)值類型)
}

這種形式的枚舉聲明在其他語言中有時被叫做可識別聯(lián)合。

在這種形式中,每個用例塊由關鍵字 case 開始,后面緊接一個或多個以逗號分隔的枚舉用例。每個用例名必須是獨一無二的。每個用例也可以指定它所存儲的指定類型的值,這些類型在關聯(lián)值類型的元組中被指定,緊跟用例名之后。

具有關聯(lián)值的枚舉用例可以像函數(shù)一樣使用,通過指定的關聯(lián)值創(chuàng)建枚舉實例。和真正的函數(shù)一樣,你可以獲取枚舉用例的引用,然后在后續(xù)代碼中調(diào)用它。

enum Number {
    case Integer(Int)
    case Real(Double)
}

// f 的類型為 (Int) -> Number
let f = Number.Integer

// 利用 f 把一個整數(shù)數(shù)組轉(zhuǎn)成 Number 數(shù)組
let evenInts: [Number] = [0, 2, 4, 6].map(f)

要獲得更多關于具有關聯(lián)值的枚舉用例的信息和例子,請參閱 關聯(lián)值。

遞歸枚舉

枚舉類型可以具有遞歸結構,就是說,枚舉用例的關聯(lián)值類型可以是枚舉類型自身。然而,枚舉類型的實例具有值語義,這意味著它們在內(nèi)存中有固定布局。為了支持遞歸,編譯器必須插入一個間接層。

要讓某個枚舉用例支持遞歸,使用 indirect 聲明修飾符標記該用例。

enum Tree<T> {
    case Empty
    indirect case Node(value: T, left: Tree, right:Tree)
}

要讓一個枚舉類型的所有用例都支持遞歸,使用 indirect 修飾符標記整個枚舉類型,當枚舉有多個用例且每個用例都需要使用 indirect 修飾符標記的時候這將非常便利。

indirect 修飾符標記的枚舉用例必須有一個關聯(lián)值。使用 indirect 修飾符標記的枚舉類型可以既包含有關聯(lián)值的用例,同時還可包含沒有關聯(lián)值的用例。但是,它不能再單獨使用 indirect 修飾符來標記某個用例。

擁有原始值的枚舉用例

以下形式聲明了一種枚舉類型,其中各個枚舉用例的類型均為同一種基本類型:

enum 枚舉名稱: 原始值類型, 采納的協(xié)議 {
    case 枚舉用例1 = 原始值1
    case 枚舉用例2 = 原始值2
}

在這種形式中,每一個用例塊由 case 關鍵字開始,后面緊跟一個或多個以逗號分隔的枚舉用例。和第一種形式的枚舉用例不同,這種形式的枚舉用例包含一個基礎值,叫做原始值,各個枚舉用例的原始值的類型必須相同。這些原始值的類型通過原始值類型指定,必須表示一個整數(shù)、浮點數(shù)、字符串或者字符。原始值類型必須符合 Equatable 協(xié)議和下列字面量轉(zhuǎn)換協(xié)議中的一種:整型字面量需符合 IntergerLiteralConvertible 協(xié)議,浮點型字面量需符合 FloatingPointLiteralConvertible 協(xié)議,包含任意數(shù)量字符的字符串型字面量需符合 StringLiteralConvertible 協(xié)議,僅包含一個單一字符的字符串型字面量需符合 ExtendedGraphemeClusterLiteralConvertible 協(xié)議。每一個用例的名字和原始值必須唯一。

如果原始值類型被指定為 Int,則不必為用例顯式地指定原始值,它們會隱式地被賦值 0、12 等。每個未被賦值的 Int 類型的用例會被隱式地賦值,其值為上一個用例的原始值加 1。

enum ExampleEnum: Int {
    case A, B, C = 5, D
}

在上面的例子中,ExampleEnum.A 的原始值是 0ExampleEnum.B 的原始值是 1。因為 ExampleEnum.C 的原始值被顯式地設定為 5,因此 ExampleEnum.D 的原始值會自動增長為 6。

如果原始值類型被指定為 String 類型,你不用明確地為用例指定原始值,每個沒有指定原始值的用例會隱式地將用例名字作為原始值。

enum WeekendDay: String {
    case Saturday, Sunday
}

在上面這個例子中,WeekendDay.Saturday 的原始值是 "Saturday",WeekendDay.Sunday 的原始值是 "Sunday"

枚舉用例具有原始值的枚舉類型隱式地符合定義在 Swift 標準庫中的 RawRepres