# 聲明(Declarations) {#declarations}
*聲明(declaration)* 用以向程序里引入新的名字或者結構。舉例來說,可以使用聲明來引入函數和方法,變量和常量,或者定義新的具有命名的枚舉、結構體、類和協議類型。還可以使用聲明來擴展一個既有的具有命名的類型的行為,或者在程序里引入在其它地方聲明的符號。
在 Swift 中,大多數聲明在某種意義上講也是定義,因為它們在聲明時往往伴隨著實現或初始化。由于協議并不提供實現,大多數協議成員僅僅只是聲明而已。為了方便起見,也是因為這些區別在 Swift 中并不是很重要,“聲明”這個術語同時包含了聲明和定義兩種含義。
> 聲明語法
>
>
#### declaration {#declaration}
>
> *聲明* → [導入聲明](#import-declaration)
>
> *聲明* → [常量聲明](#constant-declaration)
>
> *聲明* → [變量聲明](#variable-declaration)
>
> *聲明* → [類型別名聲明](#typealias-declaration)
>
> *聲明* → [函數聲明](#function-declaration)
>
> *聲明* → [枚舉聲明](#enum-declaration)
>
> *聲明* → [結構體聲明](#struct-declaration)
>
> *聲明* → [類聲明](#class-declaration)
>
> *聲明* → [協議聲明](#protocol-declaration)
>
> *聲明* → [構造器聲明](#initializer-declaration)
>
> *聲明* → [析構器聲明](#deinitializer-declaration)
>
> *聲明* → [擴展聲明](#extension-declaration)
>
> *聲明* → [下標聲明](#subscript-declaration)
>
> *聲明* → [運算符聲明](#operator-declaration)
>
>
#### declarations {#declarations}
>
> *多條聲明* → [聲明](#declaration) [多條聲明](#declarations)<sub>可選</sub>
>
## 頂級代碼 {#top-level-code}
Swift 的源文件中的頂級代碼(top-level code)由零個或多個語句、聲明和表達式組成。默認情況下,在一個源文件的頂層聲明的變量,常量和其他具有命名的聲明可以被同模塊中的每一個源文件中的代碼訪問。可以使用一個訪問級別修飾符來標記聲明來覆蓋這種默認行為,請參閱 [訪問控制級別](#access-control-levels)。
> 頂級聲明語法
>
> *頂級聲明* → [多條語句](./05_Statements.md#statements)<sub>可選</sub>
>
## 代碼塊 {#code-blocks}
*代碼塊(code block)* 可以將一些聲明和控制結構體組織在一起。它有如下的形式:
```swift
{
語句
}
```
代碼塊中的“語句”包括聲明、表達式和各種其他類型的語句,它們按照在源碼中的出現順序被依次執行。
> 代碼塊語法
>
>
#### code-block {#code-block}
>
> *代碼塊* → **{** [多條語句](./05_Statements.md#statements)<sub>可選</sub> **}**
>
## 導入聲明 {#import-declaration}
*導入聲明(import declaration)* 讓你可以使用在其他文件中聲明的內容。導入語句的基本形式是導入整個模塊,它由 `import` 關鍵字和緊隨其后的模塊名組成:
```swift
import 模塊
```
可以對導入操作提供更細致的控制,如指定一個特殊的子模塊或者指定一個模塊或子模塊中的某個聲明。提供了這些限制后,在當前作用域中,只有被導入的符號是可用的,而不是整個模塊中的所有聲明。
```swift
import 導入類型 模塊.符號名
import 模塊.子模塊
```
#### grammer-of-an-import-declaration {#grammer-of-an-import-declaration}
> 導入聲明語法
>
>
#### import-declaration {#import-declaration}
>
> *導入聲明* → [特性列表](./07_Attributes.md#attributes)<sub>可選</sub> **import** [導入類型](#import-kind)<sub>可選</sub> [導入路徑](#import-path)
>
>
#### import-kind {#import-kind}
>
> *導入類型* → **typealias** | **struct** | **class** | **enum** | **protocol** | **let** | **var** | **func**
>
>
#### import-path {#import-path}
>
> *導入路徑* → [導入路徑標識符](#import-path-identifier) | [導入路徑標識符](#import-path-identifier) **.** [導入路徑](#import-path)
>
>
#### import-path-identifier {#import-path-identifier}
>
> *導入路徑標識符* → [標識符](./02_Lexical_Structure.md#identifier) | [運算符](./02_Lexical_Structure.md#operator)
>
## 常量聲明 {#constant-declaration}
*常量聲明(constant declaration)* 可以在程序中引入一個具有命名的常量。常量以關鍵字 `let` 來聲明,遵循如下格式:
```swift
let 常量名稱: 類型 = 表達式
```
常量聲明在“常量名稱”和用于初始化的“表達式”的值之間定義了一種不可變的綁定關系;當常量的值被設定之后,它就無法被更改。這意味著,如果常量以類對象來初始化,對象本身的內容是可以改變的,但是常量和該對象之間的綁定關系是不能改變的。
當一個常量被聲明為全局常量時,它必須擁有一個初始值。在函數或者方法中聲明一個常量時,它并不需要擁有一個初始值,只需要保證在第一次對其進行讀操作之前為其設置一個值。在類或者結構體中聲明一個常量時,它將作為*常量屬性(constant property)*。常量聲明不能是計算型屬性,因此也沒有存取方法。
如果常量名稱是元組形式,元組中每一項的名稱都會和初始化表達式中對應的值進行綁定。
```swift
let (firstNumber, secondNumber) = (10, 42)
```
在上例中,`firstNumber` 是一個值為 `10` 的常量,`secnodeName` 是一個值為 `42` 的常量。所有常量都可以獨立地使用:
```swift
print("The first number is \(firstNumber).")
// 打印“The first number is 10.”
print("The second number is \(secondNumber).")
// 打印“The second number is 42.”
```
當常量名稱的類型(`:` 類型)可以被推斷出時,類型注解在常量聲明中是可選的,正如 [類型推斷](./03_Types.md#type-inference) 中所描述的。
聲明一個常量類型屬性要使用 `static` 聲明修飾符。類的常量類型屬性總是隱式地被標記為 `final` ;你無法用 `class` 或 `final` 聲明修飾符實現允許或禁止被子類重寫的目的。類型屬性在 [類型屬性](../02_language_guide/10_Properties.md#type-properties) 中有介紹。
如果還想獲得更多關于常量的信息或者想在使用中獲得幫助,請參閱 [常量和變量](../02_language_guide/01_The_Basics.md#constants-and-variables) 和 [存儲屬性](../02_language_guide/10_Properties.md#stored-properties)。
#### grammer-of-a-constant-declaration {#grammer-of-a-constant-declaration}
> 常量聲明語法
>
>
#### constant-declaration {#constant-declaration}
>
> *常量聲明* → [特性列表](./07_Attributes.md#attributes)<sub>可選</sub> [聲明修飾符列表](#declaration-modifiers)<sub>可選</sub> **let** [模式構造器列表](#pattern-initializer-list)
>
>
#### pattern-initializer-list {#pattern-initializer-list}
>
> *模式構造器列表* → [模式構造器](#pattern-initializer) | [模式構造器](#pattern-initializer) **,** [模式構造器列表](#pattern-initializer-list)
>
>
#### pattern-initializer {#pattern-initializer}
>
> *模式構造器* → [模式](./08_Patterns.md#pattern) [構造器](#initializer)<sub>可選</sub>
>
>
#### initializer {#initializer}
>
> *構造器* → **=** [表達式](./04_Expressions.md#expression)
>
## 變量聲明 {#variable-declaration}
*變量聲明(variable declaration)* 可以在程序中引入一個具有命名的變量,它以關鍵字 `var` 來聲明。
變量聲明有幾種不同的形式,可以聲明不同種類的命名值和可變值,如存儲型和計算型變量和屬性,屬性觀察器,以及靜態變量屬性。所使用的聲明形式取決于變量聲明的適用范圍和打算聲明的變量類型。
> 注意
>
> 也可以在協議聲明中聲明屬性,詳情請參閱 [協議屬性聲明](#protocol-property-declaration)。
>
可以在子類中重寫繼承來的變量屬性,使用 `override` 聲明修飾符標記屬性的聲明即可,詳情請參閱 [重寫](../02_language_guide/13_Inheritance.md#overriding)。
### 存儲型變量和存儲型變量屬性 {#stored-variables-and-stored-variable-properties}
使用如下形式聲明一個存儲型變量或存儲型變量屬性:
```swift
var 變量名稱: 類型 = 表達式
```
可以在全局范圍,函數內部,或者在類和結構體的聲明中使用這種形式來聲明一個變量。當變量以這種形式在全局范圍或者函數內部被聲明時,它代表一個存儲型變量。當它在類或者結構體中被聲明時,它代表一個*存儲型變量屬性(stored variable property)*。
用于初始化的表達式不可以在協議的聲明中出現,在其他情況下,該表達式是可選的。如果沒有初始化表達式,那么變量聲明必須包含類型注解(`:` *type*)。
如同常量聲明,如果變量名稱是元組形式,元組中每一項的名稱都會和初始化表達式中對應的值進行綁定。
正如名字所示,存儲型變量和存儲型變量屬性的值會存儲在內存中。
### 計算型變量和計算型屬性 {#computed-variables-and-computed-properties}
使用如下形式聲明一個計算型變量或計算型屬性:
```swift
var 變量名稱: 類型 {
get {
語句
}
set(setter 名稱) {
語句
}
}
```
可以在全局范圍、函數內部,以及類、結構體、枚舉、擴展的聲明中使用這種形式的聲明。當變量以這種形式在全局范圍或者函數內部被聲明時,它表示一個計算型變量。當它在類、結構體、枚舉、擴展聲明的上下文中被聲明時,它表示一個*計算型屬性(computed property)*。
getter 用來讀取變量值,setter 用來寫入變量值。setter 子句是可選的,getter 子句是必須的。不過也可以將這些子句都省略,直接返回請求的值,正如在 [只讀計算型屬性](../02_language_guide/10_Properties.md#computed-properties) 中描述的那樣。但是如果提供了一個 setter 子句,就必須也提供一個 getter 子句。
setter 的圓括號以及 setter 名稱是可選的。如果提供了 setter 名稱,它就會作為 setter 的參數名稱使用。如果不提供 setter 名稱,setter 的參數的默認名稱為 `newValue`,正如在 [便捷 setter 聲明](../02_language_guide/10_Properties.md#shorthand-setter-declaration) 中描述的那樣。
與存儲型變量和存儲型屬性不同,計算型變量和計算型屬性的值不存儲在內存中。
要獲得更多關于計算型屬性的信息和例子,請參閱 [計算型屬性](../02_language_guide/10_Properties.md#computed-properties)。
### 存儲型變量和屬性的觀察器 {#stored-variable-observers-and-property-observers}
可以在聲明存儲型變量或屬性時提供 `willSet` 和 `didSet` 觀察器。一個包含觀察器的存儲型變量或屬性以如下形式聲明:
```swift
var 變量名稱: 類型 = 表達式 {
willSet(setter 名稱) {
語句
}
didSet(setter 名稱) {
語句
}
}
```
可以在全局范圍、函數內部,或者類、結構體的聲明中使用這種形式的聲明。當變量以這種形式在全局范圍或者函數內部被聲明時,觀察器表示一個存儲型變量觀察器。當它在類和結構體的聲明中被聲明時,觀察器表示一個屬性觀察器。
可以為任何存儲型屬性添加觀察器。也可以通過重寫父類屬性的方式為任何繼承的屬性(無論是存儲型還是計算型的)添加觀察器
,正如 [重寫屬性觀察器](../02_language_guide/13_Inheritance.md#overriding-property-observers) 中所描述的。
用于初始化的表達式在類或者結構的聲明中是可選的,但是在其他聲明中則是必須的。如果可以從初始化表達式中推斷出類型信息,那么可以不提供類型注解。
當變量或屬性的值被改變時,`willSet` 和 `didSet` 觀察器提供了一種觀察方法。觀察器會在變量的值被改變時調用,但不會在初始化時被調用。
`willSet` 觀察器只在變量或屬性的值被改變之前調用。新的值作為一個常量傳入 `willSet` 觀察器,因此不可以在 `willSet` 中改變它。`didSet` 觀察器在變量或屬性的值被改變后立即調用。和 `willSet` 觀察器相反,為了方便獲取舊值,舊值會傳入 `didSet` 觀察器。這意味著,如果在變量或屬性的 `didiset` 觀察器中設置值,設置的新值會取代剛剛在 `willSet` 觀察器中傳入的那個值。
在 `willSet` 和 `didSet` 中,圓括號以及其中的 setter 名稱是可選的。如果提供了一個 setter 名稱,它就會作為 `willSet` 和 `didSet` 的參數被使用。如果不提供 setter 名稱,`willSet` 觀察器的默認參數名為 `newValue`,`didSet` 觀察器的默認參數名為 `oldValue`。
提供了 `willSet` 時,`didSet` 是可選的。同樣的,提供了 `didSet` 時,`willSet` 則是可選的。
要獲得更多信息以及查看如何使用屬性觀察器的例子,請參閱 [屬性觀察器](../02_language_guide/10_Properties.md#property-observers)。
### 類型變量屬性 {#type-variable-properties}
要聲明一個類型變量屬性,用 `static` 聲明修飾符標記該聲明。類可以改用 `class` 聲明修飾符標記類的類型計算型屬性從而允許子類重寫超類的實現。類型屬性在 [類型屬性](../02_language_guide/10_Properties.md#type-properties) 章節有詳細討論。
#### grammer-of-a-variable-declaration {#grammer-of-a-variable-declaration}
> 變量聲明語法
>
#### variable-declaration {#variable-declaration}
> *變量聲明* → [變量聲明頭](#variable-declaration-head) [模式構造器列表](#pattern-initializer-list)
>
> *變量聲明* → [變量聲明頭](#variable-declaration-head) [變量名稱](#variable-name) [類型注解](./03_Types.md#type-annotation) [代碼塊](#code-block)
>
> *變量聲明* → [變量聲明頭](#variable-declaration-head) [變量名稱](#variable-name) [類型注解](./03_Types.md#type-annotation) [getter-setter 代碼塊](#getter-setter-block)
>
> *變量聲明* → [變量聲明頭](#variable-declaration-head) [變量名稱](#variable-name) [類型注解](./03_Types.md#type-annotation) [getter-setter 關鍵字代碼塊](#getter-setter-keyword-block)
>
> *變量聲明* → [變量聲明頭](#variable-declaration-head) [變量名稱](#variable-name) [構造器](#initializer) [willSet-didSet 代碼塊](#willSet-didSet-block)
>
> *變量聲明* → [變量聲明頭](#variable-declaration-head) [變量名稱](#variable-name) [類型注解](./03_Types.md#type-annotation) [構造器](#initializer)<sub>可選</sub> [willSet-didSet 代碼塊](#willSet-didSet-block)
>
#### variable-declaration-head {#variable-declaration-head}
> *變量聲明頭* → [特性列表](./07_Attributes.md#attributes)<sub>可選</sub> [聲明修飾符列表](#declaration-modifiers)<sub>可選</sub> **var**
>
>
#### variable-name {#variable-name}
>
> *變量名稱* → [標識符](./02_Lexical_Structure.md#identifier)
>
#### getter-setter-block {#getter-setter-block}
> *getter-setter 代碼塊* → [代碼塊](#code-block)
>
> *getter-setter 代碼塊* → **{** [getter 子句](#getter-clause) [setter 子句](#setter-clause)<sub>可選</sub> **}**
>
> *getter-setter 代碼塊* → **{** [setter 子句](#setter-clause) [getter 子句](#getter-clause) **}**
>
>
#### getter-clause {#getter-clause}
>
> *getter 子句* → [特性列表](./07_Attributes.md#attributes)<sub>可選</sub> **get** [代碼塊](#code-block)
>
>
#### setter-clause {#setter-clause}
>
> *setter 子句* → [特性列表](./07_Attributes.md#attributes)<sub>可選</sub> **set** [setter 名稱](#setter-name)<sub>可選</sub> [代碼塊](#code-block)
>
>
#### setter-name {#setter-name}
>
> *setter 名稱* → **(** [標識符](./02_Lexical_Structure.md#identifier) **)**
>
#### getter-setter-keyword-block {#getter-setter-keyword-block}
> *getter-setter 關鍵字代碼塊* → **{** [getter 關鍵字子句](#getter-keyword-clause) [setter 關鍵字子句](#setter-keyword-clause)<sub>可選</sub> **}**
>
> *getter-setter 關鍵字代碼塊* → **{** [setter 關鍵字子句](#setter-keyword-clause) [getter 關鍵字子句](#getter-keyword-clause) **}**
>
>
#### getter-keyword-clause {#getter-keyword-clause}
>
> *getter 關鍵字子句* → [特性列表](./07_Attributes.md#attributes)<sub>可選</sub> **get**
>
>
#### setter-keyword-clause {#setter-keyword-clause}
>
> *setter 關鍵字子句* → [特性列表](./07_Attributes.md#attributes)<sub>可選</sub> **set**
>
#### willSet-didSet-block {#willSet-didSet-block}
> *willSet-didSet 代碼塊* → **{** [willSet 子句](#willSet-clause) [didSet 子句](#didSet-clause)<sub>可選</sub> **}**
>
> *willSet-didSet 代碼塊* → **{** [didSet 子句](#didSet-clause) [willSet 子句](#willSet-clause)<sub>可選</sub> **}**
>
>
#### willSet-clause {#willSet-clause}
>
> *willSet 子句* → [特性列表](./07_Attributes.md#attributes)<sub>可選</sub> **willSet** [setter 名稱](#setter-name)<sub>可選</sub> [代碼塊](#code-block)
>
>
#### didSet-clause {#didSet-clause}
>
> *didSet 子句* → [特性列表](./07_Attributes.md#attributes)<sub>可選</sub> **didSet** [setter 名稱](#setter-name)<sub>可選</sub> [代碼塊](#code-block)
>
## 類型別名聲明 {#type-alias-declaration}
*類型別名(type alias)* 聲明可以在程序中為一個既有類型聲明一個別名。類型別名聲明語句使用關鍵字 `typealias` 聲明,遵循如下的形式:
```swift
typealias 類型別名 = 現存類型
```
當聲明一個類型的別名后,可以在程序的任何地方使用“別名”來代替現有類型。現有類型可以是具有命名的類型或者混合類型。類型別名不產生新的類型,它只是使用別名來引用現有類型。
類型別名聲明可以通過泛型參數來給一個現有泛型類型提供名稱。類型別名為現有類型的一部分或者全部泛型參數提供具體類型。例如:
```swift
typealias StringDictionary<Value> = Dictionary<String, Value>
// 下列兩個字典擁有同樣的類型
var dictionary1: StringDictionary<Int> = [:]
var dictionary2: Dictionary<String, Int> = [:]
```
當一個類型別名帶著泛型參數一起被聲明時,這些參數的約束必須與現有參數的約束完全匹配。例如:
```swift
typealias DictionaryOfInts<Key: Hashable> = Dictionary<Key, Int>
```
因為類型別名可以和現有類型相互交換使用,類型別名不可以引入額外的類型約束。
如果在聲明處省略所有泛型參數,一個類型別名可以傳遞已有類型的所有泛型參數。例如,此處聲明的 `Diccionario` 類型別名擁有和 `Dictionary` 同樣的約束和泛型參數。
```swift
typealias Diccionario = Dictionary
```
在協議聲明中,類型別名可以為那些經常使用的類型提供一個更短更方便的名稱,例如:
```swift
protocol Sequence {
associatedtype Iterator: IteratorProtocol
typealias Element = Iterator.Element
}
func sum<T: Sequence>(_ sequence: T) -> Int where T.Element == Int {
// ...
}
```
假如沒有類型別名,sum 函數將必須引用關聯類型通過 T.Iterator.Element 的形式來替代 T.Element。
另請參閱 [協議關聯類型聲明](#protocol-associated-type-declaration)。
#### grammer-of-a-type-alias-declaration {#grammer-of-a-type-alias-declaration}
> 類型別名聲明語法
>
>
>
#### typealias-declaration {#typealias-declaration}
>
> *類型別名聲明* → [特性列表](./07_Attributes.md#attributes)<sub>可選</sub> [訪問級別修飾符](#access-level-modifier)<sub>可選</sub> **typealias** [類型別名名稱](#typealias-name) [類型別子句](#typealias-clause) [類型別名賦值](#typealias-assignment)
>
>
#### typealias-name {#typealias-name}
>
> *類型別名名稱* → [標識符](./02_Lexical_Structure.md#identifier)
>
>
#### typealias-assignment {#typealias-assignment}
>
> *類型別名賦值* → **=** [類型](./03_Types.md#type)
>
## 函數聲明 {#function-declaration}
使用*函數聲明(function declaration)* 在程序中引入新的函數或者方法。在類、結構體、枚舉,或者協議中聲明的函數會作為方法。函數聲明使用關鍵字 `func`,遵循如下的形式:
```swift
func 函數名稱(參數列表) -> 返回類型 {
語句
}
```
如果函數返回 `Void` 類型,返回類型可以省略,如下所示:
```swift
func 函數名稱(參數列表) {
語句
}
```
每個參數的類型都要標明,因為它們不能被推斷出來。如果您在某個參數類型前面加上了 `inout`,那么這個參數就可以在這個函數作用域當中被修改。更多關于 `inout` 參數的討論,請參閱 [輸入輸出參數](#in-out-parameters)。
函數聲明中語句只包含一個表達式,可以理解為返回該表達式的值。
函數可以使用元組類型作為返回類型來返回多個值。
函數定義可以出現在另一個函數聲明內。這種函數被稱作*嵌套函數(nested function)*。
大多數時候,嵌套函數都是可逃逸的函數。僅當一個嵌套函數捕獲了某個確保了永不逃逸的值——例如一個輸入輸出參數——或者傳入一個非逃逸函數參數的時候,這個嵌套函數才是非逃逸的。
更多關于嵌套函數的討論,請參閱 [嵌套函數](../02_language_guide/06_Functions.md#Nested-Functions)。
### 參數名 {#parameter-names}
函數的參數列表由一個或多個函數參數組成,參數間以逗號分隔。函數調用時的參數順序必須和函數聲明時的參數順序一致。最簡單的參數列表有著如下的形式:
`參數名稱`: `參數類型`
每個參數有一個參數名稱,這個名稱與實參標簽一樣都可以在函數體內被使用。默認情況下,參數名也會被作為實參標簽來使用。例如:
```swift
func f(x: Int, y: Int) -> Int { return x + y }
f(x: 1, y: 2) // 參數 x 和 y 都有標簽
```
可以按照如下兩種形式之一,重寫參數名稱的默認行為:
`實參標簽` `參數名稱`: `參數類型`
_ `參數名稱`: `參數類型`
在參數名稱前的名稱會作為這個參數的顯式實參標簽,它可以和參數名稱不同。在函數或方法調用時,相對應的參數必須使用這個實參標簽。
參數名稱前的下劃線(`_`)可以去除參數的實參標簽。在函數或方法調用時,相對應的參數必須去除標簽。
```swift
func repeatGreeting(_ greeting: String, count n: Int) { /* Greet n times */ }
repeatGreeting("Hello, world!", count: 2) // count 有標簽, greeting 沒有
```
### 輸入輸出參數 {#in-out-parameters}
輸入輸出參數被傳遞時遵循如下規則:
1. 函數調用時,參數的值被拷貝。
2. 函數體內部,拷貝后的值被修改。
3. 函數返回后,拷貝后的值被賦值給原參數。
這種行為被稱為*拷入拷出(copy-in copy-out)* 或*值結果調用(call by value result)*。例如,當一個計算型屬性或者一個具有屬性觀察器的屬性被用作函數的輸入輸出參數時,其 getter 會在函數調用時被調用,而其 setter 會在函數返回時被調用。
作為一種優化手段,當參數值存儲在內存中的物理地址時,在函數體內部和外部均會使用同一內存位置。這種優化行為被稱為*引用調用(call by reference)*,它滿足了拷入拷出模式的所有要求,且消除了復制帶來的開銷。在代碼中,要規范使用拷入拷出模式,不要依賴于引用調用。
不要使用傳遞給輸入輸出參數的值,即使原始值在當前作用域中依然可用。當函數返回時,你對原始值所做的更改會被拷貝的值所覆蓋。不要依賴于引用調用的優化機制來試圖避免這種覆蓋。
不能將同一個值傳遞給多個輸入輸出參數,因為這種情況下的拷貝與覆蓋行為的順序是不確定的,因此原始值的最終值也將無法確定。
更多關于內存安全和內存獨占權的討論,請參閱 [內存安全](../02_language_guide/24_Memory_Safety.md)。
如果一個閉包或者嵌套函數捕獲了一個輸入輸出參數,那么這個閉包或者嵌套函數必須是非逃逸的。如果你需要捕獲一個輸入輸出參數,但并不對其進行修改或者在其他代碼中觀察其值變化,那么你可以使用捕獲列表來顯式地表明這是個不可變捕獲。
```swift
func someFunction(a: inout Int) -> () -> Int {
return { [a] in return a + 1 }
}
```
如果你需要捕獲并修改一個輸入輸出參數,使用一個顯式局部拷貝來進行修改操作,在一些例如多線程的場景中,這樣做可以確保函數返回之前所有的修改都已完成。
如果嵌套函數在外層函數返回后才調用,嵌套函數對輸入輸出參數造成的任何改變將不會影響到原始值。例如:
```swift
func multithreadedFunction(queue: DispatchQueue, x: inout Int) {
// 創建一個局部拷貝并在適當時候手動拷貝回去
var localX = x
defer { x = localX }
// 并行地操作 localX,然后在函數返回前一直等待
queue.async { someMutatingOperation(&localX) }
queue.sync {}
}
```
關于輸入輸出參數的詳細討論,請參閱 [輸入輸出參數](../02_language_guide/06_Functions.md#in-out-parameters)。
### 特殊參數 {#special-kinds-of-parameters}
參數可以被忽略,數量可以不固定,還可以為其提供默認值,使用形式如下:
```swift
_ : 參數類型
參數名稱: 參數類型...
參數名稱: 參數類型 = 默認參數值
```
以下劃線(`_`)命名的參數會被顯式忽略,無法在函數內使用。
一個參數的基本類型名稱如果緊跟著三個點(`...`),會被視為可變參數。一個函數至多可以擁有一個可變參數,且必須是最后一個參數。可變參數會作為包含該參數類型元素的數組處理。舉例來講,可變參數 `Int...` 會作為 `[Int]` 來處理。關于使用可變參數的例子,請參閱 [可變參數](../02_language_guide/06_Functions.md#variadic-parameters)。
如果在參數類型后面有一個以等號(`=`)連接的表達式,該參數會擁有默認值,即給定表達式的值。當函數被調用時,給定的表達式會被求值。如果參數在函數調用時被省略了,就會使用其默認值。
```swift
func f(x: Int = 42) -> Int { return x }
f() // 有效,使用默認值
f(7) // 有效,提供了值
f(x: 7) // 無效,該參數沒有外部名稱
```
### 特殊方法 {#special-kinds-of-methods}
枚舉或結構體的方法如果會修改 `self`,則必須以 `mutating` 聲明修飾符標記。
子類重寫超類中的方法必須以 `override` 聲明修飾符標記。重寫方法時不使用 `override` 修飾符,或者被 `override` 修飾符修飾的方法并未對超類方法構成重寫,都會導致編譯錯誤。
枚舉或者結構體中的類型方法,要以 `static` 聲明修飾符標記,而對于類中的類型方法,除了使用 `static`,還可使用 `class` 聲明修飾符標記。類中使用 `class` 聲明修飾的方法可以被子類實現重寫;類中使用 `class final` 或 `static` 聲明修飾的方法不可被重寫。
### 特殊名稱方法 {#methods-with-special-names}
一些含有特殊名稱的方法允許使用函數調用語法糖。如果一個類型定義了某個此類型的方法,那這些類型的實例對象都可以使用函數調用語法。這些函數調用會被解析為某個具有特殊名稱的實例方法調用。
如同 [dynamicCallable](./07_Attributes.md#dynamicCallable) 中描述的一樣,只要定義了 `dynamicallyCall(withArguments:)` 方法或者 `dynamicallyCall(withKeywordArguments:)` 方法,一個類、結構體或者枚舉類型都支持函數調用語法。同時如下面的描述一樣,定義了一個函數調用方法(call-as-function method)也可以達到上述效果。如果一個類型同時定義了一個函數調用方法和使用 `dynamicCallable` 屬性的方法,那么在合適的情況下,編譯器會優先使用函數調用方法。
函數調用方法的名稱是 `callAsFunction()`,或者任意一個以 `callAsFunction(` 開頭并跟隨著一些已標簽化或未標簽化的參數——例如 `callAsFunction(_:_:)` 和 `callAsFunction(something:)` 都是合法的函數調用方法名稱。
如下的函數調用是相同的:
```swift
struct CallableStruct {
var value: Int
func callAsFunction(_ number: Int, scale: Int) {
print(scale * (number + value))
}
}
let callable = CallableStruct(value: 100)
callable(4, scale: 2)
callable.callAsFunction(4, scale: 2)
// 兩次函數調用都會打印 208
```
函數調用方法和使用 `dynamicCallable` 屬性定義的方法在編碼到類型系統中的信息量和運行時的動態行為能力上會有些差別。當你定義了一個函數調用方法時,你需要指定參數的數量,以及每個參數的類型和標簽。與此不同的是,`dynamicCallable` 屬性定義的方法只需要指定用于承載參數的數組類型。
函數調用方法或 `dynamicCallable` 屬性定義的方法并不允許你在任何上下文中把實例對象作為函數類型來處理。示例代碼如下:
``` swift
let someFunction1: (Int, Int) -> Void = callable(_:scale:) // Error
let someFunction2: (Int, Int) -> Void = callable.callAsFunction(_:scale:)
```
如 [dynamicmemberlookup](./07_Attributes.md#dynamicmemberlookup) 描述的一樣,`subscript(dynamicMemberLookup:)` 下標允許成員查找的語法糖。
### 拋出錯誤的函數和方法 {#throwing-functions-and-methods}
可以拋出錯誤的函數或方法必須使用 `throws` 關鍵字標記。這類函數和方法被稱為拋出函數和拋出方法。它們有著下面的形式:
```swift
func 函數名稱(參數列表) throws -> 返回類型 {
語句
}
```
拋出函數或拋出方法的調用必須包裹在 `try` 或者 `try!` 表達式中(也就是說,在作用域內使用 `try` 或者 `try!` 運算符)。
`throws` 關鍵字是函數的類型的一部分,非拋出函數是拋出函數的子類型。所以,可以在使用拋出函數的地方使用非拋出函數。
不能僅基于函數能否拋出錯誤來進行函數重寫。也就是說,可以基于函數的函數類型的參數能否拋出錯誤來進行函數重寫。
拋出方法不能重寫非拋出方法,而且拋出方法不能滿足協議對于非拋出方法的要求。也就是說,非拋出方法可以重寫拋出方法,而且非拋出方法可以滿足協議對于拋出方法的要求。
### 重拋錯誤的函數和方法 {#rethrowing-functions-and-methods}
函數或方法可以使用 `rethrows` 關鍵字來聲明,從而表明僅當該函數或方法的一個函數類型的參數拋出錯誤時,該函數或方法才拋出錯誤。這類函數和方法被稱為重拋函數和重拋方法。重新拋出錯誤的函數或方法必須至少有一個參數的類型為拋出函數。
```swift
func someFunction(callback: () throws -> Void) rethrows {
try callback()
}
```
重拋函數或者方法不能夠從自身直接拋出任何錯誤,這意味著它不能夠包含 `throw` 語句。它只能夠傳遞作為參數的拋出函數所拋出的錯誤。例如,在 `do-catch` 語句中調用拋出函數,并在 `catch` 子句中拋出其它錯誤都是不允許的。
```swift
func alwaysThrows() throws {
throw SomeError.error
}
func someFunction(callback: () throws -> Void) rethrows {
do {
try callback()
try alwaysThrows() // 非法, alwaysThrows() 不是一個拋出函數類型的參數
} catch {
throw AnotherError.error
}
}
```
拋出方法不能重寫重拋方法,而且拋出方法不能滿足協議對于重拋方法的要求。也就是說,重拋方法可以重寫拋出方法,而且重拋方法可以滿足協議對于拋出方法的要求。
### 永不返回的函數 {#functions-that-never-return}
Swift 定義了 `Never` 類型,它表示函數或者方法不會返回給它的調用者。`Never` 返回類型的函數或方法可以稱為不歸,不歸函數、方法要么引發不可恢復的錯誤,要么永遠不停地運作,這會使調用后本應執行得代碼就不再執行了。但即使是不歸函數、方法,拋錯函數和重拋出函數也可以將程序控制轉移到合適的 `catch` 代碼塊。
不歸函數、方法可以在 guard 語句的 else 字句中調用,具體討論在 [*Guard 語句*](./05_Statements.md#guard-statements)。
你可以重寫一個不歸方法,但是新的方法必須保持原有的返回類型和沒有返回的行為。
#### grammer-of-a-function-declaration {#grammer-of-a-function-declaration}
> 函數聲明語法
>
#### function-declaration {#function-declaration}
> *函數聲明* → [函數頭](#function-head) [函數名](#function-name) [泛型形參子句](./09_Generic_Parameters_and_Arguments.md#generic-parameter-clause)<sub>可選</sub> [函數簽名](#function-signature) [泛型 where 子句](./09_Generic_Parameters_and_Arguments.md#generic-where-clause) [函數體](#function-body)<sub>可選</sub>
>
#### function-head {#function-head}
> *函數頭* → [特性列表](./07_Attributes.md#attributes)<sub>可選</sub> [聲明修飾符列表](#declaration-modifiers)<sub>可選</sub> **func**
>
>
#### function-name {#function-name}
>
> *函數名* → [標識符](./02_Lexical_Structure.md#identifier) | [運算符](./02_Lexical_Structure.md#operator)
>
>
>
#### function-signature {#function-signature}
>
>
> *函數簽名* → [參數子句列表](#parameter-clauses) **throws**<sub>可選</sub> [函數結果](#function-result)<sub>可選</sub>
>
> *函數簽名* → [參數子句列表](#parameter-clauses) **rethrows** [函數結果](#function-result)<sub>可選</sub>
>
>
#### function-result {#function-result}
>
> *函數結果* → **->** [特性列表](./07_Attributes.md#attributes)<sub>可選</sub> [類型](./03_Types.md#type)
>
>
#### function-body {#function-body}
>
> *函數體* → [代碼塊](#code-block)
>
>
#### parameter-clause {#parameter-clause}
>
> *參數子句* → **(** **)** | **(** [參數列表](#parameter-list) **)**
>
>
#### parameter-list {#parameter-list}
>
> *參數列表* → [參數](#parameter) | [參數](#parameter) **,** [參數列表](#parameter-list)
>
>
#### parameter {#parameter}
>
> *參數* → [外部參數名](#external-parameter-name)<sub>可選</sub> [內部參數名](#local-parameter-name) [類型注解](./03_Types.md#type-annotation) [默認參數子句](#default-argument-clause)<sub>可選</sub>
>
> *參數* → [外部參數名](#external-parameter-name)<sub>可選</sub> [內部參數名](#local-parameter-name) [類型注解](./03_Types.md#type-annotation)
>
> *參數* → [外部參數名](#external-parameter-name)<sub>可選</sub> [內部參數名](#local-parameter-name) [類型注解](./03_Types.md#type-annotation) **...**
>
>
#### external-parameter-name {#external-parameter-name}
>
> *外部參數名* → [標識符](./02_Lexical_Structure.md#identifier) | **-**
>
>
#### local-parameter-name {#local-parameter-name}
>
> *內部參數名* → [標識符](./02_Lexical_Structure.md#identifier) | **-**
>
>
#### default-argument-clause {#default-argument-clause}
>
> *默認參數子句* → **=** [表達式](./04_Expressions.md#expression)
>
## 枚舉聲明 {#enumeration-declaration}
在程序中使用*枚舉聲明(enumeration declaration)* 來引入一個枚舉類型。
枚舉聲明有兩種基本形式,使用關鍵字 `enum` 來聲明。枚舉聲明體包含零個或多個值,稱為枚舉用例,還可包含任意數量的聲明,包括計算型屬性、實例方法、類型方法、構造器、類型別名,甚至其他枚舉、結構體和類。枚舉聲明不能包含析構器或者協議聲明。
枚舉類型可以采納任意數量的協議,但是枚舉不能從類、結構體和其他枚舉繼承。
不同于類或者結構體,枚舉類型并不隱式提供默認構造器,所有構造器必須顯式聲明。一個構造器可以委托給枚舉中的其他構造器,但是構造過程僅當構造器將一個枚舉用例賦值給 `self` 后才算完成。
和結構體類似但是和類不同,枚舉是值類型。枚舉實例在被賦值到變量或常量時,或者傳遞給函數作為參數時會被復制。更多關于值類型的信息,請參閱 [結構體和枚舉是值類型](../02_language_guide/09_Structures_And_Classes.md#structures-and-enumerations-are-value-types)。
可以擴展枚舉類型,正如在 [擴展聲明](#extension-declaration) 中討論的一樣。
### 任意類型的枚舉用例 {#enumerations-with-cases-of-any-type}
如下的形式聲明了一個包含任意類型枚舉用例的枚舉變量:
```swift
enum 枚舉名稱: 采納的協議 {
case 枚舉用例1
case 枚舉用例2(關聯值類型)
}
```
這種形式的枚舉聲明在其他語言中有時被叫做可識別聯合。
在這種形式中,每個用例塊由關鍵字 `case` 開始,后面緊接一個或多個以逗號分隔的枚舉用例。每個用例名必須是獨一無二的。每個用例也可以指定它所存儲的指定類型的值,這些類型在關聯值類型的元組中被指定,緊跟用例名之后。
具有關聯值的枚舉用例可以像函數一樣使用,通過指定的關聯值創建枚舉實例。和真正的函數一樣,你可以獲取枚舉用例的引用,然后在后續代碼中調用它。
```swift
enum Number {
case integer(Int)
case real(Double)
}
// f 的類型為 (Int) -> Number
let f = Number.integer
// 利用 f 把一個整數數組轉成 Number 數組
let evenInts: [Number] = [0, 2, 4, 6].map(f)
```
要獲得更多關于具有關聯值的枚舉用例的信息和例子,請參閱 [關聯值](../02_language_guide/08_Enumerations.md#associated-values)。
#### 遞歸枚舉 {#enumerations-with-indirection}
枚舉類型可以具有遞歸結構,就是說,枚舉用例的關聯值類型可以是枚舉類型自身。然而,枚舉類型的實例具有值語義,這意味著它們在內存中有固定布局。為了支持遞歸,編譯器必須插入一個間接層。
要讓某個枚舉用例支持遞歸,使用 `indirect` 聲明修飾符標記該用例。
```swift
enum Tree<T> {
case empty
indirect case node(value: T, left: Tree, right:Tree)
}
```
要讓一個枚舉類型的所有用例都支持遞歸,使用 `indirect` 修飾符標記整個枚舉類型,當枚舉有多個用例且每個用例都需要使用 `indirect` 修飾符標記的時候這將非常便利。
被 `indirect` 修飾符標記的枚舉用例必須有一個關聯值。使用 `indirect` 修飾符標記的枚舉類型可以既包含有關聯值的用例,同時還可包含沒有關聯值的用例。但是,它不能再單獨使用 `indirect` 修飾符來標記某個用例。
### 擁有原始值的枚舉用例 {#enumerations-with-cases-of-a-raw-value-type}
以下形式聲明了一種枚舉類型,其中各個枚舉用例的類型均為同一種基本類型:
```swift
enum 枚舉名稱: 原始值類型, 采納的協議 {
case 枚舉用例1 = 原始值1
case 枚舉用例2 = 原始值2
}
```
在這種形式中,每一個用例塊由 `case` 關鍵字開始,后面緊跟一個或多個以逗號分隔的枚舉用例。和第一種形式的枚舉用例不同,這種形式的枚舉用例包含一個基礎值,叫做原始值,各個枚舉用例的原始值的類型必須相同。這些原始值的類型通過原始值類型指定,必須表示一個整數、浮點數、字符串或者字符。原始值類型必須符合 `Equatable` 協議和下列字面量轉換協議中的一種:整型字面量需符合 `IntergerLiteralConvertible` 協議,浮點型字面量需符合 `FloatingPointLiteralConvertible` 協議,包含任意數量字符的字符串型字面量需符合 `StringLiteralConvertible` 協議,僅包含一個單一字符的字符串型字面量需符合 `ExtendedGraphemeClusterLiteralConvertible` 協議。每一個用例的名字和原始值必須唯一。
如果原始值類型被指定為 `Int`,則不必為用例顯式地指定原始值,它們會隱式地被賦值 `0`、`1`、`2` 等。每個未被賦值的 `Int` 類型的用例會被隱式地賦值,其值為上一個用例的原始值加 `1`。
```Swift
enum ExampleEnum: Int {
case a, b, c = 5, d
}
```
在上面的例子中,`ExampleEnum.A` 的原始值是 `0`,`ExampleEnum.B` 的原始值是 `1`。因為 `ExampleEnum.C` 的原始值被顯式地設定為 `5`,因此 `ExampleEnum.D` 的原始值會自動增長為 `6`。
如果原始值類型被指定為 `String` 類型,你不用明確地為用例指定原始值,每個沒有指定原始值的用例會隱式地將用例名字作為原始值。
```swift
enum GamePlayMode: String {
case cooperative, individual, competitive
}
```
在上面這個例子中,`GamePlayMode.cooperative` 的原始值是 `"cooperative"`,`GamePlayMode.individual` 的原始值是 `"individual"`,`GamePlayMode.competitive` 的原始值是 `"competitive"`。
枚舉用例具有原始值的枚舉類型隱式地符合定義在 Swift 標準庫中的 `RawRepresentable` 協議。所以,它們擁有一個 `rawValue` 屬性和一個可失敗構造器 `init?(rawValue: RawValue)`。可以使用 `rawValue` 屬性去獲取枚舉用例的原始值,例如 `ExampleEnum.b.rawValue`。還可以根據原始值來創建一個相對應的枚舉用例,只需調用枚舉的可失敗構造器,例如 `ExampleEnum(rawValue: 5)`,這個可失敗構造器返回一個可選類型的用例。要獲得更多關于具有原始值的枚舉用例的信息和例子,請參閱 [原始值](../02_language_guide/08_Enumerations.md#raw-values)。
### 訪問枚舉用例 {#accessing-enumeration-cases}
使用點語法(`.`)來引用枚舉類型的枚舉用例,例如 `EnumerationType.enumerationCase`。當枚舉類型可以由上下文推斷而出時,可以省略它(但是 `.` 仍然需要),正如 [枚舉語法](../02_language_guide/08_Enumerations.md#enumeration-syntax) 和 [顯式成員表達式](./04_Expressions.md#explicit-member-expression) 所述。
可以使用 `switch` 語句來檢驗枚舉用例的值,正如 [使用 switch 語句匹配枚舉值](../02_language_guide/08_Enumerations.md#matching-enumeration-values-with-a-switch-statement) 所述。枚舉類型是模式匹配的,依靠 `switch` 語句 `case` 塊中的枚舉用例模式,正如 [枚舉用例模式](./08_Patterns.md#enumeration-case-pattern) 所述。
#### grammer-of-an-enumeration-declaration {#grammer-of-an-enumeration-declaration}
> 枚舉聲明語法
>
>
>
#### enum-declaration {#enum-declaration}
>
> *枚舉聲明* → [特性列表](./07_Attributes.md#attributes)<sub>可選</sub> [訪問級別修飾符](#access-level-modifier)<sub>可選</sub> [聯合風格枚舉](#union-style-enum)
>
> *枚舉聲明* → [特性列表](./07_Attributes.md#attributes)<sub>可選</sub> [訪問級別修飾符](#access-level-modifier) <sub>可選</sub> [原始值風格枚舉](#raw-value-style-enum)
>
>
> *聯合風格枚舉* → **indirect**<sub>可選</sub> **enum** [枚舉名稱](#enum-name) [泛型形參子句](./09_Generic_Parameters_and_Arguments.md#generic-parameter-clause)<sub>可選</sub> [類型繼承子句](./03_Types.md#type-inheritance-clause)<sub>可選</sub> **{** [多個聯合風格枚舉成員](#union-style-enum-members)<sub>可選</sub> **}**
>
>
#### union-style-enum-members {#union-style-enum-members}
>
> *多個聯合風格枚舉成員* → [聯合風格枚舉成員](#union-style-enum-member) [多個聯合風格枚舉成員](#union-style-enum-members)<sub>可選</sub>
>
>
#### union-style-enum-member {#union-style-enum-member}
>
> *聯合風格枚舉成員* → [聲明](#declaration) | [聯合風格枚舉用例子句](#union-style-enum-case-clause) | [編譯控制流語句](./05_Statements.md#compiler-control-statement)
>
>
#### union-style-enum-case-clause {#union-style-enum-case-clause}
>
> *聯合風格枚舉用例子句* → [特性列表](./07_Attributes.md#attributes)<sub>可選</sub> **indirect**<sub>可選</sub> **case** [聯合風格枚舉用例列表](#union-style-enum-case-list)
>
>
#### union-style-enum-case-list {#union-style-enum-case-list}
>
> *聯合風格枚舉用例列表* → [聯合風格枚舉用例](#union-style-enum-case) | [聯合風格枚舉用例](#union-style-enum-case) **,** [聯合風格枚舉用例列表](#union-style-enum-case-list)
>
>
#### union-style-enum-case {#union-style-enum-case}
>
> *聯合風格枚舉用例* → [枚舉用例名稱](#enum-case-name) [元組類型](./03_Types.md#tuple-type)<sub>可選</sub>
>
>
#### enum-name {#enum-name}
>
> *枚舉名稱* → [標識符](./02_Lexical_Structure.md#identifier)
>
>
#### enum-case-name {#enum-case-name}
>
> *枚舉用例名稱* → [標識符](./02_Lexical_Structure.md#identifier)
>
>
> #### raw-value-style-enum {#raw-value-style-enum}
>
>
> *原始值風格枚舉* → **enum** [枚舉名稱](#enum-name) [泛型形參子句](./09_Generic_Parameters_and_Arguments.md#generic-parameter-clause)<sub>可選</sub> [類型繼承子句](./03_Types.md#type-inheritance-clause) [泛型 where 子句](./09_Generic_Parameters_and_Arguments.md#generic-where-clause) **{** [多個原始值風格枚舉成員](#raw-value-style-enum-members) **}**
>
>
#### raw-value-style-enum-members {#raw-value-style-enum-members}
>
> *多個原始值風格枚舉成員* → [原始值風格枚舉成員](#raw-value-style-enum-member) [多個原始值風格枚舉成員](#raw-value-style-enum-members)<sub>可選</sub>
>
>
#### raw-value-style-enum-member {#raw-value-style-enum-member}
>
> *原始值風格枚舉成員* → [聲明](#declaration) | [原始值風格枚舉用例子句](#raw-value-style-enum-case-clause) | [編譯控制流語句](./05_Statements.md#compiler-control-statement)
>
>
#### raw-value-style-enum-case-clause {#raw-value-style-enum-case-clause}
>
> *原始值風格枚舉用例子句* → [特性列表](./07_Attributes.md#attributes)<sub>可選</sub> **case** [原始值風格枚舉用例列表](#raw-value-style-enum-case-list)
>
>
#### raw-value-style-enum-case-list {#raw-value-style-enum-case-list}
>
> *原始值風格枚舉用例列表* → [原始值風格枚舉用例](#raw-value-style-enum-case) | [原始值風格枚舉用例](#raw-value-style-enum-case) **,** [原始值風格枚舉用例列表](#raw-value-style-enum-case-list)
>
>
#### raw-value-style-enum-case {#raw-value-style-enum-case}
>
> *原始值風格枚舉用例* → [枚舉用例名稱](#enum-case-name) [原始值賦值](#raw-value-assignment)<sub>可選</sub>
>
>
#### raw-value-assignment {#raw-value-assignment}
>
> *原始值賦值* → **=** [原始值字面量](#raw-value-literal)
>
>
#### raw-value-literal {#raw-value-literal}
>
> *原始值字面量* → [數字型字面量](./02_Lexical_Structure.md#numeric-literal) | [字符串型字面量](./02_Lexical_Structure.md#static-string-literal) | [布爾型字面量](./02_Lexical_Structure.md#boolean-literal)
>
## 結構體聲明 {#structure-declaration}
使用*結構體聲明(structure declaration)* 可以在程序中引入一個結構體類型。結構體聲明使用 `struct` 關鍵字,遵循如下的形式:
```swift
struct 結構體名稱: 采納的協議 {
多條聲明
}
```
結構體內可包含零個或多個聲明。這些聲明可以包括存儲型和計算型屬性、類型屬性、實例方法、類型方法、構造器、下標、類型別名,甚至其他結構體、類、和枚舉聲明。結構體聲明不能包含析構器或者協議聲明。關于結構體的詳細討論和示例,請參閱 [類和結構體](../02_language_guide/09_Structures_And_Classes.md)。
結構體可以采納任意數量的協議,但是不能繼承自類、枚舉或者其他結構體。
有三種方法可以創建一個已聲明的結構體實例:
* 調用結構體內聲明的構造器,正如 [構造器](../02_language_guide/14_Initialization.md#initializers) 所述。
* 如果沒有聲明構造器,調用結構體的成員逐一構造器,正如 [結構體類型的成員逐一構造器](../02_language_guide/14_Initialization.md#memberwise-initializers-for-structure-types) 所述。
* 如果沒有聲明構造器,而且結構體的所有屬性都有初始值,調用結構體的默認構造器,正如 [默認構造器](../02_language_guide/14_Initialization.md#default-initializers) 所述。
結構體的構造過程請參閱 [構造過程](../02_language_guide/14_Initialization.md)。
結構體實例的屬性可以用點語法(`.`)來訪問,正如 [訪問屬性](../02_language_guide/09_Structures_And_Classes.md#accessing-properties) 所述。
結構體是值類型。結構體的實例在被賦予變量或常量,或傳遞給函數作為參數時會被復制。關于值類型的更多信息,請參閱
[結構體和枚舉是值類型](../02_language_guide/09_Structures_And_Classes.md#structures-and-enumerations-are-value-types)。
可以使用擴展聲明來擴展結構體類型的行為,請參閱 [擴展聲明](#extension-declaration)。
#### grammer-of-a-structure-declaration {#grammer-of-a-structure-declaration}
> 結構體聲明語法
>
>
>
#### struct-declaration {#struct-declaration}
>
> *結構體聲明* → [特性列表](./07_Attributes.md#attributes)<sub>可選</sub> [訪問級別修飾符](#access-level-modifier) <sub>可選</sub> **struct** [結構體名稱](#struct-name) [泛型形參子句](./09_Generic_Parameters_and_Arguments.md#generic-parameter-clause)<sub>可選</sub> [類型繼承子句](./03_Types.md#type-inheritance-clause)<sub>可選</sub> [泛型 where 子句](./09_Generic_Parameters_and_Arguments.md#generic-where-clause)<sub>可選</sub> [結構體主體](#struct-body)
>
>
#### struct-name {#struct-name}
>
> *結構體名稱* → [標識符](./02_Lexical_Structure.md#identifier)
>
>
#### struct-body {#struct-body}
>
> *結構體主體* → **{** [多條聲明](#declarations)<sub>可選</sub> **}**
>
>
#### struct-name {#struct-name}
>
>
> *結構體多個成員* → [結構體成員](#struct-member) [結構體多個成員](#struct-members)<sub>可選</sub>
>
>
#### struct-member {#struct-member}
>
> *結構體成員* → [聲明](#declaration) | [編譯控制流語句](./05_Statements.md#compiler-control-statement)
>
## 類聲明 {#class-declaration}
可以在程序中使用*類聲明(class declaration)* 來引入一個類。類聲明使用關鍵字 `class`,遵循如下的形式:
```swift
class 類名: 超類, 采納的協議 {
多條聲明
}
```
類內可以包含零個或多個聲明。這些聲明可以包括存儲型和計算型屬性、實例方法、類型方法、構造器、唯一的析構器、下標、類型別名,甚至其他結構體、類和枚舉聲明。類聲明不能包含協議聲明。關于類的詳細討論和示例,請參閱 [類和結構體](../02_language_guide/09_Structures_And_Classes.md)。
一個類只能繼承自一個超類,但是可以采納任意數量的協議。超類緊跟在類名和冒號后面,其后跟著采納的協議。泛型類可以繼承自其它泛型類和非泛型類,但是非泛型類只能繼承自其它非泛型類。當在冒號后面寫泛型超類的名稱時,必須寫上泛型類的全名,包括它的泛型形參子句。
正如 [構造器聲明](#initializer-declaration) 所討論的,類可以有指定構造器和便利構造器。類的指定構造器必須初始化類中聲明的所有屬性,并且必須在調用超類構造器之前。
類可以重寫屬性、方法、下標以及構造器。重寫的屬性、方法、下標和指定構造器必須以 `override` 聲明修飾符標記。
為了要求子類去實現超類的構造器,使用 `required` 聲明修飾符標記超類的構造器。子類實現超類構造器時也必須使用 `required` 聲明修飾符。
雖然超類屬性和方法聲明可以被當前類繼承,但是超類聲明的指定構造器卻不能。即便如此,如果當前類重寫了超類的所有指定構造器,它就會繼承超類的所有便利構造器。Swift 的類并不繼承自一個通用基礎類。
有兩種方法來創建已聲明的類的實例:
* 調用類中聲明的構造器,請參閱 [構造器](../02_language_guide/14_Initialization.md#initializers)。
* 如果沒有聲明構造器,而且類的所有屬性都被賦予了初始值,調用類的默認構造器,請參閱 [默認構造器](../02_language_guide/14_Initialization.md#default-initializers)。
類實例屬性可以用點語法(`.`)來訪問,請參閱 [訪問屬性](../02_language_guide/09_Structures_And_Classes.md#accessing-properties)。
類是引用類型。當被賦予常量或變量,或者傳遞給函數作為參數時,類的實例會被引用,而不是被復制。關于引用類型的更多信息,請參閱 [結構體和枚舉是值類型](../02_language_guide/09_Structures_And_Classes.md#structures-and-enumerations-are-value-types)。
可以使用擴展聲明來擴展類的行為,請參閱 [擴展聲明](#extension-declaration)。
#### grammer-of-a-class-declaration {#grammer-of-a-class-declaration}
> 類聲明語法
>
>
>
#### class-declaration {#class-declaration}
>
> *類聲明* → [特性列表](./07_Attributes.md#attributes)<sub>可選</sub> [訪問級別修飾符](#access-level-modifier)<sub>可選</sub> **final**<sub>可選</sub> **class** [類名](#class-name) [泛型形參子句](./09_Generic_Parameters_and_Arguments.md#generic-parameter-clause)<sub>可選</sub> [類型繼承子句](./03_Types.md#type-inheritance-clause)<sub>可選</sub> [泛型 where 子句](./09_Generic_Parameters_and_Arguments.md#generic-where-clause)<sub>可選</sub> [類主體](#class-body)
>
> *類聲明* → [特性列表](./07_Attributes.md#attributes)<sub>可選</sub> **final** [訪問級別修飾符](#access-level-modifier)<sub>可選</sub> **class** [類名](#class-name) [泛型形參子句](./09_Generic_Parameters_and_Arguments.md#generic-parameter-clause)<sub>可選</sub> [類型繼承子句](./03_Types.md#type-inheritance-clause)<sub>可選</sub> [泛型 where 子句](./09_Generic_Parameters_and_Arguments.md#generic-where-clause)<sub>可選</sub> [類主體](#class-body)
>
>
#### class-name {#class-name}
>
> *類名* → [標識符](./02_Lexical_Structure.md#identifier)
>
>
#### class-body {#class-body}
>
> *類主體* → **{** [多條聲明](#declarations)<sub>可選</sub> **}**
>
>
> *類多個成員* → [類成員](#class-member) [類多個成員](#class-members)<sub>可選</sub>
>
>
#### class-member {#class-member}
>
> *類成員* → [聲明](#declaration) | [編譯控制流語句](./05_Statements.md#compiler-control-statement)
>
## 協議聲明 {#protocol-declaration}
*協議聲明(protocol declaration)* 可以為程序引入一個命名的協議類型。協議聲明只能在全局區域使用 `protocol` 關鍵字來進行聲明,并遵循如下形式:
```swift
protocol 協議名稱: 繼承的協議 {
協議成員聲明
}
```
協議的主體包含零個或多個協議成員聲明,這些成員描述了任何采納該協議的類型必須滿足的一致性要求。一個協議可以聲明采納者必須實現的某些屬性、方法、構造器以及下標。協議也可以聲明各種各樣的類型別名,叫做關聯類型,它可以指定協議的不同聲明之間的關系。協議聲明不能包括類、結構體、枚舉或者其它協議的聲明。協議成員聲明會在后面進行討論。
協議類型可以繼承自任意數量的其它協議。當一個協議類型繼承自其它協議的時候,來自其它協議的所有要求會聚合在一起,而且采納當前協議的類型必須符合所有的這些要求。關于如何使用協議繼承的例子,請參閱 [協議繼承](../02_language_guide/21_Protocols.md#protocol-inheritance)。
> 注意
>
> 也可以使用協議合成類型來聚合多個協議的一致性要求,請參閱 [協議合成類型](./03_Types.md#protocol-composition-type) 和 [協議合成](../02_language_guide/21
_Protocols.md#protocol-composition)。
>
可以通過類型的擴展聲明來采納協議,從而為之前聲明的類型添加協議一致性。在擴展中,必須實現所有采納協議的要求。如果該類型已經實現了所有的要求,可以讓這個擴展聲明的主體留空。
默認地,符合某個協議的類型必須實現所有在協議中聲明的屬性、方法和下標。即便如此,可以用 `optional` 聲明修飾符標注協議成員聲明,以指定它們的實現是可選的。`optional` 修飾符僅僅可以用于使用 `objc` 特性標記過的協議。因此,僅僅類類型可以采用并符合包含可選成員要求的協議。更多關于如何使用 `optional` 聲明修飾符的信息,以及如何訪問可選協議成員的指導——例如不能確定采納協議的類型是否實現了它們時——請參閱 [可選協議要求](../02_language_guide/21_Protocols.md#optional-protocol-requirements)。
為了限制協議只能被類類型采納,需要使用 `AnyObject` 關鍵字來標記協議,將 `AnyObject` 關鍵在寫在冒號后面的繼承的協議列表的首位。例如,下面的協議只能被類類型采納:
```swift
protocol SomeProtocol: AnyObject {
/* 這里是協議成員 */
}
```
任何繼承自標記有 `AnyObject` 關鍵字的協議的協議也僅能被類類型采納。
> 注意
>
> 如果協議已經用 `objc` 特性標記了,`AnyObject` 要求就隱式地應用于該協議,無需顯式使用 `AnyObject` 關鍵字。
>
協議類型是命名的類型,因此它們可以像其他命名類型一樣使用,正如 [協議作為類型](../02_language_guide/21_Protocols.md#protocols-as-types) 所討論的。然而,不能構造一個協議的實例,因為協議實際上不提供它們指定的要求的實現。
可以使用協議來聲明作為代理的類或者結構體應該實現的方法,正如 [委托(代理)模式](../02_language_guide/21_Protocols.md#delegation) 中所述。
#### grammer-of-a-protocol-declaration {#grammer-of-a-protocol-declaration}
> 協議聲明語法
>
>
>
#### protocol-declaration {#protocol-declaration}
>
> *協議聲明* → [特性列表](./07_Attributes.md#attributes)<sub>可選</sub> [訪問級別修飾符](#access-level-modifier)<sub>可選</sub> **protocol** [協議名稱](#protocol-name) [類型繼承子句](./03_Types.md#type-inheritance-clause)<sub>可選</sub> [泛型 where 子句](./09_Generic_Parameters_and_Arguments.md#generic-where-clause)<sub>可選</sub> [協議主體](#protocol-body)
>
>
#### protocol-name {#protocol-name}
>
> *協議名稱* → [標識符](./02_Lexical_Structure.md#identifier)
>
>
#### protocol-body {#protocol-body}
>
> *協議主體* → **{** [協議成員聲明列表](#protocol-member-declarations)<sub>可選</sub> **}**
>
>
> *協議多個成員* → [協議成員](#protocol-member) [協議多個成員](#protocol-members)<sub>可選</sub>
>
>
#### protocol-member {#protocol-member}
>
> *協議成員* → [協議成員聲明](#protocol-member-declaration) | [編譯控制流語句](./05_Statements.md#compiler-control-statement)
>
>
>
#### protocol-member-declaration {#protocol-member-declaration}
>
> *協議成員聲明* → [協議屬性聲明](#protocol-property-declaration)
>
> *協議成員聲明* → [協議方法聲明](#protocol-method-declaration)
>
> *協議成員聲明* → [協議構造器聲明](#protocol-initializer-declaration)
>
> *協議成員聲明* → [協議下標聲明](#protocol-subscript-declaration)
>
> *協議成員聲明* → [協議關聯類型聲明](#protocol-associated-type-declaration)
>
>
#### protocol-member-declarations {#protocol-member-declarations}
>
> *協議成員聲明列表* → [協議成員聲明](#protocol-member-declaration) [協議成員聲明列表](#protocol-member-declarations)<sub>可選</sub>
>
### 協議屬性聲明 {#protocol-property-declaration}
協議可以通過在協議聲明主體中引入一個協議屬性聲明,來聲明符合的類型必須實現的屬性。協議屬性聲明有一種特殊的變量聲明形式:
```swift
var 屬性名: 類型 { get set }
```
同其它協議成員聲明一樣,這些屬性聲明僅僅針對符合該協議的類型聲明了 getter 和 setter 要求,你不能在協議中直接實現 getter 和 setter。
符合類型可以通過多種方式滿足 getter 和 setter 要求。如果屬性聲明包含 `get` 和 `set` 關鍵字,符合類型就可以用存儲型變量屬性或可讀可寫的計算型屬性來滿足此要求,但是屬性不能以常量屬性或只讀計算型屬性實現。如果屬性聲明僅僅包含 `get` 關鍵字的話,它可以作為任意類型的屬性被實現。關于如何實現協議中的屬性要求的例子,請參閱 [屬性要求](../02_language_guide/21_Protocols.md#property-requirements) 。
協議聲明中聲明一個類型屬性,屬性聲明語句必須用 `static` 聲明修飾符。當結構體和枚舉遵循該協議時,使用 `static` 關鍵字修飾,而類遵循該協議時,使用 `static` 或 `class` 關鍵字皆可。當結構體,枚舉或類添加擴展遵循協議時,和之前擴展用到的關鍵字保持一致。擴展為類屬性提供默認實現時,必須使用 `static` 關鍵字修飾。
另請參閱 [變量聲明](#variable-declaration)。
#### grammer-of-an-import-declaration {#grammer-of-an-import-declaration}
> 協議屬性聲明語法
>
>
#### protocol-property-declaration {#protocol-property-declaration}
>
> *協議屬性聲明* → [變量聲明頭](#variable-declaration-head) [變量名稱](#variable-name) [類型注解](./03_Types.md#type-annotation) [getter-setter 關鍵字代碼塊](#getter-setter-keyword-block)
>
### 協議方法聲明 {#protocol-method-declaration}
協議可以通過在協議聲明主體中引入一個協議方法聲明,來聲明符合的類型必須實現的方法。協議方法聲明和函數方法聲明有著相同的形式,但有兩項例外:它們不包括函數體,也不能包含默認參數。關于如何實現協議中的方法要求的例子,請參閱 [方法要求](../02_language_guide/21_Protocols.md#method-requirements)。
協議聲明中聲明一個類型方法,方法聲明語句必須用 `static` 聲明修飾符。結構體和枚舉遵循協議時,必須使用 `static` 關鍵字修飾,而類遵循協議時,使用 `static` 或 `class` 關鍵字皆可。當結構體,枚舉或類添加擴展遵循協議時,和之前擴展用到的關鍵字保持一致。擴展為類方法提供默認實現時,必須使用 `static` 關鍵字修飾。
另請參閱 [函數聲明](#function-declaration)。
#### grammer-of-a-protocol-declaration {#grammer-of-a-protocol-declaration}
> 協議方法聲明語法
>
>
#### protocol-method-declaration {#protocol-method-declaration}
>
> *協議方法聲明* → [函數頭](#function-head) [函數名](#function-name) [泛型形參子句](./09_Generic_Parameters_and_Arguments.md#generic-parameter-clause)<sub>可選</sub> [函數簽名](#function-signature) [泛型 where 子句](./09_Generic_Parameters_and_Arguments.md#generic-where-clause)<sub>可選</sub>
>
### 協議構造器聲明 {#protocol-initializer-declaration}
協議可以通過在協議聲明主體中引入一個協議構造器聲明,來聲明符合的類型必須實現的構造器。協議構造器聲明
除了不包含實現主體外,和構造器聲明有著相同的形式。
符合類型可以通過實現一個非可失敗構造器或者 `init!` 可失敗構造器來滿足一個非可失敗協議構造器的要求,可以通過實現任意類型的構造器來滿足一個可失敗協議構造器的要求。
類在實現一個構造器去滿足一個協議的構造器要求時,如果這個類還沒有用 `final` 聲明修飾符標記,這個構造器必須用 `required` 聲明修飾符標記。
另請參閱 [構造器聲明](#initializer-declaration)。
#### grammer-of-a-protocol-initializer-declaration {#grammer-of-a-protocol-initializer-declaration}
> 協議構造器聲明語法
>
>
#### protocol-initializer-declaration {#protocol-initializer-declaration}
>
> *協議構造器聲明* → [構造器頭](#initializer-head) [泛型形參子句](./09_Generic_Parameters_and_Arguments.md#generic-parameter-clause)<sub>可選</sub> [參數子句](#parameter-clause) **throws**<sub>可選</sub> [泛型 where 子句](./09_Generic_Parameters_and_Arguments.md#generic-where-clause)<sub>可選</sub>
>
> *協議構造器聲明* → [構造器頭](#initializer-head) [泛型形參子句](./09_Generic_Parameters_and_Arguments.md#generic-parameter-clause)<sub>可選</sub> [參數子句](#parameter-clause) **rethrows** [泛型 where 子句](./09_Generic_Parameters_and_Arguments.md#generic-where-clause)<sub>可選</sub>
>
### 協議下標聲明 {#protocol-subscript-declaration}
協議可以通過在協議聲明主體中引入一個協議下標聲明,來聲明符合的類型必須實現的下標。協議下標聲明有一個特殊的下標聲明形式:
```swift
subscript (參數列表) -> 返回類型 { get set }
```
下標聲明只為符合類型聲明了 getter 和 setter 要求。如果下標聲明包含 `get` 和 `set` 關鍵字,符合類型也必須實現 getter 和 setter 子句。如果下標聲明只包含 `get` 關鍵字,符合類型必須實現 getter 子句,可以選擇是否實現 setter 子句。
協議聲明中聲明一個靜態下標,下標聲明語句必須用 `static` 聲明修飾符。當結構體和枚舉遵循該協議時,下標聲明使用 `static` 關鍵字修飾,而類遵循該協議時,使用 `static` 或 `class` 關鍵字皆可。當結構體,枚舉或類添加擴展遵循協議時,和之前擴展用到的關鍵字保持一致。擴展為下標聲明提供默認實現時,必須使用 `static` 關鍵字修飾。
另請參閱 [下標聲明](#subscript-declaration)。
#### grammer-of-a-protocol-subscript-declaration {#grammer-of-a-protocol-subscript-declaration}
> 協議下標聲明語法
>
>
#### protocol-subscript-declaration {#protocol-subscript-declaration}
>
> *協議下標聲明* → [下標頭](#subscript-head) [下標結果](#subscript-result) [泛型 where 子句](./09_Generic_Parameters_and_Arguments.md#generic-where-clause)<sub>可選</sub> [getter-setter 關鍵字代碼塊](#getter-setter-keyword-block)
>
### 協議關聯類型聲明 {#protocol-associated-type-declaration}
使用關鍵字 `associatedtype` 來聲明協議關聯類型。關聯類型為作為協議聲明的一部分,為某種類型提供了一個別名。關聯類型和泛型參數子句中的類型參數很相似,但是它們和 `Self` 一樣,用于協議中。`Self` 指代采納協議的類型。要獲得更多信息和例子,請參閱 [關聯類型](../02_language_guide/22_Generics.md#associated-types)。
在協議聲明中使用泛型 `where` 子句來為繼承的協議關聯類型添加約束,且不需要重新聲明關聯類型。例如下面代碼中的 `SubProtocol` 聲明。
```swift
protocol SomeProtocol {
associatedtype SomeType
}
protocol SubProtocolA: SomeProtocol {
// 此類語法會引發警告。
associatedtype SomeType: Equatable
}
// 建議使用此語法。
protocol SubProtocolB: SomeProtocol where SomeType: Equatable { }
```
另請參閱 [類型別名聲明](#type-alias-declaration)。
#### grammer-of-a-protocol-associated-type-declaration {#grammer-of-a-protocol-associated-type-declaration}
> 協議關聯類型聲明語法
>
>
#### protocol-associated-type-declaration {#protocol-associated-type-declaration}
>
> *協議關聯類型聲明* → [特性列表](./07_Attributes.md#attributes)<sub>可選</sub> [訪問級別修飾符](#access-level-modifier)<sub>可選</sub> **associatedtype** [類型別名頭](#typealias-head) [類型繼承子句](./03_Types.md#type-inheritance-clause)<sub>可選</sub> [類型別名賦值](#typealias-assignment)<sub>可選</sub> [泛型 where 子句](./09_Generic_Parameters_and_Arguments.md#generic-where-clause)<sub>可選</sub>
## 構造器聲明 {#initializer-declaration}
構造器聲明會為程序中的類、結構體或枚舉引入構造器。構造器使用關鍵字 `init` 來聲明,有兩種基本形式。
結構體、枚舉、類可以有任意數量的構造器,但是類的構造器具有不同的規則和行為。不同于結構體和枚舉,類有兩種構造器,即指定構造器和便利構造器,請參閱 [構造過程](../02_language_guide/14_Initialization.md)。
采用如下形式聲明結構體和枚舉的構造器,以及類的指定構造器:
```swift
init(參數列表) {
構造語句
}
```
類的指定構造器直接將類的所有屬性初始化。它不能調用類中的其他構造器,如果類有超類,則必須調用超類的一個指定構造器。如果該類從它的超類繼承了屬性,必須在調用超類的指定構造器后才能修改這些屬性。
指定構造器只能在類聲明中聲明,不能在擴展聲明中聲明。
結構體和枚舉的構造器可以調用其他已聲明的構造器,從而委托其他構造器來進行部分或者全部構造過程。
要為類聲明一個便利構造器,用 `convenience` 聲明修飾符來標記構造器聲明:
```swift
convenience init(參數列表) {
構造語句
}
```
便利構造器可以將構造過程委托給另一個便利構造器或一個指定構造器。但是,類的構造過程必須以一個將類中所有屬性完全初始化的指定構造器的調用作為結束。便利構造器不能調用超類的構造器。
可以使用 `required` 聲明修飾符,將便利構造器和指定構造器標記為每個子類都必須實現的構造器。這種構造器的子類實現也必須使用 `required` 聲明修飾符標記。
默認情況下,超類中的構造器不會被子類繼承。但是,如果子類的所有存儲型屬性都有默認值,而且子類自身沒有定義任何構造器,它將繼承超類的構造器。如果子類重寫了超類的所有指定構造器,子類將繼承超類的所有便利構造器。
和方法、屬性和下標一樣,需要使用 `override` 聲明修飾符標記重寫的指定構造器。
> 注意
>
> 如果使用 `required` 聲明修飾符標記一個構造器,在子類中重寫這種構造器時,無需使用 `override` 修飾符。
>
就像函數和方法,構造器也可以拋出或者重拋錯誤,你可以在構造器參數列表的圓括號之后使用 `throws` 或 `rethrows` 關鍵字來表明相應的拋出行為。
關于在不同類型中聲明構造器的例子,請參閱 [構造過程](../02_language_guide/14_Initialization.md)。
### 可失敗構造器 {#failable-initializers}
可失敗構造器可以生成所屬類型的可選實例或者隱式解包可選實例,因此,這種構造器通過返回 `nil` 來指明構造過程失敗。
聲明生成可選實例的可失敗構造器時,在構造器聲明的 `init` 關鍵字后加追加一個問號(`init?`)。聲明生成隱式解包可選實例的可失敗構造器時,在構造器聲明后追加一個嘆號(`init!`)。使用 `init?` 可失敗構造器生成結構體的一個可選實例的例子如下。
```swift
struct SomeStruct {
let string: String
//生成一個 SomeStruct 的可選實例
init?(input: String) {
if input.isEmpty {
// 丟棄 self,并返回 nil
return nil
}
string = input
}
}
```
調用 `init?` 可失敗構造器和調用非可失敗構造器的方式相同,不過你需要處理可選類型的返回值。
```swift
if let actualInstance = SomeStruct(input: "Hello") {
// 利用 SomeStruct 實例做些事情
} else {
// SomeStruct 實例的構造過程失敗,構造器返回了 nil
}
```
可失敗構造器可以在構造器實現中的任意位置返回 `nil`。
可失敗構造器可以委托任意種類的構造器。非可失敗可以委托其它非可失敗構造器或者 `init!` 可失敗構造器。非可失敗構造器可以委托超類的 `init?` 可失敗指定構造器,但是需要使用強制解包,例如 `super.init()!`。
構造過程失敗通過構造器委托來傳遞。具體來說,如果可失敗構造器委托的可失敗構造器構造過程失敗并返回 `nil`,那么該可失敗構造器也會構造失敗并隱式地返回 `nil`。如果非可失敗構造器委托的 `init!` 可失敗構造器構造失敗并返回了 `nil`,那么會發生運行時錯誤(如同使用 `!` 操作符去強制解包一個值為 `nil` 的可選值)。
子類可以用任意種類的指定構造器重寫超類的可失敗指定構造器,但是只能用非可失敗指定構造器重寫超類的非可失敗指定構造器。
更多關于可失敗構造器的信息和例子,請參閱 [可失敗構造器](../02_language_guide/14_Initialization.md#failable-initializers)。
#### grammer-of-an-initializer-declaration {#grammer-of-an-initializer-declaration}
> 構造器聲明語法
>
>
#### initializer-declaration {#initializer-declaration}
>
> *構造器聲明* → [構造器頭](#initializer-head) [泛型形參子句](./09_Generic_Parameters_and_Arguments.md#generic-parameter-clause)<sub>可選</sub> [參數子句](#parameter-clause) **throws**<sub>可選</sub> [泛型 where 子句](./09_Generic_Parameters_and_Arguments.md#generic-where-clause)<sub>可選</sub> [構造器主體](#initializer-body)
>
> *構造器聲明* → [構造器頭](#initializer-head) [泛型形參子句](./09_Generic_Parameters_and_Arguments.md#generic-parameter-clause)<sub>可選</sub> [參數子句](#parameter-clause) **rethrows**<sub>可選</sub> [泛型 where 子句](./09_Generic_Parameters_and_Arguments.md#generic-where-clause)<sub>可選</sub> [構造器主體](#initializer-body)
>
>
#### initializer-head {#initializer-head}
>
> *構造器頭* → [特性列表](./07_Attributes.md#attributes)<sub>可選</sub> [聲明修飾符列表](#declaration-modifiers)<sub>可選</sub> **init**
>
> *構造器頭* → [特性列表](./07_Attributes.md#attributes)<sub>可選</sub> [聲明修飾符列表](#declaration-modifiers)<sub>可選</sub> **init** **?**
>
> *構造器頭* → [特性列表](./07_Attributes.md#attributes)<sub>可選</sub> [聲明修飾符列表](#declaration-modifiers)<sub>可選</sub> **init** **!**
>
>
#### initializer-body {#initializer-body}
>
> *構造器主體* → [代碼塊](#code-block)
>
## 析構器聲明 {#deinitializer-declaration}
*析構器聲明(deinitializer declaration)* 可以為類聲明一個析構器。析構器沒有參數,遵循如下格式:
```swift
deinit {
語句
}
```
當沒有任何強引用引用著類的對象,對象即將被釋放時,析構器會被自動調用。析構器只能在類主體的聲明中聲明,不能在類的擴展聲明中聲明,并且每個類最多只能有一個析構器。
子類會繼承超類的析構器,并會在子類對象將要被釋放時隱式調用。繼承鏈上的所有析構器全部調用完畢后子類對象才會被釋放。
析構器不能直接調用。
關于如何在類聲明中使用析構器的例子,請參閱 [析構過程](../02_language_guide/15_Deinitialization.md)。
#### grammer-of-a-deinitializer-declaration {#grammer-of-a-deinitializer-declaration}
> 析構器聲明語法
>
>
#### deinitializer-declaration {#deinitializer-declaration}
>
> *析構器聲明* → [特性列表](./07_Attributes.md#attributes)<sub>可選</sub> **deinit** [代碼塊](#code-block)
>
## 擴展聲明 {#extension-declaration}
*擴展聲明(extension declaration)* 可以擴展一個現存的類型的行為。擴展聲明使用關鍵字 `extension`,遵循如下格式:
```swift
extension 類型名稱 where 要求 {
聲明語句
}
```
擴展聲明體可包含零個或多個聲明語句。這些聲明語句可以包括計算型屬性、計算型類型屬性、實例方法、類型方法、構造器、下標聲明,甚至是類、結構體和枚舉聲明。擴展聲明不能包含析構器、協議聲明、存儲型屬性、屬性觀察器或其他擴展聲明。關于擴展聲明的詳細討論,以及各種擴展聲明的例子,請參閱 [擴展](../02_language_guide/20_Extensions.md)。
如果類型為類,結構體,或枚舉類型,則擴展聲明會擴展相應的類型。如果類型為協議類型,則擴展聲明會擴展所有遵守這個協議的類型。在擴展的協議體中聲明語句不能使用 `final` 標識符。
擴展聲明可以為現存的類、結構體、枚舉添加協議一致性,但是不能為類添加超類,因此在擴展聲明的類型名稱的冒號后面僅能指定一個協議列表。
擴展聲明可以包含構造器聲明。這意味著,如果被擴展的類型在其他模塊中定義,構造器聲明必須委托另一個在那個模塊中聲明的構造器,以確保該類型能被正確地初始化。
現存類型的屬性、方法、構造器不能在擴展中被重寫。
通過指定采納的協議,擴展聲明可以為一個現有的類、結構體或者枚舉類型添加協議遵循:
```swift
extension 類型名稱: 采納的協議 where 約束條件 {
多條聲明
}
```
協議聲明不能為現有的類添加類的繼承關系,因此你只能在 “類型名稱” 的冒號后面添加一系列協議。
### 條件遵循 {#conditional-conformance}
你可以擴展一個泛型類型并使其有條件地遵循某協議,此后此類型的實例只有在特定的限制條件滿足時才遵循此協議。在擴展聲明中加入限制條件來為協議添加條件遵循。
## 已重寫的限制條件會在某些泛型上下文中失效 {#overridden-requirements-aren't-Used-in-some-generic-contexts}
對于一些通過條件遵循獲得了特定行為的類型,在某些泛型上下文中,并不能夠確保能夠使用協議限制中的特定實現。為了說明這個行為,下面的例子中定義了兩個協議以及一個有條件地遵循兩個協議的泛型類型。
```swift
protocol Loggable {
func log()
}
extension Loggable {
func log() {
print(self)
}
}
protocol TitledLoggable: Loggable {
static var logTitle: String { get }
}
extension TitledLoggable {
func log() {
print("\(Self.logTitle): \(self)")
}
}
struct Pair<T>: CustomStringConvertible {
let first: T
let second: T
var description: String {
return "(\(first), \(second))"
}
}
extension Pair: Loggable where T: Loggable { }
extension Pair: TitledLoggable where T: TitledLoggable {
static var logTitle: String {
return "Pair of '\(T.logTitle)'"
}
}
extension String: TitledLoggable {
static var logTitle: String {
return "String"
}
}
```
當其泛型類型遵循 `Loggable` 協議以及 `TitleLoggale` 協議時,結構體 `Pair` 遵循 `Loggable` 協議以及 `TitleLoggale` 協議。下面的例子中,`oneAndTwo` 是 `Pair<String>` 的一個實例。因為 `String` 遵循 `TitleLoggable` ,因此 `oneAndTwo` 也遵循此協議。當 `log()` 方法被 `oneAndTwo` 直接調用時,此方法使用的是包含標題的特定版本。
```swift
let oneAndTwo = Pair(first: "one", second: "two")
oneAndTwo.log()
// Prints "Pair of 'String': (one, two)"
```
雖然如此,當 `oneAndTwo` 在泛型上下文中使用,或者它是 `Loggable` 類型的實例時,包含標題的特定版本 `log()` 方法不會被使用。Swift 只會根據這樣的規則來選擇 `log()` 的實現版本—— `Pair` 遵循 `Loggable` 所需要的最少的限制條件。因此 `Loggable` 所提供的默認實現版本會被使用。
```swift
func doSomething<T: Loggable>(with x: T) {
x.log()
}
doSomething(with: oneAndTwo)
// Prints "(one, two)"
```
當傳入 `doSomething(_:)` 的實例調用 `log()` 時,打印結果省略了自定義標題。
### 協議遵循決不能冗余 {#protocol-conformance-must-not-be-redundant}
一個具體的類型只能夠遵循某特定協議一次。Swift 會把冗余的協議遵循標記為錯誤。你會在兩種場景中遇到這種錯誤。第一種場景是,使用不同的限制條件來多次顯式地遵循同一協議。第二種場景是,多次隱式地繼承同一協議。以上兩種場景會在下面章節中討論。
## 解決顯式冗余 {#resolving-explicit-redundancy}
對同一具體類型的多個擴展不能遵循同一協議,即便這些擴展有不同的顯式限制條件。這個限制的具體示例在下面的例子中。兩個擴展聲明都試圖添加對 `Serializable` 的條件遵循,一個為 `Int` 類型元素的數組,另一個為 `String` 類型元素的數組。
```swift
protocol Serializable {
func serialize() -> Any
}
extension Array: Serializable where Element == Int {
func serialize() -> Any {
// implementation
}
}
extension Array: Serializable where Element == String {
func serialize() -> Any {
// implementation
}
}
// 報錯: redundant conformance of 'Array<Element>' to protocol 'Serializable'
```
如果你需要基于多個具體類型來添加條件遵循,那么創建一個新的協議,然后讓每個類型都遵循此協議,最后在聲明條件遵循時使用此協議作為條件限制。
```swift
protocol SerializableInArray { }
extension Int: SerializableInArray { }
extension String: SerializableInArray { }
extension Array: Serializable where Element: SerializableInArray {
func serialize() -> Any {
// 具體實現
}
}
```
## 解決隱式冗余 {#resolving-implicit-redundancy}
當一個具體類型有條件地遵循某協議,此類型會隱式地使用相同的條件遵循任一父協議。
如果你需要讓一個類型有條件地遵循兩個繼承自同一父協議的協議,請顯式地聲明對父協議的遵循。這可以避免使用不同的限制條件隱式遵循同一父協議兩次。
下面的例子中顯式地聲明了 `Array` 對 `Loggable` 的條件遵循,避免了在聲明對 `TitledLoggable` 和 `TitledLoggable` 聲明條件遵循時發生沖突。
```swift
protocol MarkedLoggable: Loggable {
func markAndLog()
}
extension MarkedLoggable {
func markAndLog() {
print("----------")
log()
}
}
extension Array: Loggable where Element: Loggable { }
extension Array: TitledLoggable where Element: TitledLoggable {
static var logTitle: String {
return "Array of '\(Element.logTitle)'"
}
}
extension Array: MarkedLoggable where Element: MarkedLoggable { }
```
如果不顯式聲明對 `Loggable` 的條件遵循,`Array` 其他的擴展會隱式地創建此聲明,并引發錯誤:
```swift
extension Array: Loggable where Element: TitledLoggable { }
extension Array: Loggable where Element: MarkedLoggable { }
// 報錯: redundant conformance of 'Array<Element>' to protocol 'Loggable'
```
#### grammer-of-an-extension-declaration {#grammer-of-an-extension-declaration}
> 擴展聲明語法
>
>
>
#### extension-declaration {#extension-declaration}
>
> *擴展聲明* → [特性](./07_Attributes.md#type-attributes)<sub>可選</sub> [訪問級別修飾符](#access-level-modifier)<sub>可選</sub> **extension** [類型標識符](./03_Types.md#type-identifier) [類型-繼承-子句](./03_Types.md#type-inheritance-clause)<sub>可選</sub> [泛型 where 子句](./09_Generic_Parameters_and_Arguments.md#generic-where-clause)<sub>可選</sub> [擴展主體](#extension-body)
>
>
#### extension-body {#extension-body}
>
> *擴展主體* → **{** [多條聲明](#declarations)<sub>可選</sub> **}**
>
> *多條聲明* → [單條聲明](#subscript-declaration) [多條聲明](#declarations) <sub>可選</sub>
>
> *單條聲明* → [聲明語句](#declarations) | [編譯控制流語句](./05_Statements.md#compiler-control-statement)
>
## 下標聲明 {#subscript-declaration}
*下標聲明(subscript declaration)* 用于為特定類型的對象添加下標支持,通常也用于為訪問集合、列表和序列中的元素提供語法便利。下標聲明使用關鍵字 `subscript`,形式如下:
```swift
subscript (參數列表) -> 返回類型 {
get {
語句
}
set(setter 名稱) {
語句
}
}
```
下標聲明只能出現在類、結構體、枚舉、擴展和協議的聲明中。
參數列表指定一個或多個用于在相關類型的下標表達式中訪問元素的索引(例如,表達式 `object[i]` 中的 `i`)。索引可以是任意類型,但是必須包含類型注解。返回類型指定了被訪問的元素的類型。
和計算型屬性一樣,下標聲明支持對元素的讀寫操作。getter 用于讀取值,setter 用于寫入值。setter 子句是可選的,當僅需要一個 getter 子句時,可以將二者都忽略,直接返回請求的值即可。但是,如果提供了 setter 子句,就必須提供 getter 子句。
圓括號以及其中的 setter 名稱是可選的。如果提供了 setter 名稱,它會作為 setter 的參數名稱。如果不提供 setter 名稱,那么 setter 的參數名稱默認是 `value`。setter 的參數類型必須與返回類型相同。
可以重寫下標,只要參數列表或返回類型不同即可。還可以重寫繼承自超類的下標,此時必須使用 `override` 聲明修飾符聲明被重寫的下標。
下標參數遵循與函數參數相同的規則,但有兩個例外。默認情況下,下標中使用的參數不需要指定標簽,這與函數,方法和構造器不同。但是你也可以同它們一樣,顯式地提供參數標簽。此外,下標不能有 `In-out` 參數。下標參數可以具有默認值,具體的語法請參考 [特殊參數](#special-kinds-of-parameters)。
同樣可以在協議聲明中聲明下標,正如 [協議下標聲明](#protocol-subscript-declaration) 中所述。
更多關于下標的信息和例子,請參閱 [下標](../02_language_guide/12_Subscripts.md)。
### 類型下標聲明
聲明一個由類型而不是類型實例公開的下標,請使用 `static` 聲明修飾符標記下標聲明。類可以使用 `class` 聲明修飾符標記類型計算屬性,以允許子類重寫父類的實現。在類聲明中,`static` 關鍵字具有與用 `class` 和 `final` 聲明修飾符標記聲明相同的效果。
#### grammer-of-a-subscript-declaration {#grammer-of-a-subscript-declaration}
> 下標聲明語法
>
>
>
#### subscript-declaration {#subscript-declaration}
>
> *下標聲明* → [下標頭](#subscript-head) [下標結果](#subscript-result) [泛型 where 子句](./09_Generic_Parameters_and_Arguments.md#generic-where-clause)<sub>可選</sub> [代碼塊](#code-block)
>
> *下標聲明* → [下標頭](#subscript-head) [下標結果](#subscript-result) [泛型 where 子句](./09_Generic_Parameters_and_Arguments.md#generic-where-clause)<sub>可選</sub> [getter-setter 代碼塊](#getter-setter-block)
>
> *下標聲明* → [下標頭](#subscript-head) [下標結果](#subscript-result) [泛型 where 子句](./09_Generic_Parameters_and_Arguments.md#generic-where-clause)<sub>可選</sub> [getter-setter 關鍵字代碼塊](#getter-setter-keyword-block)
>
>
#### subscript-head {#subscript-head}
>
> *下標頭* → [特性列表](./07_Attributes.md#attributes)<sub>可選</sub> [聲明修飾符列表](#declaration-modifiers)<sub>可選</sub> **subscript** [泛型參數子句](./09_Generic_Parameters_and_Arguments.md#generic-parameter-clause)<sub>可選</sub> [參數子句](#parameter-clause)
>
>
#### subscript-result {#subscript-result}
>
> *下標結果* → **->** [特性列表](./07_Attributes.md#attributes)<sub>可選</sub> [類型](./03_Types.md#type)
>
## 運算符聲明 {#operator-declaration}
*運算符聲明(operator declaration)* 會向程序中引入中綴、前綴或后綴運算符,使用關鍵字 `operator` 來聲明。
可以聲明三種不同的綴性:中綴、前綴和后綴。運算符的綴性指定了運算符與其運算對象的相對位置。
運算符聲明有三種基本形式,每種綴性各一種。運算符的綴性通過在 `operator` 關鍵字之前添加聲明修飾符 `infix`,`prefix` 或 `postfix` 來指定。每種形式中,運算符的名字只能包含 [運算符](./02_Lexical_Structure.md#operators) 中定義的運算符字符。
下面的形式聲明了一個新的中綴運算符:
```swift
infix operator 運算符名稱: 優先級組
```
中綴運算符是二元運算符,置于兩個運算對象之間,例如加法運算符(`+`)位于表達式 `1 + 2` 的中間。
中綴運算符可以選擇指定優先級組。如果沒有為運算符設置優先級組,Swift 會設置默認優先級組 `DefaultPrecedence`,它的優先級比三目優先級 `TernaryPrecedence` 要高,更多內容參考[*優先級組聲明*](#precedence-group-declaration-modifiers)
下面的形式聲明了一個新的前綴運算符:
```swift
prefix operator 運算符名稱
```
出現在運算對象前邊的前綴運算符是一元運算符,例如表達式 `!a` 中的前綴非運算符(`!`)。
前綴運算符的聲明中不指定優先級,而且前綴運算符是非結合的。
下面的形式聲明了一個新的后綴運算符:
```swift
postfix operator 運算符名稱
```
緊跟在運算對象后邊的后綴運算符是一元運算符,例如表達式 `a!` 中的后綴強制解包運算符(`!`)。
和前綴運算符一樣,后綴運算符的聲明中不指定優先級,而且后綴運算符是非結合的。
聲明了一個新的運算符以后,需要實現一個跟這個運算符同名的函數來實現這個運算符。如果是實現一個前綴或者后綴運算符,也必須使用相符的 `prefix` 或者 `postfix` 聲明修飾符標記函數聲明。如果是實現中綴運算符,則不需要使用 `infix` 聲明修飾符標記函數聲明。關于如何實現一個新的運算符的例子,請參閱 [自定義運算符](../02_language_guide/27_Advanced_Operators.md#custom-operators)。
#### grammer-of-an-operator-declaration {#grammer-of-an-operator-declaration}
> 運算符聲明語法
>
#### operator-declaration {#operator-declaration}
> *運算符聲明* → [前綴運算符聲明](#prefix-operator-declaration) | [后綴運算符聲明](#postfix-operator-declaration) | [中綴運算符聲明](#infix-operator-declaration)
>
#### prefix-operator-declaration {#prefix-operator-declaration}
> *前綴運算符聲明* → **prefix** **運算符** [運算符](./02_Lexical_Structure.md#operator) **{** **}**
>
>
#### postfix-operator-declaration {#postfix-operator-declaration}
>
> *后綴運算符聲明* → **postfix** **運算符** [運算符](./02_Lexical_Structure.md#operator) **{** **}**
>
>
#### infix-operator-declaration {#infix-operator-declaration}
>
> *中綴運算符聲明* → **infix** **運算符** [運算符](./02_Lexical_Structure.md#operator) **{** [中綴運算符組](#infix-operator-group)<sub>可選</sub> **}**
#### infix-operator-group {#infix-operator-group}
> *中綴運算符組* → [優先級組名稱](#precedence-group-name)
>
## 優先級組聲明 {#precedence-group-declaration-modifiers}
*優先級組聲明(A precedence group declaration)* 會向程序的中綴運算符引入一個全新的優先級組。當沒有用圓括號分組時,運算符優先級反應了運算符與它的操作數的關系的緊密程度。
優先級組的聲明如下所示:
```swift
precedencegroup 優先級組名稱{
higherThan: 較低優先級組的名稱
lowerThan: 較高優先級組的名稱
associativity: 結合性
assignment: 賦值性
}
```
較低優先級組和較高優先級組的名稱說明了新建的優先級組是依賴于現存的優先級組的。`lowerThan` 優先級組的屬性只可以引用當前模塊外的優先級組。當兩個運算符為同一個操作數競爭時,比如表達式 `2 + 3 * 5`,優先級更高的運算符將優先參與運算。
> 注意
>
> 使用較低和較高優先級組相互聯系的優先級組必須保持單一層次關系,但它們不必是線性關系。這意味著優先級組也許會有未定義的相關優先級。這些優先級組的運算符在沒有用圓括號分組的情況下是不能緊鄰著使用的。
>
Swift 定義了大量的優先級組來與標準庫的運算符配合使用,例如相加(`+`)和相減(`-`)屬于 `AdditionPrecedence` 組,相乘(`*`)和相除(`/`)屬于 `MultiplicationPrecedence` 組,詳細關于 Swift 標準庫中一系列運算符和優先級組內容,參閱 [Swift 標準庫操作符參考](https://developer.apple.com/documentation/swift/operator_declarations)。
運算符的結合性表示在沒有圓括號分組的情況下,同樣優先級的一系列運算符是如何被分組的。你可以指定運算符的結合性通過上下文關鍵字 `left`、`right` 或者 `none`,如果沒有指定結合性,默認是 `none` 關鍵字。左關聯性的運算符是從左至右分組的,例如,相減操作符(-)是左關聯性的,所以表達式 `4 - 5 - 6` 被分組為 `(4 - 5) - 6`,得出結果-7。右關聯性的運算符是從右往左分組的,指定為 `none` 結合性的運算符就沒有結合性。同樣優先級沒有結合性的運算符不能相鄰出現,例如 `<` 運算符是 `none` 結合性,那表示 `1 < 2 < 3` 就不是一個有效表達式。
優先級組的賦值性表示在包含可選鏈操作時的運算符優先級。當設為 true 時,與優先級組對應的運算符在可選鏈操作中使用和標準庫中賦值運算符同樣的分組規則,當設為 false 或者不設置,該優先級組的運算符與不賦值的運算符遵循同樣的可選鏈規則。
#### grammer-of-a-precedence-group-declaration {#grammer-of-a-precedence-group-declaration}
> 優先級組聲明語法
>
#### precedence-group-declaration {#precedence-group-declaration}
> *優先級組聲明* → **precedence**[優先級組名稱](#precedence-group-name){[多優先級組屬性](#precedence-group-attributes)<sub>可選</sub> }
>
#### precedence-group-attributes {#precedence-group-attributes}
> *優先級組屬性* → [優先級組屬性](#precedence-group-attribute)[多優先級組屬性](#precedence-group-attributes)<sub>可選</sub> **{** **}**
>
#### precedence-group-attribute {#precedence-group-attribute}
> *優先級組屬性* → [優先級組關系](#precedence-group-relation)
>
> *優先級組屬性* → [優先級組賦值性](#precedence-group-assignment)
>
> *優先級組屬性* → [優先級組相關性](#precedence-group-associativity)
>
>
#### precedence-group-relation {#precedence-group-relation}
>
> *優先級組關系* → **higherThan:**[多優先級組名稱](#precedence-group-names)
>
> *優先級組關系* → **lowerThan:**[多優先級組名稱](#precedence-group-names)
>
>
#### precedence-group-assignment {#precedence-group-assignment}
>
> *優先級組賦值* → **assignment:**[布爾字面值](./02_Lexical_Structure.md#boolean-literal)
>
#### precedence-group-associativity {#precedence-group-associativity}
> *優先級組結合性* → **associativity:left**
>
> *優先級組結合性* → **associativity:right**
>
> *優先級組結合性* → **associativity:none**
>
#### precedence-group-names {#precedence-group-names}
> *多優先級組名稱* → [優先級組名稱](#precedence-group-name) | [優先級組名稱](#precedence-group-name) | [優先級組名稱](#precedence-group-name)
>
#### precedence-group-name {#precedence-group-name}
> *優先級組名稱* →[標識符](./02_Lexical_Structure.md#identifier)
>
## 聲明修飾符 {#Declaration-Modifiers}
聲明修飾符都是關鍵字或上下文相關的關鍵字,可以修改一個聲明的行為或者含義。可以在聲明的特性(如果存在)和引入該聲明的關鍵字之間,利用聲明修飾符的關鍵字或上下文相關的關鍵字指定一個聲明修飾符。
`class`
該修飾符用于修飾任何類成員,表明是類自身的成員,而不是類實例的成員。父類中使用該修飾符標記或者未被 `final` 修飾符標記的成員,都允許被子類重寫。
`dynamic`
該修飾符用于修飾任何兼容 Objective-C 的類的成員。訪問被 `dynamic` 修飾符標記的類成員將總是由 Objective-C 運行時系統進行動態派發,而不會由編譯器進行內聯或消虛擬化。
因為被標記 `dynamic` 修飾符的類成員會由 Objective-C 運行時系統進行動態派發,所以它們會被隱式標記 `objc` 特性。
`final`
該修飾符用于修飾類或類中的屬性、方法以及下標。如果用它修飾一個類,那么這個類不能被繼承。如果用它修飾類中的屬性、方法或下標,那么它們不能在子類中被重寫。
`lazy`
該修飾符用于修飾類或結構體中的存儲型變量屬性,表示該屬性的初始值最多只被計算和存儲一次,且發生在它被第一次訪問時。關于如何使用 `lazy` 修飾符的例子,請參閱 [惰性存儲型屬性](../02_language_guide/10_Properties.md#lazy-stored-properties)。
`optional`
該修飾符用于修飾協議中的屬性、方法以及下標成員,表示符合類型可以不實現這些成員要求。
只能將 `optional` 修飾符用于被 `objc` 特性標記的協議。這樣一來,就只有類類型可以采納并符合擁有可選成員要求的協議。關于如何使用 `optional` 修飾符,以及如何訪問可選協議成員(比如,不確定符合類型是否已經實現了這些可選成員)的信息,請參閱 [可選協議要求](../02_language_guide/21_Protocols.md#optional-protocol-requirements)。
`required`
該修飾符用于修飾類的指定構造器或便利構造器,表示該類所有的子類都必須實現該構造器。在子類實現該構造器時,必須同樣使用 `required` 修飾符修飾該構造器。
`static`
該修飾符用于修飾結構體、類、枚舉或協議的成員,表明是類型成員,而不是類型實例的成員。在類聲明的作用范圍內,使用 `static` 修飾符標記成員聲明語句,同 `class` 和 `final` 修飾符具有相同的效果。但是類的常量類型屬性是一個例外: `static` 沒有問題,但是你無法為常量聲明使用 `class` 或 `final` 修飾符。
`unowned`
該修飾符用于修飾存儲型變量、常量或者存儲型變量屬性,表示該變量或屬性持有其存儲對象的無主引用。如果在此存儲對象釋放后嘗試訪問該對象,會引發運行時錯誤。如同弱引用一樣,該引用類型的變量或屬性必須是類類型。與弱引用不同的是,這種類型的變量或屬性是非可選的。關于 `unowned` 更多的信息和例子,請參閱 [無主引用](../02_language_guide/24_Automatic_Reference_Counting.md#unowned-references)
`unowned(safe)`
`unowned` 的顯式寫法
`unowned(unsafe)`
該修飾符用于修飾存儲型變量、常量或者存儲型變量屬性,表示該變量或屬性持有其存儲對象的無主引用。如果在此存儲對象釋放后嘗試訪問該對象,會直接訪問該對象釋放前存儲的內存地址,因此這是非內存安全的操作。如同弱引用一樣,該引用類型的變量或屬性必須是類類型。與弱引用不同的是,這種類型的變量或屬性是非可選的。關于 `unowned` 更多的信息和例子,請參閱 [無主引用](../02_language_guide/24_Automatic_Reference_Counting.md#resolving-strong-reference-cycles-between-class-instances)。
`weak`
該修飾符用于修飾變量或存儲型變量屬性,表示該變量或屬性持有其存儲的對象的弱引用。這種變量或屬性的類型必須是可選的類類型。使用 `weak` 修飾符可避免強引用循環。關于 `weak` 修飾符的更多信息和例子,請參閱 [弱引用](../02_language_guide/24_Automatic_Reference_Counting.md#resolving-strong-reference-cycles-between-class-instances)。
### 訪問控制級別 {#access-control-levels}
Swift 提供了三個級別的訪問控制:`public`、`internal` 和 `private`。可以使用以下任意一種訪問級別修飾符來指定聲明的訪問級別。訪問控制在 [訪問控制](../02_language_guide/26_Access_Control.md) 中有詳細討論。
`public`
該修飾符表示聲明可被同模塊的代碼訪問,只要其他模塊導入了聲明所在的模塊,也可以進行訪問。
`internal`
該修飾符表示聲明只能被同模塊的代碼訪問。默認情況下,絕大多數聲明會被隱式標記 `internal` 訪問級別修飾符。
`private`
該修飾符表示聲明只能被所在源文件的代碼訪問。
以上訪問級別修飾符都可以選擇帶上一個參數,該參數由一對圓括號和其中的 `set` 關鍵字組成(例如,`private(set)`)。使用這種形式的訪問級別修飾符來限制某個屬性或下標的 setter 的訪問級別低于其本身的訪問級別,正如 [Getter 和 Setter](../02_language_guide/26_Access_Control.md#getters-and-setters) 中所討論的。
#### grammer-of-a-declaration-modifier {#grammer-of-a-declaration-modifier}
> 聲明修飾符的語法
>
#### declaration-modifier {#declaration-modifier}
> *聲明修飾符* → **class** | **convenience**| **dynamic** | **final** | **infix** | **lazy** | **mutating** | **nonmutating** | **optional** | **override** | **postfix** | **prefix** | **required** | **static** | **unowned** | **unowned ( safe )** | **unowned ( unsafe )** | **weak**
>
> 聲明修飾符 → [訪問級別修飾符](#access-level-modifier)
>
>
#### declaration-modifiers {#declaration-modifiers}
>
> *聲明修飾符列表* → [聲明修飾符](#declaration-modifier) [聲明修飾符列表](#declaration-modifiers)<sub>可選</sub>
#### access-level-modifier {#access-level-modifier}
> 訪問級別修飾符 → **internal** | **internal ( set )**
>
> 訪問級別修飾符 → **private** | **private ( set )**
>
> 訪問級別修飾符 → **public** | **public ( set )**
>
- 1.關于 Swift
- 2.Swift 初見
- 2-1基礎部分
- 2-2基本運算符
- 2-3字符串和字符
- 2-4集合類型
- 2-5控制流
- 2-6函數
- 2-7閉包
- 2-8枚舉
- 2-9類和結構體
- 2-10屬性
- 2-11方法
- 2-12下標
- 2-13繼承
- 2-14構造過程
- 2-15析構過程
- 2-16可選鏈
- 2-17錯誤處理
- 2-18類型轉換
- 2-19嵌套類型
- 2-20擴展
- 2-21協議
- 2-22泛型
- 2-23不透明類型
- 2-24自動引用計數
- 2-25內存安全
- 2-26訪問控制
- 2-27高級運算符
- 3-1關于語言參考
- 3-2詞法結構
- 3-3類型
- 3-4表達式
- 3-5語句
- 3-6聲明
- 3-7特性
- 3-8模式
- 3-9泛型參數
- 4語法總結