diff --git a/source/chapter3/06_Declarations.md b/source/chapter3/06_Declarations.md index 8a03290a..f7061888 100755 --- a/source/chapter3/06_Declarations.md +++ b/source/chapter3/06_Declarations.md @@ -1,9 +1,10 @@ + # 声明(Declarations) -*声明(declaration)* 用以向程序里引入新的名字或者结构。举例来说,可以使用声明来引入函数和方法,变量和常量,或者定义新的具有命名的枚举、结构、类和协议类型。还可以使用声明来扩展一个既有的具有命名的类型的行为,或者在程序里引入在其它地方声明的符号。 +*声明(declaration)* 用以向程序里引入新的名字或者结构。举例来说,可以使用声明来引入函数和方法,变量和常量,或者定义新的具有命名的枚举、结构体、类和协议类型。还可以使用声明来扩展一个既有的具有命名的类型的行为,或者在程序里引入在其它地方声明的符号。 -在 Swift 中,大多数声明在某种意义上讲也是定义,因为声明往往伴随着实现或初始化。由于协议并不提供实现,大多数协议成员仅仅只是声明而已。为了方便起见,也是因为这些区别在 Swift 中并不是很重要,“声明”这个术语同时包含了声明和定义两种含义。 +在 Swift 中,大多数声明在某种意义上讲也是定义,因为它们在声明时往往伴随着实现或初始化。由于协议并不提供实现,大多数协议成员仅仅只是声明而已。为了方便起见,也是因为这些区别在 Swift 中并不是很重要,“声明”这个术语同时包含了声明和定义两种含义。 > 声明语法 > @@ -36,7 +37,7 @@ Swift 的源文件中的顶级代码(top-level code)由零个或多个语句 ## 代码块 -*代码块(code block)* 可以将一些声明和控制结构组织在一起。它有如下的形式: +*代码块(code block)* 可以将一些声明和控制结构体组织在一起。它有如下的形式: ```swift { @@ -91,7 +92,7 @@ let 常量名称: 类型 = 表达式 常量声明在“常量名称”和用于初始化的“表达式”的值之间定义了一种不可变的绑定关系;当常量的值被设定之后,它就无法被更改。这意味着,如果常量以类对象来初始化,对象本身的内容是可以改变的,但是常量和该对象之间的绑定关系是不能改变的。 -当一个常量被声明为全局常量时,它必须拥有一个初始值。在类或者结构中声明一个常量时,它将作为*常量属性(constant property)*。常量声明不能是计算型属性,因此也没有存取方法。 +当一个常量被声明为全局常量时,它必须拥有一个初始值。在函数或者方法中声明一个常量时,它并不需要拥有一个初始值,只需要保证在第一次对其进行读操作之前为其设置一个值。在类或者结构体中声明一个常量时,它将作为*常量属性(constant property)*。常量声明不能是计算型属性,因此也没有存取方法。 如果常量名称是元组形式,元组中每一项的名称都会和初始化表达式中对应的值进行绑定。 @@ -118,7 +119,7 @@ print("The second number is \(secondNumber).") > 常量声明语法 > > -> *常量声明* → [*特性列表*](06_Attributes.html#attributes)可选 [*声明修饰符列表*](#declaration-modifiers)可选 **let** [*模式构造器列表*](pattern-initializer-list) +> *常量声明* → [*特性列表*](06_Attributes.html#attributes)可选 [*声明修饰符列表*](#declaration-modifiers)可选 **let** [*模式构造器列表*](pattern-initializer-list) > > *模式构造器列表* → [*模式构造器*](#pattern-initializer) | [*模式构造器*](#pattern-initializer) **,** [*模式构造器列表*](#pattern-initializer-list) > @@ -127,6 +128,7 @@ print("The second number is \(secondNumber).") > *构造器* → **=** [*表达式*](04_Expressions.html#expression) + ## 变量声明 *变量声明(variable declaration)* 可以在程序中引入一个具有命名的变量,它以关键字 `var` 来声明。 @@ -148,7 +150,7 @@ print("The second number is \(secondNumber).") var 变量名称: 类型 = 表达式 ``` -可以在全局范围,函数内部,或者在类和结构的声明中使用这种形式来声明一个变量。当变量以这种形式在全局范围或者函数内部被声明时,它代表一个存储型变量。当它在类或者结构中被声明时,它代表一个*存储型变量属性(stored variable property)*。 +可以在全局范围,函数内部,或者在类和结构体的声明中使用这种形式来声明一个变量。当变量以这种形式在全局范围或者函数内部被声明时,它代表一个存储型变量。当它在类或者结构体中被声明时,它代表一个*存储型变量属性(stored variable property)*。 用于初始化的表达式不可以在协议的声明中出现,在其他情况下,该表达式是可选的。如果没有初始化表达式,那么变量声明必须包含类型标注(`:` *type*)。 @@ -172,7 +174,7 @@ var 变量名称: 类型 { } ``` -可以在全局范围、函数内部,以及类、结构、枚举、扩展的声明中使用这种形式的声明。当变量以这种形式在全局范围或者函数内部被声明时,它表示一个计算型变量。当它在类、结构、枚举、扩展声明的上下文中被声明时,它表示一个*计算型属性(computed property)*。 +可以在全局范围、函数内部,以及类、结构体、枚举、扩展的声明中使用这种形式的声明。当变量以这种形式在全局范围或者函数内部被声明时,它表示一个计算型变量。当它在类、结构体、枚举、扩展声明的上下文中被声明时,它表示一个*计算型属性(computed property)*。 getter 用来读取变量值,setter 用来写入变量值。setter 子句是可选的,getter 子句是必须的。不过也可以将这些子句都省略,直接返回请求的值,正如在 [只读计算型属性](../chapter2/10_Properties.html#computed_properties) 中描述的那样。但是如果提供了一个 setter 子句,就必须也提供一个 getter 子句。 @@ -198,7 +200,7 @@ var 变量名称: 类型 = 表达式 { } ``` -可以在全局范围、函数内部,或者类、结构的声明中使用这种形式的声明。当变量以这种形式在全局范围或者函数内部被声明时,观察器表示一个存储型变量观察器。当它在类和结构的声明中被声明时,观察器表示一个属性观察器。 +可以在全局范围、函数内部,或者类、结构体的声明中使用这种形式的声明。当变量以这种形式在全局范围或者函数内部被声明时,观察器表示一个存储型变量观察器。当它在类和结构体的声明中被声明时,观察器表示一个属性观察器。 可以为任何存储型属性添加观察器。也可以通过重写父类属性的方式为任何继承的属性(无论是存储型还是计算型的)添加观察器 ,正如 [重写属性观察器](../chapter2/13_Inheritance.html#overriding_property_observers) 中所描述的。 @@ -277,6 +279,7 @@ typealias 类型别名 = 现存类型 ``` 当声明一个类型的别名后,可以在程序的任何地方使用“别名”来代替现有类型。现有类型可以是具有命名的类型或者混合类型。类型别名不产生新的类型,它只是使用别名来引用现有类型。 + 类型别名声明可以通过泛型参数来给一个现有泛型类型提供名称。类型别名为现有类型的一部分或者全部泛型参数提供具体类型。例如: ```swift @@ -294,6 +297,13 @@ typealias DictionaryOfInts = Dictionary ``` 因为类型别名可以和现有类型相互交换使用,类型别名不可以引入额外的类型约束。 + +如果在声明处省略所有泛型参数,一个类型别名可以传递已有类型的所有泛型参数。例如,此处声明的 `Diccionario` 类型别名拥有和 `Dictionary` 同样的约束和泛型参数。 + +```swift +typealias Diccionario = Dictionary +``` + 在协议声明中,类型别名可以为那些经常使用的类型提供一个更短更方便的名称,例如: ```swift @@ -313,11 +323,9 @@ func sum(_ sequence: T) -> Int where T.Element == Int { > 类型别名声明语法 -> +> > -> *类型别名声明* → [*类型别名头*](#typealias-head) [*类型别名赋值*](#typealias-assignment) -> -> *类型别名头* → [*特性列表*](06_Attributes.html#attributes)可选 [*访问级别修饰符*](#access-level-modifier)可选 **typealias** [*类型别名名称*](#typealias-name) +> *类型别名声明* → [*特性列表*](06_Attributes.html#attributes)可选 [*访问级别修饰符*](#access-level-modifier)可选 **typealias** [*类型别名名称*](#typealias-name) [*类型别子句*](#typealias-clause) [*类型别名赋值*](#typealias-assignment) > > *类型别名名称* → [*标识符*](02_Lexical_Structure.html#identifier) > @@ -346,7 +354,11 @@ func 函数名称(参数列表) { 函数可以使用元组类型作为返回类型来返回多个值。 -函数定义可以出现在另一个函数声明内。这种函数被称作*嵌套函数(nested function)*。更多关于嵌套函数的讨论,请参阅 [嵌套函数](../chapter2/06_Functions.html#Nested_Functions)。 +函数定义可以出现在另一个函数声明内。这种函数被称作*嵌套函数(nested function)*。 + +大多数时候,嵌套函数都是可逃逸的函数。仅当一个嵌套函数捕获了某个确保了永不逃逸的值——例如一个输入输出参数——或者传入一个非逃逸函数参数的时候,这个嵌套函数才是非逃逸的。 + +更多关于嵌套函数的讨论,请参阅 [嵌套函数](../chapter2/06_Functions.html#Nested_Functions)。 ### 参数名 @@ -355,25 +367,25 @@ func 函数名称(参数列表) { `参数名称`: `参数类型` -一个参数有一个内部名称,这个内部名称可以在函数体内被使用。还有一个外部名称,当调用函数时这个外部名称被作为实参的标签来使用。默认情况下,第一个参数的外部名称会被省略,第二个和之后的参数使用它们的内部名称作为它们的外部名称。例如: +每个参数有一个参数名称,这个名称与实参标签一样都可以在函数体内被使用。默认情况下,参数名也会被作为实参标签来使用。例如: ```swift func f(x: Int, y: Int) -> Int { return x + y } -f(1, y: 2) // 参数 y 有标签,参数 x 则没有 +f(x: 1, y: 2) // 参数 x 和 y 都有标签 ``` 可以按照如下两种形式之一,重写参数名称的默认行为: -`外部参数名称` `内部参数名称`: `参数类型` -_ `内部参数名称`: `参数类型` +`实参标签` `参数名称`: `参数类型` +_ `参数名称`: `参数类型` -在内部参数名称前的名称会作为这个参数的外部名称,这个名称可以和内部参数的名称不同。外部参数名称在函数被调用时必须被使用,即对应的参数在方法或函数被调用时必须有外部名。 +在参数名称前的名称会作为这个参数的显式实参标签,它可以和参数名称不同。在函数或方法调用时,相对应的参数必须使用这个实参标签。 -内部参数名称前的下划线(`_`)可使该参数在函数被调用时没有名称。在函数或方法调用时,对应的参数必须没有名字。 +参数名称前的下划线(`_`)可以去除参数的实参标签。在函数或方法调用时,相对应的参数必须去除标签。 ```swift -func f(x x: Int, withY y: Int, _ z: Int) -> Int { return x + y + z } -f(x: 1, withY: 2, 3) // 参数 x 和 y 是有标签的,参数 z 则没有 +func repeatGreeting(_ greeting: String, count n: Int) { /* Greet n times */ } +repeatGreeting("Hello, world!", count: 2) // count 有标签, greeting 没有 ``` @@ -391,39 +403,38 @@ f(x: 1, withY: 2, 3) // 参数 x 和 y 是有标签的,参数 z 则没有 不要使用传递给输入输出参数的值,即使原始值在当前作用域中依然可用。当函数返回时,你对原始值所做的更改会被拷贝的值所覆盖。不要依赖于引用调用的优化机制来试图避免这种覆盖。 -不能将同一个值传递给多个输入输出参数,因为这种情况下的拷贝与覆盖行为的顺序是不确定的,因此原始值的最终值也将无法确定。例如: +不能将同一个值传递给多个输入输出参数,因为这种情况下的拷贝与覆盖行为的顺序是不确定的,因此原始值的最终值也将无法确定。 + +更多关于内存安全和内存独占权的讨论,请参阅 [内存安全](../chapter2/24_MemorySafety.html)。 + +如果一个闭包或者嵌套函数捕获了一个输入输出参数,那么这个闭包或者嵌套函数必须是非逃逸的。如果你需要捕获一个输入输出参数,但并不对其进行修改或者在其他代码中观察其值变化,那么你可以使用捕获列表来显式地表明这是个不可变捕获。 ```swift -var x = 10 -func f(inout a: Int, inout _ b: Int) { - a += 1 - b += 10 +func someFunction(a: inout Int) -> () -> Int { + return { [a] in return a + 1 } } -f(&x, &x) // 编译报错 error: inout arguments are not allowed to alias each other ``` +如果你需要捕获并修改一个输入输出参数,使用一个显式局部拷贝来进行修改操作,在一些例如多线程的场景中,这样做可以确保函数返回之前所有的修改都已完成。 + 如果嵌套函数在外层函数返回后才调用,嵌套函数对输入输出参数造成的任何改变将不会影响到原始值。例如: ```swift -func outer(inout a: Int) -> () -> Void { - func inner() { - a += 1 - } - return inner +func multithreadedFunction(queue: DispatchQueue, x: inout Int) { + // 创建一个局部拷贝并在适当时候手动拷贝回去 + var localX = x + defer { x = localX } + + // 并行地操作 localX,然后在函数返回前一直等待 + queue.async { someMutatingOperation(&localX) } + queue.sync {} } - -var x = 10 -let f = outer(&x) -f() -print(x) -// 打印“10” ``` -调用嵌套函数 `inner()` 对 `a` 递增后,`x` 的值并未发生改变,因为 `inner()` 在外层函数 `outer()` 返回后才被调用。若要改变 `x` 的值,必须在 `outer()` 返回前调用 `inner()`。 - 关于输入输出参数的详细讨论,请参阅 [输入输出参数](../chapter2/06_Functions.html#in_out_parameters)。 + ### 特殊参数 参数可以被忽略,数量可以不固定,还可以为其提供默认值,使用形式如下: @@ -434,7 +445,7 @@ _ : 参数类型 参数名称: 参数类型 = 默认参数值 ``` -以下划线(`_`)命名的参数会被显式忽略,无法在函数体内使用。 +以下划线(`_`)命名的参数会被显式忽略,无法在函数内使用。 一个参数的基本类型名称如果紧跟着三个点(`...`),会被视为可变参数。一个函数至多可以拥有一个可变参数,且必须是最后一个参数。可变参数会作为包含该参数类型元素的数组处理。举例来讲,可变参数 `Int...` 会作为 `[Int]` 来处理。关于使用可变参数的例子,请参阅 [可变参数](../chapter2/06_Functions.html#variadic_parameters)。 @@ -454,7 +465,7 @@ f(x: 7) // 无效,该参数没有外部名称 子类重写超类中的方法必须以 `override` 声明修饰符标记。重写方法时不使用 `override` 修饰符,或者被 `override` 修饰符修饰的方法并未对超类方法构成重写,都会导致编译错误。 -枚举或者结构体中的类型方法,要以 `static` 声明修饰符标记,而对于类中的类型方法,除了使用 `static`,还可使用 `class` 声明修饰符标记。 +枚举或者结构体中的类型方法,要以 `static` 声明修饰符标记,而对于类中的类型方法,除了使用 `static`,还可使用 `class` 声明修饰符标记。类中使用 `class` 声明修饰的方法可以被子类实现重写;类中使用 `static` 声明修饰的方法不可被重写。 ### 抛出错误的函数和方法 @@ -471,7 +482,7 @@ func 函数名称(参数列表) throws -> 返回类型 { `throws` 关键字是函数的类型的一部分,非抛出函数是抛出函数的子类型。所以,可以在使用抛出函数的地方使用非抛出函数。 -不能仅基于函数能否抛出错误来进行函数重载。也就是说,可以基于函数的函数类型的参数能否抛出错误来进行函数重载。 +不能仅基于函数能否抛出错误来进行函数重写。也就是说,可以基于函数的函数类型的参数能否抛出错误来进行函数重写。 抛出方法不能重写非抛出方法,而且抛出方法不能满足协议对于非抛出方法的要求。也就是说,非抛出方法可以重写抛出方法,而且非抛出方法可以满足协议对于抛出方法的要求。 @@ -488,6 +499,20 @@ func someFunction(callback: () throws -> Void) rethrows { 重抛函数或者方法不能够从自身直接抛出任何错误,这意味着它不能够包含 `throw` 语句。它只能够传递作为参数的抛出函数所抛出的错误。例如,在 `do-catch` 代码块中调用抛出函数,并在 `catch` 子句中抛出其它错误都是不允许的。 +```swift +func alwaysThrows() throws { + throw SomeError.error +} +func someFunction(callback: () throws -> Void) rethrows { + do { + try callback() + try alwaysThrows() // 非法, alwaysThrows() 不是一个抛出函数类型的参数 + } catch { + throw AnotherError.error + } +} +``` + 抛出方法不能重写重抛方法,而且抛出方法不能满足协议对于重抛方法的要求。也就是说,重抛方法可以重写抛出方法,而且重抛方法可以满足协议对于抛出方法的要求。 @@ -496,20 +521,22 @@ func someFunction(callback: () throws -> Void) rethrows { Swift 定义了 `Never` 类型,它表示函数或者方法不会返回给它的调用者。`Never` 返回类型的函数或方法可以称为不归,不归函数、方法要么引发不可恢复的错误,要么永远不停地运作,这会使调用后本应执行得代码就不再执行了。但即使是不归函数、方法,抛错函数和重抛出函数也可以将程序控制转移到合适的 `catch` 代码块。 不归函数、方法可以在 guard 语句的 else 字句中调用,具体讨论在[*Guard 语句*](10_Statements.html#guard_statements)。 -你可以重载一个不归方法,但是新的方法必须保持原有的返回类型和没有返回的行为。 + +你可以重写一个不归方法,但是新的方法必须保持原有的返回类型和没有返回的行为。 > 函数声明语法 > -> *函数声明* → [*函数头*](#function-head) [*函数名*](#function-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.html#generic-parameter-clause)可选 [*函数签名*](#function-signature) [*函数体*](#function-body)可选 +> *函数声明* → [*函数头*](#function-head) [*函数名*](#function-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.html#generic-parameter-clause)可选 [*函数签名*](#function-signature) [*泛型 where 子句*](08_Generic_Parameters_and_Arguments.html#generic-where-clause) [*函数体*](#function-body)可选 > *函数头* → [*特性列表*](06_Attributes.html#attributes)可选 [*声明修饰符列表*](#declaration-modifiers)可选 **func** > > *函数名* → [*标识符*](02_Lexical_Structure.html#identifier) | [*运算符*](02_Lexical_Structure.html#operator) - - +> +> +> > *函数签名* → [*参数子句列表*](#parameter-clauses) **throws**可选 [*函数结果*](#function-result)可选 > *函数签名* → [*参数子句列表*](#parameter-clauses) **rethrows** [*函数结果*](#function-result)可选 > @@ -517,15 +544,13 @@ Swift 定义了 `Never` 类型,它表示函数或者方法不会返回给它 > > *函数体* → [*代码块*](#code-block) - -> *参数子句列表* → [*参数子句*](#parameter-clause) [*参数子句列表*](#parameter-clauses)可选 > > *参数子句* → **(** **)** | **(** [*参数列表*](#parameter-list) **)** > > *参数列表* → [*参数*](#parameter) | [*参数*](#parameter) **,** [*参数列表*](#parameter-list) > -> *参数* → **let**可选 [*外部参数名*](#external-parameter-name)可选 [*内部参数名*](#local-parameter-name) [*类型标注*](03_Types.html#type-annotation) [*默认参数子句*](#default-argument-clause)可选 -> *参数* → **inout** [*外部参数名*](#external-parameter-name)可选 [*内部参数名*](#local-parameter-name) [*类型标注*](03_Types.html#type-annotation) +> *参数* → [*外部参数名*](#external-parameter-name)可选 [*内部参数名*](#local-parameter-name) [*类型标注*](03_Types.html#type-annotation) [*默认参数子句*](#default-argument-clause)可选 +> *参数* → [*外部参数名*](#external-parameter-name)可选 [*内部参数名*](#local-parameter-name) [*类型标注*](03_Types.html#type-annotation) > *参数* → [*外部参数名*](#external-parameter-name)可选 [*内部参数名*](#local-parameter-name) [*类型标注*](03_Types.html#type-annotation) **...** > > *外部参数名* → [*标识符*](02_Lexical_Structure.html#identifier) | **_** @@ -535,6 +560,7 @@ Swift 定义了 `Never` 类型,它表示函数或者方法不会返回给它 > *默认参数子句* → **=** [*表达式*](04_Expressions.html#expression) + ## 枚举声明 在程序中使用*枚举声明(enumeration declaration)* 来引入一个枚举类型。 @@ -569,12 +595,12 @@ enum 枚举名称: 采纳的协议 { ```swift enum Number { - case Integer(Int) - case Real(Double) + case integer(Int) + case real(Double) } // f 的类型为 (Int) -> Number -let f = Number.Integer +let f = Number.integer // 利用 f 把一个整数数组转成 Number 数组 let evenInts: [Number] = [0, 2, 4, 6].map(f) @@ -591,8 +617,8 @@ let evenInts: [Number] = [0, 2, 4, 6].map(f) ```swift enum Tree { - case Empty - indirect case Node(value: T, left: Tree, right:Tree) + case empty + indirect case node(value: T, left: Tree, right:Tree) } ``` @@ -618,7 +644,7 @@ enum 枚举名称: 原始值类型, 采纳的协议 { ```Swift enum ExampleEnum: Int { - case A, B, C = 5, D + case a, b, c = 5, d } ``` @@ -627,35 +653,34 @@ enum ExampleEnum: Int { 如果原始值类型被指定为 `String` 类型,你不用明确地为用例指定原始值,每个没有指定原始值的用例会隐式地将用例名字作为原始值。 ```swift -enum WeekendDay: String { - case Saturday, Sunday +enum GamePlayMode: String { + case cooperative, individual, competitive } ``` -在上面这个例子中,`WeekendDay.Saturday` 的原始值是 `"Saturday"`,`WeekendDay.Sunday` 的原始值是 `"Sunday"`。 +在上面这个例子中,`GamePlayMode.cooperative` 的原始值是 `"cooperative"`,`GamePlayMode.individual` 的原始值是 `"individual"`,`GamePlayMode.competitive` 的原始值是 `"competitive"`。 -枚举用例具有原始值的枚举类型隐式地符合定义在 Swift 标准库中的 `RawRepresentable` 协议。所以,它们拥有一个 `rawValue` 属性和一个可失败构造器 `init?(rawValue: RawValue)`。可以使用 `rawValue` 属性去获取枚举用例的原始值,例如 `ExampleEnum.B.rawValue`。还可以根据原始值来创建一个相对应的枚举用例,只需调用枚举的可失败构造器,例如 `ExampleEnum(rawValue: 5)`,这个可失败构造器返回一个可选类型的用例。要获得更多关于具有原始值的枚举用例的信息和例子,请参阅 [原始值](../chapter2/08_Enumerations.html#raw_values)。 +枚举用例具有原始值的枚举类型隐式地符合定义在 Swift 标准库中的 `RawRepresentable` 协议。所以,它们拥有一个 `rawValue` 属性和一个可失败构造器 `init?(rawValue: RawValue)`。可以使用 `rawValue` 属性去获取枚举用例的原始值,例如 `ExampleEnum.b.rawValue`。还可以根据原始值来创建一个相对应的枚举用例,只需调用枚举的可失败构造器,例如 `ExampleEnum(rawValue: 5)`,这个可失败构造器返回一个可选类型的用例。要获得更多关于具有原始值的枚举用例的信息和例子,请参阅 [原始值](../chapter2/08_Enumerations.html#raw_values)。 ### 访问枚举用例 -使用点语法(`.`)来引用枚举类型的枚举用例,例如 `EnumerationType.EnumerationCase`。当枚举类型可以由上下文推断而出时,可以省略它(但是 `.` 仍然需要),正如 [枚举语法](../chapter2/08_Enumerations.html#enumeration_syntax) 和 [显式成员表达式](04_Expressions.html#explicit_member_expression) 所述。 +使用点语法(`.`)来引用枚举类型的枚举用例,例如 `EnumerationType.enumerationCase`。当枚举类型可以由上下文推断而出时,可以省略它(但是 `.` 仍然需要),正如 [枚举语法](../chapter2/08_Enumerations.html#enumeration_syntax) 和 [显式成员表达式](04_Expressions.html#explicit_member_expression) 所述。 可以使用 `switch` 语句来检验枚举用例的值,正如 [使用 switch 语句匹配枚举值](../chapter2/08_Enumerations.html#matching_enumeration_values_with_a_switch_statement) 所述。枚举类型是模式匹配的,依靠 `switch` 语句 `case` 块中的枚举用例模式,正如 [枚举用例模式](07_Patterns.html#enumeration_case_pattern) 所述。 > 枚举声明语法 -> - +> +> > *枚举声明* → [*特性列表*](06_Attributes.html#attributes)可选 [*访问级别修饰符*](#access-level-modifier)可选 [*联合风格枚举*](#union-style-enum) > *枚举声明* → [*特性列表*](06_Attributes.html#attributes)可选 [*访问级别修饰符*](#access-level-modifier) 可选 [*原始值风格枚举*](#raw-value-style-enum) - - +> > *联合风格枚举* → **indirect**可选 **enum** [*枚举名称*](#enum-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.html#generic-parameter-clause)可选 [类型继承子句](03_Types.html#type-inheritance-clause)可选 **{** [*多个联合风格枚举成员*](#union-style-enum-members)可选 **}** > > *多个联合风格枚举成员* → [*联合风格枚举成员*](#union-style-enum-member) [*多个联合风格枚举成员*](#union-style-enum-members)可选 > -> *联合风格枚举成员* → [*声明*](#declaration) | [*联合风格枚举用例子句*](#union-style-enum-case-clause) +> *联合风格枚举成员* → [*声明*](#declaration) | [*联合风格枚举用例子句*](#union-style-enum-case-clause) | [*编译控制流语句*](05_Statements.html#compiler-control-statement) > > *联合风格枚举用例子句* → [*特性列表*](06_Attributes.html#attributes)可选 **indirect**可选 **case** [*联合风格枚举用例列表*](#union-style-enum-case-list) > @@ -666,13 +691,14 @@ enum WeekendDay: String { > *枚举名称* → [*标识符*](02_Lexical_Structure.html#identifier) > > *枚举用例名称* → [*标识符*](02_Lexical_Structure.html#identifier) - - -> *原始值风格枚举* → **enum** [*枚举名称*](#enum-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.html#generic-parameter-clause)可选 [类型继承子句](03_Types.html#type-inheritance-clause) **{** [*多个原始值风格枚举成员*](#raw-value-style-enum-members) **}** +> +> +> +> *原始值风格枚举* → **enum** [*枚举名称*](#enum-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.html#generic-parameter-clause)可选 [*类型继承子句*](03_Types.html#type-inheritance-clause) [*泛型 where 子句*](08_Generic_Parameters_and_Arguments.html#generic-where-clause) **{** [*多个原始值风格枚举成员*](#raw-value-style-enum-members) **}** > > *多个原始值风格枚举成员* → [*原始值风格枚举成员*](#raw-value-style-enum-member) [*多个原始值风格枚举成员*](#raw-value-style-enum-members)可选 > -> *原始值风格枚举成员* → [*声明*](#declaration) | [*原始值风格枚举用例子句*](#raw-value-style-enum-case-clause) +> *原始值风格枚举成员* → [*声明*](#declaration) | [*原始值风格枚举用例子句*](#raw-value-style-enum-case-clause) | [*编译控制流语句*](05_Statements.html#compiler-control-statement) > > *原始值风格枚举用例子句* → [*特性列表*](06_Attributes.html#attributes)可选 **case** [*原始值风格枚举用例列表*](#raw-value-style-enum-case-list) > @@ -718,15 +744,21 @@ struct 结构体名称: 采纳的协议 { > 结构体声明语法 -> +> > -> *结构体声明* → [*特性列表*](06_Attributes.html#attributes)可选 [*访问级别修饰符*](#access-level-modifier) 可选 **struct** [*结构体名称*](#struct-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.html#generic-parameter-clause)可选 [类型继承子句](03_Types.html#type-inheritance-clause)可选 [*结构体主体*](#struct-body) +> *结构体声明* → [*特性列表*](06_Attributes.html#attributes)可选 [*访问级别修饰符*](#access-level-modifier) 可选 **struct** [*结构体名称*](#struct-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.html#generic-parameter-clause)可选 [*类型继承子句*](03_Types.html#type-inheritance-clause)可选 [*泛型 where 子句*](08_Generic_Parameters_and_Arguments.html#generic-where-clause)可选 [*结构体主体*](#struct-body) > > *结构体名称* → [*标识符*](02_Lexical_Structure.html#identifier) > > *结构体主体* → **{** [*多条声明*](#declarations)可选 **}** +> +> +> *结构体多个成员* → [*结构体成员*](#struct-member) [*结构体多个成员*](#struct-members)可选 +> +> *结构体成员* → [*声明*](#declaration) | [*编译控制流语句*](05_Statements.html#compiler-control-statement) + ## 类声明 可以在程序中使用*类声明(class declaration)* 来引入一个类。类声明使用关键字 `class`,遵循如下的形式: @@ -763,13 +795,18 @@ class 类名: 超类, 采纳的协议 { > 类声明语法 -> +> > -> *类声明* → [*特性列表*](06_Attributes.html#attributes)可选 [访问级别修饰符](#access-level-modifier)可选 **class** [*类名*](#class-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.html#generic-parameter-clause)可选 [*类型继承子句*](03_Types.html#type-inheritance-clause)可选 [*类主体*](#class-body) +> *类声明* → [*特性列表*](06_Attributes.html#attributes)可选 [访问级别修饰符](#access-level-modifier)可选 **final**可选 **class** [*类名*](#class-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.html#generic-parameter-clause)可选 [*类型继承子句*](03_Types.html#type-inheritance-clause)可选 [*泛型 where 子句*](08_Generic_Parameters_and_Arguments.html#generic-where-clause)可选 [*类主体*](#class-body) +> *类声明* → [*特性列表*](06_Attributes.html#attributes)可选 **final** [访问级别修饰符](#access-level-modifier)可选 **class** [*类名*](#class-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.html#generic-parameter-clause)可选 [*类型继承子句*](03_Types.html#type-inheritance-clause)可选 [*泛型 where 子句*](08_Generic_Parameters_and_Arguments.html#generic-where-clause)可选 [*类主体*](#class-body) > > *类名* → [*标识符*](02_Lexical_Structure.html#identifier) > > *类主体* → **{** [*多条声明*](#declarations)可选 **}** +> +> *类多个成员* → [*类成员*](#class-member) [*类多个成员*](#class-members)可选 +> +> *类成员* → [*声明*](#declaration) | [*编译控制流语句*](05_Statements.html#compiler-control-statement) ## 协议声明 @@ -794,19 +831,19 @@ protocol 协议名称: 继承的协议 { 默认地,符合某个协议的类型必须实现所有在协议中声明的属性、方法和下标。即便如此,可以用 `optional` 声明修饰符标注协议成员声明,以指定它们的实现是可选的。`optional` 修饰符仅仅可以用于使用 `objc` 特性标记过的协议。因此,仅仅类类型可以采用并符合包含可选成员要求的协议。更多关于如何使用 `optional` 声明修饰符的信息,以及如何访问可选协议成员的指导——例如不能确定采纳协议的类型是否实现了它们时——请参阅 [可选协议要求](../chapter2/21_Protocols.html#optional_protocol_requirements) -为了限制协议只能被类类型采纳,需要使用 `class` 关键字来标记协议,将 `class` 关键在写在冒号后面的继承的协议列表的首位。例如,下面的协议只能被类类型采纳: +为了限制协议只能被类类型采纳,需要使用 `AnyObject` 关键字来标记协议,将 `AnyObject` 关键在写在冒号后面的继承的协议列表的首位。例如,下面的协议只能被类类型采纳: ```swift -protocol SomeProtocol: class { +protocol SomeProtocol: AnyObject { /* 这里是协议成员 */ } ``` -任何继承自标记有 `class` 关键字的协议的协议也仅能被类类型采纳。 +任何继承自标记有 `AnyObject` 关键字的协议的协议也仅能被类类型采纳。 > 注意 > -> 如果协议已经用 `objc` 特性标记了,`class` 要求就隐式地应用于该协议,无需显式使用 `class` 关键字。 +> 如果协议已经用 `objc` 特性标记了,`AnyObject` 要求就隐式地应用于该协议,无需显式使用 `AnyObject` 关键字。 协议类型是命名的类型,因此它们可以像其他命名类型一样使用,正如 [协议作为类型](../chapter2/21_Protocols.html#protocols_as_types) 所讨论的。然而,不能构造一个协议的实例,因为协议实际上不提供它们指定的要求的实现。 @@ -814,15 +851,19 @@ protocol SomeProtocol: class { > 协议声明语法 -> - -> *协议声明* → [*特性列表*](06_Attributes.html#attributes)可选 [访问级别修饰符](#access-level-modifier)可选 **protocol** [*协议名称*](#protocol-name) [*类型继承子句*](03_Types.html#type-inheritance-clause)可选 [*协议主体*](#protocol-body) +> +> +> *协议声明* → [*特性列表*](06_Attributes.html#attributes)可选 [*访问级别修饰符*](#access-level-modifier)可选 **protocol** [*协议名称*](#protocol-name) [*类型继承子句*](03_Types.html#type-inheritance-clause)可选 [*泛型 where 子句*](08_Generic_Parameters_and_Arguments.html#generic-where-clause)可选 [*协议主体*](#protocol-body) > > *协议名称* → [*标识符*](02_Lexical_Structure.html#identifier) > > *协议主体* → **{** [*协议成员声明列表*](#protocol-member-declarations)可选 **}** -> - +> +> *协议多个成员* → [*协议成员*](#protocol-member) [*协议多个成员*](#protocol-members)可选 +> +> *协议成员* → [*协议成员声明*](#protocol-member-declaration) | [*编译控制流语句*](05_Statements.html#compiler-control-statement) +> +> > *协议成员声明* → [*协议属性声明*](#protocol-property-declaration) > *协议成员声明* → [*协议方法声明*](#protocol-method-declaration) > *协议成员声明* → [*协议构造器声明*](#protocol-initializer-declaration) @@ -857,7 +898,7 @@ var 属性名: 类型 { get set } 协议可以通过在协议声明主体中引入一个协议方法声明,来声明符合的类型必须实现的方法。协议方法声明和函数方法声明有着相同的形式,但有两项例外:它们不包括函数体,也不能包含默认参数。关于如何实现协议中的方法要求的例子,请参阅 [方法要求](../chapter2/21_Protocols.html#method_requirements)。 -使用 `static` 声明修饰符可以在协议声明中声明一个类型方法。结构体实现这些方法时使用 `static` 声明修饰符,类在实现这些方法时,除了使用 `static` 声明修饰符,还可以选择使用 `class` 声明修饰符。通过扩展实现时亦是如此。 +使用 `static` 声明修饰符可以在协议声明中声明一个类型方法。类在实现这些方法时使用 `class` 声明修饰符。结构体实现这些方法时必须使用 `static` 声明修饰符。通过扩展实现时亦是如此(类的扩展中使用 `class` 声明修饰符,结构体的扩展中使用 `static` 声明修饰符)。 另请参阅 [函数声明](#function_declaration)。 @@ -865,9 +906,10 @@ var 属性名: 类型 { get set } > 协议方法声明语法 > > -> *协议方法声明* → [*函数头*](#function-head) [*函数名*](#function-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.html#generic-parameter-clause)可选 [*函数签名*](#function-signature) +> *协议方法声明* → [*函数头*](#function-head) [*函数名*](#function-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.html#generic-parameter-clause)可选 [*函数签名*](#function-signature) [*泛型 where 子句*](08_Generic_Parameters_and_Arguments.html#generic-where-clause)可选 + ### 协议构造器声明 协议可以通过在协议声明主体中引入一个协议构造器声明,来声明符合的类型必须实现的构造器。协议构造器声明 @@ -883,8 +925,8 @@ var 属性名: 类型 { get set } > 协议构造器声明语法 > > -> *协议构造器声明* → [*构造器头*](#initializer-head) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.html#generic-parameter-clause)可选 [*参数子句*](#parameter-clause) **throws**可选 -> *协议构造器声明* → [*构造器头*](#initializer-head) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.html#generic-parameter-clause)可选 [*参数子句*](#parameter-clause) **rethrows** +> *协议构造器声明* → [*构造器头*](#initializer-head) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.html#generic-parameter-clause)可选 [*参数子句*](#parameter-clause) **throws**可选 [*泛型 where 子句*](08_Generic_Parameters_and_Arguments.html#generic-where-clause)可选 +> *协议构造器声明* → [*构造器头*](#initializer-head) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.html#generic-parameter-clause)可选 [*参数子句*](#parameter-clause) **rethrows** [*泛型 where 子句*](08_Generic_Parameters_and_Arguments.html#generic-where-clause)可选 ### 协议下标声明 @@ -902,20 +944,39 @@ subscript (参数列表) -> 返回类型 { get set } > 协议下标声明语法 > -> *协议下标声明* → [*下标头*](#subscript-head) [*下标结果*](#subscript-result) [*getter-setter 关键字代码块*](#getter-setter-keyword-block) +> *协议下标声明* → [*下标头*](#subscript-head) [*下标结果*](#subscript-result) [*泛型 where 子句*](08_Generic_Parameters_and_Arguments.html#generic-where-clause)可选 [*getter-setter 关键字代码块*](#getter-setter-keyword-block) + ### 协议关联类型声明 使用关键字 `associatedtype` 来声明协议关联类型。关联类型为作为协议声明的一部分,为某种类型提供了一个别名。关联类型和泛型参数子句中的类型参数很相似,但是它们和 `Self` 一样,用于协议中。`Self` 指代采纳协议的类型。要获得更多信息和例子,请参阅 [关联类型](../chapter2/22_Generics.html#associated_types)。 +在协议声明中使用泛型 `where` 子句来为继承的协议关联类型添加约束,且不需要重新声明关联类型。例如下面代码中的 `SubProtocol` 声明。 + +```swift +protocol SomeProtocol { + associatedtype SomeType +} + +protocol SubProtocolA: SomeProtocol { + // 此类语法会引发警告。 + associatedtype SomeType: Equatable +} + +// 建议使用此语法。 +protocol SubProtocolB: SomeProtocol where SomeType: Equatable { } +``` + + + 另请参阅 [类型别名声明](#type_alias_declaration)。 > 协议关联类型声明语法 > > -> *协议关联类型声明* → [*类型别名头*](#typealias-head) [*类型继承子句*](03_Types.html#type-inheritance-clause)可选 [*类型别名赋值*](#typealias-assignment)可选 +> *协议关联类型声明* → [*特性列表*](06_Attributes.html#attributes)可选 [*访问级别修饰符*](#access-level-modifier)可选 **associatedtype** [*类型别名头*](#typealias-head) [*类型继承子句*](03_Types.html#type-inheritance-clause)可选 [*类型别名赋值*](#typealias-assignment)可选 [*泛型 where 子句*](08_Generic_Parameters_and_Arguments.html#generic-where-clause)可选 ## 构造器声明 @@ -1007,8 +1068,8 @@ if let actualInstance = SomeStruct(input: "Hello") { > 构造器声明语法 > > -> *构造器声明* → [*构造器头*](#initializer-head) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.html#generic-parameter-clause)可选 [*参数子句*](#parameter-clause) **throws**可选 [*构造器主体*](#initializer-body) -> *构造器声明* → [*构造器头*](#initializer-head) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.html#generic-parameter-clause)可选 [*参数子句*](#parameter-clause) **rethrows**可选 [*构造器主体*](#initializer-body) +> *构造器声明* → [*构造器头*](#initializer-head) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.html#generic-parameter-clause)可选 [*参数子句*](#parameter-clause) **throws**可选 [*泛型 where 子句*](08_Generic_Parameters_and_Arguments.html#generic-where-clause)可选 [*构造器主体*](#initializer-body) +> *构造器声明* → [*构造器头*](#initializer-head) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.html#generic-parameter-clause)可选 [*参数子句*](#parameter-clause) **rethrows**可选 [*泛型 where 子句*](08_Generic_Parameters_and_Arguments.html#generic-where-clause)可选 [*构造器主体*](#initializer-body) > > *构造器头* → [*特性列表*](06_Attributes.html#attributes)可选 [*声明修饰符列表*](#declaration-modifiers)可选 **init** > *构造器头* → [*特性列表*](06_Attributes.html#attributes)可选 [*声明修饰符列表*](#declaration-modifiers)可选 **init** **?** @@ -1046,12 +1107,6 @@ deinit { *扩展声明(extension declaration)* 可以扩展一个现存的类型的行为。扩展声明使用关键字 `extension`,遵循如下格式: -```swift -extension 类型名称: 采纳的协议 { - 声明语句 -} -``` - ```swift extension 类型名称 where 要求 { 声明语句 @@ -1064,22 +1119,189 @@ extension 类型名称 where 要求 { 扩展声明可以为现存的类、结构体、枚举添加协议一致性,但是不能为类添加超类,因此在扩展声明的类型名称的冒号后面仅能指定一个协议列表。 +扩展声明可以包含构造器声明。这意味着,如果被扩展的类型在其他模块中定义,构造器声明必须委托另一个在那个模块中声明的构造器,以确保该类型能被正确地初始化。 + 现存类型的属性、方法、构造器不能在扩展中被重写。 -扩展声明可以包含构造器声明。这意味着,如果被扩展的类型在其他模块中定义,构造器声明必须委托另一个在那个模块中声明的构造器,以确保该类型能被正确地初始化。 +通过指定采纳的协议,扩展声明可以为一个现有的类、结构体或者枚举类型添加协议遵循: + +```swift +extension 类型名称: 采纳的协议 where 约束条件 { + 多条声明 +} +``` + +协议声明不能为现有的类添加类的继承关系,因此你只能在 “类型名称” 的冒号后面添加一系列协议。 + + + +### 条件遵循 + +你可以扩展一个泛型类型并使其有条件地遵循某协议,此后此类型的实例只有在特定的限制条件满足时才遵循此协议。在扩展声明中加入限制条件来为协议添加条件遵循。 + + + +## 已重写的限制条件会在某些泛型上下文中失效 + +对于一些通过条件遵循获得了特定行为的类型,在某些泛型上下文中,并不能够确保能够使用协议限制中的特定实现。为了说明这个行为,下面的例子中定义了两个协议以及一个有条件地遵循两个协议的泛型类型。 + +```swift +protocol Loggable { + func log() +} +extension Loggable { + func log() { + print(self) + } +} + +protocol TitledLoggable: Loggable { + static var logTitle: String { get } +} +extension TitledLoggable { + func log() { + print("\(Self.logTitle): \(self)") + } +} + +struct Pair: CustomStringConvertible { + let first: T + let second: T + var description: String { + return "(\(first), \(second))" + } +} + +extension Pair: Loggable where T: Loggable { } +extension Pair: TitledLoggable where T: TitledLoggable { + static var logTitle: String { + return "Pair of '\(T.logTitle)'" + } +} + +extension String: TitledLoggable { + static var logTitle: String { + return "String" + } +} +``` + +当其泛型类型遵循 `Loggable` 协议以及 `TitleLoggale` 协议时,结构体 `Pair` 遵循 `Loggable` 协议以及 `TitleLoggale` 协议。下面的例子中,`oneAndTwo` 是 `Pair` 的一个实例。因为 `String` 遵循 `TitleLoggable` ,因此 `oneAndTwo` 也遵循此协议。当 `log()` 方法被 `oneAndTwo` 直接调用时,此方法使用的是包含标题的特定版本。 + +```swift +let oneAndTwo = Pair(first: "one", second: "two") +oneAndTwo.log() +// Prints "Pair of 'String': (one, two)" +``` + +虽然如此,当 `oneAndTwo` 在泛型上下文中使用,或者它是 `Loggable` 类型的实例时,包含标题的特定版本 `log()` 方法不会被使用。Swift 只会根据这样的规则来选择 `log()` 的实现版本—— `Pair` 遵循 `Loggable` 所需要的最少的限制条件。因此 `Loggable` 所提供的默认实现版本会被使用。 + +```swift +func doSomething(with x: T) { + x.log() +} +doSomething(with: oneAndTwo) +// Prints "(one, two)" +``` + +当传入 `doSomething(_:)` 的实例调用 `log()` 时,打印结果省略了自定义标题。 + + + +### 协议遵循决不能冗余 + +一个具体的类型只能够遵循某特定协议一次。Swift 会把冗余的协议遵循标记为错误。你会在两种场景中遇到这种错误。第一种场景是,使用不同的限制条件来多次显式地遵循同一协议。第二种场景是,多次隐式地继承同一协议。以上两种场景会在下面章节中讨论。 + + + +## 解决显式冗余 + +对同一具体类型的多个扩展不能遵循同一协议,即便这些扩展有不同的显式限制条件。这个限制的具体示例在下面的例子中。两个扩展声明都试图添加对 `Serializable` 的条件遵循,一个为 `Int` 类型元素的数组,另一个为 `String` 类型元素的数组。 + +```swift +protocol Serializable { + func serialize() -> Any +} + +extension Array: Serializable where Element == Int { + func serialize() -> Any { + // implementation + } +} +extension Array: Serializable where Element == String { + func serialize() -> Any { + // implementation + } +} +// 报错: redundant conformance of 'Array' to protocol 'Serializable' +``` + +如果你需要基于多个具体类型来添加条件遵循,那么创建一个新的协议,然后让每个类型都遵循此协议,最后在声明条件遵循时使用此协议作为条件限制。 + +```swift +protocol SerializableInArray { } +extension Int: SerializableInArray { } +extension String: SerializableInArray { } + +extension Array: Serializable where Element: SerializableInArray { + func serialize() -> Any { + // 具体实现 + } +} +``` + + + +## 解决隐式冗余 + +当一个具体类型有条件地遵循某协议,此类型会隐式地使用相同的条件遵循任一父协议。 + +如果你需要让一个类型有条件地遵循两个继承自同一父协议的协议,请显式地声明对父协议的遵循。这可以避免使用不同的限制条件隐式遵循同一父协议两次。 + +下面的例子中显式地声明了 `Array` 对 `Loggable` 的条件遵循,避免了在声明对 `TitledLoggable` 和 `TitledLoggable` 声明条件遵循时发生冲突。 + +```swift +protocol MarkedLoggable: Loggable { + func markAndLog() +} + +extension MarkedLoggable { + func markAndLog() { + print("----------") + log() + } +} + +extension Array: Loggable where Element: Loggable { } +extension Array: TitledLoggable where Element: TitledLoggable { + static var logTitle: String { + return "Array of '\(Element.logTitle)'" + } +} +extension Array: MarkedLoggable where Element: MarkedLoggable { } + +``` + +如果不显式声明对 `Loggable` 的条件遵循,`Array` 其他的扩展会隐式地创建此声明,并引发错误: + +```swift +extension Array: Loggable where Element: TitledLoggable { } +extension Array: Loggable where Element: MarkedLoggable { } +// 报错: redundant conformance of 'Array' to protocol 'Loggable' +``` > 扩展声明语法 -> +> > -> *扩展声明* → [特性](06_Attributes.html#type_attributes)可选 [访问级别修饰符](#access-level-modifier)可选 **extension** [*类型标识符*](03_Types.html#type-identifier) [*类型-继承-子句*](03_Types.html#type-inheritance-clause)可选 [*扩展主体*](#extension-body) +> *扩展声明* → [特性](06_Attributes.html#type_attributes)可选 [访问级别修饰符](#access-level-modifier)可选 **extension** [*类型标识符*](03_Types.html#type-identifier) [*类型-继承-子句*](03_Types.html#type-inheritance-clause)可选 [*泛型 where 子句*](08_Generic_Parameters_and_Arguments.html#generic-where-clause)可选 [*扩展主体*](#extension-body) > -> *扩展声明* → [特性](06_Attributes.html#type_attributes)可选 [访问级别修饰符](#access-level-modifier)可选 **extension** [*类型标识符*](03_Types.html#type-identifier) [*泛型-where-子句*](03_Types.html#type-inheritance-clause) [*扩展主体*](#extension-body) > *扩展主体* → **{** [*多条声明*](#declarations)可选 **}** > *多条声明* → [单条声明](#subscript_declaration) [多条声明](#declarations) 可选 -> *单条声明* → [声明语句](#declarations) | [编译器-控制-语句](10_Statements.html#compiler-control-statement) +> *单条声明* → [声明语句](#declarations) | [*编译控制流语句*](05_Statements.html#compiler-control-statement) + ## 下标声明 *下标声明(subscript declaration)* 用于为特定类型的对象添加下标支持,通常也用于为访问集合、列表和序列中的元素提供语法便利。下标声明使用关键字 `subscript`,形式如下: @@ -1103,7 +1325,9 @@ subscript (参数列表) -> 返回类型 { 圆括号以及其中的 setter 名称是可选的。如果提供了 setter 名称,它会作为 setter 的参数名称。如果不提供 setter 名称,那么 setter 的参数名称默认是 `value`。setter 名称的类型必须与返回类型相同。 -可以重载下标,只要参数列表或返回类型不同即可。还可以重写继承自超类的下标,此时必须使用 `override` 声明修饰符声明被重写的下标。 +可以重写下标,只要参数列表或返回类型不同即可。还可以重写继承自超类的下标,此时必须使用 `override` 声明修饰符声明被重写的下标。 + +在默认情况下,下标中的参数不会含有 同样可以在协议声明中声明下标,正如 [协议下标声明](#protocol_subscript_declaration) 中所述。 @@ -1111,13 +1335,13 @@ subscript (参数列表) -> 返回类型 { > 下标声明语法 -> +> > -> *下标声明* → [*下标头*](#subscript-head) [*下标结果*](#subscript-result) [*代码块*](#code-block) -> *下标声明* → [*下标头*](#subscript-head) [*下标结果*](#subscript-result) [*getter-setter 代码块*](#getter-setter-block) -> *下标声明* → [*下标头*](#subscript-head) [*下标结果*](#subscript-result) [*getter-setter 关键字代码块*](#getter-setter-keyword-block) +> *下标声明* → [*下标头*](#subscript-head) [*下标结果*](#subscript-result) [*泛型 where 子句*](08_Generic_Parameters_and_Arguments.html#generic-where-clause)可选 [*代码块*](#code-block) +> *下标声明* → [*下标头*](#subscript-head) [*下标结果*](#subscript-result) [*泛型 where 子句*](08_Generic_Parameters_and_Arguments.html#generic-where-clause)可选 [*getter-setter 代码块*](#getter-setter-block) +> *下标声明* → [*下标头*](#subscript-head) [*下标结果*](#subscript-result) [*泛型 where 子句*](08_Generic_Parameters_and_Arguments.html#generic-where-clause)可选 [*getter-setter 关键字代码块*](#getter-setter-keyword-block) > -> *下标头* → [*特性列表*](06_Attributes.html#attributes)可选 [*声明修饰符列表*](#declaration-modifiers)可选 **subscript** [*参数子句*](#parameter-clause) +> *下标头* → [*特性列表*](06_Attributes.html#attributes)可选 [*声明修饰符列表*](#declaration-modifiers)可选 **subscript** [*泛型参数子句*](08_Generic_Parameters_and_Arguments.html#generic-parameter-clause)可选 [*参数子句*](#parameter-clause) > > *下标结果* → **->** [*特性列表*](06_Attributes.html#attributes)可选 [*类型*](03_Types.html#type) @@ -1260,6 +1484,18 @@ Swift 定义了大量的优先级组来与标准库的运算符配合使用, 该修饰符用于修饰类的指定构造器或便利构造器,表示该类所有的子类都必须实现该构造器。在子类实现该构造器时,必须同样使用 `required` 修饰符修饰该构造器。 +`unowned` + +该修饰符用于修饰存储型变量、常量或者存储型变量属性,表示该变量或属性持有其存储对象的无主引用。如果在此存储对象释放后尝试访问该对象,会引发运行时错误。如同弱引用一样,该引用类型的变量或属性必须是类类型。与弱引用不同的是,这种类型的变量或属性是非可选的。关于 `unowned` 更多的信息和例子,请参阅 [无主引用](../chapter2/16_Automatic_Reference_Counting.html#unowned_references) + +`unowned(safe)` + +`unowned` 的显式写法 + +`unowned(unsafe)` + +该修饰符用于修饰存储型变量、常量或者存储型变量属性,表示该变量或属性持有其存储对象的无主引用。如果在此存储对象释放后尝试访问该对象,会直接访问该对象释放前存储的内存地址,因此这是非内存安全的操作。如同弱引用一样,该引用类型的变量或属性必须是类类型。与弱引用不同的是,这种类型的变量或属性是非可选的。关于 `unowned` 更多的信息和例子,请参阅 [无主引用]( + `weak` 该修饰符用于修饰变量或存储型变量属性,表示该变量或属性持有其存储的对象的弱引用。这种变量或属性的类型必须是可选的类类型。使用 `weak` 修饰符可避免强引用循环。关于 `weak` 修饰符的更多信息和例子,请参阅 [弱引用](../chapter2/16_Automatic_Reference_Counting.html#resolving_strong_reference_cycles_between_class_instances)。