1.0 翻譯:lyuka 校對:numbbbbb, stanzhai
2.0 翻譯+校對:EudeMorgen
2.1 翻譯:mmoaay
本頁包含內(nèi)容:
Swift 語言存在兩種類型:命名型類型和復(fù)合型類型。命名型類型是指定義時可以給定名字的類型。命名型類型包括類、結(jié)構(gòu)體、枚舉和協(xié)議。比如,一個用戶定義的類 MyClass
的實(shí)例擁有類型 MyClass
。除了用戶定義的命名型類型,Swift 標(biāo)準(zhǔn)庫也定義了很多常用的命名型類型,包括那些表示數(shù)組、字典和可選值的類型。
那些通常被其它語言認(rèn)為是基本或原始的數(shù)據(jù)型類型,比如表示數(shù)字、字符和字符串的類型,實(shí)際上就是命名型類型,這些類型在 Swift 標(biāo)準(zhǔn)庫中是使用結(jié)構(gòu)體來定義和實(shí)現(xiàn)的。因?yàn)樗鼈兪敲皖愋?,因此你可以按?擴(kuò)展 和 擴(kuò)展聲明 中討論的那樣,聲明一個擴(kuò)展來增加它們的行為以滿足你程序的需求。
復(fù)合型類型是沒有名字的類型,它由 Swift 本身定義。Swift 存在兩種復(fù)合型類型:函數(shù)類型和元組類型。一個復(fù)合型類型可以包含命名型類型和其它復(fù)合型類型。例如,元組類型 (Int, (Int, Int))
包含兩個元素:第一個是命名型類型 Int
,第二個是另一個復(fù)合型類型 (Int, Int)
。
本節(jié)討論 Swift 語言本身定義的類型,并描述 Swift 中的類型推斷行為。
類型語法
類型 → 數(shù)組類型 | 字典類型 | 函數(shù)類型 | 類型標(biāo)識 | 元組類型 | 可選類型 | 隱式解析可選類型 | 協(xié)議合成類型 | 元型類型 | 任意類型 | 自身類型
類型注解顯式地指定一個變量或表達(dá)式的值。類型注解始于冒號 :
終于類型,比如下面兩個例子:
let someTuple: (Double, Double) = (3.14159, 2.71828)
func someFunction(a: Int) { /* ... */ }
在第一個例子中,表達(dá)式 someTuple
的類型被指定為 (Double, Double)
。在第二個例子中,函數(shù) someFunction
的參數(shù) a
的類型被指定為 Int
。
類型注解可以在類型之前包含一個類型特性的可選列表。
類型標(biāo)識符引用命名型類型,還可引用命名型或復(fù)合型類型的別名。
大多數(shù)情況下,類型標(biāo)識符引用的是與之同名的命名型類型。例如類型標(biāo)識符 Int
引用命名型類型 Int
,同樣,類型標(biāo)識符 Dictionary<String, Int>
引用命名型類型 Dictionary<String, Int>
。
在兩種情況下類型標(biāo)識符不引用同名的類型。情況一,類型標(biāo)識符引用的是命名型或復(fù)合型類型的類型別名。比如,在下面的例子中,類型標(biāo)識符使用 Point
來引用元組 (Int, Int)
:
typealias Point = (Int, Int)
let origin: Point = (0, 0)
情況二,類型標(biāo)識符使用點(diǎn)語法(.
)來表示在其它模塊或其它類型嵌套內(nèi)聲明的命名型類型。例如,下面例子中的類型標(biāo)識符引用在 ExampleModule
模塊中聲明的命名型類型 MyType
:
var someValue: ExampleModule.MyType
類型標(biāo)識符語法
</a> 類型標(biāo)識符 → 類型名稱 泛型參數(shù)子句可選 | 類型名稱 泛型參數(shù)子句可選 . 類型標(biāo)識符
類型名稱 → 標(biāo)識符
元組類型是使用括號括起來的零個或多個類型,類型間用逗號隔開。
你可以使用元組類型作為一個函數(shù)的返回類型,這樣就可以使函數(shù)返回多個值。你也可以命名元組類型中的元素,然后用這些名字來引用每個元素的值。元素的名字由一個標(biāo)識符緊跟一個冒號 (:)
組成。函數(shù)和多返回值 章節(jié)里有一個展示上述特性的例子。
當(dāng)一個元組類型的元素有名字的時候,這個名字就是類型的一部分。
var someTuple = (top: 10, bottom: 12) // someTuple 的類型為 (top: Int, bottom: Int)
someTuple = (top: 4, bottom: 42) // 正確:命名類型匹配
someTuple = (9, 99) // 正確:命名類型被自動推斷
someTuple = (left: 5, right: 5) // 錯誤:命名類型不匹配
Void
是空元組類型 ()
的別名。如果括號內(nèi)只有一個元素,那么該類型就是括號內(nèi)元素的類型。比如,(Int)
的類型是 Int
而不是 (Int)
。所以,只有當(dāng)元組類型包含的元素個數(shù)在兩個及以上時才可以命名元組元素。
元組類型語法 </a> 元組類型 → ( 元組類型元素列表 可選 )
元組類型元素列表 → 元組類型元素 | 元組類型元素 , 元組類型元素列表
</a> 元組類型元素 → 元素名 類型注解 | 類型 元素名 → 標(biāo)識符
函數(shù)類型表示一個函數(shù)、方法或閉包的類型,它由參數(shù)類型和返回值類型組成,中間用箭頭(->
)隔開:
參數(shù)類型
->返回值類型
參數(shù)類型是由逗號間隔的類型列表。由于參數(shù)類型和返回值類型可以是元組類型,所以函數(shù)類型支持多參數(shù)與多返回值的函數(shù)與方法。
你可以對函數(shù)參數(shù)使用 autoclosure
特性。這會自動將參數(shù)表達(dá)式轉(zhuǎn)化為閉包,表達(dá)式的結(jié)果即閉包返回值。這從語法結(jié)構(gòu)上提供了一種便捷:延遲對表達(dá)式的求值,直到其值在函數(shù)體中被使用。以自動閉包做為參數(shù)的函數(shù)類型的例子詳見 自動閉包 。
函數(shù)類型可以擁有一個可變長參數(shù)作為參數(shù)類型中的最后一個參數(shù)。從語法角度上講,可變長參數(shù)由一個基礎(chǔ)類型名字緊隨三個點(diǎn)(...
)組成,如 Int...
??勺冮L參數(shù)被認(rèn)為是一個包含了基礎(chǔ)類型元素的數(shù)組。即 Int...
就是 [Int]
。關(guān)于使用可變長參數(shù)的例子,請參閱 可變參數(shù)。
為了指定一個 in-out
參數(shù),可以在參數(shù)類型前加 inout
前綴。但是你不可以對可變長參數(shù)或返回值類型使用 inout
。關(guān)于這種參數(shù)的詳細(xì)講解請參閱 輸入輸出參數(shù)。
函數(shù)和方法中的參數(shù)名并不是函數(shù)類型的一部分。例如:
func someFunction(left: Int, right: Int) {}
func anotherFunction(left: Int, right: Int) {}
func functionWithDifferentLabels(top: Int, bottom: Int) {}
var f = someFunction // 函數(shù)f的類型為 (Int, Int) -> Void, 而不是 (left: Int, right: Int) -> Void.
f = anotherFunction // 正確
f = functionWithDifferentLabels // 正確
func functionWithDifferentArgumentTypes(left: Int, right: String) {}
func functionWithDifferentNumberOfArguments(left: Int, right: Int, top: Int) {}
f = functionWithDifferentArgumentTypes // 錯誤
f = functionWithDifferentNumberOfArguments // 錯誤
如果一個函數(shù)類型包涵多個箭頭(->),那么函數(shù)類型將從右向左進(jìn)行組合。例如,函數(shù)類型 Int -> Int -> Int
可以理解為 Int -> (Int -> Int)
,也就是說,該函數(shù)類型的參數(shù)為 Int
類型,其返回類型是一個參數(shù)類型為 Int
,返回類型為 Int
的函數(shù)類型。
函數(shù)類型若要拋出錯誤就必須使用 throws
關(guān)鍵字來標(biāo)記,若要重拋錯誤則必須使用 rethrows
關(guān)鍵字來標(biāo)記。throws
關(guān)鍵字是函數(shù)類型的一部分,非拋出函數(shù)是拋出函數(shù)函數(shù)的一個子類型。因此,在使用拋出函數(shù)的地方也可以使用不拋出函數(shù)。拋出和重拋函數(shù)的相關(guān)描述見章節(jié) 拋出函數(shù)與方法 和 重拋函數(shù)與方法。
函數(shù)類型語法
</a> 函數(shù)類型 → 特性列表可選 函數(shù)類型子句 throws可選 -> 類型 函數(shù)類型 → 特性列表可選 函數(shù)類型子句 rethrows- -> 類型 函數(shù)類型子句 → (-)- 函數(shù)類型子句 → (函數(shù)類型參數(shù)列表...-可選)- </a> 函數(shù)類型參數(shù)列表 → 函數(shù)類型參數(shù) | 函數(shù)類型參數(shù), 函數(shù)類型參數(shù)列表 函數(shù)類型參數(shù) → 特性列表可選 輸入輸出參數(shù)可選 類型 | 參數(shù)標(biāo)簽 類型注解 參數(shù)標(biāo)簽 → 標(biāo)識符
Swift 語言為標(biāo)準(zhǔn)庫中定義的 Array<Element>
類型提供了如下語法糖:
[
類型
]
換句話說,下面兩個聲明是等價的:
let someArray: Array<String> = ["Alex", "Brian", "Dave"]
let someArray: [String] = ["Alex", "Brian", "Dave"]
上面兩種情況下,常量 someArray
都被聲明為字符串?dāng)?shù)組。數(shù)組的元素也可以通過下標(biāo)訪問:someArray[0]
是指第 0 個元素 "Alex"
。
你也可以嵌套多對方括號來創(chuàng)建多維數(shù)組,最里面的方括號中指明數(shù)組元素的基本類型。比如,下面例子中使用三對方括號創(chuàng)建三維整數(shù)數(shù)組:
var array3D: [[[Int]]] = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
訪問一個多維數(shù)組的元素時,最左邊的下標(biāo)指向最外層數(shù)組的相應(yīng)位置元素。接下來往右的下標(biāo)指向第一層嵌入的相應(yīng)位置元素,依次類推。這就意味著,在上面的例子中,array3D[0]
是 [[1, 2], [3, 4]]
,array3D[0][1]
是 [3, 4]
,array3D[0][1][1]
則是 4
。
關(guān)于 Swift 標(biāo)準(zhǔn)庫中 Array
類型的詳細(xì)討論,請參閱 數(shù)組。
數(shù)組類型語法
數(shù)組類型 → [ 類型 ]
Swift 語言為標(biāo)準(zhǔn)庫中定義的 Dictionary<Key, Value>
類型提供了如下語法糖:
[
鍵類型
:值類型
]
換句話說,下面兩個聲明是等價的:
let someDictionary: [String: Int] = ["Alex": 31, "Paul": 39]
let someDictionary: Dictionary<String, Int> = ["Alex": 31, "Paul": 39]
上面兩種情況,常量 someDictionary
被聲明為一個字典,其中鍵為 String
類型,值為 Int
類型。
字典中的值可以通過下標(biāo)來訪問,這個下標(biāo)在方括號中指明了具體的鍵:someDictionary["Alex"]
返回鍵 Alex
對應(yīng)的值。如果鍵在字典中不存在的話,則這個下標(biāo)返回 nil
。
字典中鍵的類型必須符合 Swift 標(biāo)準(zhǔn)庫中的 Hashable
協(xié)議。
關(guān)于 Swift 標(biāo)準(zhǔn)庫中 Dictionary
類型的詳細(xì)討論,請參閱 字典。
Swift 定義后綴 ?
來作為標(biāo)準(zhǔn)庫中的定義的命名型類型 Optional<Wrapped>
的語法糖。換句話說,下面兩個聲明是等價的:
var optionalInteger: Int?
var optionalInteger: Optional<Int>
在上述兩種情況下,變量 optionalInteger
都被聲明為可選整型類型。注意在類型和 ?
之間沒有空格。
類型 Optional<Wrapped>
是一個枚舉,有兩個成員,none
和 some(Wrapped)
,用來表示可能有也可能沒有的值。任意類型都可以被顯式地聲明(或隱式地轉(zhuǎn)換)為可選類型。如果你在聲明或定義可選變量或?qū)傩缘臅r候沒有提供初始值,它的值則會自動賦為默認(rèn)值 nil
。
如果一個可選類型的實(shí)例包含一個值,那么你就可以使用后綴運(yùn)算符 !
來獲取該值,正如下面描述的:
optionalInteger = 42
optionalInteger! // 42
使用 !
運(yùn)算符解包值為 nil
的可選值會導(dǎo)致運(yùn)行錯誤。
你也可以使用可選鏈?zhǔn)秸{(diào)用和可選綁定來選擇性地在可選表達(dá)式上執(zhí)行操作。如果值為 nil
,不會執(zhí)行任何操作,因此也就沒有運(yùn)行錯誤產(chǎn)生。
更多細(xì)節(jié)以及更多如何使用可選類型的例子,請參閱 可選類型。
可選類型語法
可選類型 → 類型 ?
當(dāng)可以被訪問時,Swift 語言定義后綴 !
作為標(biāo)準(zhǔn)庫中命名類型 Optional<Wrapped>
的語法糖,來實(shí)現(xiàn)自動解包的功能。換句話說,下面兩個聲明等價:
var implicitlyUnwrappedString: String!
var explicitlyUnwrappedString: Optional<String>
注意類型與 !
之間沒有空格。
由于隱式解包修改了包涵其類型的聲明語義,嵌套在元組類型或泛型的可選類型(比如字典元素類型或數(shù)組元素類型),不能被標(biāo)記為隱式解包。例如:
let tupleOfImplicitlyUnwrappedElements: (Int!, Int!) // 錯誤
let implicitlyUnwrappedTuple: (Int, Int)! // 正確
let arrayOfImplicitlyUnwrappedElements: [Int!] // 錯誤
let implicitlyUnwrappedArray: [Int]! // 正確
由于隱式解析可選類型和可選類型有同樣的表達(dá)式Optional<Wrapped>
,你可以在使用可選類型的地方使用隱式解析可選類型。比如,你可以將隱式解析可選類型的值賦給變量、常量和可選屬性,反之亦然。
正如可選類型一樣,你在聲明隱式解析可選類型的變量或?qū)傩缘臅r候也不用指定初始值,因?yàn)樗心J(rèn)值 nil
。
可以使用可選鏈?zhǔn)秸{(diào)用來在隱式解析可選表達(dá)式上選擇性地執(zhí)行操作。如果值為 nil
,就不會執(zhí)行任何操作,因此也不會產(chǎn)生運(yùn)行錯誤。
關(guān)于隱式解析可選類型的更多細(xì)節(jié),請參閱 隱式解析可選類型。
隱式解析可選類型語法
隱式解析可選類型 → 類型 !
協(xié)議合成類型是一種符合協(xié)議列表中每個指定協(xié)議的類型。協(xié)議合成類型可能會用在類型注解和泛型參數(shù)中。
協(xié)議合成類型的形式如下:
Protocol 1
&Procotol 2
協(xié)議合成類型允許你指定一個值,其類型符合多個協(xié)議的要求且不需要定義一個新的命名型協(xié)議來繼承它想要符合的各個協(xié)議。比如,協(xié)議合成類型 Protocol A & Protocol B & Protocol C
等效于一個從 Protocol A
,Protocol B
, Protocol C
繼承而來的新協(xié)議 Protocol D
,很顯然這樣做有效率的多,甚至不需引入一個新名字。
協(xié)議合成列表中的每項(xiàng)必須是協(xié)議名或協(xié)議合成類型的類型別名。
協(xié)議合成類型語法
</a> 協(xié)議合成類型 → 協(xié)議標(biāo)識符 & 協(xié)議合成延續(xù) 協(xié)議合成延續(xù) → 協(xié)議標(biāo)識符 | 協(xié)議合成類型 協(xié)議標(biāo)識符 → 類型標(biāo)識符
元類型是指類型的類型,包括類類型、結(jié)構(gòu)體類型、枚舉類型和協(xié)議類型。
類、結(jié)構(gòu)體或枚舉類型的元類型是相應(yīng)的類型名緊跟 .Type
。協(xié)議類型的元類型——并不是運(yùn)行時符合該協(xié)議的具體類型——而是該協(xié)議名字緊跟 .Protocol
。比如,類 SomeClass
的元類型就是 SomeClass.Type
,協(xié)議 SomeProtocol
的元類型就是 SomeProtocal.Protocol
。
你可以使用后綴 self
表達(dá)式來獲取類型。比如,SomeClass.self
返回 SomeClass
本身,而不是 SomeClass
的一個實(shí)例。同樣,SomeProtocol.self
返回 SomeProtocol
本身,而不是運(yùn)行時符合 SomeProtocol
的某個類型的實(shí)例。還可以對類型的實(shí)例使用 type(of:)
表達(dá)式來獲取該實(shí)例在運(yùn)行階段的類型,如下所示:
class SomeBaseClass {
class func printClassName() {
println("SomeBaseClass")
}
}
class SomeSubClass: SomeBaseClass {
override class func printClassName() {
println("SomeSubClass")
}
}
let someInstance: SomeBaseClass = SomeSubClass()
// someInstance 在編譯期是 SomeBaseClass 類型,
// 但是在運(yùn)行期則是 SomeSubClass 類型
type(of: someInstance).printClassName()
// 打印 “SomeSubClass”
可以使用恒等運(yùn)算符(===
和 !==
)來測試一個實(shí)例的運(yùn)行時類型和它的編譯時類型是否一致。
if type(of: someInstance) === someInstance.self {
print("The dynamic and static type of someInstance are the same")
} else {
print("The dynamic and static type of someInstance are different")
}
// 打印 "The dynamic and static type of someInstance are different"
可以使用初始化表達(dá)式從某個類型的元類型構(gòu)造出一個該類型的實(shí)例。對于類實(shí)例,被調(diào)用的構(gòu)造器必須使用 required
關(guān)鍵字標(biāo)記,或者整個類使用 final
關(guān)鍵字標(biāo)記。
class AnotherSubClass: SomeBaseClass {
let string: String
required init(string: String) {
self.string = string
}
override class func printClassName() {
print("AnotherSubClass")
}
}
let metatype: AnotherSubClass.Type = AnotherSubClass.self
let anotherInstance = metatype.init(string: "some string")
類型繼承子句被用來指定一個命名型類型繼承自哪個類、采納哪些協(xié)議。類型繼承子句也用來指定一個類類型專屬協(xié)議。類型繼承子句開始于冒號 :
,其后是所需要的類、類型標(biāo)識符列表或兩者都有。
類可以繼承單個超類,采納任意數(shù)量的協(xié)議。當(dāng)定義一個類時,超類的名字必須出現(xiàn)在類型標(biāo)識符列表首位,然后跟上該類需要采納的任意數(shù)量的協(xié)議。如果一個類不是從其它類繼承而來,那么列表可以以協(xié)議開頭。關(guān)于類繼承更多的討論和例子,請參閱 繼承。
其它命名型類型可能只繼承或采納一系列協(xié)議。協(xié)議類型可以繼承自任意數(shù)量的其他協(xié)議。當(dāng)一個協(xié)議類型繼承自其它協(xié)議時,其它協(xié)議中定義的要求會被整合在一起,然后從當(dāng)前協(xié)議繼承的任意類型必須符合所有這些條件。正如在 協(xié)議聲明 中所討論的那樣,可以把 class
關(guān)鍵字放到協(xié)議類型的類型繼承子句的首位,這樣就可以聲明一個類類型專屬協(xié)議。
枚舉定義中的類型繼承子句可以是一系列協(xié)議,或是枚舉的原始值類型的命名型類型。在枚舉定義中使用類型繼承子句來指定原始值類型的例子,請參閱 原始值。
類型繼承子句語法
</a> 類型繼承子句 → : 類要求 , 類型繼承列表
類型繼承子句 → : 類要求
類型繼承子句 → : 類型繼承列表
類型繼承列表 → 類型標(biāo)識符 | 類型標(biāo)識符 , 類型繼承列表
類要求 → class
Swift 廣泛使用類型推斷,從而允許你省略代碼中很多變量和表達(dá)式的類型或部分類型。比如,對于 var x: Int = 0
,你可以完全省略類型而簡寫成 var x = 0
,編譯器會正確推斷出 x
的類型 Int
。類似的,當(dāng)完整的類型可以從上下文推斷出來時,你也可以省略類型的一部分。比如,如果你寫了 let dict: Dictionary = ["A" : 1]
,編譯器能推斷出 dict
的類型是 Dictionary<String, Int>
。
在上面的兩個例子中,類型信息從表達(dá)式樹的葉子節(jié)點(diǎn)傳向根節(jié)點(diǎn)。也就是說,var x: Int = 0
中 x
的類型首先根據(jù) 0
的類型進(jìn)行推斷,然后將該類型信息傳遞到根節(jié)點(diǎn)(變量 x
)。
在 Swift 中,類型信息也可以反方向流動——從根節(jié)點(diǎn)傳向葉子節(jié)點(diǎn)。在下面的例子中,常量 eFloat
上的顯式類型注解(: Float
)將導(dǎo)致數(shù)字字面量 2.71828
的類型是 Float
而非 Double
。
let e = 2.71828 // e 的類型會被推斷為 Double
let eFloat: Float = 2.71828 // eFloat 的類型為 Float
Swift 中的類型推斷在單獨(dú)的表達(dá)式或語句上進(jìn)行。這意味著所有用于類型推斷的信息必須可以從表達(dá)式或其某個子表達(dá)式的類型檢查中獲取到。