> 1.0 翻譯:[lyuka](https://github.com/lyuka)?校對:[numbbbbb](https://github.com/numbbbbb),?[stanzhai](https://github.com/stanzhai)
>
> 2.0 翻譯+校對:[EudeMorgen](https://github.com/EudeMorgen)
本頁包含內容:
[TOC]
Swift 語言存在兩種類型:命名型類型和復合型類型。命名型類型是指定義時可以給定名字的類型。命名型類型包括類、結構體、枚舉和協議。比如,一個用戶定義的類MyClass的實例擁有類型MyClass。除了用戶定義的命名型類型,Swift 標準庫也定義了很多常用的命名型類型,包括那些表示數組、字典和可選值的類型。
那些通常被其它語言認為是基本或初級的數據型類型(Data types)——比如表示數字、字符和字符串的類型——實際上就是命名型類型,這些類型在Swift 標準庫中是使用結構體來定義和實現的。因為它們是命名型類型,因此你可以按照“擴展和擴展聲明”章節里討論的那樣,聲明一個擴展來增加它們的行為以迎合你程序的需求。
_復合型類型_是沒有名字的類型,它由 Swift 本身定義。Swift 存在兩種復合型類型:函數類型和元組類型。一個復合型類型可以包含命名型類型和其它復合型類型。例如,元組類型(Int, (Int, Int))包含兩個元素:第一個是命名型類型Int,第二個是另一個復合型類型(Int, Int).
本節討論 Swift 語言本身定義的類型,并描述 Swift 中的類型推斷行為。
> 類型語法
> _類型_?→?[ _數組類型_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#array_type)?|?[ _字典類型_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#dictionary_type)?|?[ _函數類型_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#function_type)?|?[ _類型標識_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type_identifier)?|?[ _元組類型_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#tuple_type)?|?[ _可選類型_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#optional_type)?|?[ _隱式解析可選類型_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#implicitly_unwrapped_optional_type)?|?[_協議合成類型_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#protocol_composition_type)|?[_元型類型_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#metatype_type)
## 類型注解
類型注解顯式地指定一個變量或表達式的值。類型注解始于冒號`:`終于類型,比如下面兩個例子:
~~~
let someTuple: (Double, Double) = (3.14159, 2.71828)
func someFunction(a: Int){ /* ... */ }
~~~
在第一個例子中,表達式`someTuple`的類型被指定為`(Double, Double)`。在第二個例子中,函數`someFunction`的參數`a`的類型被指定為`Int`。
類型注解可以在類型之前包含一個類型特性(type attributes)的可選列表。
> 類型注解語法
> _類型注解_?→?:?[_特性(Attributes)列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/06_Attributes.html#attributes)?_可選_?[_類型_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type)
## 類型標識符
類型標識符引用命名型類型或者是命名型/復合型類型的別名。
大多數情況下,類型標識符引用的是與之同名的命名型類型。例如類型標識符`Int`引用命名型類型`Int`,同樣,類型標識符`Dictionary<String, Int>`引用命名型類型`Dictionary<String, Int>`。
在兩種情況下類型標識符不引用同名的類型。情況一,類型標識符引用的是命名型/復合型類型的類型別名。比如,在下面的例子中,類型標識符使用`Point`來引用元組`(Int, Int)`:
~~~
typealias Point = (Int, Int)
let origin: Point = (0, 0)
~~~
情況二,類型標識符使用dot(`.`)語法來表示在其它模塊(modules)或其它類型嵌套內聲明的命名型類型。例如,下面例子中的類型標識符引用在`ExampleModule`模塊中聲明的命名型類型`MyType`:
~~~
var someValue: ExampleModule.MyType
~~~
> 類型標識語法
> _類型標識_?→?[_類型名稱_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type_name)?[_泛型參數子句_](http://wiki.jikexueyuan.com/project/swift/chapter3/GenericParametersAndArguments.html#generic_argument_clause)?_可選_?|?[_類型名稱_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type_name)?[_泛型參數子句_](http://wiki.jikexueyuan.com/project/swift/chapter3/GenericParametersAndArguments.html#generic_argument_clause)?_可選_?.?[_類型標識_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type_identifier)
> _類名_?→?[_標識符_](http://wiki.jikexueyuan.com/project/swift/chapter3/LexicalStructure.html#identifier)
## 元組類型
元組類型使用逗號隔開并使用括號括起來的0個或多個類型組成的列表。
你可以使用元組類型作為一個函數的返回類型,這樣就可以使函數返回多個值。你也可以命名元組類型中的元素,然后用這些名字來引用每個元素的值。元素的名字由一個標識符緊跟一個冒號`(:)`組成。“函數和多返回值”章節里有一個展示上述特性的例子。
`void`是空元組類型`()`的別名。如果括號內只有一個元素,那么該類型就是括號內元素的類型。比如,`(Int)`的類型是`Int`而不是`(Int)`。所以,只有當元組類型包含的元素個數在兩個及以上時才可以命名元組元素。
> 元組類型語法
> _元組類型_?→?(?[_元組類型主體_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#tuple_type_body)?_可選_?)
> _元組類型主體_?→?[_元組類型的元素列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#tuple_type_element_list)?...?_可選_
> _元組類型的元素列表_?→?[_元組類型的元素_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#tuple_type_element)?|?[_元組類型的元素_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#tuple_type_element)?,?[_元組類型的元素列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#tuple_type_element_list)
> _元組類型的元素_?→?[_特性(Attributes)列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/06_Attributes.html#attributes)?_可選_?inout?_可選_?[_類型_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type)?|?inout?_可選_?[_元素名_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#element_name)?[_類型注解_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type_annotation)
> _元素名_?→?[_標識符_](http://wiki.jikexueyuan.com/project/swift/chapter3/LexicalStructure.html#identifier)
## 函數類型
函數類型表示一個函數、方法或閉包的類型,它由一個參數類型和返回值類型組成,中間用箭頭`->`隔開:
~~~
`parameter type` -> `return type`
~~~
由于?_參數類型_?和?_返回值類型_?可以是元組類型,所以函數類型支持多參數與多返回值的函數與方法。。
對于參數類型是空元組類型`()`以及返回值類型為表達式類型的函數類型,你可以對其參數聲明使用`autoclosure`(見聲明屬性章節)。一個自動閉包函數捕獲特定表達式上的隱式閉包而非表達式本身。下面的例子使用`autoclosure`屬性來定義一個很簡單的assert函數:
~~~
func simpleAssert(@autoclosure condition: Void -> Bool, _ message: String) {
if !condition() {
print(message)
}
}
let testNumber = 5
simpleAssert(testNumber % 2 == 0, "testNumber isn't an even number.")
// prints "testNumber isn't an even number."
~~~
函數類型可以擁有一個可變長參數作為參數類型中的最后一個參數。從語法角度上講,可變長參數由一個基礎類型名字緊隨三個點`(...)`組成,如`Int...`。可變長參數被認為是一個包含了基礎類型元素的數組。即`Int...`就是`[Int]`。關于使用可變長參數的例子,見章節Variadic Parameters。
為了指定一個`in-out`參數,可以在參數類型前加`inout`前綴。但是你不可以對可變長參數或返回值類型使用`inout`。關于In-Out參數的討論見章節In-Out參數部分。
柯里化函數(Curried fuction)的函數類型從右向左遞歸地組成一組。例如,函數類型`Int -> Int -> Int`可以被理解為`Int -> (Int -> Int)`——也就是說,一個函數的參數為`Int`類型,其返回類型是一個參數類型為`Int`返回類型為`Int`的函數類型。關于柯里化函數的討論見章節Curried Fuctions。
函數類型若要拋出錯誤就必須使用`throws`關鍵字來標記,若要重拋錯誤則必須使用`rethrows`關鍵字來標記。`throws`關鍵字是函數類型的一部分,不拋出函數(nonthrowing function)是拋出函數(throwing function)函數的一個子類型。因此,在使用拋出函數的地方也可以使用不拋出函數。對于柯里化函數,`throws`關鍵字只應用于最里層的函數。拋出和重拋函數(rethrowing function)的相關描述見章節拋出函數與方法和重拋函數與方法。
> 函數類型語法
> _函數類型_?→?[_類型_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type)?_拋出_?_可選_?->?[_類型_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type)
> _函數類型_?→?[_類型_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type)_重拋_?->?[_類型_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type)
## 數組類型
Swift語言中使用[`type`]來簡化標準庫中定義`Array<T>`類型的操作。 換句話說,下面兩個聲明是等價的:
~~~
let someArray: [String] = ["Alex", "Brian", "Dave"]
let someArray: Array<String> = ["Alex", "Brian", "Dave"]
~~~
上面兩種情況下,常量`someArray`都被聲明為字符串數組。數組的元素也可以通過`[]`獲取訪問:`someArray[0]`是指第0個元素`“Alex”`。
你也可以嵌套多對方括號來創建多維數組,最里面的方括號中指明數組元素的基本類型。比如,下面例子中使用三對方括號創建三維整數數組。
~~~
var array3D: [[[Int]]] = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
~~~
訪問一個多維數組的元素時,最左邊的下標指向最外層數組的相應位置元素。接下來往右的下標指向第一層嵌入的相應位置元素,依次類推。這就意味著,在上面的例子中,`array3D[0]`是指`[[1, 2], [3, 4]]`,`array3D[0][1]`是指`[3, 4]`,`array3D[0][1][1]`則是指值`4`。
關于Swift標準庫中`Array`類型的細節討論,見章節Arrays。
> 數組類型語法
> _數組類型_?→?[_類型_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type)
## 字典類型
Swift語言中使用[`key type: value type`]來簡化標準庫中定義`Dictionary<Key,Value>`類型的操作。 換句話說,下面兩個聲明是等價的:
~~~
let someDictionary: [String: Int] = ["Alex": 31, "Paul": 39]
let someDictionary: Dictionary<String, Int> = ["Alex": 31, "Paul": 39]
~~~
上面兩種情況,常量`someDictionary`被聲明為一個字典,其中鍵為String類型,值為Int類型。
字典中的值可以通過下標來訪問,這個下標在方括號中指明了具體的鍵:`someDictionary["Alex"]`返回鍵`Alex`對應的值。如果鍵在字典中不存在的話,則這個下標返回`nil`。
字典中鍵的類型必須遵循Swift標準庫中的可哈希協議。
關于Swift標準庫中`Dictionary`類型的更多細節可查看章節Dictionaries。
> 字典類型語法
> _字典類型_?→?[[_類型_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type)?:?[_類型_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type)?]
## 可選類型
Swift定義后綴`?`來作為標準庫中的定義的命名型類型`Optional<T>`的簡寫。換句話說,下面兩個聲明是等價的:
~~~
var optionalInteger: Int?
var optionalInteger: Optional<Int>
~~~
在上述兩種情況下,變量`optionalInteger`都被聲明為可選整型類型。注意在類型和`?`之間沒有空格。
類型`Optional<T>`是一個枚舉,有兩種形式,`None`和`Some(T)`,又來代表可能出現或可能不出現的值。任意類型都可以被顯式的聲明(或隱式的轉換)為可選類型。當聲明一個可選類型時,確保使用括號給`?`提供合適的作用范圍。比如說,聲明一個整型的可選數組,應寫作`(Int[])?`,寫成`Int[]?`的話則會出錯。
如果你在聲明或定義可選變量或特性的時候沒有提供初始值,它的值則會自動賦成缺省值`nil`。
如果一個可選類型的實例包含一個值,那么你就可以使用后綴操作符`!`來獲取該值,正如下面描述的:
~~~
optionalInteger = 42
optionalInteger! // 42
~~~
使用`!`操作符獲取值為`nil`的可選項會導致運行錯誤(runtime error)。
你也可以使用可選鏈和可選綁定來選擇性的執行可選表達式上的操作。如果值為`nil`,不會執行任何操作因此也就沒有運行錯誤產生。
更多細節以及更多如何使用可選類型的例子,見章節Optionals。
> 可選類型語法
> _可選類型_?→?[_類型_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type)??
## 隱式解析可選類型
Swift語言定義后綴`!`作為標準庫中命名類型`ImplicitlyUnwrappedOptional<T>`的簡寫。換句話說,下面兩個聲明等價:
~~~
var implicitlyUnwrappedString: String!
var implicitlyUnwrappedString: ImplicitlyUnwrappedOptional<String>
~~~
上述兩種情況下,變量`implicitlyUnwrappedString`被聲明為一個隱式解析可選類型的字符串。注意類型與`!`之間沒有空格。
你可以在使用可選類型的地方同樣使用隱式解析可選類型。比如,你可以將隱式解析可選類型的值賦給變量、常量和可選特性,反之亦然。
有了可選,你在聲明隱式解析可選變量或特性的時候就不用指定初始值,因為它有缺省值`nil`。
由于隱式解析可選的值會在使用時自動解析,所以沒必要使用操作符`!`來解析它。也就是說,如果你使用值為`nil`的隱式解析可選,就會導致運行錯誤。
使用可選鏈會選擇性的執行隱式解析可選表達式上的某一個操作。如果值為`nil`,就不會執行任何操作,因此也不會產生運行錯誤。
關于隱式解析可選的更多細節,見章節Implicitly Unwrapped Optionals。
> 隱式解析可選類型(Implicitly Unwrapped Optional Type)語法
> _隱式解析可選類型_?→?[_類型_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type)?!
## 協議合成類型
協議合成類型是一種遵循具體協議列表中每個協議的類型。協議合成類型可能會用在類型注解和泛型參數中。
協議合成類型的形式如下:
~~~
protocol<Protocol 1, Procotol 2>
~~~
協議合成類型允許你指定一個值,其類型遵循多個協議的條件且不需要定義一個新的命名型協議來繼承其它想要遵循的各個協議。比如,協議合成類型`protocol<Protocol A, Protocol B, Protocol C>`等效于一個從`Protocol A`,`Protocol B`,?`Protocol C`繼承而來的新協議`Protocol D`,很顯然這樣做有效率的多,甚至不需引入一個新名字。
協議合成列表中的每項必須是協議名或協議合成類型的類型別名。如果列表為空,它就會指定一個空協議合成列表,這樣每個類型都能遵循。
> 協議合成類型語法
> _協議合成類型_?→?protocol??[_協議標識符列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#protocol_identifier_list)?_可選_?>
> _協議標識符列表_?→?[_協議標識符_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#protocol_identifier)?|?[_協議標識符_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#protocol_identifier)?,?[_協議標識符列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#protocol_identifier_list)
> _協議標識符_?→?[_類型標識_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type_identifier)
## 元類型
元類型是指所有類型的類型,包括類、結構體、枚舉和協議。
類、結構體或枚舉類型的元類型是相應的類型名緊跟`.Type`。協議類型的元類型——并不是運行時遵循該協議的具體類型——是該協議名字緊跟`.Protocol`。比如,類`SomeClass`的元類型就是`SomeClass.Type`,協議`SomeProtocol`的元類型就是`SomeProtocal.Protocol`。
你可以使用后綴`self`表達式來獲取類型。比如,`SomeClass.self`返回`SomeClass`本身,而不是`SomeClass`的一個實例。同樣,`SomeProtocol.self`返回`SomeProtocol`本身,而不是運行時遵循`SomeProtocol`的某個類型的實例。還可以對類型的實例使用`dynamicType`表達式來獲取該實例在運行階段的類型,如下所示:
~~~
class SomeBaseClass {
class func printClassName() {
println("SomeBaseClass")
}
}
class SomeSubClass: SomeBaseClass {
override class func printClassName() {
println("SomeSubClass")
}
}
let someInstance: SomeBaseClass = SomeSubClass()
// someInstance is of type SomeBaseClass at compile time, but
// someInstance is of type SomeSubClass at runtime
someInstance.dynamicType.printClassName()
// prints "SomeSubClass
~~~
> 注意?不能創建元類型類的實例,因為不能保證其子類會提供初始化的代碼。
>
> 元(Metatype)類型語法
> _元類型_?→?[_類型_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type)?.?Type?|?[_類型_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type)?.?Protocol
## 類型繼承子句
類型繼承子句被用來指定一個命名型類型繼承的哪個類、遵循的哪些協議。類型繼承子句也用來指定一個類需要遵循的協議。類型繼承子句開始于冒號`:`,其后是類所需遵循的協議或者類型標識符列表或者兩者均有。
類可以繼承單個超類,遵循任意數量的協議。當定義一個類時,超類的名字必須出現在類型標識符列表首位,然后跟上該類需要遵循的任意數量的協議。如果一個類不是從其它類繼承而來,那么列表可以以協議開頭。關于類繼承更多的討論和例子,見章節Inheritance。
其它命名型類型可能只繼承或遵循一個協議列表。協議類型可能繼承于其它任意數量的協議。當一個協議類型繼承于其它協議時,其它協議的條件集合會被整合在一起,然后其它從當前協議繼承的任意類型必須遵循所有這些條件。正如在協議聲明中所討論的那樣,可以把類的關鍵字放到類型繼承子句中的首位,這樣就可以用一個類的條件來標記一個協議聲明。
枚舉定義中的類型繼承子句可以是一個協議列表,或是指定原始值的枚舉——一個單獨的指定原始值類型的命名型類型。使用類型繼承子句來指定原始值類型的枚舉定義的例子,見章節Raw Values。
> 類型繼承子句語法
> _類型繼承子句_?→?:?[_類需求_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#class_requirement)?,?[_類型繼承列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type_inheritance_list)?_類型繼承子句_?→?:?[_類需求_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#class_requirement)?_類型繼承子句_?→?:?[_類型繼承列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type_inheritance_list)?_類型繼承列表_?→?[_類型標識_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type_identifier)?|?[_類型標識_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type_identifier)?,?[_類型繼承列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type_inheritance_list)?_類需求_?→?類
## 類型推斷
Swift廣泛的使用類型推斷,從而允許你可以忽略代碼中很多變量和表達式的類型或部分類型。比如,對于`var x: Int = 0`,你可以完全忽略類型而簡寫成`var x = 0`——編譯器會正確的推斷出`x`的類型`Int`。類似的,當完整的類型可以從上下文推斷出來時,你也可以忽略類型的一部分。比如,如果你寫了`let dict: Dictionary = ["A": 1]`,編譯提也能推斷出`dict`的類型是`Dictionary<String, Int>`。
在上面的兩個例子中,類型信息從表達式樹(expression tree)的葉子節點傳向根節點。也就是說,`var x: Int = 0`中`x`的類型首先根據`0`的類型進行推斷,然后將該類型信息傳遞到根節點(變量`x`)。
在Swift中,類型信息也可以反方向流動——從根節點傳向葉子節點。在下面的例子中,常量`eFloat`上的顯式類型注解(`:Float`)導致數字字面量`2.71828`的類型是`Float`而非`Double`。
~~~
let e = 2.71828 // The type of e is inferred to be Double.
let eFloat: Float = 2.71828 // The type of eFloat is Float.
~~~
Swift中的類型推斷在單獨的表達式或語句水平上進行。這意味著所有用于推斷類型的信息必須可以從表達式或其某個子表達式的類型檢查中獲取。
- 介紹
- 歡迎使用 Swift
- 關于 Swift
- Swift 初見
- Swift 版本歷史記錄
- Swift1.0 發布內容
- Swift 教程
- 基礎部分
- 基本運算符
- 字符串和字符
- 集合類型
- 控制流
- 函數
- 閉包
- 枚舉
- 類和結構體
- 屬性
- 方法
- 下標腳本
- 繼承
- 構造過程
- 析構過程
- 自動引用計數
- 可選鏈
- 錯誤處理
- 類型轉換
- 嵌套類型
- 擴展
- 協議
- 泛型
- 權限控制
- 高級操作符
- 語言參考
- 關于語言參考
- 詞法結構
- 類型
- 表達式
- 語句
- 聲明
- 特性
- 模式
- 泛型參數
- 語法總結
- 蘋果官方Blog官方翻譯
- Access Control 權限控制的黑與白
- 造個類型不是夢-白話Swift類型創建
- WWDC里面的那個“大炮打氣球”
- Swift與C語言指針友好合作
- 引用類型和值類型的恩怨
- 訪問控制和Protected
- 可選類型完美解決占位問題