Merge pull request #904 from dzyding/gh-pages

03_Types
This commit is contained in:
Jie Liang
2019-03-27 08:12:40 -05:00
committed by GitHub

View File

@ -15,11 +15,11 @@
- [类型继承子句](#type_inheritance_clause) - [类型继承子句](#type_inheritance_clause)
- [类型推断](#type_inference) - [类型推断](#type_inference)
Swift 语言存在两种类型:命名型类型和复合型类型。命名型类型是指定义时可以给定名字的类型。命名型类型包括类、结构体、枚举和协议。比如,一个用户定义`MyClass` 的实例拥有类型 `MyClass`。除了用户定义的命名型类型Swift 标准库也定义了很多常用的命名型类型,包括那些表示数组、字典和可选值的类型。 Swift 语言存在两种类型:命名型类型和复合型类型。*命名型类型*是指定义时可以给定名字的类型。命名型类型包括类、结构体、枚举和协议。比如,一个用户定义类 `MyClass` 的实例拥有类型 `MyClass`。除了用户定义的命名型类型Swift 标准库也定义了很多常用的命名型类型,包括那些表示数组、字典和可选值的类型。
那些通常被其它语言认为是基本或原始的数据型类型,比如表示数字、字符和字符串的类型,实际上就是命名型类型,这些类型在 Swift 标准库中是使用结构体来定义和实现的。因为它们是命名型类型,因此你可以按照 [扩展](../chapter2/20_Extensions.md) 和 [扩展声明](./06_Declarations.md#extension_declaration) 中讨论的那样,声明一个扩展来增加它们的行为以满足你程序的需求。 那些通常被其它语言认为是基本或原始的数据型类型,比如表示数字、字符和字符串的类型,实际上就是命名型类型,这些类型在 Swift 标准库中是使用结构体来定义和实现的。因为它们是命名型类型,因此你可以按照 [扩展](../chapter2/20_Extensions.md) 和 [扩展声明](./06_Declarations.md#extension_declaration) 中讨论的那样,声明一个扩展来增加它们的行为以满足你程序的需求。
复合型类型是没有名字的类型,它由 Swift 本身定义。Swift 存在两种复合型类型:函数类型和元组类型。一个复合型类型可以包含命名型类型和其它复合型类型。例如,元组类型 `(Int, (Int, Int))` 包含两个元素:第一个是命名型类型 `Int`,第二个是另一个复合型类型 `(Int, Int)` *复合型类型*是没有名字的类型,它由 Swift 本身定义。Swift 存在两种复合型类型:函数类型和元组类型。一个复合型类型可以包含命名型类型和其它复合型类型。例如,元组类型 `(Int, (Int, Int))` 包含两个元素:第一个是命名型类型 `Int`,第二个是另一个复合型类型 `(Int, Int)`
你可以在命名型类型和复合型类型使用小括号。但是在类型旁加小括号没有任何作用。举个例子,`(Int)` 等同于 `Int` 你可以在命名型类型和复合型类型使用小括号。但是在类型旁加小括号没有任何作用。举个例子,`(Int)` 等同于 `Int`
@ -54,7 +54,7 @@ Swift 语言存在两种类型:命名型类型和复合型类型。命名型
> >
## 类型注解 {#type_annotation} ## 类型注解 {#type_annotation}
类型注解显式地指定一个变量或表达式的。类型注解始于冒号 `:` 终于类型,比如下面两个例子: *类型注解*显式地指定一个变量或表达式的类型。类型注解始于冒号 `:` 终于类型,比如下面两个例子:
```swift ```swift
let someTuple: (Double, Double) = (3.14159, 2.71828) let someTuple: (Double, Double) = (3.14159, 2.71828)
@ -92,7 +92,7 @@ var someValue: ExampleModule.MyType
> 类型标识符语法 > 类型标识符语法
> >
###### type-identifier {#type-identifier} ###### type-identifier {#type-identifier}
> *类型标识符* → [*类型名称*](#type-name) [*泛型参数子句*](08_Generic_Parameters_and_Arguments.md#generic_argument_clause)<sub>可选</sub> | [*类型名称*](#type-name) [*泛型参数子句*](08_Generic_Parameters_and_Arguments.md#generic_argument_clause)<sub>可选</sub> **.** [*类型标识符*](#type-identifier) > *类型标识符* → [*类型名称*](#type-name) [*泛型参数子句*](./09_Generic_Parameters_and_Arguments.md#generic_argument_clause)<sub>可选</sub> | [*类型名称*](#type-name) [*泛型参数子句*](./09_Generic_Parameters_and_Arguments.md#generic_argument_clause)<sub>可选</sub> **.** [*类型标识符*](#type-identifier)
> >
###### type-name {#type-name} ###### type-name {#type-name}
> *类型名称* → [*标识符*](./02_Lexical_Structure.md#identifier) > *类型名称* → [*标识符*](./02_Lexical_Structure.md#identifier)
@ -117,7 +117,7 @@ someTuple = (left: 5, right: 5) // 错误:命名类型不匹配
> 元组类型语法 > 元组类型语法
> >
###### tuple-type {#tuple-type} ###### tuple-type {#tuple-type}
> *元组类型* → **(** [*元组类型元素列表*](#tuple-type-element-list) <sub>可选</sub> **)** > *元组类型* → **(** **)** | **(** [*元组类型元素*](#tuple-type-element) **,** [*元组类型元素列表*](#tuple-type-element-list) **)**
> >
###### tuple-type-element-list {#tuple-type-element-list} ###### tuple-type-element-list {#tuple-type-element-list}
> *元组类型元素列表* → [*元组类型元素*](#tuple-type-element) | [*元组类型元素*](#tuple-type-element) **,** [*元组类型元素列表*](#tuple-type-element-list) > *元组类型元素列表* → [*元组类型元素*](#tuple-type-element) | [*元组类型元素*](#tuple-type-element) **,** [*元组类型元素列表*](#tuple-type-element-list)
@ -132,20 +132,17 @@ someTuple = (left: 5, right: 5) // 错误:命名类型不匹配
## 函数类型 {#function_type} ## 函数类型 {#function_type}
函数类型表示一个函数、方法或闭包的类型,它由参数类型和返回值类型组成,中间用箭头(`->`)隔开: 函数类型表示一个函数、方法或闭包的类型,它由参数类型和返回值类型组成,中间用箭头(`->`)隔开:
> `参数类型` -> `返回值类型` > `参数类型`->`返回值类型`
>
参数类型是由逗号间隔的类型列表。由于参数类型和返回值类型可以是元组类型,所以函数类型支持多参数与多返回值的函数与方法。 *参数类型*是由逗号间隔的类型列表。由于*返回值类型*可以是元组类型,所以函数类型支持多返回值的函数与方法。
你可以对函数参数 `() -> T`(其中 T 是任何类型)使用 `autoclosure` 特性。这会自动将参数表达式转化为闭包,表达式的结果即闭包返回值。这从语法结构上提供了一种便捷:延迟对表达式的求值,直到其值在函数体中被调用。以自动闭包做为参数的函数类型的例子详见 [自动闭包](../chapter2/07_Closures.md#autoclosures) 你可以对参数类型为 `() -> T`(其中 T 是任何类型)的函数使用 `autoclosure` 特性。这会自动将参数表达式转化为闭包,表达式的结果即闭包返回值。这从语法结构上提供了一种便捷:延迟对表达式的求值,直到其值在函数体中被调用。以自动闭包做为参数的函数类型的例子详见 [自动闭包](../chapter2/07_Closures.md#autoclosures)。
>
函数类型可以拥有一个可变长参数作为参数类型中的最后一个参数。从语法角度上讲,可变长参数由一个基础类型名字紧随三个点(`...`)组成,如 `Int...`。可变长参数被认为是一个包含了基础类型元素的数组。即 `Int...` 就是 `[Int]`。关于使用可变长参数的例子,请参阅 [可变参数](../chapter2/06_Functions.md#variadic_parameters)。 函数类型可以拥有一个可变长参数作为*参数类型*中的最后一个参数。从语法角度上讲,可变长参数由一个基础类型名字紧随三个点(`...`)组成,如 `Int...`。可变长参数被认为是一个包含了基础类型元素的数组。即 `Int...` 就是 `[Int]`。关于使用可变长参数的例子,请参阅 [可变参数](../chapter2/06_Functions.md#variadic_parameters)。
为了指定一个 `in-out` 参数,可以在参数类型前加 `inout` 前缀。但是你不可以对可变长参数或返回值类型使用 `inout`。关于这种参数的详细讲解请参阅 [输入输出参数](../chapter2/06_Functions.md#in_out_parameters)。 为了指定一个 `in-out` 参数,可以在参数类型前加 `inout` 前缀。但是你不可以对可变长参数或返回值类型使用 `inout`。关于这种参数的详细讲解请参阅 [输入输出参数](../chapter2/06_Functions.md#in_out_parameters)。
如果一个函数类型只有一个形式参数而且形式参数的类型是元组类型,那么元组类型在写函数类型的时候必须用圆括号括起来。比如说,`((Int, Int)) -> Void` 是接收一个元组 `(Int, Int)` 作为形式参数的函数类型。与此相,不加括号的 `(Int, Int) -> Void` 是一个接收两个 `Int` 形式参数并且不返回任何值的函数类型。相似地,因为 `Void` 是空元组类型 `()` 的别名, 函数类型 `(Void)-> Void`一个空元组的变量的函数类型 `(()) -> ()` 是一样的。但这些类型和无变量的函数类型 `() -> ()` 是不一样的。 如果一个函数类型只有一个形式参数而且形式参数的类型是元组类型,那么元组类型在写函数类型的时候必须用圆括号括起来。比如说,`((Int, Int)) -> Void` 是接收一个元组 `(Int, Int)` 作为形式参数并且不返回任何值的函数类型。与此相,不加括号的 `(Int, Int) -> Void` 是一个接收两个 `Int` 作为形式参数并且不返回任何值的函数类型。相似地,因为 `Void` 是空元组类型 `()` 的别名,函数类型 `(Void)-> Void``(()) -> ()` 是一样的 - 一个将空元组作为唯一参数的函数。但这些类型和无变量的函数类型 `() -> ()` 是不一样的。
>
函数和方法中的变量名并不是函数类型的一部分。例如: 函数和方法中的变量名并不是函数类型的一部分。例如:
@ -155,14 +152,14 @@ func anotherFunction(left: Int, right: Int) {}
func functionWithDifferentLabels(top: Int, bottom: Int) {} func functionWithDifferentLabels(top: Int, bottom: Int) {}
var f = someFunction // 函数 f 的类型为 (Int, Int) -> Void, 而不是 (left: Int, right: Int) -> Void. var f = someFunction // 函数 f 的类型为 (Int, Int) -> Void, 而不是 (left: Int, right: Int) -> Void.
>
f = anotherFunction // 正确 f = anotherFunction // 正确
f = functionWithDifferentLabels // 正确 f = functionWithDifferentLabels // 正确
func functionWithDifferentArgumentTypes(left: Int, right: String) {} func functionWithDifferentArgumentTypes(left: Int, right: String) {}
func functionWithDifferentNumberOfArguments(left: Int, right: Int, top: Int) {}
f = functionWithDifferentArgumentTypes // 错误 f = functionWithDifferentArgumentTypes // 错误
func functionWithDifferentNumberOfArguments(left: Int, right: Int, top: Int) {}
f = functionWithDifferentNumberOfArguments // 错误 f = functionWithDifferentNumberOfArguments // 错误
``` ```
@ -170,25 +167,22 @@ f = functionWithDifferentNumberOfArguments // 错误
```swift ```swift
var operation: (lhs: Int, rhs: Int) -> Int // 错误 var operation: (lhs: Int, rhs: Int) -> Int // 错误
>
var operation: (_ lhs: Int, _ rhs: Int) -> Int // 正确 var operation: (_ lhs: Int, _ rhs: Int) -> Int // 正确
>
var operation: (Int, Int) -> Int // 正确 var operation: (Int, Int) -> Int // 正确
>
``` ```
如果一个函数类型包涵多个箭头(->),那么函数类型将从右向左进行组合。例如,函数类型 `Int -> Int -> Int` 可以理解为 `Int -> (Int -> Int)`,也就是说,该函数类型的参数为 `Int` 类型,其返回类型是一个参数类型为 `Int`,返回类型为 `Int` 的函数类型 如果一个函数类型包涵多个箭头(->),那么函数类型将从右向左进行组合。例如,函数类型 `(Int) -> (Int) -> Int` 可以理解为 `(Int) -> ((Int) -> Int)`,也就是说,该函数类型的参数为 `Int` 类型,其返回类型是一个参数类型为 `Int`,返回类型为 `Int` 的函数。
>
函数类型若要抛出错误就必须使用 `throws` 关键字来标记,若要重抛错误则必须使用 `rethrows` 关键字来标记。`throws` 关键字是函数类型的一部分,非抛出函数是抛出函数函数的一个子类型。因此,在使用抛出函数的地方也可以使用不抛出函数。抛出和重抛函数的相关描述见章节 [抛出函数与方法](./06_Declarations.md#throwing_functions_and_methods) 和 [重抛函数与方法](./06_Declarations.md#rethrowing_functions_and_methods)。 函数类型若要抛出错误就必须使用 `throws` 关键字来标记,若要重抛错误则必须使用 `rethrows` 关键字来标记。`throws` 关键字是函数类型的一部分,非抛出函数是抛出函数函数的一个子类型。因此,在使用抛出函数的地方也可以使用不抛出函数。抛出和重抛函数的相关描述见章节 [抛出函数与方法](./06_Declarations.md#throwing_functions_and_methods) 和 [重抛函数与方法](./06_Declarations.md#rethrowing_functions_and_methods)。
### 对非逃逸闭包的限制 {#Restrictions for Nonescaping Closures}非逃逸闭包函数不能作为参数传递到另一个非逃逸闭包函数的参数。这样的限制可以让 Swift 在编译时就完成更多的内存访问冲突检查, 而不是在运行时。举个例子: ### 对非逃逸闭包的限制 {#Restrictions for Nonescaping Closures}
当非逃逸闭包函数是参数时,不能存储在属性、变量或任何 `Any` 类型的常量中,因为这可能导致值的逃逸。
当非逃逸闭包函数是参数时,不能作为参数传递到另一个非逃逸闭包函数中。这样的限制可以让 Swift 在编译时就完成更多的内存访问冲突检查,而不是在运行时。举个例子:
```swift ```swift
let external: (Any) -> Void = { _ in () } let external: (Any) -> Void = { _ in () }
>
func takesTwoFunctions(first: (Any) -> Void, second: (Any) -> Void) { func takesTwoFunctions(first: (Any) -> Void, second: (Any) -> Void) {
>
first(first) // 错误 first(first) // 错误
second(second) // 错误 second(second) // 错误
@ -202,11 +196,10 @@ func takesTwoFunctions(first: (Any) -> Void, second: (Any) -> Void) {
在上面代码里,`takesTwoFunctions(first:second:)` 的两个参数都是函数。它们都没有标记为 `@escaping`, 因此它们都是非逃逸的。 在上面代码里,`takesTwoFunctions(first:second:)` 的两个参数都是函数。它们都没有标记为 `@escaping`, 因此它们都是非逃逸的。
上述例子里的被标记为“错误”的四个函数调用会产生编译错误。因为第一个和第二个参数是非逃逸函数,它们不能够被当作变量被传递到另一个非闭包函数参数。与此相反, 标记“正确”的两个函数不产生编译错误。这些函数调用不会违反限制, 因为 `外部(external` 不是 `takesTwoFunctions(first:second:)` 里的一个参数 上述例子里的被标记为“错误”的四个函数调用会产生编译错误。因为参数 `first``second` 是非逃逸函数,它们不能够作为参数被传递到另一个非闭包函数。相对的, 标记“正确”的两个函数不产生编译错误。这些函数调用不会违反限制,因为 `external` 不是 `takesTwoFunctions(first:second:)` 的参数之一
如果你需要避免这个限制,标记其中之一的参数为逃逸,或者使用 `withoutActuallyEscaping(_:do:)` 函数临时地转换非逃逸函数的其中一个参数为逃逸函数。关于避免内存访问冲突,可以参阅[内存安全](../chapter2/24_Memory_Safety.md)。 如果你需要避免这个限制,标记其中之一的参数为逃逸,或者使用 `withoutActuallyEscaping(_:do:)` 函数临时地转换非逃逸函数的其中一个参数为逃逸函数。关于避免内存访问冲突,可以参阅[内存安全](../chapter2/24_Memory_Safety.md)。
> 函数类型语法 > 函数类型语法
> >
###### function-type {#function-type} ###### function-type {#function-type}
@ -215,9 +208,8 @@ func takesTwoFunctions(first: (Any) -> Void, second: (Any) -> Void) {
> *函数类型* → [*特性列表*](./07_Attributes.md#attributes)<sub>可选</sub> [*函数类型子句*](#function-type-argument-clause) **rethrows­** **->** [*类型*](#type) > *函数类型* → [*特性列表*](./07_Attributes.md#attributes)<sub>可选</sub> [*函数类型子句*](#function-type-argument-clause) **rethrows­** **->** [*类型*](#type)
> >
###### function-type-argument-clause {#function-type-argument-clause} ###### function-type-argument-clause {#function-type-argument-clause}
> *函数类型子句* → (­)­ > *函数类型子句* → **(**­ **)**­
> > *函数类型子句* → **(** [*函数类型参数列表*](#function-type-argument-list) *...*­ <sub>可选</sub> **)**
> *函数类型子句* → ([*函数类型参数列表*](#function-type-argument-list)*...*­<sub>可选</sub>)­
> >
###### function-type-argument-list {#function-type-argument-list} ###### function-type-argument-list {#function-type-argument-list}
> *函数类型参数列表* → [*函数类型参数*](function-type-argument) | [*函数类型参数*](function-type-argument) [*函数类型参数列表*](#function-type-argument-list) > *函数类型参数列表* → [*函数类型参数*](function-type-argument) | [*函数类型参数*](function-type-argument) [*函数类型参数列表*](#function-type-argument-list)
@ -239,7 +231,6 @@ Swift 语言为标准库中定义的 `Array<Element>` 类型提供了如下语
```swift ```swift
let someArray: Array<String> = ["Alex", "Brian", "Dave"] let someArray: Array<String> = ["Alex", "Brian", "Dave"]
>
let someArray: [String] = ["Alex", "Brian", "Dave"] let someArray: [String] = ["Alex", "Brian", "Dave"]
``` ```
@ -272,12 +263,11 @@ Swift 语言为标准库中定义的 `Dictionary<Key, Value>` 类型提供了如
```swift ```swift
let someDictionary: [String: Int] = ["Alex": 31, "Paul": 39] let someDictionary: [String: Int] = ["Alex": 31, "Paul": 39]
let someDictionary: Dictionary<String, Int> = ["Alex": 31, "Paul": 39] let someDictionary: Dictionary<String, Int> = ["Alex": 31, "Paul": 39]
>
``` ```
上面两种情况,常量 `someDictionary` 被声明为一个字典,其中键为 `String` 类型,值为 `Int` 类型。 上面两种情况,常量 `someDictionary` 被声明为一个字典,其中键为 `String` 类型,值为 `Int` 类型。
字典中的值可以通过下标来访问,这个下标在方括号中指明了具体的键:`someDictionary["Alex"]` 返回键 `Alex` 对应的值。如果键在字典中不存在的话,则这个下标返回 `nil` 字典中的值可以通过下标来访问,这个下标在方括号中指明了具体的键:`someDictionary["Alex"]` 返回键 `Alex` 对应的值。通过下标访问会获取对应值的可选类型。如果键在字典中不存在的话,则这个下标返回 `nil`
字典中键的类型必须符合 Swift 标准库中的 `Hashable` 协议。 字典中键的类型必须符合 Swift 标准库中的 `Hashable` 协议。
@ -290,7 +280,7 @@ let someDictionary: Dictionary<String, Int> = ["Alex": 31, "Paul": 39]
> >
## 可选类型 {#optional_type} ## 可选类型 {#optional_type}
Swift 定义后缀 `?` 来作为标准库中定义的命名型类型 `Optional<Wrapped>` 的语法糖。换句话说,下面两个声明是等价的: Swift 定义后缀 `?` 来作为标准库中定义的命名型类型 `Optional<Wrapped>` 的语法糖。换句话说,下面两个声明是等价的:
```swift ```swift
var optionalInteger: Int? var optionalInteger: Int?
@ -299,7 +289,7 @@ var optionalInteger: Optional<Int>
在上述两种情况下,变量 `optionalInteger` 都被声明为可选整型类型。注意在类型和 `?` 之间没有空格。 在上述两种情况下,变量 `optionalInteger` 都被声明为可选整型类型。注意在类型和 `?` 之间没有空格。
类型 `Optional<Wrapped>` 是一个枚举,有两个成员,`none``some(Wrapped)`,用来表示可能有也可能没有的值。任意类型都可以被显式地声明(或隐式地转换)为可选类型。如果你在声明或定义可选变量或属性的时候没有提供初始值,它的值则会自动赋为默认值 `nil` 类型 `Optional<Wrapped>` 是一个枚举,有两个成员,`none``some(Wrapped)`,用来表示可能有也可能没有的值。任意类型都可以被显式地声明(或隐式地转换)为可选类型。如果你在声明可选变量或属性的时候没有提供初始值,它的值则会自动赋为默认值 `nil`
如果一个可选类型的实例包含一个值,那么你就可以使用后缀运算符 `!` 来获取该值,正如下面描述的: 如果一个可选类型的实例包含一个值,那么你就可以使用后缀运算符 `!` 来获取该值,正如下面描述的:
@ -321,7 +311,7 @@ optionalInteger! // 42
> >
## 隐式解析可选类型 {#implicitly_unwrapped_optional_type} ## 隐式解析可选类型 {#implicitly_unwrapped_optional_type}
当可以被访问时Swift 语言定义后缀 `!` 作为标准库中命名类型 `Optional<Wrapped>` 的语法糖,来实现自动解包的功能。换句话说,下面两个声明等价: 当可以被访问时Swift 语言定义后缀 `!` 作为标准库中命名类型 `Optional<Wrapped>` 的语法糖,来实现自动解包的功能。如果尝试对一个值为 `nil` 的可选类型进行隐式解包,将会产生运行时错误。因为隐式解包,下面两个声明等价:
```swift ```swift
var implicitlyUnwrappedString: String! var implicitlyUnwrappedString: String!
@ -330,7 +320,7 @@ var explicitlyUnwrappedString: Optional<String>
注意类型与 `!` 之间没有空格。 注意类型与 `!` 之间没有空格。
由于隐式解包修改了包涵其类型的声明语义,嵌套在元组类型或泛型可选类型(比如字典元素类型或数组元素类型),不能被标记为隐式解包。例如: 由于隐式解包会更改包含该类型的声明语义,嵌套在元组类型或泛型可选类型(比如字典元素类型或数组元素类型),不能被标记为隐式解包。例如:
```swift ```swift
let tupleOfImplicitlyUnwrappedElements: (Int!, Int!) // 错误 let tupleOfImplicitlyUnwrappedElements: (Int!, Int!) // 错误
@ -340,11 +330,11 @@ let arrayOfImplicitlyUnwrappedElements: [Int!] // 错误
let implicitlyUnwrappedArray: [Int]! // 正确 let implicitlyUnwrappedArray: [Int]! // 正确
``` ```
由于隐式解析可选类型和可选类型有同样的表达式 `Optional<Wrapped>`,你可以在使用可选类型的地方使用隐式解析可选类型。比如,你可以将隐式解析可选类型的值赋给变量、常量和可选属性,反之亦然。 由于隐式解析可选类型和可选类型有同样的类型 `Optional<Wrapped>`,你可以在所有使用可选类型的地方使用隐式解析可选类型。比如,你可以将隐式解析可选类型的值赋给变量、常量和可选属性,反之亦然。
正如可选类型一样,你在声明隐式解析可选类型的变量或属性的时候也不用指定初始值,因为它有默认值 `nil` 正如可选类型一样,如果你在声明隐式解析可选类型的变量或属性的时候没有指定初始值,它的值则会自动赋为默认值 `nil`
可以使用可选链式调用来在隐式解析可选表达式选择性地执行操作。如果值为 `nil`,就不会执行任何操作,因此也不会产生运行错误。 可以使用可选链式调用隐式解析可选表达式选择性地执行操作。如果值为 `nil`,就不会执行任何操作,因此也不会产生运行错误。
关于隐式解析可选类型的更多细节,请参阅 [隐式解析可选类型](../chapter2/01_The_Basics.md#implicityly_unwrapped_optionals)。 关于隐式解析可选类型的更多细节,请参阅 [隐式解析可选类型](../chapter2/01_The_Basics.md#implicityly_unwrapped_optionals)。
@ -355,16 +345,27 @@ let implicitlyUnwrappedArray: [Int]! // 正确
> >
## 协议合成类型 {#protocol_composition_type} ## 协议合成类型 {#protocol_composition_type}
协议合成类型是一种符合协议列表中每个指定协议的类型。协议合成类型可能会用在类型注解泛型参数 协议合成类型定义了一种遵循协议列表中每个指定协议的类型,或者一个现有类型的子类并遵循协议列表中每个指定协议。协议合成类型只能用在类型注解泛型参数子句和泛型 `where` 子句中指定类型
协议合成类型的形式如下: 协议合成类型的形式如下:
> `Protocol 1` & `Procotol 2` > `Protocol 1` & `Procotol 2`
> >
协议合成类型允许你指定一个值,其类型符合多个协议的要求不需要定义一个新的命名型协议来继承它想要符合的各个协议。比如,协议合成类型 `Protocol A & Protocol B & Protocol C` 等效于一个从 `Protocol A``Protocol B``Protocol C` 继承而来的新协议 `Protocol D`,很显然这样做有效率的多,甚至不需引入一个新名字 协议合成类型允许你指定一个值,其类型遵循多个协议的要求不需要定义一个新的命名型协议来继承它想要符合的各个协议。比如,协议合成类型 `Protocol A & Protocol B & Protocol C` 等效于一个从 `Protocol A``Protocol B``Protocol C` 继承而来的新协议。同样的,你可以使用 `SuperClass & ProtocolA` 来取代申明一个新的协议作为 `SuperClass` 的子类并遵循 `ProtocolA`
协议合成列表中的每项必须是协议名或协议合成类型的类型别名。 协议合成列表中的每一项都必须是下面所列情况之一,列表中最多只能包含一个类:
- 类名
- 协议名
- 一个类型别名,它的潜在类型是一个协议合成类型、一个协议或者一个类
当协议合成类型包含类型别名时,同一个协议可能多次出现在定义中 — 重复被忽略。例如,下面代码中定义的 `PQR` 等同于 `P & Q & R`
```swift
typealias PQ = P & Q
typealias PQR = PQ & Q & R
```
> 协议合成类型语法 > 协议合成类型语法
> >
@ -374,16 +375,13 @@ let implicitlyUnwrappedArray: [Int]! // 正确
###### protocol-composition-continuation {#protocol-composition-continuation} ###### protocol-composition-continuation {#protocol-composition-continuation}
> *协议合成延续* → [*协议标识符*](#protocol-identifier) | [*协议合成类型*](#protocol-composition-type) > *协议合成延续* → [*协议标识符*](#protocol-identifier) | [*协议合成类型*](#protocol-composition-type)
> >
###### protocol-identifier {#protocol-identifier}
> *协议标识符* → [*类型标识符*](#type-identifier)
>
## 元类型 {#metatype_type} ## 元类型 {#metatype_type}
元类型是指类型的类型,包括类类型、结构体类型、枚举类型和协议类型。 元类型是指任意类型的类型,包括类类型、结构体类型、枚举类型和协议类型。
类、结构体或枚举类型的元类型是相应的类型名紧跟 `.Type`。协议类型的元类型——并不是运行时符合该协议的具体类型——是该协议名字紧跟 `.Protocol`。比如,类 `SomeClass` 的元类型就是 `SomeClass.Type`,协议 `SomeProtocol` 的元类型就是 `SomeProtocal.Protocol` 类、结构体或枚举类型的元类型是相应的类型名紧跟 `.Type`。协议类型的元类型——并不是运行时遵循该协议的具体类型——是该协议名字紧跟 `.Protocol`。比如,类 `SomeClass` 的元类型就是 `SomeClass.Type`,协议 `SomeProtocol` 的元类型就是 `SomeProtocal.Protocol`
你可以使用后缀 `self` 表达式来获取类型。比如,`SomeClass.self` 返回 `SomeClass` 本身,而不是 `SomeClass` 的一个实例。同样,`SomeProtocol.self` 返回 `SomeProtocol` 本身,而不是运行时符合 `SomeProtocol` 的某个类型的实例。还可以对类型的实例使用 `type(of:)` 表达式来获取该实例在运行阶段的类型,如下所示: 你可以使用后缀 `self` 表达式来获取类型。比如,`SomeClass.self` 返回 `SomeClass` 本身,而不是 `SomeClass` 的一个实例。同样,`SomeProtocol.self` 返回 `SomeProtocol` 本身,而不是运行时遵循 `SomeProtocol` 的某个类型的实例。还可以对类型的实例使用 `type(of:)` 表达式来获取该实例动态的、在运行阶段的类型,如下所示:
```swift ```swift
class SomeBaseClass { class SomeBaseClass {
@ -403,7 +401,7 @@ type(of: someInstance).printClassName()
// 打印“SomeSubClass” // 打印“SomeSubClass”
``` ```
更多信息可以查看 Swift 标准库里的 `type(of:)` 更多信息可以查看 Swift 标准库里的 [type(of:)](https://developer.apple.com/documentation/swift/2885064-type)
可以使用初始化表达式从某个类型的元类型构造出一个该类型的实例。对于类实例,被调用的构造器必须使用 `required` 关键字标记,或者整个类使用 `final` 关键字标记。 可以使用初始化表达式从某个类型的元类型构造出一个该类型的实例。对于类实例,被调用的构造器必须使用 `required` 关键字标记,或者整个类使用 `final` 关键字标记。
@ -428,13 +426,13 @@ let anotherInstance = metatype.init(string: "some string")
> >
## 类型继承子句 {#type_inheritance_clause} ## 类型继承子句 {#type_inheritance_clause}
类型继承子句被用来指定一个命名型类型继承自哪个类、采纳哪些协议。类型继承子句也用来指定一个类类型专属协议。类型继承子句开始于冒号 `:`,其后是所需要的类、类型标识符列表或两者都有 类型继承子句被用来指定一个命名型类型继承自哪个类、采纳哪些协议。类型继承子句开始于冒号 `:`,其后是类型标识符列表。
类可以继承单个超类,采纳任意数量的协议。当定义一个类时,超类的名字必须出现在类型标识符列表首位,然后跟上该类需要采纳的任意数量的协议。如果一个类不是从其它类继承而来,那么列表可以以协议开头。关于类继承更多的讨论和例子,请参阅 [继承](../chapter2/13_Inheritance.md)。 类可以继承单个超类,并遵循任意数量的协议。当定义一个类时,超类的名字必须出现在类型标识符列表首位,然后跟上该类需要遵循的任意数量的协议。如果一个类不是从其它类继承而来,那么列表可以以协议开头。关于类继承更多的讨论和例子,请参阅 [继承](../chapter2/13_Inheritance.md)。
其它命名型类型可能只继承或采纳一系列协议。协议类型可以继承自任意数量的其他协议。当一个协议类型继承自其它协议时,其它协议中定义的要求会被整合在一起,然后从当前协议继承的任意类型必须符合所有这些条件。正如在 [协议声明](./06_Declarations.md#protocol_declaration) 中所讨论的那样,可以把 `class` 关键字放到协议类型的类型继承子句的首位,这样就可以声明一个类类型专属协议。 其它命名型类型只继承或采纳一系列协议。协议类型可以继承自任意数量的其他协议。当一个协议类型继承自其它协议时,其它协议中定义的要求会被整合在一起,然后从当前协议继承的任意类型必须符合所有这些条件。
枚举定义中的类型继承子句可以是一系列协议,或是枚举的原始值类型的命名类型。在枚举定义中使用类型继承子句来指定原始值类型的例子,请参阅 [原始值](../chapter2/08_Enumerations.md#raw_values)。 枚举定义中的类型继承子句可以是一系列协议,或者是指定单一的命名类型,此时枚举为其用例分配原始值。在枚举定义中使用类型继承子句来指定原始值类型的例子,请参阅 [原始值](../chapter2/08_Enumerations.md#raw_values)。
> 类型继承子句语法 > 类型继承子句语法
> >