天天看點

Swift類型參考!

Swift 語言存在兩種類型:命名型類型和複合型類型。命名型類型是指定義時可以給定名字的類型。命名型類型包括類、結構體、枚舉和協定。比如,一個使用者定義的類MyClass的執行個體擁有類型MyClass。除了使用者定義的命名型類型,Swift 标準庫也定義了很多常用的命名型類型,包括那些表示數組、字典和可選值的類型。

那些通常被其它語言認為是基本或初級的資料型類型(Data types)——比如表示數字、字元和字元串——實際上就是命名型類型,Swift 标準庫是使用結構體定義和實作它們的。因為它們是命名型類型,是以你可以按照“擴充和擴充聲明”章節裡讨論的那樣,聲明一個擴充來增加它們的行為以适應你程式的需求。

複合型類型是沒有名字的類型,它由 Swift 本身定義。Swift 存在兩種複合型類型:函數類型和元組類型。一個複合型類型可以包含命名型類型和其它複合型類型。例如,元組類型(Int, (Int, Int))包含兩個元素:第一個是命名型類型Int,第二個是另一個複合型類型(Int, Int).

本節讨論 Swift 語言本身定義的類型,并描述 Swift 中的類型推斷行為。

類型的文法: type → array-type | function-type | type-identifier | tuple-type | optional-type | implicitly-unwrapped-optional-type | protocol-composition-type | metatype-type 作為一個開發者,有一個學習的氛圍跟一個交流圈子特别重要,這是一個我的iOS開發交流群:130 595 548,不管你是小白還是大牛都歡迎入駐 ,讓我們一起進步,共同發展!(群内會免費提供一些群主收藏的免費學習書籍資料以及整理好的幾百道面試題和答案文檔!)

類型注解顯式地指定一個變量或表達式的值。類型注解始于冒号:終于類型,比如下面兩個例子:

在第一個例子中,表達式someTuple的類型被指定為(Double, Double)。在第二個例子中,函數someFunction的參數a的類型被指定為Int。

類型注解可以在類型之前包含一個類型特性(type attributes)的可選清單。

類型注解的文法: type-annotation → :attributes[opt] type

類型辨別符引用命名型類型或者是命名型/複合型類型的别名。

大多數情況下,類型辨別符引用的是同名的命名型類型。例如類型辨別符Int引用命名型類型Int,同樣,類型辨別符Dictionary<String, Int>引用命名型類型Dictionary<String, Int>。

在兩種情況下類型辨別符引用的不是同名的類型。情況一,類型辨別符引用的是命名型/複合型類型的類型别名。比如,在下面的例子中,類型辨別符使用Point來引用元組(Int, Int):

情況二,類型辨別符使用dot(.)文法來表示在其它子產品(modules)或其它類型嵌套内聲明的命名型類型。例如,下面例子中的類型辨別符引用在ExampleModule子產品中聲明的命名型類型MyType:

類型辨別符的文法: type-identifier → type-name generic-argument-clause[opt] | type-name generic-argument-clause[opt].type-identifier type-name → identifier

元組類型使用逗号隔開并使用括号括起來的0個或多個類型組成的清單。

你可以使用元組類型作為一個函數的傳回類型,這樣就可以使函數傳回多個值。你也可以命名元組類型中的元素,然後用這些名字來引用每個元素的值。元素的名字由一個辨別符和:組成。“函數和多傳回值”章節裡有一個展示上述特性的例子。

void是空元組類型()的别名。如果括号内隻有一個元素,那麼該類型就是括号内元素的類型。比如,(Int)的類型是Int而不是(Int)。是以,隻有當元組類型包含兩個元素以上時才可以标記元組元素。

元組類型文法: tuple → (tuple-type-body[opt]) tuple-type-body → tuple-type-element-list ...[opt] tuple-type-element-list → tuple-type-element | tuple-type-element, tuple-type-element-list tuple-type-element → attributes[opt] inout [opt] type | inout [opt] element-name type-annotation element-name → identifier

函數類型表示一個函數、方法或閉包的類型,它由一個參數類型和傳回值類型組成,中間用箭頭->隔開:

parameter type -> return type

由于 參數類型 和 傳回值類型 可以是元組類型,是以函數類型可以讓函數與方法支援多參數與多傳回值。

你可以對函數類型應用帶有參數類型()并傳回表達式類型的auto_closure屬性(見類型屬性章節)。一個自動閉包函數捕獲特定表達式上的隐式閉包而非表達式本身。下面的例子使用auto_closure屬性來定義一個很簡單的assert函數:

函數類型可以擁有一個可變長參數作為參數類型中的最後一個參數。從文法角度上講,可變長參數由一個基礎類型名字和...組成,如Int...。可變長參數被認為是一個包含了基礎類型元素的數組。即Int...就是Int[]。關于使用可變長參數的例子,見章節“可變長參數”。

為了指定一個in-out參數,可以在參數類型前加inout字首。但是你不可以對可變長參數或傳回值類型使用inout。關于In-Out參數的讨論見章節In-Out參數部分。

柯裡化函數(curried function)的類型相當于一個嵌套函數類型。例如,下面的柯裡化函數addTwoNumber()()的類型是Int -> Int -> Int:

柯裡化函數的函數類型從右向左組成一組。例如,函數類型Int -> Int -> Int可以被了解為Int -> (Int -> Int)——也就是說,一個函數傳入一個Int然後輸出作為另一個函數的輸入,然後又傳回一個Int。例如,你可以使用如下嵌套函數來重寫柯裡化函數addTwoNumbers()():

函數類型的文法: function-type → type -> type

Swift語言使用類型名緊接中括号[]來簡化标準庫中定義的命名型類型Array<T>。換句話說,下面兩個聲明是等價的:

上面兩種情況下,常量someArray都被聲明為字元串數組。數組的元素也可以通過[]擷取通路:someArray[0]是指第0個元素“Alex”。

上面的例子同時顯示,你可以使用[]作為初始值構造數組,空的[]則用來來構造指定類型的空數組。

你也可以使用連結起來的多個[]集合來構造多元數組。例如,下例使用三個[]集合來構造三維整型數組:

通路一個多元數組的元素時,最左邊的下标指向最外層數組的相應位置元素。接下來往右的下标指向第一層嵌入的相應位置元素,依次類推。這就意味着,在上面的例子中,array3D[0]是指[[1, 2], [3, 4]],array3D[0][1]是指[3, 4],array3D[0][1][1]則是指值4。

關于Swift标準庫中Array類型的細節讨論,見章節Arrays。

數組類型的文法: array-type → type[ ] | array-type[ ]

Swift定義字尾?來作為标準庫中的定義的命名型類型Optional<T>的簡寫。換句話說,下面兩個聲明是等價的:

在上述兩種情況下,變量optionalInteger都被聲明為可選整型類型。注意在類型和?之間沒有空格。

類型Optional<T>是一個枚舉,有兩種形式,None和Some(T),又來代表可能出現或可能不出現的值。任意類型都可以被顯式的聲明(或隐式的轉換)為可選類型。當聲明一個可選類型時,確定使用括号給?提供合适的作用範圍。比如說,聲明一個整型的可選數組,應寫作(Int[])?,寫成Int[]?的話則會出錯。

如果你在聲明或定義可選變量或特性的時候沒有提供初始值,它的值則會自動賦成預設值nil。

可選符合LogicValue協定,是以可以出現在布爾值環境下。此時,如果一個可選類型T?執行個體包含有類型為T的值(也就是說值為Optional.Some(T)),那麼此可選類型就為true,否則為false。

如果一個可選類型的執行個體包含一個值,那麼你就可以使用字尾操作符!來擷取該值,正如下面描述的:

使用!操作符擷取值為nil的可選項會導緻運作錯誤(runtime error)。

你也可以使用可選鍊和可選綁定來選擇性的執行可選表達式上的操作。如果值為nil,不會執行任何操作是以也就沒有運作錯誤産生。

更多細節以及更多如何使用可選類型的例子,見章節“可選”。

可選類型文法: optional-type → type?

Swift語言定義字尾!作為标準庫中命名類型ImplicitlyUnwrappedOptional<T>的簡寫。換句話說,下面兩個聲明等價:

上述兩種情況下,變量implicitlyUnwrappedString被聲明為一個隐式解析可選類型的字元串。注意類型與!之間沒有空格。

你可以在使用可選的地方同樣使用隐式解析可選。比如,你可以将隐式解析可選的值賦給變量、常量和可選特性,反之亦然。

有了可選,你在聲明隐式解析可選變量或特性的時候就不用指定初始值,因為它有預設值nil。

由于隐式解析可選的值會在使用時自動解析,是以沒必要使用操作符!來解析它。也就是說,如果你使用值為nil的隐式解析可選,就會導緻運作錯誤。

使用可選鍊會選擇性的執行隐式解析可選表達式上的某一個操作。如果值為nil,就不會執行任何操作,是以也不會産生運作錯誤。

關于隐式解析可選的更多細節,見章節“隐式解析可選”。

隐式解析可選的文法: implicitly-unwrapped-optional-type → type!

協定合成類型是一種符合每個協定的指定協定清單類型。協定合成類型可能會用在類型注解和泛型參數中。

協定合成類型的形式如下:

    protocol<Protocol 1, Procotol 2>

協定合成類型允許你指定一個值,其類型可以适配多個協定的條件,而且不需要定義一個新的命名型協定來繼承其它想要适配的各個協定。比如,協定合成類型protocol<Protocol A, Protocol B, Protocol C>等效于一個從Protocol A,Protocol B, Protocol C繼承而來的新協定Protocol D,很顯然這樣做有效率的多,甚至不需引入一個新名字。

協定合成清單中的每項必須是協定名或協定合成類型的類型别名。如果清單為空,它就會指定一個空協定合成清單,這樣每個類型都能适配。

協定合成類型的文法: protocol-composition-type → protocol <protocol-identifier-list[opt]> protocol-identifier-list → protocol-identifier | protocol-identifier, protocol-identifier-list protocol-identifier → type-identifier

元類型是指所有類型的類型,包括類、結構體、枚舉和協定。

類、結構體或枚舉類型的元類型是相應的類型名緊跟.Type。協定類型的元類型——并不是運作時适配該協定的具體類型——是該協定名字緊跟.Protocol。比如,類SomeClass的元類型就是SomeClass.Type,協定SomeProtocol的元類型就是SomeProtocal.Protocol。

你可以使用字尾self表達式來擷取類型。比如,SomeClass.self傳回SomeClass本身,而不是SomeClass的一個執行個體。同樣,SomeProtocol.self傳回SomeProtocol本身,而不是運作時适配SomeProtocol的某個類型的執行個體。還可以對類型的執行個體使用dynamicType表達式來擷取該執行個體在運作階段的類型,如下所示:

元類型的文法: metatype-type → type.Type | type.Protocol

類型繼承子句被用來指定一個命名型類型繼承哪個類且适配哪些協定。類型繼承子句開始于冒号:,緊跟由,隔開的類型辨別符清單。

類可以繼承單個超類,适配任意數量的協定。當定義一個類時,超類的名字必須出現在類型辨別符清單首位,然後跟上該類需要适配的任意數量的協定。如果一個類不是從其它類繼承而來,那麼清單可以以協定開頭。關于類繼承更多的讨論和例子,見章節“繼承”。

其它命名型類型可能隻繼承或适配一個協定清單。協定類型可能繼承于其它任意數量的協定。當一個協定類型繼承于其它協定時,其它協定的條件集合會被內建在一起,然後其它從目前協定繼承的任意類型必須适配所有這些條件。

枚舉定義中的類型繼承子句可以是一個協定清單,或是指定原始值的枚舉,一個單獨的指定原始值類型的命名型類型。使用類型繼承子句來指定原始值類型的枚舉定義的例子,見章節“原始值”。

類型繼承子句的文法: type-inheritance-clause → :type-inheritance-list type-inheritance-list → type-identifier | type-identifier, 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。

Swift中的類型推斷在單獨的表達式或語句水準上進行。這意味着所有用于推斷類型的資訊必須可以從表達式或其某個子表達式的類型檢查中擷取。

上一篇: 1.1關于 Swift
下一篇: Swift 泛型