1. 程式人生 > >Swift4.2語言參考(三) 類型

Swift4.2語言參考(三) 類型

哪些 res sample value 表達式 類型參數 不同 類型 文字

在Swift中,有兩種類型:命名類型和復合類型。一個名為類型是當它的定義可以給出一個特定名稱的類型。命名類型包括類,結構,枚舉和協議。例如,名為的用戶定義類的實例MyClass具有該類型MyClass除了用戶定義的命名類型之外,Swift標準庫還定義了許多常用的命名類型,包括表示數組,字典和可選值的類型。

通常在其他語言中被認為是基本或原始的數據類型(例如表示數字,字符和字符串的類型)實際上是命名類型,使用結構在Swift標準庫中定義和實現。因為它們是命名類型,所以您可以使用“ 擴展擴展聲明”中討論的擴展聲明來擴展其行為以滿足程序的需要

化合物類型是沒有名字的類型,在夫特語言本身定義的。

有兩種復合類型:函數類型和元組類型。復合類型可以包含命名類型和其他復合類型。例如,元組類型包含兩個元素:第一個是命名類型,第二個是另一個復合類型(Int, (Int, Int))Int(Int, Int)

您可以在命名類型或復合類型周圍放置括號。但是,在類型周圍添加括號不會產生任何影響。例如,(Int)相當於Int

本章討論Swift語言本身定義的類型,並描述Swift的類型推斷行為。

一種類型的語法

 1 GRAMMAR OF A TYPE
 2 
 3 type → array-type
 4 
 5 type → dictionary-type
6 7 type → function-type 8 9 type → type-identifier 10 11 type → tuple-type 12 13 type → optional-type 14 15 type → implicitly-unwrapped-optional-type 16 17 type → protocol-composition-type 18 19 type → metatype-type 20 21 type → Any
22 23 type → Self 24 25 type → ( type )

類型註釋

類型標註明確指定的變量或表達式的類型。類型註釋以冒號(:開頭,以類型結尾,如以下示例所示:

1 let someTuple: (Double, Double) = (3.14159, 2.71828)
2 func someFunction(a: Int) { /* ... */ }

在第一個示例中,表達式someTuple被指定為具有元組類型在第二個示例中,函數的參數被指定為具有類型(Double, Double)asomeFunctionInt

類型註釋可以在類型之前包含類型屬性的可選列表。

1 GRAMMAR OF A TYPE ANNOTATION
2 
3 type-annotation → : attributes opt inoutopt type

類型標識符

類型標識符指的是命名類型或命名類型或復合類型的類型別名。

大多數情況下,類型標識符直接引用與標識符同名的命名類型。例如,Int是直接引用命名類型Int的類型標識符,類型標識符直接引用命名類型Dictionary<String, Int>Dictionary<String, Int>

有兩種情況,類型標識符不引用具有相同名稱的類型。在第一種情況下,類型標識符指的是命名或復合類型的類型別名。例如,在下面的示例中,Point類型註釋中的使用是指元組類型(Int, Int)

1 typealias Point = (Int, Int)
2 let origin: Point = (0, 0)

在第二種情況下,類型標識符使用dot(.)語法來引用在其他模塊中聲明或嵌套在其他類型中的命名類型。例如,以下代碼中的類型標識符引用模塊中MyType聲明的命名類型ExampleModule

var someValue: ExampleModule.MyType

類型標識符的語法

1 type-identifier → type-name generic-argument-clause opt | type-name generic-argument-clause opt . type-identifier
2 
3 type-name → identifier

元組類型

元組類型是以逗號分隔的類型列表,括在括號中。

您可以使用元組類型作為函數的返回類型,以使函數能夠返回包含多個值的單個元組。您還可以命名元組類型的元素,並使用這些名稱來引用各個元素的值。元素名稱由標識符後跟冒號(:)組成。有關演示這兩個功能的示例,請參閱具有多個返回值的函數

當元組類型的元素具有名稱時,該名稱是該類型的一部分。

1 var someTuple = (top: 10, bottom: 12)  // someTuple is of type (top: Int, bottom: Int)
2 someTuple = (top: 4, bottom: 42) // OK: names match
3 someTuple = (9, 99)              // OK: names are inferred
4 someTuple = (left: 5, right: 5)  // Error: names don‘t match

所有元組類型都包含兩種或更多類型,除了Void它是空元組類型的類型別名,()

1 GRAMMAR OF A TUPLE TYPE
2 
3 tuple-type → ( ) | ( tuple-type-element , tuple-type-element-list )
4 
5 tuple-type-element-list → tuple-type-element | tuple-type-element , tuple-type-element-list
6 
7 tuple-type-element → element-name type-annotation | type
8 
9 element-name → identifier

功能類型

函數類型表示函數,方法或閉包的類型,由一個參數和由箭頭(->分隔的返回類型組成

(parameter type) -> return type

參數類型是類型逗號分隔的列表。因為返回類型可以是元組類型,所以函數類型支持返回多個值的函數和方法。

函數類型的參數(其中是任何類型)可以應用該屬性在其調用站點隱式創建閉包。這提供了一種語法上方便的方法來推遲表達式的求值,而無需在調用函數時編寫顯式閉包。有關autoclosure函數類型參數的示例,請參見Autoclosures() -> TTautoclosure

函數類型可以在其一個可變參數參數參數類型從語法上講,一個可變參數包含一個基本類型名稱,後面緊跟三個點(...),如下所示Int...可變參數被視為包含基本類型名稱元素的數組。例如,可變參數Int...被視為[Int]有關使用可變參數的示例,請參閱可變參數

要指定輸入輸出參數,請在參數類型前加上inout關鍵字前綴您無法使用inout關鍵字標記可變參數或返回類型在輸出參數在討論的In-Out參數

如果函數類型只有一個參數且該參數的類型是元組類型,則在編寫函數類型時必須將元組類型括起來。例如,是一個函數的類型,它接受元組類型的單個參數,並且不返回任何值。相反,沒有括號,是一個帶有兩個參數但不返回任何值的函數的類型同樣,因為是類型別名,函數類型-a函數相同,該函數接受一個空元組的參數。這些類型與-a不帶參數的函數不同。((Int, Int)) -> Void(Int, Int)(Int, Int) -> VoidIntVoid()(Void) -> Void(()) -> ()() -> ()

函數和方法中的參數名稱不是相應函數類型的一部分。例如:

 1 func someFunction(left: Int, right: Int) {}
 2 func anotherFunction(left: Int, right: Int) {}
 3 func functionWithDifferentLabels(top: Int, bottom: Int) {}
 4 
 5 var f = someFunction // The type of f is (Int, Int) -> Void, not (left: Int, right: Int) -> Void.
 6 f = anotherFunction              // OK
 7 f = functionWithDifferentLabels  // OK
 8 
 9 func functionWithDifferentArgumentTypes(left: Int, right: String) {}
10 f = functionWithDifferentArgumentTypes     // Error
11 
12 func functionWithDifferentNumberOfArguments(left: Int, right: Int, top: Int) {}
13 f = functionWithDifferentNumberOfArguments // Error

因為參數標簽不是函數類型的一部分,所以在編寫函數類型時省略它們。

1 var operation: (lhs: Int, rhs: Int) -> Int     // Error
2 var operation: (_ lhs: Int, _ rhs: Int) -> Int // OK
3 var operation: (Int, Int) -> Int               // OK

如果函數類型包含多個箭頭(->),則函數類型從右到左分組。例如,函數類型被理解為- 也就是說,一個函數接受並返回另一個獲取並返回的函數(Int) -> (Int) -> Int(Int) -> ((Int) -> Int)IntInt

必須使用throws關鍵字標記可以拋出錯誤的函數類型,並且必須使用rethrows關鍵字標記可以重新拋出錯誤的函數類型throws關鍵字是一個函數的類型的一部分,和nonthrowing函數是投擲功能的亞型。因此,您可以在與拋出函數相同的位置使用非拋出函數。投擲和重新拋出功能被描述在投擲函數和方法以及重新拋出函數和方法

Nonescaping閉包的限制

作為非出錯函數的參數不能存儲在屬性,變量或類型常量中Any,因為這可能允許值轉義。

作為非出錯函數的參數不能作為參數傳遞給另一個非出錯函數參數。此限制有助於Swift在編譯時而不是在運行時執行更多的檢查以查看對內存的沖突訪問。例如:

 1 let external: (() -> Void) -> Void = { _ in () }
 2 func takesTwoFunctions(first: (() -> Void) -> Void, second: (() -> Void) -> Void) {
 3     first { first {} }       // Error
 4     second { second {}  }    // Error
 5 
 6     first { second {} }      // Error
 7     second { first {} }      // Error
 8 
 9     first { external {} }    // OK
10     external { first {} }    // OK
11 }

在上面的代碼中,兩個參數takesTwoFunctions(first:second:)都是函數。兩個參數都沒有標記@escaping,因此它們都是非脫節的。

上例中標記為“Error”的四個函數調用會導致編譯器錯誤。因為firstsecond參數是非脫節函數,所以它們不能作為參數傳遞給另一個非脫節函數參數。相反,標記為“OK”的兩個函數調用不會導致編譯器錯誤。這些函數調用不違反限制,因為external它不是參數之一takesTwoFunctions(first:second:)

如果需要避免此限制,請將其中一個參數標記為轉義,或者使用該withoutActuallyEscaping(_:do:)函數將其中一個非轉義函數參數臨時轉換為轉義函數。有關避免對內存的沖突訪問的信息,請參閱內存安全

 1 GRAMMAR OF A FUNCTION TYPE
 2 
 3 function-type → attributes opt function-type-argument-clause throwsopt -> type
 4 
 5 function-type → attributes opt function-type-argument-clause rethrows -> type
 6 
 7 function-type-argument-clause → ( )
 8 
 9 function-type-argument-clause → ( function-type-argument-list ...opt )
10 
11 function-type-argument-list → function-type-argument | function-type-argument , function-type-argument-list
12 
13 function-type-argument → attributes opt inoutopt type | argument-label type-annotation
14 
15 argument-label → identifier

數組類型

Swift語言為Swift標準庫Array<Element>類型提供以下語法糖

[type]

換句話說,以下兩個聲明是等效的:

1 let someArray: Array<String> = ["Alex", "Brian", "Dave"]
2 let someArray: [String] = ["Alex", "Brian", "Dave"]

在這兩種情況下,常量someArray都聲明為字符串數組。可以通過在方括號中指定有效索引值來通過下標來訪問數組的元素:someArray[0]引用索引0處的元素"Alex"

您可以通過嵌套方括號對來創建多維數組,其中元素的基本類型的名稱包含在最裏面的方括號對中。例如,您可以使用三組方括號創建三維整數數組:

var array3D: [[[Int]]] = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]

在訪問多維數組中的元素時,最左側的下標索引引用最外層數組中該索引處的元素。右邊的下一個下標索引是指數組中嵌套一個級別的索引處的元素。依此類推。這意味著在上面的示例中,array3D[0]引用引用引用值4。[[1, 2], [3, 4]]array3D[0][1][3, 4]array3D[0][1][1]

有關Swift標準庫Array類型的詳細討論,請參閱數組

1 GRAMMAR OF AN ARRAY TYPE
2 
3 array-type → [ type ]

字典類型

Swift語言為Swift標準庫類型提供以下語法糖Dictionary<Key, Value>

[key type: value type]

換句話說,以下兩個聲明是等效的:

1 let someDictionary: [String: Int] = ["Alex": 31, "Paul": 39]
2 let someDictionary: Dictionary<String, Int> = ["Alex": 31, "Paul": 39]

在這兩種情況下,常量都someDictionary被聲明為字典,其中字符串為鍵,整數為值。

可以通過在方括號中指定相應的鍵來通過下標來訪問字典的值:someDictionary["Alex"]引用與鍵相關聯的值"Alex"下標返回字典值類型的可選值。如果指定的鍵未包含在字典中,則下標返回nil

字典的鍵類型必須符合Swift標準庫Hashable協議。

有關Swift標準庫Dictionary類型的詳細討論,請參閱字典

1 GRAMMAR OF A DICTIONARY TYPE
2 
3 dictionary-type → [ type : type ]

可選類型

Swift語言將後綴定義?為命名類型的語法糖,該類型Optional<Wrapped>在Swift標準庫中定義。換句話說,以下兩個聲明是等效的:

1 var optionalInteger: Int?
2 var optionalInteger: Optional<Int>

在這兩種情況下,變量optionalInteger都聲明為具有可選整數的類型。請註意,類型和類型之間不能出現空格?

該類型Optional<Wrapped>是具有兩種情況的枚舉,none並且some(Wrapped)用於表示可能存在或不存在的值。任何類型都可以顯式聲明為(或隱式轉換為)可選類型。如果在聲明可選變量或屬性時未提供初始值,則其值自動默認為nil

如果可選類型的實例包含值,則可以使用後綴運算符訪問該值!,如下所示:

1 optionalInteger = 42
2 optionalInteger! // 42

使用!運算符解包具有nil運行時錯誤結果值的可選項

您還可以使用可選鏈接和可選綁定來有條件地對可選表達式執行操作。如果值為nil,則不執行任何操作,因此不會產生運行時錯誤。

有關更多信息以及查看顯示如何使用可選類型的示例,請參閱Optionals

1 GRAMMAR OF AN OPTIONAL TYPE
2 
3 optional-type → type ?

隱式展開的可選類型

Swift語言將後綴定義!為命名類型的語法糖,該類型Optional<Wrapped>在Swift標準庫中定義,並具有在訪問時自動解包的附加行為。如果您嘗試使用具有值的隱式展開的可選項,nil則會出現運行時錯誤。除了隱式解包行為外,以下兩個聲明是等效的:

1 var implicitlyUnwrappedString: String!
2 var explicitlyUnwrappedString: Optional<String>

請註意,類型和類型之間不能出現空格!

因為隱式解包會更改包含該類型的聲明的含義,所以嵌套在元組類型或泛型類型中的可選類型(如字典或數組的元素類型)不能標記為隱式解包。例如:

1 let tupleOfImplicitlyUnwrappedElements: (Int!, Int!)  // Error
2 let implicitlyUnwrappedTuple: (Int, Int)!             // OK
3 
4 let arrayOfImplicitlyUnwrappedElements: [Int!]        // Error
5 let implicitlyUnwrappedArray: [Int]!                  // OK

由於隱式解包的選項Optional<Wrapped>與可選值具有相同的類型,因此您可以在代碼中可以使用選項的所有相同位置使用隱式解包的選項。例如,您可以將隱式解包的選項的值分配給變量,常量和選項的屬性,反之亦然。

與optionals一樣,如果在聲明隱式展開的可選變量或屬性時未提供初始值,則其值自動默認為nil

使用可選鏈接有條件地對隱式展開的可選表達式執行操作。如果值為nil,則不執行任何操作,因此不會產生運行時錯誤。

有關隱式展開的可選類型的更多信息,請參閱隱式展開的可選項

1 GRAMMAR OF AN IMPLICITLY UNWRAPPED OPTIONAL TYPE
2 
3 implicitly-unwrapped-optional-type → type !

協議組成類型

協議組合類型定義符合指定協議列表中的每個協議的類型,或者是給定類的子類並且符合指定協議列表中的每個協議的類型。只有在類型註釋,通用參數子句和通用where子句中指定類型時,才可以使用協議組合類型

協議組成類型具有以下形式:

Protocol 1 & Protocol 2

協議組合類型允許您指定其類型符合多個協議要求的值,而無需顯式定義從您希望類型符合的每個協議繼承的新的命名協議。例如,可以使用的協議的組合物類型,而不是聲明一個新的協議,從繼承,和同樣,您可以使用而不是聲明作為子類並符合的新協議ProtocolA & ProtocolB & ProtocolCProtocolAProtocolBProtocolCSuperClass & ProtocolASuperClassProtocolA

協議組合列表中的每個項目都是以下之一; 該列表最多可包含一個類:

  • 一個類的名稱
  • 協議的名稱
  • 類型別名,其基礎類型是協議組合類型,協議或類。

當協議組合類型包含類型別名時,同一協議可能在定義中出現多次 - 重復項被忽略。例如,PQR下面代碼中的定義等同於P & Q & R

1 typealias PQ = P & Q
2 typealias PQR = PQ & Q & R
1 GRAMMAR OF A PROTOCOL COMPOSITION TYPE
2 
3 protocol-composition-type → type-identifier & protocol-composition-continuation
4 
5 protocol-composition-continuation → type-identifier | protocol-composition-type

元類型

元類型類型是指任何類型的類型,包括類類型,結構類型,枚舉類型和協議類型。

類,結構或枚舉類型的元類型是該類型的名稱,後跟.Type協議類型的元類型 - 不是在運行時符合協議的具體類型 - 是後面的協議的名稱.Protocol例如,類類型的元類型SomeClassSomeClass.Type協議的元SomeProtocol類型SomeProtocol.Protocol

您可以使用後綴self表達式將類型作為值進行訪問。例如,SomeClass.self返回SomeClass自身,而不是實例SomeClassSomeProtocol.self返回SomeProtocol自身,而不是SomeProtocol在運行時符合的類型的實例您可以type(of:)使用類型的實例調用該函數,以將該實例的動態,運行時類型作為值進行訪問,如以下示例所示:

 1 class SomeBaseClass {
 2     class func printClassName() {
 3         print("SomeBaseClass")
 4     }
 5 }
 6 class SomeSubClass: SomeBaseClass {
 7     override class func printClassName() {
 8         print("SomeSubClass")
 9     }
10 }
11 let someInstance: SomeBaseClass = SomeSubClass()
12 // The compile-time type of someInstance is SomeBaseClass,
13 // and the runtime type of someInstance is SomeSubClass
14 type(of: someInstance).printClassName()
15 // Prints "SomeSubClass"

有關更多信息,請參閱type(of:)Swift標準庫。

使用初始化表達式從該類型的元類型值構造類型的實例。對於類實例,調用的初始值設定項必須使用required關鍵字標記,或者使用final關鍵字標記整個類

 1 class AnotherSubClass: SomeBaseClass {
 2     let string: String
 3     required init(string: String) {
 4         self.string = string
 5     }
 6     override class func printClassName() {
 7         print("AnotherSubClass")
 8     }
 9 }
10 let metatype: AnotherSubClass.Type = AnotherSubClass.self
11 let anotherInstance = metatype.init(string: "some string")
1 GRAMMAR OF A METATYPE TYPE
2 
3 metatype-type → type . Type | type . Protocol

類型繼承子句

類型繼承子句用於指定命名類型從哪個類繼承以及命名類型符合哪些協議。類型繼承子句以冒號(:開頭,後跟類型標識符列表。

類類型可以從單個超類繼承並符合任意數量的協議。定義類時,超類的名稱必須首先出現在類型標識符列表中,然後是類必須符合的任意數量的協議。如果該類不從另一個類繼承,則該列表可以以協議開頭。有關類繼承的擴展討論和幾個示例,請參閱繼承

其他命名類型只能繼承或符合協議列表。協議類型可以從任何數量的其他協議繼承。當協議類型繼承自其他協議時,來自那些其他協議的要求集合在一起,並且從當前協議繼承的任何類型必須符合所有這些要求。

枚舉定義中的類型繼承子句可以是協議列表,也可以是為其案例分配原始值的枚舉,也可以是指定這些原始值類型的單個命名類型。有關使用類型繼承子句指定其原始值類型的枚舉定義的示例,請參閱原始值

1 GRAMMAR OF A TYPE INHERITANCE CLAUSE
2 
3 type-inheritance-clause → : type-inheritance-list
4 
5 type-inheritance-list → type-identifier | type-identifier , type-inheritance-list

類型推斷

Swift廣泛使用類型推斷,允許您省略代碼中許多變量和表達式的類型或部分類型。例如,您可以編寫,完全省略類型,而不是寫入編譯器正確地推斷出名稱類型的值類似地,當可以從上下文推斷出完整類型時,可以省略部分類型。例如,如果您編寫,編譯器會推斷出具有該類型的編譯器var x: Int = 0var x = 0xIntlet dict: Dictionary = ["A": 1]dictDictionary<String, Int>

在上面的兩個例子中,類型信息從表達式樹的葉子傳遞到它的根。也就是說,通過首先檢查類型然後將此類型信息傳遞到根(變量來推斷xin 的類型var x: Int = 00x

在Swift中,類型信息也可以以相反的方向流動 - 從根到葉。例如,在下面的示例中,常量上的顯式類型註釋(會導致數字文字具有推斷類型而不是: FloateFloat2.71828FloatDouble

1 let e = 2.71828 // The type of e is inferred to be Double.
2 let eFloat: Float = 2.71828 // The type of eFloat is Float.

Swift中的類型推斷在單個表達式或語句的級別上運行。這意味著,必須可以通過對表達式或其子表達式之一進行類型檢查來訪問在表達式中推斷省略類型或類型的一部分所需的所有信息。

Swift4.2語言參考(三) 類型