From fc86ccb932f2274efeb896c85fcf5fde4f52ac94 Mon Sep 17 00:00:00 2001 From: Jie Liang Date: Wed, 27 Mar 2019 00:14:08 -0500 Subject: [PATCH] fix all anchor format --- source/chapter1/03_a_swift_tour.md | 24 +- source/chapter2/01_The_Basics.md | 87 ++-- source/chapter2/02_Basic_Operators.md | 45 +- source/chapter2/03_Strings_and_Characters.md | 72 +-- source/chapter2/04_Collection_Types.md | 75 +-- source/chapter2/05_Control_Flow.md | 66 +-- source/chapter2/06_Functions.md | 54 +-- source/chapter2/07_Closures.md | 37 +- source/chapter2/08_Enumerations.md | 24 +- source/chapter2/09_Structures_And_Classes.md | 27 +- source/chapter2/10_Properties.md | 36 +- source/chapter2/11_Methods.md | 15 +- source/chapter2/12_Subscripts.md | 9 +- source/chapter2/13_Inheritance.md | 15 +- source/chapter2/14_Initialization.md | 89 ++-- source/chapter2/15_Deinitialization.md | 6 +- source/chapter2/16_Optional_Chaining.md | 24 +- source/chapter2/17_Error_Handling.md | 12 +- source/chapter2/18_Type_Casting.md | 12 +- source/chapter2/19_Nested_Types.md | 6 +- source/chapter2/21_Protocols.md | 59 +-- source/chapter2/22_Generics.md | 54 +-- .../23_Automatic_Reference_Counting.md | 35 +- source/chapter2/24_Memory_Safety.md | 15 +- source/chapter2/25_Access_Control.md | 81 ++-- source/chapter2/26_Advanced_Operators.md | 51 +- .../01_About_the_Language_Reference.md | 3 +- source/chapter3/02_Lexical_Structure.md | 138 +++--- source/chapter3/03_Types.md | 74 +-- source/chapter3/04_Expressions.md | 231 ++++----- source/chapter3/05_Statements.md | 234 ++++------ source/chapter3/06_Declarations.md | 439 +++++++----------- source/chapter3/07_Attributes.md | 85 ++-- source/chapter3/08_Patterns.md | 54 +-- .../09_Generic_Parameters_and_Arguments.md | 34 +- source/chapter3/10_Summary_of_the_Grammar.md | 24 +- source/chapter4/02_Type_Custom.md | 19 +- .../chapter4/05_Value_and_Reference_Types.md | 9 +- source/chapter4/07_Optional_Case_Study.md | 15 +- 39 files changed, 878 insertions(+), 1511 deletions(-) diff --git a/source/chapter1/03_a_swift_tour.md b/source/chapter1/03_a_swift_tour.md index f4ad3366..dc1fed4b 100755 --- a/source/chapter1/03_a_swift_tour.md +++ b/source/chapter1/03_a_swift_tour.md @@ -16,8 +16,7 @@ print("Hello, world!") > > [Download Playground](https://docs.swift.org/swift-book/GuidedTour/GuidedTour.playground.zip) - -## 简单值 +## 简单值 {#simple_values} 使用 `let` 来声明常量,使用 `var` 来声明变量。一个常量的值,在编译的时候,并不需要有明确的值,但是你只能为它赋值一次。这说明你可以用一个常量来命名一个值,一次赋值就即可在多个地方使用。 @@ -102,8 +101,7 @@ shoppingList = [] occupations = [:] ``` - -## 控制流 +## 控制流 {#control_flow} 使用 `if` 和 `switch` 来进行条件操作,使用 `for-in`、`while` 和 `repeat-while` 来进行循环。包裹条件和循环变量的括号可以省略,但是语句体的大括号是必须的。 @@ -223,8 +221,7 @@ print(total) 使用 `..<` 创建的范围不包含上界,如果想包含的话需要使用 `...`。 - -## 函数和闭包 +## 函数和闭包 {#functions_and_closures} 使用 `func` 来声明一个函数,使用名字和参数来调用函数。使用 `->` 来指定函数返回值的类型。 @@ -345,8 +342,7 @@ let sortedNumbers = numbers.sorted { $0 > $1 } print(sortedNumbers) ``` - -## 对象和类 +## 对象和类 {#objects_and_classes} 使用 `class` 和类名来创建一个类。类中属性的声明和常量、变量声明一样,唯一的区别就是它们的上下文是类。同样,方法和函数声明也一样。 @@ -495,8 +491,7 @@ let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square") let sideLength = optionalSquare?.sideLength ``` - -## 枚举和结构体 +## 枚举和结构体 {#enumerations_and_structure} 使用 `enum` 来创建一个枚举。就像类和其他所有命名类型一样,枚举可以包含方法。 @@ -609,8 +604,7 @@ let threeOfSpadesDescription = threeOfSpades.simpleDescription() > > 给 `Card` 添加一个方法,创建一副完整的扑克牌并把每张牌的 rank 和 suit 对应起来。 - -## 协议和扩展 +## 协议和扩展 {#protocols_and_extensions} 使用 `protocol` 来声明一个协议。 @@ -680,8 +674,7 @@ print(protocolValue.simpleDescription) 即使 `protocolValue` 变量运行时的类型是 `simpleClass` ,编译器还是会把它的类型当做 `ExampleProtocol`。这表示你不能调用在协议之外的方法或者属性。 - -## 错误处理 +## 错误处理 {#error_handling} 使用采用 `Error` 协议的类型来表示错误。 @@ -764,8 +757,7 @@ fridgeContains("banana") print(fridgeIsOpen) ``` - -## 泛型 +## 泛型 {#generics} 在尖括号里写一个名字来创建一个泛型函数或者类型。 diff --git a/source/chapter2/01_The_Basics.md b/source/chapter2/01_The_Basics.md index 9e28e84c..ac5a5263 100755 --- a/source/chapter2/01_The_Basics.md +++ b/source/chapter2/01_The_Basics.md @@ -13,13 +13,11 @@ Swift 还增加了可选(Optional)类型,用于处理值缺失的情况。 Swift 是一门*类型安全*的语言,这意味着 Swift 可以让你清楚地知道值的类型。如果你的代码需要一个 `String` ,类型安全会阻止你不小心传入一个 `Int` 。同样的,如果你的代码需要一个 `String`,类型安全会阻止你意外传入一个可选的 `String` 。类型安全可以帮助你在开发阶段尽早发现并修正错误。 - -## 常量和变量 +## 常量和变量 {#constants_and_variables} 常量和变量把一个名字(比如 `maximumNumberOfLoginAttempts` 或者 `welcomeMessage` )和一个指定类型的值(比如数字 `10` 或者字符串 `"Hello"` )关联起来。*常量*的值一旦设定就不能改变,而*变量*的值可以随意更改。 - -### 声明常量和变量 +### 声明常量和变量 {#declaring} 常量和变量必须在使用前声明,用 `let` 来声明常量,用 `var` 来声明变量。下面的例子展示了如何用常量和变量来记录用户尝试登录的次数: @@ -44,8 +42,7 @@ var x = 0.0, y = 0.0, z = 0.0 > > 如果你的代码中有不需要改变的值,请使用 `let` 关键字将它声明为常量。只将需要改变的值声明为变量。 - -### 类型标注 +### 类型标注 {#type_annotations} 当你声明常量或者变量的时候可以加上*类型标注(type annotation)*,说明常量或者变量中要存储的值的类型。如果要添加类型标注,需要在常量或者变量名后面加上一个冒号和空格,然后加上类型名称。 @@ -77,8 +74,7 @@ var red, green, blue: Double > > 一般来说你很少需要写类型标注。如果你在声明常量或者变量的时候赋了一个初始值,Swift 可以推断出这个常量或者变量的类型,请参考[类型安全和类型推断](#type_safety_and_type_inference)。在上面的例子中,没有给 `welcomeMessage` 赋初始值,所以变量 `welcomeMessage` 的类型是通过一个类型标注指定的,而不是通过初始值推断的。 - -### 常量和变量的命名 +### 常量和变量的命名 {#naming} 常量和变量名可以包含任何字符,包括 Unicode 字符: @@ -112,8 +108,7 @@ languageName = "Swift++" // 这会报编译时错误 - languageName 不可改变 ``` - -### 输出常量和变量 +### 输出常量和变量 {#printing} 你可以用 `print(_:separator:terminator:)` 函数来输出当前常量或变量的值: @@ -135,8 +130,7 @@ print("The current value of friendlyWelcome is \(friendlyWelcome)") > > 字符串插值所有可用的选项,请参考[字符串插值](./03_Strings_and_Characters.md#string_interpolation)。 - -## 注释 +## 注释 {#comments} 请将你的代码中的非执行文本注释成提示或者笔记以方便你将来阅读。Swift 的编译器将会在编译代码时自动忽略掉注释部分。 @@ -163,8 +157,7 @@ Swift 中的注释与 C 语言的注释非常相似。单行注释以双正斜 通过运用嵌套多行注释,你可以快速方便的注释掉一大段代码,即使这段代码之中已经含有了多行注释块。 - -## 分号 +## 分号 {#semicolons} 与其他大部分编程语言不同,Swift 并不强制要求你在每条语句的结尾处使用分号(`;`),当然,你也可以按照你自己的习惯添加分号。有一种情况下必须要用分号,即你打算在同一行内写多条独立的语句: @@ -173,15 +166,13 @@ let cat = "🐱"; print(cat) // 输出“🐱” ``` - -## 整数 +## 整数 {#integers} 整数就是没有小数部分的数字,比如 `42` 和 `-23` 。整数可以是 `有符号`(正、负、零)或者 `无符号`(正、零)。 Swift 提供了8、16、32和64位的有符号和无符号整数类型。这些整数类型和 C 语言的命名方式很像,比如8位无符号整数类型是 `UInt8`,32位有符号整数类型是 `Int32` 。就像 Swift 的其他类型一样,整数类型采用大写命名法。 - -### 整数范围 +### 整数范围 {#integer_bounds} 你可以访问不同整数类型的 `min` 和 `max` 属性来获取对应类型的最小值和最大值: @@ -192,8 +183,7 @@ let maxValue = UInt8.max // maxValue 为 255,是 UInt8 类型 `min` 和 `max` 所传回值的类型,正是其所对的整数类型(如上例 UInt8, 所传回的类型是 UInt8),可用在表达式中相同类型值旁。 - -### Int +### Int {#Int} 一般来说,你不需要专门指定整数的长度。Swift 提供了一个特殊的整数类型 `Int`,长度与当前平台的原生字长相同: @@ -202,8 +192,7 @@ let maxValue = UInt8.max // maxValue 为 255,是 UInt8 类型 除非你需要特定长度的整数,一般来说使用 `Int` 就够了。这可以提高代码一致性和可复用性。即使是在32位平台上,`Int` 可以存储的整数范围也可以达到 `-2,147,483,648` ~ `2,147,483,647`,大多数时候这已经足够大了。 - -### UInt +### UInt {#UInt} Swift 也提供了一个特殊的无符号类型 `UInt`,长度与当前平台的原生字长相同: @@ -214,8 +203,7 @@ Swift 也提供了一个特殊的无符号类型 `UInt`,长度与当前平台 > > 尽量不要使用 `UInt`,除非你真的需要存储一个和当前平台原生字长相同的无符号整数。除了这种情况,最好使用 `Int`,即使你要存储的值已知是非负的。统一使用 `Int` 可以提高代码的可复用性,避免不同类型数字之间的转换,并且匹配数字的类型推断,请参考[类型安全和类型推断](#type_safety_and_type_inference)。 - -## 浮点数 +## 浮点数 {#floating-point_numbers} 浮点数是有小数部分的数字,比如 `3.14159`、`0.1` 和 `-273.15`。 @@ -228,8 +216,7 @@ Swift 也提供了一个特殊的无符号类型 `UInt`,长度与当前平台 > > `Double` 精确度很高,至少有15位数字,而 `Float` 只有6位数字。选择哪个类型取决于你的代码需要处理的值的范围,在两种类型都匹配的情况下,将优先选择 `Double`。 - -## 类型安全和类型推断 +## 类型安全和类型推断 {#type_safety_and_type_inference} Swift 是一个*类型安全(type safe)*的语言。类型安全的语言可以让你清楚地知道代码要处理的值的类型。如果你的代码需要一个 `String`,你绝对不可能不小心传进去一个 `Int`。 @@ -266,8 +253,7 @@ let anotherPi = 3 + 0.14159 原始值 `3` 没有显式声明类型,而表达式中出现了一个浮点字面量,所以表达式会被推断为 `Double` 类型。 - -## 数值型字面量 +## 数值型字面量 {#numeric_literals} 整数字面量可以被写作: @@ -313,15 +299,13 @@ let oneMillion = 1_000_000 let justOverOneMillion = 1_000_000.000_000_1 ``` - -## 数值型类型转换 +## 数值型类型转换 {#numeric_type_conversion} 通常来讲,即使代码中的整数常量和变量已知非负,也请使用 `Int` 类型。总是使用默认的整数类型可以保证你的整数常量和变量可以直接被复用并且可以匹配整数类字面量的类型推断。 只有在必要的时候才使用其他整数类型,比如要处理外部的长度明确的数据或者为了优化性能、内存占用等等。使用显式指定长度的类型可以及时发现值溢出并且可以暗示正在处理特殊数据。 - -### 整数转换 +### 整数转换 {#integer_conversion} 不同整数类型的变量和常量可以存储不同范围的数字。`Int8` 类型的常量或者变量可以存储的数字范围是 `-128`~`127`,而 `UInt8` 类型的常量或者变量能存储的数字范围是 `0`~`255`。如果数字超出了常量或者变量可存储的范围,编译的时候会报错: @@ -346,8 +330,7 @@ let twoThousandAndOne = twoThousand + UInt16(one) `SomeType(ofInitialValue)` 是调用 Swift 构造器并传入一个初始值的默认方法。在语言内部,`UInt16` 有一个构造器,可以接受一个 `UInt8` 类型的值,所以这个构造器可以用现有的 `UInt8` 来创建一个新的 `UInt16`。注意,你并不能传入任意类型的值,只能传入 `UInt16` 内部有对应构造器的值。不过你可以扩展现有的类型来让它可以接收其他类型的值(包括自定义类型),请参考[扩展](./20_Extensions.md)。 - -### 整数和浮点数转换 +### 整数和浮点数转换 {#integer_and_floating_point_conversion} 整数和浮点数的转换必须显式指定类型: @@ -373,8 +356,7 @@ let integerPi = Int(pi) > > 结合数字类常量和变量不同于结合数字类字面量。字面量 `3` 可以直接和字面量 `0.14159` 相加,因为数字字面量本身没有明确的类型。它们的类型只在编译器需要求值的时候被推测。 - -## 类型别名 +## 类型别名 {#type_aliases} *类型别名(type aliases)*就是给现有类型定义另一个名字。你可以使用 `typealias` 关键字来定义类型别名。 @@ -393,8 +375,7 @@ var maxAmplitudeFound = AudioSample.min 本例中,`AudioSample` 被定义为 `UInt16` 的一个别名。因为它是别名,`AudioSample.min` 实际上是 `UInt16.min`,所以会给 `maxAmplitudeFound` 赋一个初值 `0`。 - -## 布尔值 +## 布尔值 {#booleans} Swift 有一个基本的*布尔(Boolean)类型*,叫做 `Bool`。布尔值指*逻辑*上的值,因为它们只能是真或者假。Swift 有两个布尔常量,`true` 和 `false`: @@ -440,8 +421,7 @@ if i == 1 { 和 Swift 中的其他类型安全的例子一样,这个方法可以避免错误并保证这块代码的意图总是清晰的。 - -## 元组 +## 元组 {#tuples} *元组(tuples)*把多个值组合成一个复合值。元组内的值可以是任意类型,并不要求是相同类型。 @@ -504,8 +484,7 @@ print("The status message is \(http200Status.description)") > > 元组在临时组织值的时候很有用,但是并不适合创建复杂的数据结构。如果你的数据结构并不是临时使用,请使用类或者结构体而不是元组。请参考[类和结构体](./09_Classes_and_Structures.md)。 - -## 可选类型 +## 可选类型 {#optionals} 使用*可选类型(optionals)*来处理值可能缺失的情况。可选类型表示两种可能: 或者有值, 你可以解析可选类型访问这个值, 或者根本没有值。 @@ -526,8 +505,7 @@ let convertedNumber = Int(possibleNumber) 因为该构造器可能会失败,所以它返回一个*可选类型*(optional)`Int`,而不是一个 `Int`。一个可选的 `Int` 被写作 `Int?` 而不是 `Int`。问号暗示包含的值是可选类型,也就是说可能包含 `Int` 值也可能*不包含值*。(不能包含其他任何值比如 `Bool` 值或者 `String` 值。只能是 `Int` 或者什么都没有。) - -### nil +### nil {#nil} 你可以给可选变量赋值为 `nil` 来表示它没有值: @@ -553,8 +531,7 @@ var surveyAnswer: String? > > Swift 的 `nil` 和 Objective-C 中的 `nil` 并不一样。在 Objective-C 中,`nil` 是一个指向不存在对象的指针。在 Swift 中,`nil` 不是指针——它是一个确定的值,用来表示值缺失。任何类型的可选状态都可以被设置为 `nil`,不只是对象类型。 - -### if 语句以及强制解析 +### if 语句以及强制解析 {#if} 你可以使用 `if` 语句和 `nil` 比较来判断一个可选值是否包含值。你可以使用“相等”(`==`)或“不等”(`!=`)来执行比较。 @@ -582,8 +559,7 @@ if convertedNumber != nil { > > 使用 `!` 来获取一个不存在的可选值会导致运行时错误。使用 `!` 来强制解析值之前,一定要确定可选包含一个非 `nil` 的值。 - -### 可选绑定 +### 可选绑定 {#optional_binding} 使用*可选绑定(optional binding)*来判断可选类型是否包含值,如果包含就把值赋给一个临时常量或者变量。可选绑定可以用在 `if` 和 `while` 语句中,这条语句不仅可以用来判断可选类型中是否有值,同时可以将可选类型中的值赋给一个常量或者变量。`if` 和 `while` 语句,请参考[控制流](./05_Control_Flow.md)。 @@ -636,8 +612,7 @@ if let firstNumber = Int("4") { > > 在 `if` 条件语句中使用常量和变量来创建一个可选绑定,仅在 `if` 语句的句中(`body`)中才能获取到值。相反,在 `guard` 语句中使用常量和变量来创建一个可选绑定,仅在 `guard` 语句外且在语句后才能获取到值,请参考[提前退出](./05_Control_Flow.md#early_exit)。 - -### 隐式解析可选类型 +### 隐式解析可选类型 {#implicityly_unwrapped_optionals} 如上所述,可选类型暗示了常量或者变量可以“没有值”。可选可以通过 `if` 语句来判断是否有值,如果有值的话可以通过可选绑定来解析值。 @@ -685,8 +660,7 @@ if let definiteString = assumedString { > > 如果一个变量之后可能变成 `nil` 的话请不要使用隐式解析可选类型。如果你需要在变量的生命周期中判断是否是 `nil` 的话,请使用普通可选类型。 - -## 错误处理 +## 错误处理 {#error_handling} 你可以使用 *错误处理(error handling)* 来应对程序执行中可能会遇到的错误条件。 @@ -736,8 +710,7 @@ do { 抛出,捕捉,以及传播错误会在[错误处理](./17_Error_Handling.md)章节详细说明。 - -## 断言和先决条件 +## 断言和先决条件 {#assertions_and_preconditions} 断言和先决条件是在运行时所做的检查。你可以用他们来检查在执行后续代码之前是否一个必要的条件已经被满足了。如果断言或者先决条件中的布尔条件评估的结果为 true(真),则代码像往常一样继续执行。如果布尔条件评估结果为 false(假),程序的当前状态是无效的,则代码执行结束,应用程序中止。 @@ -749,8 +722,7 @@ do { 断言和先决条件的不同点是,他们什么时候进行状态检测:断言仅在调试环境运行,而先决条件则在调试环境和生产环境中运行。在生产环境中,断言的条件将不会进行评估。这个意味着你可以使用很多断言在你的开发阶段,但是这些断言在生产环境中不会产生任何影响。 - -### 使用断言进行调试 +### 使用断言进行调试 {#debugging_with_assertions} 你可以调用 Swift 标准库的 `assert(_:_:file:line:)` 函数来写一个断言。向这个函数传入一个结果为 `true` 或者 `false` 的表达式以及一条信息,当表达式的结果为 `false` 的时候这条信息会被显示: @@ -780,8 +752,7 @@ if age > 10 { } ``` - -### 强制执行先决条件 +### 强制执行先决条件 {#enforcing_preconditions} 当一个条件可能为假,但是继续执行代码要求条件必须为真的时候,需要使用先决条件。例如使用先决条件来检查是否下标越界,或者来检查是否将一个正确的参数传给函数。 diff --git a/source/chapter2/02_Basic_Operators.md b/source/chapter2/02_Basic_Operators.md index 5fe0ac2d..3127b7d1 100755 --- a/source/chapter2/02_Basic_Operators.md +++ b/source/chapter2/02_Basic_Operators.md @@ -8,8 +8,7 @@ Swift 还提供了 C 语言没有的区间运算符,例如 `a.. -## 术语 +## 术语 {#terminology} 运算符分为一元、二元和三元运算符: @@ -19,8 +18,7 @@ Swift 还提供了 C 语言没有的区间运算符,例如 `a.. -## 赋值运算符 +## 赋值运算符 {#assignment_operator} *赋值运算符*(`a = b`),表示用 `b` 的值来初始化或更新 `a` 的值: @@ -48,8 +46,7 @@ if x = y { 通过将 `if x = y` 标记为无效语句,Swift 能帮你避免把 (`==`)错写成(`=`)这类错误的出现。 - -## 算术运算符 +## 算术运算符 {#arithmetic_operators} Swift 中所有数值类型都支持了基本的四则*算术运算符*: @@ -73,8 +70,7 @@ Swift 中所有数值类型都支持了基本的四则*算术运算符*: "hello, " + "world" // 等于 "hello, world" ``` - -### 求余运算符 +### 求余运算符 {#remainder_operator} *求余运算符*(`a % b`)是计算 `b` 的多少倍刚刚好可以容入 `a`,返回多出来的那部分(余数)。 @@ -118,8 +114,7 @@ Swift 中所有数值类型都支持了基本的四则*算术运算符*: 在对负数 `b` 求余时,`b` 的符号会被忽略。这意味着 `a % b` 和 `a % -b` 的结果是相同的。 - -### 一元负号运算符 +### 一元负号运算符 {#unary_minus_operator} 数值的正负号可以使用前缀 `-`(即*一元负号符*)来切换: @@ -131,8 +126,7 @@ let plusThree = -minusThree // plusThree 等于 3, 或 "负负3" 一元负号符(`-`)写在操作数之前,中间没有空格。 - -### 一元正号运算符 +### 一元正号运算符 {#unary_plus_operator} *一元正号符*(`+`)不做任何改变地返回操作数的值: @@ -143,8 +137,7 @@ let alsoMinusSix = +minusSix // alsoMinusSix 等于 -6 虽然一元正号符什么都不会改变,但当你在使用一元负号来表达负数时,你可以使用一元正号来表达正数,如此你的代码会具有对称美。 - -## 组合赋值运算符 +## 组合赋值运算符 {#compound_assignment_operators} 如同 C 语言,Swift 也提供把其他运算符和赋值运算(`=`)组合的*组合赋值运算符*,组合加运算(`+=`)是其中一个例子: @@ -162,8 +155,7 @@ a += 2 更多 Swift 标准库运算符的信息,请看[运算符声明](https://developer.apple.com/documentation/swift/operator_declarations)。 ‌ - -## 比较运算符(Comparison Operators) +## 比较运算符(Comparison Operators) {#comparison_operators} 所有标准 C 语言中的*比较运算符*都可以在 Swift 中使用: @@ -224,8 +216,7 @@ if name == "world" { > > Swift 标准库只能比较七个以内元素的元组比较函数。如果你的元组元素超过七个时,你需要自己实现比较运算符。 - -## 三元运算符(Ternary Conditional Operator) +## 三元运算符(Ternary Conditional Operator) {#ternary_conditional_operator} *三元运算符*的特殊在于它是有三个操作数的运算符,它的形式是 `问题 ? 答案 1 : 答案 2`。它简洁地表达根据 `问题`成立与否作出二选一的操作。如果 `问题` 成立,返回 `答案 1` 的结果;反之返回 `答案 2` 的结果。 @@ -266,8 +257,7 @@ if hasHeader { 三元运算为二选一场景提供了一个非常便捷的表达形式。不过需要注意的是,滥用三元运算符会降低代码可读性。所以我们应避免在一个复合语句中使用多个三元运算符。 - -## 空合运算符(Nil Coalescing Operator) +## 空合运算符(Nil Coalescing Operator) {#nil_coalescing_operator} *空合运算符*(`a ?? b`)将对可选类型 `a` 进行空判断,如果 `a` 包含一个值就进行解包,否则就返回一个默认值 `b`。表达式 `a` 必须是 Optional 类型。默认值 `b` 的类型必须要和 `a` 存储值的类型保持一致。 @@ -304,13 +294,11 @@ colorNameToUse = userDefinedColorName ?? defaultColorName // userDefinedColorName 非空,因此 colorNameToUse 的值为 "green" ``` - -## 区间运算符(Range Operators) +## 区间运算符(Range Operators) {#range_operators} Swift 提供了几种方便表达一个区间的值的*区间运算符*。 - -### 闭区间运算符 +### 闭区间运算符 {#closed_range_operator} *闭区间运算符*(`a...b`)定义一个包含从 `a` 到 `b`(包括 `a` 和 `b`)的所有值的区间。`a` 的值不能超过 `b`。 @@ -329,8 +317,7 @@ for index in 1...5 { 关于 `for-in` 循环,请看[控制流](./05_Control_Flow.md)。 - -### 半开区间运算符 +### 半开区间运算符 {#half-open_range_operator} *半开区间运算符*(`a.. -### 单侧区间 +### 单侧区间 {#one-sided_ranges} 闭区间操作符有另一个表达形式,可以表达往一侧无限延伸的区间 —— 例如,一个包含了数组从索引 2 到结尾的所有值的区间。在这些情况下,你可以省略掉区间操作符一侧的值。这种区间叫做单侧区间,因为操作符只有一侧有值。例如: @@ -390,8 +376,7 @@ range.contains(4) // true range.contains(-1) // true ``` - -## 逻辑运算符(Logical Operators) +## 逻辑运算符(Logical Operators) {#logical_operators} *逻辑运算符*的操作对象是逻辑布尔值。Swift 支持基于 C 语言的三个标准逻辑运算。 diff --git a/source/chapter2/03_Strings_and_Characters.md b/source/chapter2/03_Strings_and_Characters.md index a03e1d46..210487f3 100755 --- a/source/chapter2/03_Strings_and_Characters.md +++ b/source/chapter2/03_Strings_and_Characters.md @@ -12,8 +12,7 @@ Swift 的 `String` 和 `Character` 类型提供了一种快速且兼容 Unicode > > 更多关于在 Foundation 和 Cocoa 中使用 `String` 的信息请查看 *[Bridging Between String and NSString](https://developer.apple.com/documentation/swift/string#2919514)*。 - -## 字符串字面量 +## 字符串字面量 {#string_literals} 你可以在代码里使用一段预定义的字符串值作为字符串字面量。字符串字面量是由一对双引号包裹着的具有固定顺序的字符集。 @@ -25,8 +24,7 @@ let someString = "Some string literal value" 注意,Swift 之所以推断 `someString` 常量为字符串类型,是因为它使用了字面量方式进行初始化。 - -### 多行字符串字面量 +### 多行字符串字面量 {#multiline_string_literals} 如果你需要一个字符串是跨越多行的,那就使用多行字符串字面量 — 由一对三个双引号包裹着的具有固定顺序的文本字符集: @@ -78,8 +76,7 @@ It also ends with a line break. 在上面的例子中,尽管整个多行字符串字面量都是缩进的(源代码缩进),第一行和最后一行没有以空白字符串开始(实际的变量值)。中间一行的缩进用空白字符串(源代码缩进)比关闭引号(`"""`)之前的空白字符串多,所以,它的行首将有4个空格。 - -### 字符串字面量的特殊字符 +### 字符串字面量的特殊字符 {#special_characters_in_string_literals} 字符串字面量可以包含以下特殊字符: @@ -107,8 +104,7 @@ Escaping all three quotes \"\"\" """ ``` - -## 初始化空字符串 +## 初始化空字符串 {#initializing_an_empty_string} 要创建一个空字符串作为初始值,可以将空的字符串字面量赋值给变量,也可以初始化一个新的 `String` 实例: @@ -127,8 +123,7 @@ if emptyString.isEmpty { // 打印输出:“Nothing to see here” ``` - -## 字符串可变性 +## 字符串可变性 {#string_mutability} 你可以通过将一个特定字符串分配给一个变量来对其进行修改,或者分配给一个常量来保证其不会被修改: @@ -146,8 +141,7 @@ constantString += " and another Highlander" > > 在 Objective-C 和 Cocoa 中,需要通过选择两个不同的类(`NSString` 和 `NSMutableString`)来指定字符串是否可以被修改。 - -## 字符串是值类型 +## 字符串是值类型 {#strings_are_value_types} 在 Swift 中 `String` 类型是*值类型*。如果你创建了一个新的字符串,那么当其进行常量、变量赋值操作,或在函数/方法中传递时,会进行值拷贝。在前述任一情况下,都会对已有字符串值创建新副本,并对该新副本而非原始字符串进行传递或赋值操作。值类型在 [结构体和枚举是值类型](./09_Classes_and_Structures.md#structures_and_enumerations_are_value_types) 中进行了详细描述。 @@ -155,8 +149,7 @@ Swift 默认拷贝字符串的行为保证了在函数/方法向你传递的字 在实际编译时,Swift 编译器会优化字符串的使用,使实际的复制只发生在绝对必要的情况下,这意味着你将字符串作为值类型的同时可以获得极高的性能。 - -## 使用字符 +## 使用字符 {#working_with_characters} 你可通过 `for-in` 循环来遍历字符串,获取字符串中每一个字符的值: @@ -188,8 +181,7 @@ print(catString) // 打印输出:“Cat!🐱” ``` - -## 连接字符串和字符 +## 连接字符串和字符 {#concatenating_strings_and_characters} 字符串可以通过加法运算符(`+`)相加在一起(或称“连接”)创建一个新的字符串: @@ -249,8 +241,7 @@ print(goodStart + end) 上面的代码,把 `badStart` 和 `end` 拼接起来的字符串非我们想要的结果。因为 `badStart` 最后一行没有换行符,它与 `end` 的第一行结合到了一起。相反的,`goodStart` 的每一行都以换行符结尾,所以它与 `end` 拼接的字符串总共有三行,正如我们期望的那样。 - -## 字符串插值 +## 字符串插值 {#string_interpolation} *字符串插值*是一种构建新字符串的方式,可以在其中包含常量、变量、字面量和表达式。**字符串字面量**和**多行字符串字面量**都可以使用字符串插值。你插入的字符串字面量的每一项都在以反斜线为前缀的圆括号中: @@ -268,20 +259,17 @@ let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)" > > 插值字符串中写在括号中的表达式不能包含非转义反斜杠(`\`),并且不能包含回车或换行符。不过,插值字符串可以包含其他字面量。 - -## Unicode +## Unicode {#unicode} *Unicode*是一个用于在不同书写系统中对文本进行编码、表示和处理的国际标准。它使你可以用标准格式表示来自任意语言几乎所有的字符,并能够对文本文件或网页这样的外部资源中的字符进行读写操作。Swift 的 `String` 和 `Character` 类型是完全兼容 Unicode 标准的。 - -### Unicode 标量 +### Unicode 标量 {#unicode_scalars} Swift 的 `String` 类型是基于 *Unicode 标量* 建立的。Unicode 标量是对应字符或者修饰符的唯一的 21 位数字,例如 `U+0061` 表示小写的拉丁字母(`LATIN SMALL LETTER A`)("`a`"),`U+1F425` 表示小鸡表情(`FRONT-FACING BABY CHICK`)("`🐥`")。 请注意,并非所有 21 位 Unicode 标量值都分配给字符,某些标量被保留用于将来分配或用于 UTF-16 编码。已分配的标量值通常也有一个名称,例如上面示例中的 LATIN SMALL LETTER A 和 FRONT-FACING BABY CHICK。 - -### 可扩展的字形群集 +### 可扩展的字形群集 {#extended_grapheme_clusters} 每一个 Swift 的 `Character` 类型代表一个*可扩展的字形群*。而一个可扩展的字形群构成了人类可读的单个字符,它由一个或多个(当组合时) Unicode 标量的序列组成。 @@ -318,8 +306,7 @@ let regionalIndicatorForUS: Character = "\u{1F1FA}\u{1F1F8}" // regionalIndicatorForUS 是 🇺🇸 ``` - -## 计算字符数量 +## 计算字符数量 {#counting_characters} 如果想要获得一个字符串中 `Character` 值的数量,可以使用 `count` 属性: @@ -350,13 +337,11 @@ print("the number of characters in \(word) is \(word.count)") > > 另外需要注意的是通过 `count` 属性返回的字符数量并不总是与包含相同字符的 `NSString` 的 `length` 属性相同。`NSString` 的 `length` 属性是利用 UTF-16 表示的十六位代码单元数字,而不是 Unicode 可扩展的字符群集。 - -## 访问和修改字符串 +## 访问和修改字符串 {#accessing_and_modifying_a_string} 你可以通过字符串的属性和方法来访问和修改它,当然也可以用下标语法完成。 - -### 字符串索引 +### 字符串索引 {#string_indices} 每一个 `String` 值都有一个关联的索引(*index*)类型,`String.Index`,它对应着字符串中的每一个 `Character` 的位置。 @@ -401,8 +386,7 @@ for index in greeting.indices { > > 你可以使用 `startIndex` 和 `endIndex` 属性或者 `index(before:)` 、`index(after:)` 和 `index(_:offsetBy:)` 方法在任意一个确认的并遵循 `Collection` 协议的类型里面,如上文所示是使用在 `String` 中,你也可以使用在 `Array`、`Dictionary` 和 `Set` 中。 - -### 插入和删除 +### 插入和删除 {#inserting_and_removing} 调用 `insert(_:at:)` 方法可以在一个字符串的指定索引插入一个字符,调用 `insert(contentsOf:at:)` 方法可以在一个字符串的指定索引插入一个段字符串。 @@ -430,8 +414,7 @@ welcome.removeSubrange(range) > > 你可以使用 `insert(_:at:)`、`insert(contentsOf:at:)`、`remove(at:)` 和 `removeSubrange(_:)` 方法在任意一个确认的并遵循 `RangeReplaceableCollection` 协议的类型里面,如上文所示是使用在 `String` 中,你也可以使用在 `Array`、`Dictionary` 和 `Set` 中。 - -## 子字符串 +## 子字符串 {#substrings} 当你从字符串中获取一个子字符串 —— 例如,使用下标或者 `prefix(_:)` 之类的方法 —— 就可以得到一个 `SubString` 的实例,而非另外一个 `String`。Swift 里的 `SubString` 绝大部分函数都跟 `String` 一样,意味着你可以使用同样的方式去操作 `SubString` 和 `String`。然而,跟 `String` 不同的是,你只有在短时间内需要操作字符串时,才会使用 `SubString`。当你需要长时间保存结果时,就把 `SubString` 转化为 `String` 的实例: @@ -455,13 +438,11 @@ let newString = String(beginning) > > `String` 和 `SubString` 都遵循 `StringProtocol` 协议,这意味着操作字符串的函数使用 `StringProtocol` 会更加方便。你可以传入 `String` 或 `SubString` 去调用函数。 - -## 比较字符串 +## 比较字符串 {#comparing_strings} Swift 提供了三种方式来比较文本值:字符串字符相等、前缀相等和后缀相等。 - -### 字符串/字符相等 +### 字符串/字符相等 {#string_and_character_equality} 字符串/字符可以用等于操作符(`==`)和不等于操作符(`!=`),详细描述在[比较运算符](./02_Basic_Operators.md#comparison_operators): @@ -508,8 +489,7 @@ if latinCapitalLetterA != cyrillicCapitalLetterA { > > 在 Swift 中,字符串和字符并不区分地域(not locale-sensitive)。 - -### 前缀/后缀相等 +### 前缀/后缀相等 {#prefix_and_suffix_equality} 通过调用字符串的 `hasPrefix(_:)`/`hasSuffix(_:)` 方法来检查字符串是否拥有特定前缀/后缀,两个方法均接收一个 `String` 类型的参数,并返回一个布尔值。 @@ -564,8 +544,7 @@ print("\(mansionCount) mansion scenes; \(cellCount) cell scenes") > > `hasPrefix(_:)` 和 `hasSuffix(_:)` 方法都是在每个字符串中逐字符比较其可扩展的字符群集是否标准相等,详细描述在[字符串/字符相等](#string_and_character_equality)。 - -## 字符串的 Unicode 表示形式 +## 字符串的 Unicode 表示形式 {#unicode_representations_of_strings} 当一个 Unicode 字符串被写进文本文件或者其他储存时,字符串中的 Unicode 标量会用 Unicode 定义的几种 `编码格式`(encoding forms)编码。每一个字符串中的小块编码都被称 `代码单元`(code units)。这些包括 UTF-8 编码格式(编码字符串为 8 位的代码单元), UTF-16 编码格式(编码字符串位 16 位的代码单元),以及 UTF-32 编码格式(编码字符串32位的代码单元)。 @@ -583,8 +562,7 @@ Swift 提供了几种不同的方式来访问字符串的 Unicode 表示形式 let dogString = "Dog‼🐶" ``` - -### UTF-8 表示 +### UTF-8 表示 {#UTF-8_representation} 你可以通过遍历 `String` 的 `utf8` 属性来访问它的 `UTF-8` 表示。其为 `String.UTF8View` 类型的属性,`UTF8View` 是无符号 8 位(`UInt8`)值的集合,每一个 `UInt8` 值都是一个字符的 UTF-8 表示: @@ -635,8 +613,7 @@ print("") 上面的例子中,前三个 10 进制 `codeUnit` 值(`68`、`111`、`103`)代表了字符 `D`、`o` 和 `g`,它们的 UTF-8 表示与 ASCII 表示相同。接下来的三个 10 进制 `codeUnit` 值(`226`、`128`、`188`)是 `DOUBLE EXCLAMATION MARK` 的3字节 UTF-8 表示。最后的四个 `codeUnit` 值(`240`、`159`、`144`、`182`)是 `DOG FACE` 的4字节 UTF-8 表示。 - -### UTF-16 表示 +### UTF-16 表示 {#UTF-16_representation} 你可以通过遍历 `String` 的 `utf16` 属性来访问它的 `UTF-16` 表示。其为 `String.UTF16View` 类型的属性,`UTF16View` 是无符号16位(`UInt16`)值的集合,每一个 `UInt16` 都是一个字符的 UTF-16 表示: @@ -683,8 +660,7 @@ print("") 第五和第六个 `codeUnit` 值(`55357` 和 `56374`)是 `DOG FACE` 字符的 UTF-16 表示。第一个值为 `U+D83D`(十进制值为 `55357`),第二个值为 `U+DC36`(十进制值为 `56374`)。 - -### Unicode 标量表示 +### Unicode 标量表示 {#unicode_scalars_representation} 你可以通过遍历 `String` 值的 `unicodeScalars` 属性来访问它的 Unicode 标量表示。其为 `UnicodeScalarView` 类型的属性,`UnicodeScalarView` 是 `UnicodeScalar` 类型的值的集合。 diff --git a/source/chapter2/04_Collection_Types.md b/source/chapter2/04_Collection_Types.md index dac39962..0c90d31e 100755 --- a/source/chapter2/04_Collection_Types.md +++ b/source/chapter2/04_Collection_Types.md @@ -10,8 +10,7 @@ Swift 语言中的 `Arrays`、`Sets` 和 `Dictionaries` 中存储的数据值类 > > Swift 的 `Arrays`、`Sets` 和 `Dictionaries` 类型被实现为*泛型集合*。更多关于泛型类型和集合,参见 [泛型](./23_Generics.md)章节。 - -## 集合的可变性 +## 集合的可变性 {#mutability_of_collections} 如果创建一个 `Arrays`、`Sets` 或 `Dictionaries` 并且把它分配成一个变量,这个集合将会是*可变的*。这意味着你可以在创建之后添加更多或移除已存在的数据项,或者改变集合中的数据项。如果我们把 `Arrays`、`Sets` 或 `Dictionaries` 分配成常量,那么它就是*不可变的*,它的大小和内容都不能被改变。 @@ -19,8 +18,7 @@ Swift 语言中的 `Arrays`、`Sets` 和 `Dictionaries` 中存储的数据值类 > > 在我们不需要改变集合的时候创建不可变集合是很好的实践。如此 Swift 编译器可以优化我们创建的集合。 - -## 数组(Arrays) +## 数组(Arrays) {#arrays} *数组*使用有序列表存储同一类型的多个值。相同的值可以多次出现在一个数组的不同位置中。 @@ -28,13 +26,11 @@ Swift 语言中的 `Arrays`、`Sets` 和 `Dictionaries` 中存储的数据值类 > > Swift 的 `Array` 类型被桥接到 `Foundation` 中的 `NSArray` 类。更多关于在 `Foundation` 和 `Cocoa` 中使用 `Array` 的信息,参见 [*Using Swift with Cocoa and Obejective-C(Swift 4.1)*](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中[使用 Cocoa 数据类型](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)部分。 - -### 数组的简单语法 +### 数组的简单语法 {#array_type_shorthand_syntax} 写 Swift 数组应该遵循像 `Array` 这样的形式,其中 `Element` 是这个数组中唯一允许存在的数据类型。我们也可以使用像 `[Element]` 这样的简单语法。尽管两种形式在功能上是一样的,但是推荐较短的那种,而且在本文中都会使用这种形式来使用数组。 - -### 创建一个空数组 +### 创建一个空数组 {#creating_an_empty_array} 我们可以使用构造语法来创建一个由特定数据类型构成的空数组: @@ -55,8 +51,7 @@ someInts = [] // someInts 现在是空数组,但是仍然是 [Int] 类型的。 ``` - -### 创建一个带有默认值的数组 +### 创建一个带有默认值的数组 {#creating_an_array_with_a_default_value} Swift 中的 `Array` 类型还提供一个可以创建特定大小并且所有数据都被默认的构造方法。我们可以把准备加入新数组的数据项数量(`count`)和适当类型的初始值(`repeating`)传入数组构造函数: @@ -65,8 +60,7 @@ var threeDoubles = Array(repeating: 0.0, count: 3) // threeDoubles 是一种 [Double] 数组,等价于 [0.0, 0.0, 0.0] ``` - -### 通过两个数组相加创建一个数组 +### 通过两个数组相加创建一个数组 {#creating_an_array_by_adding_two_arrays_together} 我们可以使用加法操作符(`+`)来组合两种已存在的相同类型数组。新数组的数据类型会被从两个数组的数据类型中推断出来: @@ -78,8 +72,7 @@ var sixDoubles = threeDoubles + anotherThreeDoubles // sixDoubles 被推断为 [Double],等价于 [0.0, 0.0, 0.0, 2.5, 2.5, 2.5] ``` - -### 用数组字面量构造数组 +### 用数组字面量构造数组 {#creating_an_array_with_an_array_literals} 我们可以使用*数组字面量*来进行数组构造,这是一种用一个或者多个数值构造数组的简单方法。数组字面量是一系列由逗号分割并由方括号包含的数值: @@ -108,8 +101,7 @@ var shoppingList = ["Eggs", "Milk"] 因为所有数组字面量中的值都是相同的类型,Swift 可以推断出 `[String]` 是 `shoppingList` 中变量的正确类型。 - -### 访问和修改数组 +### 访问和修改数组 {#accessing_and_modifying_an_array} 我们可以通过数组的方法和属性来访问和修改数组,或者使用下标语法。 @@ -215,8 +207,7 @@ let apples = shoppingList.removeLast() // apples 常量的值现在等于“Apples”字符串 ``` - -### 数组的遍历 +### 数组的遍历 {#iterating_over_an_array} 我们可以使用 `for-in` 循环来遍历所有数组中的数据项: @@ -246,8 +237,7 @@ for (index, value) in shoppingList.enumerated() { 更多关于 `for-in` 循环的介绍请参见[for 循环](05_Control_Flow.html#for_loops)。 - -## 集合(Sets) +## 集合(Sets) {#sets} *集合(Set)*用来存储相同类型并且没有确定顺序的值。当集合元素顺序不重要时或者希望确保每个元素只出现一次时可以使用集合而不是数组。 @@ -256,8 +246,7 @@ for (index, value) in shoppingList.enumerated() { > > 关于使用 `Foundation` 和 `Cocoa` 中 `Set` 的知识,参见 [*Using Swift with Cocoa and Obejective-C(Swift 4.1)*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中[使用 Cocoa 数据类型](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)部分。 - -### 集合类型的哈希值 +### 集合类型的哈希值 {#hash_values_for_set_types} 一个类型为了存储在集合中,该类型必须是*可哈希化*的——也就是说,该类型必须提供一个方法来计算它的*哈希值*。一个哈希值是 `Int` 类型的,相等的对象哈希值必须相同,比如 `a==b`,因此必须 `a.hashValue == b.hashValue`。 @@ -275,13 +264,11 @@ Swift 的所有基本类型(比如 `String`、`Int`、`Double` 和 `Bool`) 关于遵循协议的更多信息,请看[协议](./22_Protocols.md)。 - -### 集合类型语法 +### 集合类型语法 {#set_type_syntax} Swift 中的 `Set` 类型被写为 `Set`,这里的 `Element` 表示 `Set` 中允许存储的类型,和数组不同的是,集合没有等价的简化形式。 - -### 创建和构造一个空的集合 +### 创建和构造一个空的集合 {#creating_and_initalizing_an_empty_set} 你可以通过构造器语法创建一个特定类型的空集合: @@ -304,8 +291,7 @@ letters = [] // letters 现在是一个空的 Set,但是它依然是 Set 类型 ``` - -### 用数组字面量创建集合 +### 用数组字面量创建集合 {#creating_a_set_with_an_array_literal} 你可以使用数组字面量来构造集合,并且可以使用简化形式写一个或者多个值作为集合元素。 @@ -330,8 +316,7 @@ var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"] 由于数组字面量中的所有元素类型相同,Swift 可以推断出 `Set` 作为 `favoriteGenres` 变量的正确类型。 - -### 访问和修改一个集合 +### 访问和修改一个集合 {#accesing_and_modifying_a_set} 你可以通过 `Set` 的属性和方法来访问和修改一个 `Set`。 @@ -382,8 +367,7 @@ if favoriteGenres.contains("Funk") { // 打印“It's too funky in here.” ``` - -### 遍历一个集合 +### 遍历一个集合 {#iterating_over_a_set} 你可以在一个 `for-in` 循环中遍历一个 `Set` 中的所有值。 @@ -409,13 +393,11 @@ for genre in favoriteGenres.sorted() { // Jazz ``` - -## 集合操作 +## 集合操作 {#performing_set_operations} 你可以高效地完成 `Set` 的一些基本操作,比如把两个集合组合到一起,判断两个集合共有元素,或者判断两个集合是否全包含,部分包含或者不相交。 - -### 基本集合操作 +### 基本集合操作 {#fundamental_set_operations} 下面的插图描述了两个集合 `a` 和 `b`,以及通过阴影部分的区域显示集合各种操作的结果。 @@ -441,8 +423,7 @@ oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted() // [1, 2, 9] ``` - -### 集合成员关系和相等 +### 集合成员关系和相等 {#set_membership_and_equality} 下面的插图描述了三个集合 `a`、`b` 和 `c`,以及通过重叠区域表述集合间共享的元素。集合 `a` 是集合 `b` 的父集合,因为 `a` 包含了 `b` 中所有的元素,相反的,集合 `b` 是集合 `a` 的子集合,因为属于 `b` 的元素也被 `a` 包含。集合 `b` 和集合 `c` 彼此不关联,因为它们之间没有共同的元素。 @@ -467,8 +448,7 @@ farmAnimals.isDisjoint(with: cityAnimals) // true ``` - -## 字典 +## 字典 {#dictionaries} *字典*是一种存储多个相同类型的值的容器。每个值(value)都关联唯一的键(key),键作为字典中的这个值数据的标识符。和数组中的数据项不同,字典中的数据项并没有具体顺序。我们在需要通过标识符(键)访问数据的时候使用字典,这种方法很大程度上和我们在现实世界中使用字典查字义的方法一样。 @@ -478,8 +458,7 @@ farmAnimals.isDisjoint(with: cityAnimals) > > 更多关于在 `Foundation` 和 `Cocoa` 中使用 `Dictionary` 类型的信息,参见 [*Using Swift with Cocoa and Obejective-C(Swift 4.1)*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中[使用 Cocoa 数据类型](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)部分。 - -### 字典类型简化语法 +### 字典类型简化语法 {#dictionary_type_shorthand_syntax} Swift 的字典使用 `Dictionary` 定义,其中 `Key` 是字典中键的数据类型,`Value` 是字典中对应于这些键所存储值的数据类型。 @@ -489,8 +468,7 @@ Swift 的字典使用 `Dictionary` 定义,其中 `Key` 是字典 我们也可以用 `[Key: Value]` 这样简化的形式去创建一个字典类型。虽然这两种形式功能上相同,但是后者是首选,并且这本指导书涉及到字典类型时通篇采用后者。 - -### 创建一个空字典 +### 创建一个空字典 {#creating_an_empty_dictionary} 我们可以像数组一样使用构造语法创建一个拥有确定类型的空字典: @@ -510,8 +488,7 @@ namesOfIntegers = [:] // namesOfIntegers 又成为了一个 [Int: String] 类型的空字典 ``` - -### 用字典字面量创建字典 +### 用字典字面量创建字典 {#creating_a_dictionary_with_a_dictionary_literal} 我们可以使用*字典字面量*来构造字典,这和我们刚才介绍过的数组字面量拥有相似语法。字典字面量是一种将一个或多个键值对写作 `Dictionary` 集合的快捷途径。 @@ -546,8 +523,7 @@ var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"] 因为这个语句中所有的键和值都各自拥有相同的数据类型,Swift 可以推断出 `Dictionary` 是 `airports` 字典的正确类型。 - -### 访问和修改字典 +### 访问和修改字典 {#accessing_and_modifying_a_dictionary} 我们可以通过字典的方法和属性来访问和修改字典,或者通过使用下标语法。 @@ -627,8 +603,7 @@ if let removedValue = airports.removeValue(forKey: "DUB") { // 打印“The removed airport's name is Dublin Airport.” ``` - -### 字典遍历 +### 字典遍历 {#iterating_over_a_dictionary} 我们可以使用 `for-in` 循环来遍历某个字典中的键值对。每一个字典中的数据项都以 `(key, value)` 元组形式返回,并且我们可以使用临时常量或者变量来分解这些元组: diff --git a/source/chapter2/05_Control_Flow.md b/source/chapter2/05_Control_Flow.md index 7c9fea73..015bf860 100755 --- a/source/chapter2/05_Control_Flow.md +++ b/source/chapter2/05_Control_Flow.md @@ -6,8 +6,7 @@ Swift 还提供了 `for-in` 循环,用来更简单地遍历数组(Array) Swift 的 `switch` 语句比许多类 C 语言要更加强大。case 还可以匹配很多不同的模式,包括范围匹配,元组(tuple)和特定类型匹配。`switch` 语句的 case 中匹配的值可以声明为临时常量或变量,在 case 作用域内使用,也可以配合 `where` 来描述更复杂的匹配条件。 - -## For-In 循环 +## For-In 循环 {#for_in_loops} 你可以使用 `for-in` 循环来遍历一个集合中的所有元素,例如数组中的元素、范围内的数字或者字符串中的字符。 @@ -98,16 +97,14 @@ for tickMark in stride(from: 3, through: hours, by: hourInterval) { } ``` - -## While 循环 +## While 循环 {#while_loops} `while` 循环会一直运行一段语句直到条件变成 `false`。这类循环适合使用在第一次迭代前,迭代次数未知的情况下。Swift 提供两种 `while` 循环形式: * `while` 循环,每次在循环开始时计算条件是否符合; * `repeat-while` 循环,每次在循环结束时计算条件是否符合。 - -### While +### While {#while} `while` 循环从计算一个条件开始。如果条件为 `true`,会重复运行一段语句,直到条件变为 `false`。 @@ -177,8 +174,7 @@ print("Game over!") `while` 循环比较适合本例中的这种情况,因为在 `while` 循环开始时,我们并不知道游戏要跑多久,只有在达成指定条件时循环才会结束。 - -### Repeat-While +### Repeat-While {#repeat_while} `while` 循环的另外一种形式是 `repeat-while`,它和 `while` 的区别是在判断循环条件之前,先执行一次循环的代码块。然后重复循环直到条件为 `false`。 @@ -226,15 +222,13 @@ print("Game over!") 循环条件(`while square < finalSquare`)和 `while` 方式相同,但是只会在循环结束后进行计算。在这个游戏中,`repeat-while` 表现得比 `while` 循环更好。`repeat-while` 方式会在条件判断 `square` 没有超出后直接运行 `square += board[square]`,这种方式可以比起前面 `while` 循环的版本,可以省去数组越界的检查。 - -## 条件语句 +## 条件语句 {#conditional_statement} 根据特定的条件执行特定的代码通常是十分有用的。当错误发生时,你可能想运行额外的代码;或者,当值太大或太小时,向用户显示一条消息。要实现这些功能,你就需要使用*条件语句*。 Swift 提供两种类型的条件语句:`if` 语句和 `switch` 语句。通常,当条件较为简单且可能的情况很少时,使用 `if` 语句。而 `switch` 语句更适用于条件较复杂、有更多排列组合的时候。并且 `switch` 在需要用到模式匹配(pattern-matching)的情况下会更有用。 - -### If +### If {#if} `if` 语句最简单的形式就是只包含一个条件,只有该条件为 `true` 时,才执行相关代码: @@ -291,8 +285,7 @@ if temperatureInFahrenheit <= 32 { 在这个例子中,由于既不冷也不热,所以不会触发 `if` 或 `else if` 分支,也就不会打印任何消息。 - -### Switch +### Switch {#switch} `switch` 语句会尝试把某个值与若干个模式(pattern)进行匹配。根据第一个匹配成功的模式,`switch` 语句会执行对应的代码。当有可能的情况较多时,通常用 `switch` 语句替换 `if` 语句。 @@ -333,8 +326,7 @@ default: 在这个例子中,第一个 case 分支用于匹配第一个英文字母 `a`,第二个 case 分支用于匹配最后一个字母 `z`。因为 `switch` 语句必须有一个 case 分支用于覆盖所有可能的字符,而不仅仅是所有的英文字母,所以 switch 语句使用 `default` 分支来匹配除了 `a` 和 `z` 外的所有值,这个分支保证了 swith 语句的完备性。 - -#### 不存在隐式的贯穿 +#### 不存在隐式的贯穿 {#no_implicit_fallthrough} 与 C 和 Objective-C 中的 `switch` 语句不同,在 Swift 中,当匹配的 case 分支中的代码执行完毕后,程序会终止 `switch` 语句,而不会继续执行下一个 case 分支。这也就是说,不需要在 case 分支中显式地使用 `break` 语句。这使得 `switch` 语句更安全、更易用,也避免了漏写 `break` 语句导致多个语言被执行的错误。 @@ -377,8 +369,7 @@ default: > > 如果想要显式贯穿 case 分支,请使用 `fallthrough` 语句,详情请参考[贯穿](#fallthrough)。 - -#### 区间匹配 +#### 区间匹配 {#interval_matching} case 分支的模式也可以是一个值的区间。下面的例子展示了如何使用区间匹配来输出任意数字对应的自然语言格式: @@ -406,8 +397,7 @@ print("There are \(naturalCount) \(countedThings).") 在上例中,`approximateCount` 在一个 `switch` 声明中被评估。每一个 `case` 都与之进行比较。因为 `approximateCount` 落在了 12 到 100 的区间,所以 `naturalCount` 等于 `"dozens of"` 值,并且此后的执行跳出了 `switch` 语句。 - -#### 元组 +#### 元组 {#tuples} 我们可以使用元组在同一个 `switch` 语句中测试多个值。元组中的元素可以是值,也可以是区间。另外,使用下划线(`_`)来匹配所有可能的值。 @@ -436,8 +426,7 @@ default: 不像 C 语言,Swift 允许多个 case 匹配同一个值。实际上,在这个例子中,点 (0, 0)可以匹配所有_四个 case_。但是,如果存在多个匹配,那么只会执行第一个被匹配到的 case 分支。考虑点 (0, 0)会首先匹配 `case (0, 0)`,因此剩下的能够匹配的分支都会被忽视掉。 - -#### 值绑定(Value Bindings) +#### 值绑定(Value Bindings) {#value_bindings} case 分支允许将匹配的值声明为临时常量或变量,并且在 case 分支体内使用 —— 这种行为被称为*值绑定*(value binding),因为匹配的值在 case 分支体内,与临时的常量或变量绑定。 @@ -466,8 +455,7 @@ case let (x, y): 请注意,这个 `switch` 语句不包含默认分支。这是因为最后一个 case ——`case let(x, y)` 声明了一个可以匹配余下所有值的元组。这使得 `switch` 语句已经完备了,因此不需要再书写默认分支。 - -#### Where +#### Where {#where} case 分支的模式可以使用 `where` 语句来判断额外的条件。 @@ -494,8 +482,7 @@ case let (x, y): 就像是值绑定中的例子,由于最后一个 case 分支匹配了余下所有可能的值,`switch` 语句就已经完备了,因此不需要再书写默认分支。 - -#### 复合型 Cases +#### 复合型 Cases {#compound_cases} 当多个条件可以使用同一种方法来处理时,可以将这几种可能放在同一个 `case` 后面,并且用逗号隔开。当 case 后面的任意一种模式匹配的时候,这条分支就会被匹配。并且,如果匹配列表过长,还可以分行书写: @@ -530,8 +517,7 @@ default: 上面的 case 有两个模式:`(let distance, 0)` 匹配了在 x 轴上的值,`(0, let distance)` 匹配了在 y 轴上的值。两个模式都绑定了 `distance`,并且 `distance` 在两种模式下,都是整型——这意味着分支体内的代码,只要 case 匹配,都可以获取到 `distance` 值。 - -## 控制转移语句 +## 控制转移语句 {#control_transfer_statements} 控制转移语句改变你代码的执行顺序,通过它可以实现代码的跳转。Swift 有五种控制转移语句: @@ -543,8 +529,7 @@ default: 我们将会在下面讨论 `continue`、`break` 和 `fallthrough` 语句。`return` 语句将会在[函数](./06_Functions.md)章节讨论,`throw` 语句会在[错误抛出](./18_Error_Handling.md#throwing_errors)章节讨论。 - -### Continue +### Continue {#continue} `continue` 语句告诉一个循环体立刻停止本次循环,重新开始下次循环。就好像在说“本次循环我已经执行完了”,但是并不会离开整个循环体。 @@ -567,18 +552,15 @@ print(puzzleOutput) 在上面的代码中,只要匹配到元音字母或者空格字符,就调用 `continue` 语句,使本次循环结束,重新开始下次循环。这种行为使 `switch` 匹配到元音字母和空格字符时不做处理,而不是让每一个匹配到的字符都被打印。 - -### Break +### Break {#break} `break` 语句会立刻结束整个控制流的执行。`break` 可以在 `switch` 或循环语句中使用,用来提前结束 `switch` 或循环语句。 - -#### 循环语句中的 break +#### 循环语句中的 break {#break_in_a_loop_statement} 当在一个循环体中使用 `break` 时,会立刻中断该循环体的执行,然后跳转到表示循环体结束的大括号(`}`)后的第一行代码。不会再有本次循环的代码被执行,也不会再有下次的循环产生。 - -#### Switch 语句中的 break +#### Switch 语句中的 break {#break_in_a_switch_statement} 当在一个 `switch` 代码块中使用 `break` 时,会立即中断该 `switch` 代码块的执行,并且跳转到表示 `switch` 代码块结束的大括号(`}`)后的第一行代码。 @@ -619,8 +601,7 @@ if let integerValue = possibleIntegerValue { 在上面的例子中,想要把 `Character` 所有的的可能性都枚举出来是不现实的,所以使用 `default` 分支来包含所有上面没有匹配到字符的情况。由于这个 `default` 分支不需要执行任何动作,所以它只写了一条 `break` 语句。一旦落入到 `default` 分支中后,`break` 语句就完成了该分支的所有代码操作,代码继续向下,开始执行 `if let` 语句。 - -### 贯穿(Fallthrough) +### 贯穿(Fallthrough) {#fallthrough} 在 Swift 里,`switch` 语句不会从上一个 case 分支跳转到下一个 case 分支中。相反,只要第一个匹配到的 case 分支完成了它需要执行的语句,整个 `switch` 代码块完成了它的执行。相比之下,C 语言要求你显式地插入 `break` 语句到每个 case 分支的末尾来阻止自动落入到下一个 case 分支中。Swift 的这种避免默认落入到下一个分支中的特性意味着它的 `switch` 功能要比 C 语言的更加清晰和可预测,可以避免无意识地执行多个 case 分支从而引发的错误。 @@ -650,8 +631,7 @@ print(description) > > `fallthrough` 关键字不会检查它下一个将会落入执行的 case 中的匹配条件。`fallthrough` 简单地使代码继续连接到下一个 case 中的代码,这和 C 语言标准中的 `switch` 语句特性是一样的。 - -### 带标签的语句 +### 带标签的语句 {#labeled_statements} 在 Swift 中,你可以在循环体和条件语句中嵌套循环体和条件语句来创造复杂的控制流结构。并且,循环体和条件语句都可以使用 `break` 语句来提前结束整个代码块。因此,显式地指明 `break` 语句想要终止的是哪个循环体或者条件语句,会很有用。类似地,如果你有许多嵌套的循环体,显式指明 `continue` 语句想要影响哪一个循环体也会非常有用。 @@ -722,8 +702,7 @@ print("Game over!") > > 同时请注意,当调用 `continue gameLoop` 去跳转到下一次循环迭代时,这里使用 `gameLoop` 标签并不是严格必须的。因为在这个游戏中,只有一个循环体,所以 `continue` 语句会影响到哪个循环体是没有歧义的。然而,`continue` 语句使用 `gameLoop` 标签也是没有危害的。这样做符合标签的使用规则,同时参照旁边的 `break gameLoop`,能够使游戏的逻辑更加清晰和易于理解。 - -## 提前退出 +## 提前退出 {#early_exit} 像 `if` 语句一样,`guard` 的执行取决于一个表达式的布尔值。我们可以使用 `guard` 语句来要求条件必须为真时,以执行 `guard` 语句后的代码。不同于 `if` 语句,一个 `guard` 语句总是有一个 `else` 从句,如果条件不为真则执行 `else` 从句中的代码。 @@ -757,8 +736,7 @@ greet(person: ["name": "Jane", "location": "Cupertino"]) 相比于可以实现同样功能的 `if` 语句,按需使用 `guard` 语句会提升我们代码的可读性。它可以使你的代码连贯的被执行而不需要将它包在 `else` 块中,它可以使你在紧邻条件判断的地方,处理违规的情况。 - -## 检测 API 可用性 +## 检测 API 可用性 {#checking_api_availability} Swift 内置支持检查 API 可用性,这可以确保我们不会在当前部署机器上,不小心地使用了不可用的 API。 diff --git a/source/chapter2/06_Functions.md b/source/chapter2/06_Functions.md index 7e249b73..fea07880 100755 --- a/source/chapter2/06_Functions.md +++ b/source/chapter2/06_Functions.md @@ -6,8 +6,7 @@ Swift 统一的函数语法非常的灵活,可以用来表示任何函数, 在 Swift 中,每个函数都有一个由函数的参数值类型和返回值类型组成的类型。你可以把函数类型当做任何其他普通变量类型一样处理,这样就可以更简单地把函数当做别的函数的参数,也可以从其他函数中返回函数。函数的定义可以写在其他函数定义中,这样可以在嵌套函数范围内实现功能封装。 - -## 函数的定义与调用 +## 函数的定义与调用 {#Defining_and_Calling_Functions} 当你定义一个函数时,你可以定义一个或多个有名字和类型的值,作为函数的输入,称为*参数*,也可以定义某种类型的值作为函数执行结束时的输出,称为*返回类型*。 @@ -53,13 +52,11 @@ print(greetAgain(person: "Anna")) // 打印“Hello again, Anna!” ``` - -## 函数参数与返回值 +## 函数参数与返回值 {#Function_Parameters_and_Return_Values} 函数参数与返回值在 Swift 中非常的灵活。你可以定义任何类型的函数,包括从只带一个未名参数的简单函数到复杂的带有表达性参数名和不同参数选项的复杂函数。 - -### 无参数函数 +### 无参数函数 {#functions_without_parameters} 函数可以没有参数。下面这个函数就是一个无参数函数,当被调用时,它返回固定的 `String` 消息: @@ -73,8 +70,7 @@ print(sayHelloWorld()) 尽管这个函数没有参数,但是定义中在函数名后还是需要一对圆括号。当被调用时,也需要在函数名后写一对圆括号。 - -### 多参数函数 +### 多参数函数 {#functions_with_multiple_parameters} 函数可以有多种输入参数,这些参数被包含在函数的括号之中,以逗号分隔。 @@ -94,8 +90,7 @@ print(greet(person: "Tim", alreadyGreeted: true)) 你可以通过在括号内使用逗号分隔来传递一个 `String` 参数值和一个标识为 `alreadyGreeted` 的 `Bool` 值,来调用 `greet(person:alreadyGreeted:)` 函数。注意这个函数和上面 `greet(person:)` 是不同的。虽然它们都有着同样的名字 `greet`,但是 `greet(person:alreadyGreeted:)` 函数需要两个参数,而 `greet(person:)` 只需要一个参数。 - -### 无返回值函数 +### 无返回值函数 {#functions_without_return_values} 函数可以没有返回值。下面是 `greet(person:)` 函数的另一个版本,这个函数直接打印一个 `String` 值,而不是返回它: @@ -135,8 +130,7 @@ printWithoutCounting(string: "hello, world") > > 返回值可以被忽略,但定义了有返回值的函数必须返回一个值,如果在函数定义底部没有返回任何值,将导致编译时错误。 - -### 多重返回值函数 +### 多重返回值函数 {#functions_with_multiple_return_values} 你可以用元组(tuple)类型让多个值作为一个复合值从函数中返回。 @@ -171,8 +165,7 @@ print("min is \(bounds.min) and max is \(bounds.max)") 需要注意的是,元组的成员不需要在元组从函数中返回时命名,因为它们的名字已经在函数返回类型中指定了。 - -### 可选元组返回类型 +### 可选元组返回类型 {#optional_tuple_return_types} 如果函数返回的元组类型有可能整个元组都“没有值”,你可以使用*可选的* 元组返回类型反映整个元组可以是 `nil` 的事实。你可以通过在元组类型的右括号后放置一个问号来定义一个可选元组,例如 `(Int, Int)?` 或 `(String, Int, Bool)?` @@ -209,8 +202,7 @@ if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) { // 打印“min is -6 and max is 109” ``` - -## 函数参数标签和参数名称 +## 函数参数标签和参数名称 {#Function_Argument_Labels_and_Parameter_Names} 每个函数参数都有一个*参数标签(argument label)*以及一个*参数名称(parameter name)*。参数标签在调用函数的时候使用;调用的时候需要将函数的参数标签写在对应的参数前面。参数名称在函数的实现中使用。默认情况下,函数参数使用参数名称来作为它们的参数标签。 @@ -223,8 +215,7 @@ someFunction(firstParameterName: 1, secondParameterName: 2) 所有的参数都必须有一个独一无二的名字。虽然多个参数拥有同样的参数标签是可能的,但是一个唯一的函数标签能够使你的代码更具可读性。 - -### 指定参数标签 +### 指定参数标签 {#specifying_argument_labels} 你可以在参数名称前指定它的参数标签,中间以空格分隔: @@ -246,8 +237,7 @@ print(greet(person: "Bill", from: "Cupertino")) 参数标签的使用能够让一个函数在调用时更有表达力,更类似自然语言,并且仍保持了函数内部的可读性以及清晰的意图。 - -### 忽略参数标签 +### 忽略参数标签 {#omitting_argument_labels} 如果你不希望为某个参数添加一个标签,可以使用一个下划线(`_`)来代替一个明确的参数标签。 @@ -260,8 +250,7 @@ someFunction(1, secondParameterName: 2) 如果一个参数有一个标签,那么在调用的时候必须使用标签来标记这个参数。 - -### 默认参数值 +### 默认参数值 {#default_parameter_values} 你可以在函数体中通过给参数赋值来为任意一个参数定义*默认值(Deafult Value)*。当默认值被定义后,调用这个函数时可以忽略这个参数。 @@ -275,8 +264,7 @@ someFunction(parameterWithoutDefault: 4) // parameterWithDefault = 12 将不带有默认值的参数放在函数参数列表的最前。一般来说,没有默认值的参数更加的重要,将不带默认值的参数放在最前保证在函数调用时,非默认参数的顺序是一致的,同时也使得相同的函数在不同情况下调用时显得更为清晰。 - -### 可变参数 +### 可变参数 {#variadic_parameters} 一个*可变参数(variadic parameter)*可以接受零个或多个值。函数调用时,你可以用可变参数来指定函数参数可以被传入不确定数量的输入值。通过在变量类型名后面加入(`...`)的方式来定义可变参数。 @@ -302,8 +290,7 @@ arithmeticMean(3, 8.25, 18.75) > > 一个函数最多只能拥有一个可变参数。 - -### 输入输出参数 +### 输入输出参数 {#in_out_parameters} 函数参数默认是常量。试图在函数体中更改参数值将会导致编译错误。这意味着你不能错误地更改参数值。如果你想要一个函数可以修改参数的值,并且想要在这些修改在函数调用结束后仍然存在,那么就应该把这个参数定义为*输入输出参数(In-Out Parameters)*。 @@ -343,8 +330,7 @@ print("someInt is now \(someInt), and anotherInt is now \(anotherInt)") > > 输入输出参数和返回值是不一样的。上面的 `swapTwoInts` 函数并没有定义任何返回值,但仍然修改了 `someInt` 和 `anotherInt` 的值。输入输出参数是函数对函数体外产生影响的另一种方式。 - -## 函数类型 +## 函数类型 {#Function_Types} 每个函数都有种特定的*函数类型*,函数的类型由函数的参数类型和返回类型组成。 @@ -375,8 +361,7 @@ func printHelloWorld() { 这个函数的类型是:`() -> Void`,或者叫“没有参数,并返回 `Void` 类型的函数”。 - -### 使用函数类型 +### 使用函数类型 {#using_function_types} 在 Swift 中,使用函数类型就像使用其他类型一样。例如,你可以定义一个类型为函数的常量或变量,并将适当的函数赋值给它: @@ -412,8 +397,7 @@ let anotherMathFunction = addTwoInts // anotherMathFunction 被推断为 (Int, Int) -> Int 类型 ``` - -### 函数类型作为参数类型 +### 函数类型作为参数类型 {#function_types_as_parameter_types} 你可以用 `(Int, Int) -> Int` 这样的函数类型作为另一个函数的参数类型。这样你可以将函数的一部分实现留给函数的调用者来提供。 @@ -433,8 +417,7 @@ printMathResult(addTwoInts, 3, 5) `printMathResult(_:_:_:)` 函数的作用就是输出另一个适当类型的数学函数的调用结果。它不关心传入函数是如何实现的,只关心传入的函数是不是一个正确的类型。这使得 `printMathResult(_:_:_:)` 能以一种类型安全(type-safe)的方式将一部分功能转给调用者实现。 - -### 函数类型作为返回类型 +### 函数类型作为返回类型 {#function_types_as_return_types} 你可以用函数类型作为另一个函数的返回类型。你需要做的是在返回箭头(->)后写一个完整的函数类型。 @@ -483,8 +466,7 @@ print("zero!") // zero! ``` - -## 嵌套函数 +## 嵌套函数 {#Nested_Functions} 到目前为止本章中你所见到的所有函数都叫*全局函数(global functions)*,它们定义在全局域中。你也可以把函数定义在别的函数体中,称作 *嵌套函数(nested functions)*。 diff --git a/source/chapter2/07_Closures.md b/source/chapter2/07_Closures.md index 31486436..84a07220 100755 --- a/source/chapter2/07_Closures.md +++ b/source/chapter2/07_Closures.md @@ -21,15 +21,13 @@ Swift 的闭包表达式拥有简洁的风格,并鼓励在常见场景中进 * 参数名称缩写 * 尾随闭包语法 - -## 闭包表达式 +## 闭包表达式 {#closure_expressions} [嵌套函数](./06_Functions.md#Nested_Functions)作为复杂函数的一部分时,它自包含代码块式的定义和命名形式在使用上带来了方便。当然,编写未完整声明和没有函数名的类函数结构代码是很有用的,尤其是在编码中涉及到函数作为参数的那些方法时。 *闭包表达式*是一种构建内联闭包的方式,它的语法简洁。在保证不丢失它语法清晰明了的同时,闭包表达式提供了几种优化的语法简写形式。下面通过对 `sorted(by:)` 这一个案例的多次迭代改进来展示这个过程,每次迭代都使用了更加简明的方式描述了相同功能。。 - -### 排序方法 +### 排序方法 {#the_sorted_function} Swift 标准库提供了名为 `sorted(by:)` 的方法,它会基于你提供的排序闭包表达式的判断结果对数组中的值(类型确定)进行排序。一旦它完成排序过程,`sorted(by:)` 方法会返回一个与旧数组类型大小相同类型的新数组,该数组的元素有着正确的排序顺序。原数组不会被 `sorted(by:)` 方法修改。 @@ -57,8 +55,7 @@ var reversedNames = names.sorted(by: backward) 然而,以这种方式来编写一个实际上很简单的表达式(`a > b`),确实太过繁琐了。对于这个例子来说,利用闭包表达式语法可以更好地构造一个内联排序闭包。 - -### 闭包表达式语法 +### 闭包表达式语法 {#closure_expression_syntax} 闭包表达式语法有如下的一般形式: @@ -90,8 +87,7 @@ reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 该例中 `sorted(by:)` 方法的整体调用保持不变,一对圆括号仍然包裹住了方法的整个参数。然而,参数现在变成了内联闭包。 - -### 根据上下文推断类型 +### 根据上下文推断类型 {#inferring_type_from_context} 因为排序闭包函数是作为 `sorted(by:)` 方法的参数传入的,Swift 可以推断其参数和返回值的类型。`sorted(by:)` 方法被一个字符串数组调用,因此其参数必须是 `(String, String) -> Bool` 类型的函数。这意味着 `(String, String)` 和 `Bool` 类型并不需要作为闭包表达式定义的一部分。因为所有的类型都可以被正确推断,返回箭头(`->`)和围绕在参数周围的括号也可以被省略: @@ -103,9 +99,7 @@ reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } ) 尽管如此,你仍然可以明确写出有着完整格式的闭包。如果完整格式的闭包能够提高代码的可读性,则我们更鼓励采用完整格式的闭包。而在 `sorted(by:)` 方法这个例子里,显然闭包的目的就是排序。由于这个闭包是为了处理字符串数组的排序,因此读者能够推测出这个闭包是用于字符串处理的。 - - -### 单表达式闭包的隐式返回 +### 单表达式闭包的隐式返回 {#implicit_returns_from_single_expression_closures} 单行表达式闭包可以通过省略 `return` 关键字来隐式返回单行表达式的结果,如上版本的例子可以改写为: @@ -115,8 +109,7 @@ reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } ) 在这个例子中,`sorted(by:)` 方法的参数类型明确了闭包必须返回一个 `Bool` 类型值。因为闭包函数体只包含了一个单一表达式(`s1 > s2`),该表达式返回 `Bool` 类型值,因此这里没有歧义,`return` 关键字可以省略。 - -### 参数名称缩写 +### 参数名称缩写 {#shorthand_argument_names} Swift 自动为内联闭包提供了参数名称缩写功能,你可以直接通过 `$0`,`$1`,`$2` 来顺序调用闭包的参数,以此类推。 @@ -128,8 +121,7 @@ reversedNames = names.sorted(by: { $0 > $1 } ) 在这个例子中,`$0` 和 `$1` 表示闭包中第一个和第二个 `String` 类型的参数。 - -### 运算符方法 +### 运算符方法 {#operator_methods} 实际上还有一种更*简短的*方式来编写上面例子中的闭包表达式。Swift 的 `String` 类型定义了关于大于号(`>`)的字符串实现,其作为一个函数接受两个 `String` 类型的参数并返回 `Bool` 类型的值。而这正好与 `sorted(by:)` 方法的参数需要的函数类型相符合。因此,你可以简单地传递一个大于号,Swift 可以自动推断找到系统自带的那个字符串函数的实现: @@ -139,8 +131,7 @@ reversedNames = names.sorted(by: >) 更多关于运算符方法的内容请查看[运算符方法](./26_Advanced_Operators.md#operator_methods)。 - -## 尾随闭包 +## 尾随闭包 {#trailing_closures} 如果你需要将一个很长的闭包表达式作为最后一个参数传递给函数,将这个闭包替换成为尾随闭包的形式很有用。尾随闭包是一个书写在函数圆括号之后的闭包表达式,函数支持将其作为最后一个参数调用。在使用尾随闭包时,你不用写出它的参数标签: @@ -223,8 +214,7 @@ let strings = numbers.map { 在上面的例子中,通过尾随闭包语法,优雅地在函数后封装了闭包的具体功能,而不再需要将整个闭包包裹在 `map(_:)` 方法的括号内。 - -## 值捕获 +## 值捕获 {#capturing_values} 闭包可以在其被定义的上下文中*捕获*常量或变量。即使定义这些常量和变量的原作用域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。 @@ -302,8 +292,7 @@ incrementByTen() > > 如果你将闭包赋值给一个类实例的属性,并且该闭包通过访问该实例或其成员而捕获了该实例,你将在闭包和该实例间创建一个循环强引用。Swift 使用捕获列表来打破这种循环强引用。更多信息,请参考[闭包引起的循环强引用](./23_Automatic_Reference_Counting.md#strong_reference_cycles_for_closures)。 - -## 闭包是引用类型 +## 闭包是引用类型 {#closures_are_reference_types} 上面的例子中,`incrementBySeven` 和 `incrementByTen` 都是常量,但是这些常量指向的闭包仍然可以增加其捕获的变量的值。这是因为函数和闭包都是*引用类型*。 @@ -317,8 +306,7 @@ alsoIncrementByTen() // 返回的值为50 ``` - -## 逃逸闭包 +## 逃逸闭包 {#escaping_closures} 当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中*逃逸*。当你定义接受闭包作为参数的函数时,你可以在参数名之前标注 `@escaping`,用来指明这个闭包是允许“逃逸”出这个函数的。 @@ -358,8 +346,7 @@ print(instance.x) // 打印出“100” ``` - -## 自动闭包 +## 自动闭包 {#autoclosures} *自动闭包*是一种自动创建的闭包,用于包装传递给函数作为参数的表达式。这种闭包不接受任何参数,当它被调用的时候,会返回被包装在其中的表达式的值。这种便利语法让你能够省略闭包的花括号,用一个普通的表达式来代替显式的闭包。 diff --git a/source/chapter2/08_Enumerations.md b/source/chapter2/08_Enumerations.md index a0f5b539..ed9ae60d 100755 --- a/source/chapter2/08_Enumerations.md +++ b/source/chapter2/08_Enumerations.md @@ -10,8 +10,7 @@ 想了解更多相关信息,请参见[属性](./10_Properties.md),[方法](./11_Methods.md),[构造过程](./14_Initialization.md),[扩展](./20_Extensions.md)和[协议](./21_Protocols.md)。 - -## 枚举语法 +## 枚举语法 {#enumeration_syntax} 使用 `enum` 关键词来创建枚举并且把它们的整个定义放在一对大括号内: @@ -60,8 +59,7 @@ directionToHead = .east 当 `directionToHead` 的类型已知时,再次为其赋值可以省略枚举类型名。在使用具有显式类型的枚举值时,这种写法让代码具有更好的可读性。 - -## 使用 Switch 语句匹配枚举值 +## 使用 Switch 语句匹配枚举值 {#matching_enumeration_values_with_a_switch_statement} 你可以使用 `switch` 语句匹配单个枚举值: @@ -101,8 +99,7 @@ default: // 打印“Mostly harmless” ``` - -## 枚举成员的遍历 +## 枚举成员的遍历 {#iterating over enumeration cases} 在一些情况下,你会需要得到一个包含枚举所有成员的集合。可以通过如下代码实现: @@ -130,8 +127,7 @@ for beverage in Beverage.allCases { 在前面的例子中,使用的语法表明这个枚举遵循 [CaseIterable](https://developer.apple.com/documentation/swift/caseiterable) 协议。想了解 protocols 相关信息,请参见[协议](./21_Protocols.md)。 - -## 关联值 +## 关联值 {#associated_values} 枚举语法那一小节的例子演示了如何定义和分类枚举的成员。你可以为 `Planet.earth` 设置一个常量或者变量,并在赋值之后查看这个值。然而,有时候把其他类型的值和成员值一起存储起来会很有用。这额外的信息称为*关联值*,并且你每次在代码中使用该枚举成员时,还可以修改这个关联值。 @@ -202,8 +198,7 @@ case let .qrCode(productCode): // 打印“QR code: ABCDEFGHIJKLMNOP.” ``` - -## 原始值 +## 原始值 {#raw_values} 在[关联值](#associated_values)小节的条形码例子中,演示了如何声明存储不同类型关联值的枚举成员。作为关联值的替代选择,枚举成员可以被默认值(称为*原始值*)预填充,这些原始值的类型必须相同。 @@ -225,8 +220,7 @@ enum ASCIIControlCharacter: Character { > > 原始值和关联值是不同的。原始值是在定义枚举时被预先填充的值,像上述三个 ASCII 码。对于一个特定的枚举成员,它的原始值始终不变。关联值是创建一个基于枚举成员的常量或变量时才设置的值,枚举成员的关联值可以变化。 - -### 原始值的隐式赋值 +### 原始值的隐式赋值 {#implicitly_assigned_raw_values} 在使用原始值为整数或者字符串类型的枚举时,不需要显式地为每一个枚举成员设置原始值,Swift 将会自动为你赋值。 @@ -264,8 +258,7 @@ let sunsetDirection = CompassPoint.west.rawValue // sunsetDirection 值为 "west" ``` - -### 使用原始值初始化枚举实例 +### 使用原始值初始化枚举实例 {#initializing_from_a_raw_value} 如果在定义枚举类型的时候使用了原始值,那么将会自动获得一个初始化方法,这个方法接收一个叫做 `rawValue` 的参数,参数类型即为原始值类型,返回值则是枚举成员或 `nil`。你可以使用这个初始化方法来创建一个新的枚举实例。 @@ -301,8 +294,7 @@ if let somePlanet = Planet(rawValue: positionToFind) { 这个例子使用了可选绑定(optional binding),试图通过原始值 `11` 来访问一个行星。`if let somePlanet = Planet(rawValue: 11)` 语句创建了一个可选 `Planet`,如果可选 `Planet` 的值存在,就会赋值给 `somePlanet`。在这个例子中,无法检索到位置为 `11` 的行星,所以 `else` 分支被执行。 - -## 递归枚举 +## 递归枚举 {#recursive_enumerations} *递归枚举*是一种枚举类型,它有一个或多个枚举成员使用该枚举类型的实例作为关联值。使用递归枚举时,编译器会插入一个间接层。你可以在枚举成员前加上 `indirect` 来表示该成员可递归。 diff --git a/source/chapter2/09_Structures_And_Classes.md b/source/chapter2/09_Structures_And_Classes.md index a4a972f8..58e3054b 100755 --- a/source/chapter2/09_Structures_And_Classes.md +++ b/source/chapter2/09_Structures_And_Classes.md @@ -8,8 +8,7 @@ > > 通常一个*类*的实例被称为*对象*。然而相比其他语言,Swift 中结构体和类的功能更加相近,本章中所讨论的大部分功能都可以用在结构体或者类上。因此,这里会使用*实例*这个更通用的术语。 - -## 结构体和类对比 +## 结构体和类对比 {#comparing_structures_and_classes} Swift 中结构体和类有很多共同点。两者都可以: @@ -33,8 +32,7 @@ Swift 中结构体和类有很多共同点。两者都可以: 类支持的附加功能是以增加复杂性为代价的。作为一般准则,优先使用结构体,因为它们更容易理解,仅在适当或必要时才使用类。实际上,这意味着你的大多数自定义数据类型都会是结构体和枚举。更多详细的比较参见 [在结构和类之间进行选择](https://developer.apple.com/documentation/swift/choosing_between_structures_and_classes)。 - -### 类型定义的语法 +### 类型定义的语法 {#definition_syntax} 结构体和类有着相似的定义方式。你通过 `struct` 关键字引入结构体,通过 `class` 关键字引入类,并将它们的具体定义放在一对大括号中: @@ -70,8 +68,7 @@ class VideoMode { 在上面的示例还定义了一个名为 `VideoMode` 的类,用来描述视频显示器的某个特定视频模式。这个类包含了四个可变的存储属性。第一个, `resolution`,被初始化为一个新的 `Resolution` 结构体的实例,属性类型被推断为 `Resolution`。新 `VideoMode` 实例同时还会初始化其它三个属性,它们分别是初始值为 `false` 的 `interlaced`(意为“非隔行视频”),初始值为 `0.0` 的 `frameRate`,以及值为可选 `String` 的 `name`。因为 `name` 是一个可选类型,它会被自动赋予一个默认值 `nil`,意为“没有 `name` 值”。 - -### 结构体和类的实例 +### 结构体和类的实例 {#class_and_structure_instances} `Resolution` 结构体和 `VideoMode` 类的定义仅描述了什么是 `Resolution` 和 `VideoMode`。它们并没有描述一个特定的分辨率(resolution)或者视频模式(video mode)。为此,你需要创建结构体或者类的一个实例。 @@ -84,8 +81,7 @@ let someVideoMode = VideoMode() 结构体和类都使用构造器语法来创建新的实例。构造器语法的最简单形式是在结构体或者类的类型名称后跟随一对空括号,如 `Resolution()` 或 `VideoMode()`。通过这种方式所创建的类或者结构体实例,其属性均会被初始化为默认值。[构造过程](./14_Initialization.md) 章节会对类和结构体的初始化进行更详细的讨论。 - -### 属性访问 +### 属性访问 {#accessing_properties} 你可以通过使用*点语法*访问实例的属性。其语法规则是,实例名后面紧跟属性名,两者以点号(`.`)分隔,不带空格: @@ -111,8 +107,7 @@ print("The width of someVideoMode is now \(someVideoMode.resolution.width)") // 打印 "The width of someVideoMode is now 1280" ``` - -### 结构体类型的成员逐一构造器 +### 结构体类型的成员逐一构造器 {#memberwise_initializers_for_structure_types} 所有结构体都有一个自动生成的*成员逐一构造器*,用于初始化新结构体实例中成员的属性。新实例中各个属性的初始值可以通过属性的名称传递到成员逐一构造器之中: @@ -122,8 +117,7 @@ let vga = Resolution(width: 640, height: 480) 与结构体不同,类实例没有默认的成员逐一构造器。[构造过程](./14_Initialization.md) 章节会对构造器进行更详细的讨论。 - -## 结构体和枚举是值类型 +## 结构体和枚举是值类型 {#structures_and_enumerations_are_value_types} *值类型*是这样一种类型,当它被赋值给一个变量、常量或者被传递给一个函数的时候,其值会被*拷贝*。 @@ -191,8 +185,7 @@ print("The remembered direction is \(rememberedDirection)") 当 `rememberedDirection` 被赋予了 `currentDirection` 的值,实际上它被赋予的是值的一个拷贝。赋值过程结束后再修改 `currentDirection` 的值并不影响 `rememberedDirection` 所储存的原始值的拷贝。 - -## 类是引用类型 +## 类是引用类型 {#classes_are_reference_types} 与值类型不同,*引用类型*在被赋予到一个变量、常量或者被传递到一个函数时,其值不会被拷贝。因此,使用的是已存在实例的引用,而不是其拷贝。 @@ -230,8 +223,7 @@ print("The frameRate property of tenEighty is now \(tenEighty.frameRate)") 需要注意的是 `tenEighty` 和 `alsoTenEighty` 被声明为常量而不是变量。然而你依然可以改变 `tenEighty.frameRate` 和 `alsoTenEighty.frameRate`,这是因为 `tenEighty` 和 `alsoTenEighty` 这两个常量的值并未改变。它们并不“存储”这个 `VideoMode` 实例,而仅仅是对 `VideoMode` 实例的引用。所以,改变的是底层 `VideoMode` 实例的 `frameRate` 属性,而不是指向 `VideoMode` 的常量引用的值。 - -### 恒等运算符 +### 恒等运算符 {#identity_operators} 因为类是引用类型,所以多个常量和变量可能在幕后同时引用同一个类实例。(对于结构体和枚举来说,这并不成立。因为它们作为值类型,在被赋予到常量、变量或者传递到函数时,其值总是会被拷贝。) @@ -253,7 +245,6 @@ if tenEighty === alsoTenEighty { 当在定义你的自定义结构体和类的时候,你有义务来决定判定两个实例“相等”的标准。在章节 [等价操作符](./26_Advanced_Operators.md#equivalence_operators) 中将会详细介绍实现自定义 == 和 !== 运算符的流程。 - -### 指针 +### 指针 {#pointers} 如果你有 C,C++ 或者 Objective-C 语言的经验,那么你也许会知道这些语言使用*指针*来引用内存中的地址。Swift 中引用了某个引用类型实例的常量或变量,与 C 语言中的指针类似,不过它并不直接指向某个内存地址,也不要求你使用星号(`*`)来表明你在创建一个引用。相反,Swift 中引用的定义方式与其它的常量或变量的一样。如果需要直接与指针交互,你可以使用标准库提供的指针和缓冲区类型 —— 参见 [手动管理内存](https://developer.apple.com/documentation/swift/swift_standard_library/manual_memory_management)。 diff --git a/source/chapter2/10_Properties.md b/source/chapter2/10_Properties.md index 006d349b..8a4f9bfd 100755 --- a/source/chapter2/10_Properties.md +++ b/source/chapter2/10_Properties.md @@ -6,8 +6,7 @@ 另外,还可以定义属性观察器来监控属性值的变化,以此来触发自定义的操作。属性观察器可以添加到类本身定义的存储属性上,也可以添加到从父类继承的属性上。 - -## 存储属性 +## 存储属性 {#stored_properties} 简单来说,一个存储属性就是存储在特定类或结构体实例里的一个常量或变量。存储属性可以是*变量存储属性*(用关键字 `var` 定义),也可以是*常量存储属性*(用关键字 `let` 定义)。 @@ -28,8 +27,7 @@ rangeOfThreeItems.firstValue = 6 `FixedLengthRange` 的实例包含一个名为 `firstValue` 的变量存储属性和一个名为 `length` 的常量存储属性。在上面的例子中,`length` 在创建实例的时候被初始化,且之后无法修改它的值,因为它是一个常量存储属性。 - -### 常量结构体实例的存储属性 +### 常量结构体实例的存储属性 {#stored_properties_of_constant_structure_instances} 如果创建了一个结构体实例并将其赋值给一个常量,则无法修改该实例的任何属性,即使被声明为可变属性也不行: @@ -46,8 +44,7 @@ rangeOfFourItems.firstValue = 6 属于*引用类型*的类则不一样。把一个引用类型的实例赋给一个常量后,依然可以修改该实例的可变属性。 - -### 延时加载存储属性 +### 延时加载存储属性 {#lazy_stored_properties} *延时加载存储属性*是指当第一次被调用的时候才会计算其初始值的属性。在属性声明前使用 `lazy` 来标示一个延时加载存储属性。 @@ -99,15 +96,13 @@ print(manager.importer.fileName) > > 如果一个被标记为 `lazy` 的属性在没有初始化时就同时被多个线程访问,则无法保证该属性只会被初始化一次。 - -### 存储属性和实例变量 +### 存储属性和实例变量 {#stored_properties_and_instance_variables} 如果您有过 Objective-C 经验,应该知道 Objective-C 为类实例存储值和引用提供两种方法。除了属性之外,还可以使用实例变量作为一个备份存储将变量值赋值给属性。 Swift 编程语言中把这些理论统一用属性来实现。Swift 中的属性没有对应的实例变量,属性的备份存储也无法直接访问。这就避免了不同场景下访问方式的困扰,同时也将属性的定义简化成一个语句。属性的全部信息——包括命名、类型和内存管理特征——作为类型定义的一部分,都定义在一个地方。 - -## 计算属性 +## 计算属性 {#computed_properties} 除存储属性外,类、结构体和枚举可以定义*计算属性*。计算属性不直接存储值,而是提供一个 getter 和一个可选的 setter,来间接获取和设置其他属性或变量的值。 @@ -157,8 +152,7 @@ print("square.origin is now at (\(square.origin.x), \(square.origin.y))") Computed Properties sample - -### 简化 Setter 声明 +### 简化 Setter 声明 {#shorthand_setter_declaration} 如果计算属性的 setter 没有定义表示新值的参数名,则可以使用默认名称 `newValue`。下面是使用了简化 setter 声明的 `Rect` 结构体代码: @@ -180,8 +174,7 @@ struct AlternativeRect { } ``` - -### 只读计算属性 +### 只读计算属性 {#readonly_computed_properties} 只有 getter 没有 setter 的计算属性叫*只读计算属性*。只读计算属性总是返回一个值,可以通过点运算符访问,但不能设置新的值。 @@ -205,8 +198,7 @@ print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)") 这个例子定义了一个名为 `Cuboid` 的结构体,表示三维空间的立方体,包含 `width`、`height` 和 `depth` 属性。结构体还有一个名为 `volume` 的只读计算属性用来返回立方体的体积。为 `volume` 提供 setter 毫无意义,因为无法确定如何修改 `width`、`height` 和 `depth` 三者的值来匹配新的 `volume`。然而,`Cuboid` 提供一个只读计算属性来让外部用户直接获取体积是很有用的。 - -## 属性观察器 +## 属性观察器 {#property_observers} 属性观察器监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,即使新值和当前值相同的时候也不例外。 @@ -266,8 +258,7 @@ stepCounter.totalSteps = 896 > > 如果将带有观察器的属性通过 in-out 方式传入函数,`willSet` 和 `didSet` 也会调用。这是因为 in-out 参数采用了拷入拷出内存模式:即在函数内部使用的是参数的 copy,函数结束后,又对参数重新赋值。关于 in-out 参数详细的介绍,请参考[输入输出参数](../chapter3/05_Declarations.html#in-out_parameters) - -## 全局变量和局部变量 +## 全局变量和局部变量 {#global_and_local_variables} 计算属性和观察属性所描述的功能也可以用于*全局变量*和*局部变量*。全局变量是在函数、方法、闭包或任何类型之外定义的变量。局部变量是在函数、方法或闭包内部定义的变量。 @@ -281,8 +272,7 @@ stepCounter.totalSteps = 896 > > 局部范围的常量和变量从不延迟计算。 - -## 类型属性 +## 类型属性 {#type_properties} 实例属性属于一个特定类型的实例,每创建一个实例,实例都拥有属于自己的一套属性值,实例之间的属性相互独立。 @@ -298,8 +288,7 @@ stepCounter.totalSteps = 896 > > 存储型类型属性是延迟初始化的,它们只有在第一次被访问的时候才会被初始化。即使它们被多个线程同时访问,系统也保证只会对其进行一次初始化,并且不需要对其使用 `lazy` 修饰符。 - -### 类型属性语法 +### 类型属性语法 {#type_property_syntax} 在 C 或 Objective-C 中,与某个类型关联的静态常量和静态变量,是作为 *global*(全局)静态变量定义的。但是在 Swift 中,类型属性是作为类型定义的一部分写在类型最外层的花括号内,因此它的作用范围也就在类型支持的范围内。 @@ -333,8 +322,7 @@ class SomeClass { > > 例子中的计算型类型属性是只读的,但也可以定义可读可写的计算型类型属性,跟计算型实例属性的语法相同。 - -### 获取和设置类型属性的值 +### 获取和设置类型属性的值 {#querying_and_setting_type_properties} 跟实例属性一样,类型属性也是通过点运算符来访问。但是,类型属性是通过*类型*本身来访问,而不是通过实例。比如: diff --git a/source/chapter2/11_Methods.md b/source/chapter2/11_Methods.md index 130c704b..b6649252 100755 --- a/source/chapter2/11_Methods.md +++ b/source/chapter2/11_Methods.md @@ -4,8 +4,7 @@ 结构体和枚举能够定义方法是 Swift 与 C/Objective-C 的主要区别之一。在 Objective-C 中,类是唯一能定义方法的类型。但在 Swift 中,你不仅能选择是否要定义一个类/结构体/枚举,还能灵活地在你创建的类型(类/结构体/枚举)上定义方法。 - -## 实例方法(Instance Methods) +## 实例方法(Instance Methods) {#instance_methods} *实例方法*是属于某个特定类、结构体或者枚举类型实例的方法。实例方法提供访问和修改实例属性的方法或提供与实例目的相关的功能,并以此来支撑实例的功能。实例方法的语法与函数完全一致,详情参见[函数](./06_Functions.md)。 @@ -50,8 +49,7 @@ counter.reset() 函数参数可以同时有一个局部名称(在函数体内部使用)和一个外部名称(在调用函数时使用),详情参见[指定外部参数名](./06_Functions.md#specifying_external_parameter_names)。方法参数也一样,因为方法就是函数,只是这个函数与某个类型相关联了。 - -### self 属性 +### self 属性 {#the_self_property} 类型的每一个实例都有一个隐含属性叫做 `self`,`self` 完全等同于该实例本身。你可以在一个实例的实例方法中使用这个隐含的 `self` 属性来引用当前实例。 @@ -85,8 +83,7 @@ if somePoint.isToTheRightOf(x: 1.0) { 如果不使用 `self` 前缀,Swift会认为 `x` 的两个用法都引用了名为 `x` 的方法参数。 - -### 在实例方法中修改值类型 +### 在实例方法中修改值类型 {#modifying_value_types_from_within_instance_methods} 结构体和枚举是*值类型*。默认情况下,值类型的属性不能在它的实例方法中被修改。 @@ -118,8 +115,7 @@ fixedPoint.moveBy(x: 2.0, y: 3.0) // 这里将会报告一个错误 ``` - -### 在可变方法中给 self 赋值 +### 在可变方法中给 self 赋值 {#assigning_to_self_within_a_mutating_method} 可变方法能够赋给隐含属性 `self` 一个全新的实例。上面 `Point` 的例子可以用下面的方式改写: @@ -159,8 +155,7 @@ ovenLight.next() 上面的例子中定义了一个三态切换的枚举。每次调用 `next()` 方法时,开关在不同的电源状态(`off`, `low`, `high`)之间循环切换。 - -## 类型方法 +## 类型方法 {#type_methods} 实例方法是被某个类型的实例调用的方法。你也可以定义在类型本身上调用的方法,这种方法就叫做*类型方法*。在方法的 `func` 关键字之前加上关键字 `static`,来指定类型方法。类还可以用关键字 `class` 来允许子类重写父类的方法实现。 diff --git a/source/chapter2/12_Subscripts.md b/source/chapter2/12_Subscripts.md index 54e565bc..ba5df57c 100755 --- a/source/chapter2/12_Subscripts.md +++ b/source/chapter2/12_Subscripts.md @@ -4,8 +4,7 @@ 一个类型可以定义多个下标,通过不同索引类型进行重载。下标不限于一维,你可以定义具有多个入参的下标满足自定义类型的需求。 - -## 下标语法 +## 下标语法 {#subscript_syntax} 下标允许你通过在实例名称后面的方括号中传入一个或者多个索引值来对实例进行存取。语法类似于实例方法语法和计算型属性语法的混合。与定义实例方法类似,定义下标使用 `subscript` 关键字,指定一个或多个输入参数和返回类型;与实例方法不同的是,下标可以设定为读写或只读。这种行为由 getter 和 setter 实现,有点类似计算型属性: @@ -52,8 +51,7 @@ print("six times three is \(threeTimesTable[6])") > > `TimesTable` 例子基于一个固定的数学公式,对 `threeTimesTable[someIndex]` 进行赋值操作并不合适,因此下标定义为只读的。 - -## 下标用法 +## 下标用法 {#subscript_usage} 下标的确切含义取决于使用场景。下标通常作为访问集合,列表或序列中元素的快捷方式。你可以针对自己特定的类或结构体的功能来自由地以最恰当的方式实现下标。 @@ -72,8 +70,7 @@ numberOfLegs["bird"] = 2 > > Swift 的 `Dictionary` 类型的下标接受并返回可选类型的值。上例中的 `numberOfLegs` 字典通过下标返回的是一个 `Int?` 或者说“可选的 int”。`Dictionary` 类型之所以如此实现下标,是因为不是每个键都有个对应的值,同时这也提供了一种通过键删除对应值的方式,只需将键对应的值赋值为 `nil` 即可。 - -## 下标选项 +## 下标选项 {#subscript_options} 下标可以接受任意数量的入参,并且这些入参可以是任意类型。下标的返回值也可以是任意类型。下标可以使用变量参数和可变参数,但不能使用输入输出参数,也不能给参数设置默认值。 diff --git a/source/chapter2/13_Inheritance.md b/source/chapter2/13_Inheritance.md index deac0fd8..ecc78cfc 100755 --- a/source/chapter2/13_Inheritance.md +++ b/source/chapter2/13_Inheritance.md @@ -6,8 +6,7 @@ 可以为类中继承来的属性添加属性观察器,这样一来,当属性值改变时,类就会被通知到。可以为任何属性添加属性观察器,无论它原本被定义为存储型属性还是计算型属性。 - -## 定义一个基类 +## 定义一个基类 {#defining_a_base_class} 不继承于其它类的类,称之为*基类*。 @@ -46,8 +45,7 @@ print("Vehicle: \(someVehicle.description)") `Vehicle` 类定义了一个具有通用特性的车辆类,但实际上对于它本身来说没什么用处。为了让它变得更加有用,还需要进一步完善它,从而能够描述一个具体类型的车辆。 - -## 子类生成 +## 子类生成 {#subclassing} *子类生成*指的是在一个已有类的基础上创建一个新的类。子类继承超类的特性,并且可以进一步完善。你还可以为子类添加新的特性。 @@ -107,8 +105,7 @@ print("Tandem: \(tandem.description)") // 打印:“Tandem: traveling at 22.0 miles per hour” ``` - -## 重写 +## 重写 {#overriding} 子类可以为继承来的实例方法,类方法,实例属性,类属性,或下标提供自己定制的实现。我们把这种行为叫*重写*。 @@ -185,8 +182,7 @@ print("Car: \(car.description)") // 打印“Car: traveling at 25.0 miles per hour in gear 3” ``` - -#### 重写属性观察器 +#### 重写属性观察器 {#overriding_property_observers} 你可以通过重写属性为一个继承来的属性添加属性观察器。这样一来,无论被继承属性原本是如何实现的,当其属性值发生改变时,你就会被通知到。关于属性观察器的更多内容,请看[属性观察器](../chapter2/10_Properties.html#property_observers)。 @@ -216,8 +212,7 @@ print("AutomaticCar: \(automatic.description)") // 打印“AutomaticCar: traveling at 35.0 miles per hour in gear 4” ``` - -## 防止重写 +## 防止重写 {#preventing_overrides} 你可以通过把方法,属性或下标标记为 *`final`* 来防止它们被重写,只需要在声明关键字前加上 `final` 修饰符即可(例如:`final var`、`final func`、`final class func` 以及 `final subscript`)。 diff --git a/source/chapter2/14_Initialization.md b/source/chapter2/14_Initialization.md index 27cae5fc..7fa57ba3 100755 --- a/source/chapter2/14_Initialization.md +++ b/source/chapter2/14_Initialization.md @@ -6,9 +6,7 @@ 类的实例也可以通过实现*析构器*来执行它释放之前自定义的清理工作。想了解更多关于析构器的内容,请参考[析构过程](./15_Deinitialization.md)。 - - -## 存储属性的初始赋值 +## 存储属性的初始赋值 {#setting_initial_values_for_stored_properties} 类和结构体在创建实例时,必须为所有存储型属性设置合适的初始值。存储型属性的值不能处于一个未知的状态。 @@ -18,8 +16,7 @@ > > 当你为存储型属性分配默认值或者在构造器中为设置初始值时,它们的值是被直接设置的,不会触发任何属性观察者。 - -### 构造器 +### 构造器 {#initializers} 构造器在创建某个特定类型的新实例时被调用。它的最简形式类似于一个不带任何形参的实例方法,以关键字 `init` 命名: @@ -45,8 +42,7 @@ print("The default temperature is \(f.temperature)° Fahrenheit") 这个结构体定义了一个不带形参的构造器 `init`,并在里面将存储型属性 `temperature` 的值初始化为 `32.0`(华氏温度下水的冰点)。 - -### 默认属性值 +### 默认属性值 {#default_property_values} 如前所述,你可以在构造器中为存储型属性设置初始值。同样,你也可以在属性声明时为其设置默认值。 @@ -62,14 +58,11 @@ struct Fahrenheit { } ``` - - -## 自定义构造过程 +## 自定义构造过程 {#customizing_initialization} 你可以通过输入形参和可选属性类型来自定义构造过程,也可以在构造过程中分配常量属性。这些都将在后面章节中提到。 - -### 形参的构造过程 +### 形参的构造过程 {#initialization_parameters} 自定义构造过程时,可以在定义中提供*构造形参*,指定其值的类型和名字。构造形参的功能和语法跟函数和方法的形参相同。 @@ -94,8 +87,7 @@ let freezingPointOfWater = Celsius(fromKelvin: 273.15) 第一个构造器拥有一个构造形参,其实参标签为 `fromFahrenheit`,形参命名为 `fahrenheit`;第二个构造器也拥有一个构造形参,其实参标签为 `fromKelvin`,形参命名为 `kelvin`。这两个构造器都将单一的实参转换成摄氏温度值,并保存在属性 `temperatureInCelsius` 中。 - -### 形参命名和实参标签 +### 形参命名和实参标签 {#parameter_names_and_argument_labels} 跟函数和方法形参相同,构造形参可以同时使用在构造器里使用的形参命名和一个外部调用构造器时使用的实参标签。 @@ -135,8 +127,7 @@ let veryGreen = Color(0.0, 1.0, 0.0) // 报编译期错误-需要实参标签 ``` - -### 不带实参标签的构造器形参 +### 不带实参标签的构造器形参 {#initializer_parameters_without_external_names} 如果你不希望构造器的某个形参使用实参标签,可以使用下划线(`_`)来代替显式的实参标签来重写默认行为。 @@ -162,9 +153,7 @@ let bodyTemperature = Celsius(37.0) 构造器调用 `Celsius(37.0)` 意图明确,不需要实参标签。因此适合使用 `init(_ celsius: Double)` 这样的构造器,从而可以通过提供未命名的 `Double` 值来调用构造器。 - - -### 可选属性类型 +### 可选属性类型 {#optional_property_types} 如果你自定义的类型有一个逻辑上允许值为空的存储型属性——无论是因为它无法在初始化时赋值,还是因为它在之后某个时机可以赋值为空——都需要将它声明为 `可选类型`。可选类型的属性将自动初始化为 `nil`,表示这个属性是特意在构造过程设置为空。 @@ -190,8 +179,7 @@ cheeseQuestion.response = "Yes, I do like cheese." 调查问题的答案在询问前是无法确定的,因此我们将属性 `response` 声明为 `String?` 类型,或者说是 “可选类型 `String`“。当 `SurveyQuestion` 的实例初始化时,它将自动赋值为 `nil`,表明“暂时还没有字符“。 - -### 构造过程中常量属性的赋值 +### 构造过程中常量属性的赋值 {#assigning_constant_properties_during_initialization} 你可以在构造过程中的任意时间点给常量属性赋值,只要在构造过程结束时它设置成确定的值。一旦常量属性被赋值,它将永远不可更改。 @@ -218,8 +206,7 @@ beetsQuestion.ask() beetsQuestion.response = "I also like beets. (But not with cheese.)" ``` - -## 默认构造器 +## 默认构造器 {#default_initializers} 如果结构体或类为所有属性提供了默认值,又没有提供任何自定义的构造器,那么 Swift 会给这些结构体或类提供一个*默认构造器*。这个默认构造器将简单地创建一个所有属性值都设置为它们默认值的实例。 @@ -236,8 +223,7 @@ var item = ShoppingListItem() 由于 `ShoppingListItem` 类中的所有属性都有默认值,且它是没有父类的基类,它将自动获得一个将为所有属性设置默认值的并创建实例的默认构造器(由于 `name` 属性是可选 `String` 类型,它将接收一个默认 `nil` 的默认值,尽管代码中没有写出这个值)。上面例子中使用默认构造器创造了一个 `ShoppingListItem` 类的实例(使用 `ShoppingListItem()` 形式的构造器语法),并将其赋值给变量 `item`。 - -### 结构体的逐一成员构造器 +### 结构体的逐一成员构造器 {#memberwise_initializers_for_structure_types} 结构体如果没有定义任何自定义构造器,它们将自动获得一个*逐一成员构造器(memberwise initializer)*。不像默认构造器,即使存储型属性没有默认值,结构体也能会获得逐一成员构造器。 @@ -254,8 +240,7 @@ struct Size { let twoByTwo = Size(width: 2.0, height: 2.0) ``` - -## 值类型的构造器代理 +## 值类型的构造器代理 {#initializer_delegation_for_value_types} 构造器可以通过调用其它构造器来完成实例的部分构造过程。这一过程称为*构造器代理*,它能避免多个构造器间的代码重复。 @@ -331,15 +316,13 @@ let centerRect = Rect(center: Point(x: 4.0, y: 4.0), > > 如果你想用另外一种不需要自己定义 `init()` 和 `init(origin:size:)` 的方式来实现这个例子,请参考[扩展](./21_Extensions.md)。 - -## 类的继承和构造过程 +## 类的继承和构造过程 {#class_inheritance_and_initialization} 类里面的所有存储型属性——包括所有继承自父类的属性——都*必须*在构造过程中设置初始值。 Swift 为类类型提供了两种构造器来确保实例中所有存储型属性都能获得初始值,它们被称为指定构造器和便利构造器。 - -### 指定构造器和便利构造器 +### 指定构造器和便利构造器 {#designated_initializers_and_convenience_initializers} *指定构造器*是类中最主要的构造器。一个指定构造器将初始化类中提供的所有属性,并调用合适的父类构造器让构造过程沿着父类链继续往上进行。 @@ -351,8 +334,7 @@ Swift 为类类型提供了两种构造器来确保实例中所有存储型属 你应当只在必要的时候为类提供便利构造器,比方说某种情况下通过使用便利构造器来快捷调用某个指定构造器,能够节省更多开发时间并让类的构造过程更清晰明了。 - -### 指定构造器和便利构造器的语法 +### 指定构造器和便利构造器的语法 {#syntax_for_designated_and_convenience_initializers} 类的指定构造器的写法跟值类型简单构造器一样: @@ -370,8 +352,7 @@ convenience init(parameters) { } ``` - -### 类类型的构造器代理 +### 类类型的构造器代理 {#initializer_delegation_for_class_types} 为了简化指定构造器和便利构造器之间的调用关系,Swift 构造器之间的代理调用遵循以下三条规则: @@ -408,9 +389,7 @@ convenience init(parameters) { ![复杂构造器代理图](https://docs.swift.org/swift-book/_images/initializerDelegation02_2x.png) -i - -### 两段式构造过程 +### 两段式构造过程 {#two_phase_initialization} Swift 中类的构造过程包含两个阶段。第一个阶段,类中的每个存储型属性赋一个初始值。当每个存储型属性的初始值被赋值后,第二阶段开始,它给每个类一次机会,在新实例准备使用之前进一步自定义它们的存储型属性。 @@ -480,8 +459,7 @@ Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造 最终,一旦子类的指定构造器完成调用,最开始被调用的便利构造器可以执行更多的自定义操作。 - -### 构造器的继承和重写 +### 构造器的继承和重写 {#initializer_inheritance_and_overriding} 跟 Objective-C 中的子类不同,Swift 中的子类默认情况下不会继承父类的构造器。Swift 的这种机制可以防止一个父类的简单构造器被一个更精细的子类继承,而在用来创建子类时的新实例时没有完全或错误被初始化。 @@ -572,9 +550,7 @@ print("Hoverboard: \(hoverboard.description)") > > 子类可以在构造过程修改继承来的变量属性,但是不能修改继承来的常量属性。 - - -### 构造器的自动继承 +### 构造器的自动继承 {#automatic_initializer_inheritance} 如上所述,子类在默认情况下不会继承父类的构造器。但是如果满足特定条件,父类构造器是可以被自动继承的。事实上,这意味着对于许多常见场景你不必重写父类的构造器,并且可以在安全的情况下以最小的代价继承父类的构造器。 @@ -594,8 +570,7 @@ print("Hoverboard: \(hoverboard.description)") > > 子类可以将父类的指定构造器实现为便利构造器来满足规则 2。 - -### 指定构造器和便利构造器实践 +### 指定构造器和便利构造器实践 {#designated_and_convenience_initializers_in_action} 接下来的例子将在实践中展示指定构造器、便利构造器以及构造器的自动继承。这个例子定义了包含三个类 `Food`、`RecipeIngredient` 以及 `ShoppingListItem` 的层级结构,并将演示它们的构造器是如何相互作用的。 @@ -716,8 +691,7 @@ for item in breakfastList { 如上所述,例子中通过字面量方式创建了一个数组 `breakfastList`,它包含了三个 `ShoppingListItem` 实例,因此数组的类型也能被自动推导为 `[ShoppingListItem]`。在数组创建完之后,数组中第一个 `ShoppingListItem` 实例的名字从 `[Unnamed]` 更改为 `Orange juice`,并标记状态为已购买。打印数组中每个元素的描述显示了它们都已按照预期被赋值。 - -## 可失败构造器 +## 可失败构造器 {#failable_initializers} 有时,定义一个构造器可失败的类,结构体或者枚举是很有用的。这里所指的“失败” 指的是,如给构造器传入无效的形参,或缺少某种所需的外部资源,又或是不满足某种必要的条件等。 @@ -795,8 +769,7 @@ if anonymousCreature == nil { > > 检查空字符串的值(如 `""`,而不是 `"Giraffe"` )和检查值为 `nil` 的可选类型的字符串是两个完全不同的概念。上例中的空字符串(`""`)其实是一个有效的,非可选类型的字符串。这里我们之所以让 `Animal` 的可失败构造器构造失败,只是因为对于 `Animal` 这个类的 `species` 属性来说,它更适合有一个具体的值,而不是空字符串。 - -### 枚举类型的可失败构造器 +### 枚举类型的可失败构造器 {#failable_nitializers_for_enumerations} 你可以通过一个带一个或多个形参的可失败构造器来获取枚举类型中特定的枚举成员。如果提供的形参无法匹配任何枚举成员,则构造失败。 @@ -836,8 +809,7 @@ if unknownUnit == nil { // 打印“This is not a defined temperature unit, so initialization failed.” ``` - -### 带原始值的枚举类型的可失败构造器 +### 带原始值的枚举类型的可失败构造器 {#failable_initializers_for_enumerations_with_raw_values} 带原始值的枚举类型会自带一个可失败构造器 `init?(rawValue:)`,该可失败构造器有一个合适的原始值类型的 `rawValue` 形参,选择找到的相匹配的枚举成员,找不到则构造失败。 @@ -861,8 +833,7 @@ if unknownUnit == nil { // 打印“This is not a defined temperature unit, so initialization failed.” ``` - -### 构造失败的传递 +### 构造失败的传递 {#propagation_of_initialization_failure} 类、结构体、枚举的可失败构造器可以横向代理到它们自己其他的可失败构造器。类似的,子类的可失败构造器也能向上代理到父类的可失败构造器。 @@ -926,8 +897,7 @@ if let oneUnnamed = CartItem(name: "", quantity: 1) { // 打印“Unable to initialize one unnamed product” ``` - -### 重写一个可失败构造器 +### 重写一个可失败构造器 {#overriding_a_failable_initializer} 如同其它的构造器,你可以在子类中重写父类的可失败构造器。或者你也可以用子类的非可失败构造器重写一个父类的可失败构造器。这使你可以定义一个不会构造失败的子类,即使父类的构造器允许构造失败。 @@ -985,15 +955,13 @@ class UntitledDocument: Document { 在这个例子中,如果在调用父类的可失败构造器 `init?(name:)` 时传入的是空字符串,那么强制解包操作会引发运行时错误。不过,因为这里是通过字符串常量来调用它,构造器不会失败,所以并不会发生运行时错误。 - -### init! 可失败构造器 +### init! 可失败构造器 {#the_init!_failable_initializer} 通常来说我们通过在 `init` 关键字后添加问号的方式(`init?`)来定义一个可失败构造器,但你也可以通过在 `init` 后面添加感叹号的方式来定义一个可失败构造器(`init!`),该可失败构造器将会构建一个对应类型的隐式解包可选类型的对象。 你可以在 `init?` 中代理到 `init!`,反之亦然。你也可以用 `init?` 重写 `init!`,反之亦然。你还可以用 `init` 代理到 `init!`,不过,一旦 `init!` 构造失败,则会触发一个断言。 - -## 必要构造器 +## 必要构造器 {#required_initializers} 在类的构造器前添加 `required` 修饰符表明所有该类的子类都必须实现该构造器: @@ -1019,8 +987,7 @@ class SomeSubclass: SomeClass { > > 如果子类继承的构造器能满足必要构造器的要求,则无须在子类中显式提供必要构造器的实现。 - -## 通过闭包或函数设置属性的默认值 +## 通过闭包或函数设置属性的默认值 {#setting_a_default_property_value_with_a_closure_or_function} 如果某个存储型属性的默认值需要一些自定义或设置,你可以使用闭包或全局函数为其提供定制的默认值。每当某个属性所在类型的新实例被构造时,对应的闭包或函数会被调用,而它们的返回值会当做默认值赋值给这个属性。 diff --git a/source/chapter2/15_Deinitialization.md b/source/chapter2/15_Deinitialization.md index e17a00a9..8e93b2eb 100755 --- a/source/chapter2/15_Deinitialization.md +++ b/source/chapter2/15_Deinitialization.md @@ -2,8 +2,7 @@ *析构器*只适用于类类型,当一个类的实例被释放之前,析构器会被立即调用。析构器用关键字 `deinit` 来标示,类似于构造器要用 `init` 来标示。 - -## 析构过程原理 +## 析构过程原理 {#how_deinitialization_works} Swift 会自动释放不再需要的实例以释放资源。如[自动引用计数](./23_Automatic_Reference_Counting.md)章节中所讲述,Swift 通过*自动引用计数(ARC)* 处理实例的内存管理。通常当你的实例被释放时不需要手动地去清理。但是,当使用自己的资源时,你可能需要进行一些额外的清理。例如,如果创建了一个自定义的类来打开一个文件,并写入一些数据,你可能需要在类实例被释放之前手动去关闭该文件。 @@ -19,8 +18,7 @@ deinit { 因为直到实例的析构器被调用后,实例才会被释放,所以析构器可以访问实例的所有属性,并且可以根据那些属性可以修改它的行为(比如查找一个需要被关闭的文件)。 - -## 析构器实践 +## 析构器实践 {#deinitializers_in_action} 这是一个析构器实践的例子。这个例子描述了一个简单的游戏,这里定义了两种新类型,分别是 `Bank` 和 `Player`。`Bank` 类管理一种虚拟硬币,确保流通的硬币数量永远不可能超过 10,000。在游戏中有且只能有一个 `Bank` 存在,因此 `Bank` 用类来实现,并使用类型属性和类型方法来存储和管理其当前状态。 diff --git a/source/chapter2/16_Optional_Chaining.md b/source/chapter2/16_Optional_Chaining.md index 514f947a..abd4ac92 100755 --- a/source/chapter2/16_Optional_Chaining.md +++ b/source/chapter2/16_Optional_Chaining.md @@ -6,8 +6,7 @@ > > Swift 的可选链式调用和 Objective-C 中向 `nil` 发送消息有些相像,但是 Swift 的可选链式调用可以应用于任意类型,并且能检查调用是否成功。 - -## 使用可选链式调用代替强制展开 +## 使用可选链式调用代替强制展开 {#optional_chaining_as_an_alternative_to_forced_unwrapping} 通过在想调用的属性、方法,或下标的可选值后面放一个问号(`?`),可以定义一个可选链。这一点很像在可选值后面放一个叹号(`!`)来强制展开它的值。它们的主要区别在于当可选值为空时可选链式调用只会调用失败,然而强制展开将会触发运行时错误。 @@ -80,8 +79,7 @@ if let roomCount = john.residence?.numberOfRooms { // 打印“John's residence has 1 room(s).” ``` - -## 为可选链式调用定义模型类 +## 为可选链式调用定义模型类 {#defining_model_classes_for_optional_chaining} 通过使用可选链式调用可以调用多层属性、方法和下标。这样可以在复杂的模型中向下访问各种子属性,并且判断能否访问子属性的属性、方法和下标。 @@ -156,8 +154,7 @@ class Address { `Address` 类提供了 `buildingIdentifier()` 方法,返回值为 `String?`。 如果 `buildingName` 有值则返回 `buildingName`。或者,如果 `buildingNumber` 和 `street` 均有值,则返回两者拼接得到的字符串。否则,返回 `nil`。 - -## 通过可选链式调用访问属性 +## 通过可选链式调用访问属性 {#accessing_properties_through_optional_chaining} 正如[使用可选链式调用代替强制展开](#optional_chaining_as_an_alternative_to_forced_unwrapping)中所述,可以通过可选链式调用在一个可选值上访问它的属性,并判断访问是否成功。 @@ -203,8 +200,7 @@ john.residence?.address = createAddress() 没有任何打印消息,可以看出 `createAddress()` 函数并未被执行。 - -## 通过可选链式调用来调用方法 +## 通过可选链式调用来调用方法 {#calling_methods_through_optional_chaining} 可以通过可选链式调用来调用方法,并判断是否调用成功,即使这个方法没有返回值。 @@ -240,8 +236,7 @@ if (john.residence?.address = someAddress) != nil { // 打印“It was not possible to set the address.” ``` - -## 通过可选链式调用访问下标 +## 通过可选链式调用访问下标 {#accessing_subscripts_through_optional_chaining} 通过可选链式调用,我们可以在一个可选值上访问下标,并且判断下标调用是否成功。 @@ -286,8 +281,7 @@ if let firstRoomName = john.residence?[0].name { // 打印“The first room name is Living Room.” ``` - -### 访问可选类型的下标 +### 访问可选类型的下标 {#accessing_subscripts_of_optional_type} 如果下标返回可选类型值,比如 Swift 中 `Dictionary` 类型的键的下标,可以在下标的结尾括号后面放一个问号来在其可选返回值上进行可选链式调用: @@ -301,8 +295,7 @@ testScores["Brian"]?[0] = 72 上面的例子中定义了一个 `testScores` 数组,包含了两个键值对,分别把 `String` 类型的键映射到一个 `Int` 值的数组。这个例子用可选链式调用把 `"Dave"` 数组中第一个元素设为 `91`,把 `"Bev"` 数组的第一个元素 `+1`,然后尝试把 `"Brian"` 数组中的第一个元素设为 `72`。前两个调用成功,因为 `testScores` 字典中包含 `"Dave"` 和 `"Bev"` 这两个键。但是 `testScores` 字典中没有 `"Brian"` 这个键,所以第三个调用失败。 - -## 连接多层可选链式调用 +## 连接多层可选链式调用 {#linking_multiple_levels_of_chaining} 可以通过连接多个可选链式调用在更深的模型层级中访问属性、方法以及下标。然而,多层可选链式调用不会增加返回值的可选层级。 @@ -349,8 +342,7 @@ if let johnsStreet = john.residence?.address?.street { 在上面的例子中,因为 `john.residence` 包含一个有效的 `Address` 实例,所以对 `john.residence` 的 `address` 属性赋值将会成功。 - -## 在方法的可选返回值上进行可选链式调用 +## 在方法的可选返回值上进行可选链式调用 {#chaining_on_methods_with_optional_return_values} 上面的例子展示了如何在一个可选值上通过可选链式调用来获取它的属性值。我们还可以在一个可选值上通过可选链式调用来调用方法,并且可以根据需要继续在方法的可选返回值上进行可选链式调用。 diff --git a/source/chapter2/17_Error_Handling.md b/source/chapter2/17_Error_Handling.md index 00ba3f36..c4023892 100755 --- a/source/chapter2/17_Error_Handling.md +++ b/source/chapter2/17_Error_Handling.md @@ -10,8 +10,7 @@ > > Swift 中的错误处理涉及到错误处理模式,这会用到 Cocoa 和 Objective-C 中的 `NSError`。更多详情参见 [用 Swift 解决 Cocoa 错误](https://developer.apple.com/documentation/swift/cocoa_design_patterns/handling_cocoa_errors_in_swift)。 - -## 表示与抛出错误 +## 表示与抛出错误 {#representing_and_throwing_errors} 在 Swift 中,错误用遵循 `Error` 协议的类型的值来表示。这个空协议表明该类型可以用于错误处理。 @@ -31,8 +30,7 @@ enum VendingMachineError: Error { throw VendingMachineError.insufficientFunds(coinsNeeded: 5) ``` - -## 处理错误 +## 处理错误 {#handling_errors} 某个错误被抛出时,附近的某部分代码必须负责处理这个错误,例如纠正这个问题、尝试另外一种方式、或是向用户报告错误。 @@ -44,8 +42,7 @@ Swift 中有 `4` 种处理错误的方式。你可以把函数抛出的错误传 > > Swift 中的错误处理和其他语言中用 `try`,`catch` 和 `throw` 进行异常处理很像。和其他语言中(包括 Objective-C )的异常处理不同的是,Swift 中的错误处理并不涉及解除调用栈,这是一个计算代价高昂的过程。就此而言,`throw` 语句的性能特性是可以和 `return` 语句相媲美的。 - -### 用 throwing 函数传递错误 +### 用 throwing 函数传递错误 {#propagating_errors_using_throwing_functions} 为了表示一个函数、方法或构造器可以抛出错误,在函数声明的参数之后加上 `throws` 关键字。一个标有 `throws` 关键字的函数被称作 *throwing 函数*。如果这个函数指明了返回值类型,`throws` 关键词需要写在返回箭头(`->`)的前面。 @@ -238,8 +235,7 @@ func fetchData() -> Data? { let photo = try! loadImage(atPath: "./Resources/John Appleseed.jpg") ``` - -## 指定清理操作 +## 指定清理操作 {#specifying_cleanup_actions} 你可以使用 `defer` 语句在即将离开当前代码块时执行一系列语句。该语句让你能执行一些必要的清理工作,不管是以何种方式离开当前代码块的——无论是由于抛出错误而离开,或是由于诸如 `return`、`break` 的语句。例如,你可以用 `defer` 语句来确保文件描述符得以关闭,以及手动分配的内存得以释放。 diff --git a/source/chapter2/18_Type_Casting.md b/source/chapter2/18_Type_Casting.md index 4cb08914..874c3234 100644 --- a/source/chapter2/18_Type_Casting.md +++ b/source/chapter2/18_Type_Casting.md @@ -6,8 +6,7 @@ 你也可以用它来检查一个类型是否遵循了某个协议,就像在[检验协议遵循](./21_Protocols.md#checking_for_protocol_conformance)部分讲述的一样。 - -## 为类型转换定义类层次 +## 为类型转换定义类层次 {#defining_a_class_hierarchy_for_type_casting} 你可以将类型转换用在类和子类的层次结构上,检查特定类实例的类型并且转换这个类实例的类型成为这个层次结构中的其他类型。下面的三个代码段定义了一个类层次和一个包含了这些类实例的数组,作为类型转换的例子。 @@ -57,8 +56,7 @@ let library = [ 在幕后 `library` 里存储的媒体项依然是 `Movie` 和 `Song` 类型的。但是,若你迭代它,依次取出的实例会是 `MediaItem` 类型的,而不是 `Movie` 和 `Song` 类型。为了让它们作为原本的类型工作,你需要检查它们的类型或者向下转换它们到其它类型,就像下面描述的一样。 - -## 检查类型 +## 检查类型 {#checking_type} 用*类型检查操作符*(`is`)来检查一个实例是否属于特定子类型。若实例属于那个子类型,类型检查操作符返回 `true`,否则返回 `false`。 @@ -86,8 +84,7 @@ print("Media library contains \(movieCount) movies and \(songCount) songs") 若当前 `MediaItem` 是一个 `Movie` 类型的实例,`item is Movie` 返回 `true`,否则返回 `false`。同样的,`item is Song` 检查 `item` 是否为 `Song` 类型的实例。在循环结束后,`movieCount` 和 `songCount` 的值就是被找到的属于各自类型的实例的数量。 - -## 向下转型 +## 向下转型 {#downcasting} 某类型的一个常量或变量可能在幕后实际上属于一个子类。当确定是这种情况时,你可以尝试用*类型转换操作符*(`as?` 或 `as!`)向下转到它的子类型。 @@ -132,8 +129,7 @@ for item in library { > > 转换没有真的改变实例或它的值。根本的实例保持不变;只是简单地把它作为它被转换成的类型来使用。 - -## `Any` 和 `AnyObject` 的类型转换 +## `Any` 和 `AnyObject` 的类型转换 {#type_casting_for_any_and_anyobject} Swift 为不确定类型提供了两种特殊的类型别名: diff --git a/source/chapter2/19_Nested_Types.md b/source/chapter2/19_Nested_Types.md index 812ab678..78bea31a 100755 --- a/source/chapter2/19_Nested_Types.md +++ b/source/chapter2/19_Nested_Types.md @@ -4,8 +4,7 @@ 要在一个类型中嵌套另一个类型,将嵌套类型的定义写在其外部类型的 `{}` 内,而且可以根据需要定义多级嵌套。 - -## 嵌套类型实践 +## 嵌套类型实践 {#nested_types_in_action} 下面这个例子定义了一个结构体 `BlackjackCard`(二十一点),用来模拟 `BlackjackCard` 中的扑克牌点数。`BlackjackCard` 结构体包含两个嵌套定义的枚举类型 `Suit` 和 `Rank`。 @@ -74,8 +73,7 @@ print("theAceOfSpades: \(theAceOfSpades.description)") 尽管 `Rank` 和 `Suit` 嵌套在 `BlackjackCard` 中,但它们的类型仍可从上下文中推断出来,所以在初始化实例时能够单独通过成员名称(`.ace` 和 `.spades`)引用枚举实例。在上面的例子中,`description` 属性正确地反映了黑桃 A 牌具有 `1` 和 `11` 两个值。 - -## 引用嵌套类型 +## 引用嵌套类型 {#referring_to_nested_types} 在外部引用嵌套类型时,在嵌套类型的类型名前加上其外部类型的类型名作为前缀: diff --git a/source/chapter2/21_Protocols.md b/source/chapter2/21_Protocols.md index 8646fb14..c137d6b6 100644 --- a/source/chapter2/21_Protocols.md +++ b/source/chapter2/21_Protocols.md @@ -4,8 +4,7 @@ 除了遵循协议的类型必须实现的要求外,还可以对协议进行扩展,通过扩展来实现一部分要求或者实现一些附加功能,这样遵循协议的类型就能够使用这些功能。 - -## 协议语法 +## 协议语法 {#protocol_syntax} 协议的定义方式与类、结构体和枚举的定义非常相似: @@ -31,8 +30,7 @@ class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol { } ``` - -## 属性要求 +## 属性要求 {#property_requirements} 协议可以要求遵循协议的类型提供特定名称和类型的实例属性或类型属性。协议不指定属性是存储属性还是计算属性,它只指定属性的名称和类型。此外,协议还指定属性是*可读*的还是*可读可写的*。 @@ -99,9 +97,7 @@ var ncc1701 = Starship(name: "Enterprise", prefix: "USS") `Starship` 类只能把 `fullName` 作为只读的计算属性来实现。每一个 `Starship` 类的实例都有一个名为 `name` 的非可选属性和一个名为 `prefix` 的可选属性。 当 `prefix` 存在时,计算属性 `fullName` 会将 `prefix` 插入到 `name` 之前,从而得到一个带有 `prefix` 的 `fullName`。 - - -## 方法要求 +## 方法要求 {#method_requirements} 协议可以要求遵循协议的类型实现某些指定的实例方法或类方法。这些方法作为协议的一部分,像普通方法一样放在协议的定义中,但是不需要大括号和方法体。可以在协议中定义具有可变参数的方法,和普通方法的定义方式相同。但是,不支持为协议中的方法提供默认参数。 @@ -145,8 +141,7 @@ print("And another one: \(generator.random())") // 打印 “And another one: 0.729023776863283” ``` - -## 异变方法要求 +## 异变方法要求 {#mutating_method_requirements} 有时需要在方法中改变(或*异变*)方法所属的实例。例如,在值类型(即结构体和枚举)的实例方法中,将 `mutating` 关键字作为方法的前缀,写在 `func` 关键字之前,表示可以在该方法中修改它所属的实例以及实例的任意属性的值。这一过程在 [在实例方法中修改值类型](./11_Methods.md#modifying_value_types_from_within_instance_methods) 章节中有详细描述。 @@ -187,8 +182,7 @@ lightSwitch.toggle() // lightSwitch 现在的值为 .on ``` - -## 构造器要求 +## 构造器要求 {#initializer_requirements} 协议可以要求遵循协议的类型实现指定的构造器。你可以像编写普通构造器那样,在协议的定义里写下构造器的声明,但不需要写花括号和构造器的实体: @@ -246,8 +240,7 @@ class SomeSubClass: SomeSuperClass, SomeProtocol { 遵循协议的类型可以通过可失败构造器(`init?`)或非可失败构造器(`init`)来满足协议中定义的可失败构造器要求。协议中定义的非可失败构造器要求可以通过非可失败构造器(`init`)或隐式解包可失败构造器(`init!`)来满足。 - -## 协议作为类型 +## 协议作为类型 {#protocols_as_types} 尽管协议本身并未实现任何功能,但是协议可以被当做一个功能完备的类型来使用。 @@ -299,8 +292,7 @@ for _ in 1...5 { // Random dice roll is 4 ``` - -## 委托 +## 委托 {#delegation} *委托*是一种设计模式,它允许类或结构体将一些需要它们负责的功能委托给其他类型的实例。委托模式的实现很简单:定义协议来封装那些需要被委托的功能,这样就能确保遵循协议的类型能提供这些功能。委托模式可以用来响应特定的动作,或者接收外部数据源提供的数据,而无需关心外部数据源的类型。 @@ -413,8 +405,7 @@ game.play() // The game lasted for 4 turns ``` - -## 在扩展里添加协议遵循 +## 在扩展里添加协议遵循 {#adding_protocol_conformance_with_an_extension} 即便无法修改源代码,依然可以通过扩展令已有类型遵循并符合协议。扩展可以为已有类型添加属性、方法、下标以及构造器,因此可以符合协议中的相应要求。详情请在 [扩展](./20_Extensions.md) 章节中查看。 @@ -462,8 +453,7 @@ print(game.textualDescription) // 打印 “A game of Snakes and Ladders with 25 squares” ``` - -## 有条件地遵循协议 +## 有条件地遵循协议 {#Conditionally_Conforming_to_a_Protocol} 泛型类型可能只在某些情况下满足一个协议的要求,比如当类型的泛型形式参数遵循对应协议时。你可以通过在扩展类型时列出限制让泛型类型有条件地遵循某协议。在你采纳协议的名字后面写泛型 `where` 分句。更多关于泛型 `where` 分句,见 [泛型 Where 分句](./22_Generics.md##where_clauses)。 @@ -481,8 +471,7 @@ print(myDice.textualDescription) // 打印 "[A 6-sided dice, A 12-sided dice]" ``` - -## 在扩展里声明采纳协议 +## 在扩展里声明采纳协议 {#declaring_protocol_adoption_with_an_extension} 当一个类型已经符合了某个协议中的所有要求,却还没有声明采纳该协议时,可以通过空的扩展来让它采纳该协议: @@ -509,8 +498,7 @@ print(somethingTextRepresentable.textualDescription) > > 即使满足了协议的所有要求,类型也不会自动遵循协议,必须显式地遵循协议。 - -## 协议类型的集合 +## 协议类型的集合 {#collections_of_protocol_types} 协议类型可以在数组或者字典这样的集合中使用,在 [协议类型](./21_Protocols.md##protocols_as_types) 提到了这样的用法。下面的例子创建了一个元素类型为 `TextRepresentable` 的数组: @@ -531,8 +519,7 @@ for thing in things { 注意 `thing` 常量是 `TextRepresentable` 类型而不是 `Dice`,`DiceGame`,`Hamster` 等类型,即使实例在幕后确实是这些类型中的一种。由于 `thing` 是 `TextRepresentable` 类型,任何 `TextRepresentable` 的实例都有一个 `textualDescription` 属性,所以在每次循环中可以安全地访问 `thing.textualDescription`。 - -## 协议的继承 +## 协议的继承 {#protocol_inheritance} 协议能够*继承*一个或多个其他协议,可以在继承的协议的基础上增加新的要求。协议的继承语法与类的继承相似,多个被继承的协议间用逗号分隔: @@ -587,9 +574,7 @@ print(game.prettyTextualDescription) // ○ ○ ▲ ○ ○ ▲ ○ ○ ▲ ▲ ○ ○ ○ ▼ ○ ○ ○ ○ ▼ ○ ○ ▼ ○ ▼ ○ ``` - - -## 类专属的协议 +## 类专属的协议 {#class_only_protocol} 你通过添加 `AnyObject` 关键字到协议的继承列表,就可以限制协议只能被类类型采纳(以及非结构体或者非枚举的类型)。 @@ -605,8 +590,7 @@ protocol SomeClassOnlyProtocol: AnyObject, SomeInheritedProtocol { > > 当协议定义的要求需要遵循协议的类型必须是引用语义而非值语义时,应该采用类类型专属协议。关于引用语义和值语义的更多内容,请查看 [结构体和枚举是值类型](./09_Classes_and_Structures.md#structures_and_enumerations_are_value_types) 和 [类是引用类型](./09_Classes_and_Structures.md#classes_are_reference_types)。 - -## 协议合成 +## 协议合成 {#protocol_composition} 要求一个类型同时遵循多个协议是很有用的。你可以使用*协议组合*来复合多个协议到一个要求里。协议组合行为就和你定义的临时局部协议一样拥有构成中所有协议的需求。协议组合不定义任何新的协议类型。 @@ -670,8 +654,7 @@ beginConcert(in: seattle) 将 birthdayPerson 传入 `beginConcert(in:)` 函数是不合法的,因为 Person 不是 Location 的子类。同理,如果你新建一个类继承于 Location,但是没有遵循 Named 协议,而用这个类的实例去调用 `beginConcert(in:)` 函数也是非法的。 - -## 检查协议一致性 +## 检查协议一致性 {#checking_for_protocol_conformance} 你可以使用[类型转换](./18_Type_Casting.md)中描述的 `is` 和 `as` 操作符来检查协议一致性,即是否符合某协议,并且可以转换到指定的协议类型。检查和转换协议的语法与检查和转换类型是完全一样的: @@ -744,8 +727,7 @@ for object in objects { `objects` 数组中的元素的类型并不会因为强转而丢失类型信息,它们仍然是 `Circle`,`Country`,`Animal` 类型。然而,当它们被赋值给 `objectWithArea` 常量时,只被视为 `HasArea` 类型,因此只有 `area` 属性能够被访问。 - -## 可选的协议要求 +## 可选的协议要求 {#optional_protocol_requirements} 协议可以定义*可选要求*,遵循协议的类型可以选择是否实现这些要求。在协议中使用 `optional` 关键字作为前缀来定义可选要求。可选要求用在你需要和 Objective-C 打交道的代码中。协议和可选要求都必须带上 `@objc` 属性。标记 `@objc` 特性的协议只能被继承自 Objective-C 类的类或者 `@objc` 类遵循,其他类以及结构体和枚举均不能遵循这种协议。 @@ -855,8 +837,7 @@ for _ in 1...5 { // 0 ``` - -## 协议扩展 +## 协议扩展 {#protocol_extensions} 协议可以通过扩展来为遵循协议的类型提供属性、方法以及下标的实现。通过这种方式,你可以基于协议本身来实现这些功能,而无需在每个遵循协议的类型中都重复同样的实现,也无需使用全局函数。 @@ -880,8 +861,7 @@ print("And here's a random Boolean: \(generator.randomBool())") // 打印 “And here's a random Boolean: true” ``` - -### 提供默认实现 +### 提供默认实现 {#providing_default_implementations} 可以通过协议扩展来为协议要求的属性、方法以及下标提供默认的实现。如果遵循协议的类型为这些要求提供了自己的实现,那么这些自定义实现将会替代扩展中的默认实现被使用。 @@ -899,8 +879,7 @@ extension PrettyTextRepresentable { } ``` - -### 为协议扩展添加限制条件 +### 为协议扩展添加限制条件 {#adding_constraints_to_protocol_extensions} 在扩展协议的时候,可以指定一些限制条件,只有遵循协议的类型满足这些限制条件时,才能获得协议扩展提供的默认实现。这些限制条件写在协议名之后,使用 `where` 子句来描述,正如[泛型 Where 子句](./22_Generics.md#where_clauses)中所描述的。 diff --git a/source/chapter2/22_Generics.md b/source/chapter2/22_Generics.md index bc70d0da..727adf5c 100644 --- a/source/chapter2/22_Generics.md +++ b/source/chapter2/22_Generics.md @@ -4,8 +4,7 @@ 泛型是 Swift 最强大的特性之一,很多 Swift 标准库是基于泛型代码构建的。实际上,即使你没有意识到,你也一直在*语言指南*中使用泛型。例如,Swift 的 `Array` 和 `Dictionary` 都是泛型集合。你可以创建一个 `Int` 类型数组,也可创建一个 `String` 类型数组,甚至可以是任意其他 Swift 类型的数组。同样,你也可以创建一个存储任意指定类型的字典,并对该类型没有限制。 - -## 泛型解决的问题 +## 泛型解决的问题 {#the_problem_that_generics_solve} 下面是一个标准的非泛型函数 `swapTwoInts(_:_:)`,用来交换两个 `Int` 值: @@ -53,8 +52,7 @@ func swapTwoDoubles(_ a: inout Double, _ b: inout Double) { > > 在上面三个函数中,`a` 和 `b` 类型必须相同。如果 `a` 和 `b` 类型不同,那它们俩就不能互换值。Swift 是类型安全的语言,所以它不允许一个 `String` 类型的变量和一个 `Double` 类型的变量互换值。试图这样做将导致编译错误。 - -## 泛型函数 +## 泛型函数 {#generic_functions} 泛型函数可适用于任意类型,下面是函数 `swapTwoInts(_:_:)` 的泛型版本,命名为 `swapTwoValues(_:_:)`: @@ -97,8 +95,7 @@ swapTwoValues(&someString, &anotherString) > > 上面定义的 `swapTwoValues(_:_:)` 函数是受 `swap(_:_:)` 函数启发而实现的。后者存在于 Swift 标准库,你可以在你的应用程序中使用它。如果你在代码中需要类似 `swapTwoValues(_:_:)` 函数的功能,你可以使用已存在的 `swap(_:_:)` 函数。 - -## 类型参数 +## 类型参数 {#type_parameters} 上面 `swapTwoValues(_:_:)` 例子中,占位类型 `T` 是一个类型参数的例子,类型参数指定并命名一个占位类型,并且紧随在函数名后面,使用一对尖括号括起来(例如 ``)。 @@ -106,8 +103,7 @@ swapTwoValues(&someString, &anotherString) 你可提供多个类型参数,将它们都写在尖括号中,用逗号分开。 - -## 命名类型参数 +## 命名类型参数 {#naming_type_parameters} 大多情况下,类型参数具有描述下的名称,例如字典 `Dictionary` 中的 `Key` 和 `Value` 及数组 `Array` 中的 `Element`,这能告诉阅读代码的人这些参数类型与泛型类型或函数之间的关系。然而,当它们之间没有有意义的关系时,通常使用单个字符来表示,例如 `T`、`U`、`V`,例如上面演示函数 `swapTwoValues(_:_:)` 中的 `T`。 @@ -115,8 +111,7 @@ swapTwoValues(&someString, &anotherString) > > 请始终使用大写字母开头的驼峰命名法(例如 `T` 和 `MyTypeParameter`)来为类型参数命名,以表明它们是占位类型,而不是一个值。 - -## 泛型类型 +## 泛型类型 {#generic_types} 除了泛型函数,Swift 还允许自定义*泛型类型*。这些自定义类、结构体和枚举可以适用于*任意类型*,类似于 `Array` 和 `Dictionary`。 @@ -204,8 +199,7 @@ let fromTheTop = stackOfStrings.pop() ![](https://docs.swift.org/swift-book/_images/stackPoppedOneString_2x.png) - -## 泛型扩展 +## 泛型扩展 {#extending_a_generic_type} 当对泛型类型进行扩展时,你并不需要提供类型参数列表作为定义的一部分。原始类型定义中声明的类型参数列表在扩展中可以直接使用,并且这些来自原始类型中的参数名称会被用作原始定义中类型参数的引用。 @@ -232,8 +226,7 @@ if let topItem = stackOfStrings.topItem { // 打印“The top item on the stack is tres.” ``` - -## 类型约束 +## 类型约束 {#type_constraints} `swapTwoValues(_:_:)` 函数和 `Stack` 适用于任意类型。不过,如果能对泛型函数或泛型类型中添加特定的*类型约束*,这将在某些情况下非常有用。类型约束指定类型参数必须继承自指定类、遵循特定的协议或协议组合。 @@ -243,8 +236,7 @@ if let topItem = stackOfStrings.topItem { 当自定义泛型类型时,你可以定义你自己的类型约束,这些约束将提供更为强大的泛型编程能力。像 `可哈希(hashable)` 这种抽象概念根据它们的概念特征来描述类型,而不是它们的具体类型。 -> -### 类型约束语法 +### 类型约束语法 {#type_constraint_syntax} 在一个类型参数名后面放置一个类名或者协议名,并用冒号进行分隔,来定义类型约束。下面将展示泛型函数约束的基本语法(与泛型类型的语法相同): @@ -256,8 +248,7 @@ func someFunction(someT: T, someU: U) { 上面这个函数有两个类型参数。第一个类型参数 `T` 必须是 `SomeClass` 子类;第二个类型参数 `U` 必须符合 `SomeProtocol` 协议。 - -### 类型约束实践 +### 类型约束实践 {#type_constraints_in_action} 这里有个名为 `findIndex(ofString:in:)` 的非泛型函数,该函数的功能是在一个 `String` 数组中查找给定 `String` 值的索引。若查找到匹配的字符串,`findIndex(ofString:in:)` 函数返回该字符串在数组中的索引值,否则返回 `nil`: @@ -325,13 +316,11 @@ let stringIndex = findIndex(of: "Andrea", in: ["Mike", "Malcolm", "Andrea"]) // stringIndex 类型为 Int?,其值为 2 ``` - -## 关联类型 +## 关联类型 {#associated_types} 定义一个协议时,声明一个或多个关联类型作为协议定义的一部分将会非常有用。关联类型为协议中的某个类型提供了一个占位符名称,其代表的实际类型在协议被遵循时才会被指定。关联类型通过 `associatedtype` 关键字来指定。 - -### 关联类型实践 +### 关联类型实践 {#associated_types_in_action} 下面例子定义了一个 `Container` 协议,该协议定义了一个关联类型 `Item`: @@ -417,8 +406,7 @@ struct Stack: Container { 这一次,占位类型参数 `Element` 被用作 `append(_:)` 方法的 `item` 参数和下标的返回类型。Swift 可以据此推断出 `Element` 的类型即是 `Item` 的类型。 - -### 扩展现有类型来指定关联类型 +### 扩展现有类型来指定关联类型 {#extending_an_existing_type_to_specify_an_associated_type} [在扩展添加协议一致性](./21_Protocols.md#adding_protocol_conformance_with_an_extension)中描述了如何利用扩展让一个已存在的类型符合一个协议,这包括使用了关联类型协议。 @@ -430,8 +418,7 @@ extension Array: Container {} `Array` 的 `append(_:)` 方法和下标确保了 Swift 可以推断出 `Item` 具体类型。定义了这个扩展后,你可以将任意 `Array` 当作 Container 来使用。 - -### 给关联类型添加约束 +### 给关联类型添加约束 {#adding_constraints_to_an_associated_type} 你可以在协议里给关联类型添加约束来要求遵循的类型满足约束。例如,下面的代码定义了 `Container` 协议, 要求关联类型 `Item` 必须遵循 `Equatable` 协议: @@ -446,8 +433,7 @@ protocol Container { 要遵守 `Container` 协议,`Item` 类型也必须遵守 `Equatable` 协议。 - -### 在关联类型约束里使用协议 +### 在关联类型约束里使用协议 {#using_a_protocol_in_its_associated_type’s_constraints} 协议可以作为它自身的要求出现。例如,有一个协议细化了 `Container` 协议,添加了一个` suffix(_:)` 方法。`suffix(_:)` 方法返回容器中从后往前给定数量的元素,并把它们存储在一个 `Suffix` 类型的实例里。 @@ -496,8 +482,7 @@ extension IntStack: SuffixableContainer { } ``` - -## 泛型 Where 语句 +## 泛型 Where 语句 {#where_clauses} [类型约束](#type_constraints)让你能够为泛型函数、下标、类型的类型参数定义一些强制要求。 @@ -577,8 +562,7 @@ if allItemsMatch(stackOfStrings, arrayOfStrings) { 上面的例子创建 `Stack` 实例来存储 `String` 值,然后将三个字符串压栈。这个例子还通过数组字面量创建了一个 `Array` 实例,数组中包含同栈中一样的三个字符串。即使栈和数组是不同的类型,但它们都遵从 `Container` 协议,而且它们都包含相同类型的值。因此你可以用这两个容器作为参数来调用 `allItemsMatch(_:_:)` 函数。在上面的例子中,`allItemsMatch(_:_:)` 函数正确地显示了这两个容器中的所有元素都是相互匹配的。 - -## 具有泛型 Where 子句的扩展 +## 具有泛型 Where 子句的扩展 {#extensions_with_a_generic_where_clause} 你也可以使用泛型 `where` 子句作为扩展的一部分。基于以前的例子,下面的示例扩展了泛型 `Stack` 结构体,添加一个 `isTop(_:)` 方法。 @@ -657,8 +641,7 @@ print([1260.0, 1200.0, 98.6, 37.0].average()) 就像可以在其他地方写泛型 `where` 子句一样,你可以在一个泛型 `where` 子句中包含多个条件作为扩展的一部分。用逗号分隔列表中的每个条件。 - -## 具有泛型 Where 子句的关联类型 +## 具有泛型 Where 子句的关联类型 {#associated_types_with_a_generic_where_clause} 你可以在关联类型后面加上具有泛型 `where` 的字句。例如,建立一个包含迭代器(`Iterator`)的容器,就像是标准库中使用的 `Sequence` 协议那样。你应该这么写: @@ -682,8 +665,7 @@ protocol Container { protocol ComparableContainer: Container where Item: Comparable { } ``` - -## 泛型下标 +## 泛型下标 {#generic_subscripts} 下标可以是泛型,它们能够包含泛型 `where` 子句。你可以在 `subscript` 后用尖括号来写占位符类型,你还可以在下标代码块花括号前写 `where` 子句。例如: diff --git a/source/chapter2/23_Automatic_Reference_Counting.md b/source/chapter2/23_Automatic_Reference_Counting.md index f9f4ed45..6511f575 100755 --- a/source/chapter2/23_Automatic_Reference_Counting.md +++ b/source/chapter2/23_Automatic_Reference_Counting.md @@ -8,9 +8,7 @@ Swift 使用*自动引用计数(ARC)*机制来跟踪和管理你的应用程 > > 引用计数仅仅应用于类的实例。结构体和枚举类型是值类型,不是引用类型,也不是通过引用的方式存储和传递。 - - -## 自动引用计数的工作机制 +## 自动引用计数的工作机制 {#how_arc_works} 当你每次创建一个类的新的实例的时候,ARC 会分配一块内存来储存该实例信息。内存中会包含实例的类型信息,以及这个实例所有相关的存储型属性的值。 @@ -22,8 +20,7 @@ Swift 使用*自动引用计数(ARC)*机制来跟踪和管理你的应用程 为了使上述成为可能,无论你将实例赋值给属性、常量或变量,它们都会创建此实例的强引用。之所以称之为“强”引用,是因为它会将实例牢牢地保持住,只要强引用还在,实例是不允许被销毁的。 - -## 自动引用计数实践 +## 自动引用计数实践 {#arc_in_action} 下面的例子展示了自动引用计数的工作机制。例子以一个简单的 `Person` 类开始,并定义了一个叫 `name` 的常量属性: @@ -84,8 +81,7 @@ reference3 = nil // 打印“John Appleseed is being deinitialized” ``` - -## 类实例之间的循环强引用 +## 类实例之间的循环强引用 {#strong_reference_cycles_between_class_instances} 在上面的例子中,ARC 会跟踪你所新创建的 `Person` 实例的引用数量,并且会在 `Person` 实例不再被需要时销毁它。 @@ -161,9 +157,7 @@ unit4A = nil `Person` 和 `Apartment` 实例之间的强引用关系保留了下来并且不会被断开。 - - -## 解决实例之间的循环强引用 +## 解决实例之间的循环强引用 {#resolving_strong_reference_cycles_between_class_instances} Swift 提供了两种办法用来解决你在使用类的属性时所遇到的循环强引用问题:弱引用(weak reference)和无主引用(unowned reference)。 @@ -171,8 +165,7 @@ Swift 提供了两种办法用来解决你在使用类的属性时所遇到的 当其他的实例有更短的生命周期时,使用弱引用,也就是说,当其他实例析构在先时。在上面公寓的例子中,很显然一个公寓在它的生命周期内会在某个时间段没有它的主人,所以一个弱引用就加在公寓类里面,避免循环引用。相比之下,当其他实例有相同的或者更长生命周期时,请使用无主引用。 - -### 弱引用 +### 弱引用 {#weak_references} *弱引用*不会对其引用的实例保持强引用,因而不会阻止 ARC 销毁被引用的实例。这个特性阻止了引用变为循环强引用。声明属性或者变量时,在前面加上 `weak` 关键字表明这是一个弱引用。 @@ -245,8 +238,7 @@ unit4A = nil > > 在使用垃圾收集的系统里,弱指针有时用来实现简单的缓冲机制,因为没有强引用的对象只会在内存压力触发垃圾收集时才被销毁。但是在 ARC 中,一旦值的最后一个强引用被移除,就会被立即销毁,这导致弱引用并不适合上面的用途。 - -### 无主引用 +### 无主引用 {#unowned_references} 和弱引用类似,*无主引用*不会牢牢保持住引用的实例。和弱引用不同的是,无主引用在其他实例有相同或者更长的生命周期时使用。你可以在声明属性或者变量时,在前面加上关键字 `unowned` 表示这是一个无主引用。 @@ -329,8 +321,7 @@ john = nil > 上面的例子展示了如何使用安全的无主引用。对于需要禁用运行时的安全检查的情况(例如,出于性能方面的原因),Swift 还提供了不安全的无主引用。与所有不安全的操作一样,你需要负责检查代码以确保其安全性。 > 你可以通过 `unowned(unsafe)` 来声明不安全无主引用。如果你试图在实例被销毁后,访问该实例的不安全无主引用,你的程序会尝试访问该实例之前所在的内存地址,这是一个不安全的操作。 - -### 无主引用和隐式解包可选值属性 +### 无主引用和隐式解包可选值属性 {#unowned_references_and_implicitly_unwrapped_optional_properties} 上面弱引用和无主引用的例子涵盖了两种常用的需要打破循环强引用的场景。 @@ -382,8 +373,7 @@ print("\(country.name)'s capital city is called \(country.capitalCity.name)") 在上面的例子中,使用隐式解包可选值值意味着满足了类的构造器的两个构造阶段的要求。`capitalCity` 属性在初始化完成后,能像非可选值一样使用和存取,同时还避免了循环强引用。 - -## 闭包的循环强引用 +## 闭包的循环强引用 {#strong_reference_cycles_for_closures} 前面我们看到了循环强引用是在两个类实例属性互相保持对方的强引用时产生的,还知道了如何用弱引用和无主引用来打破这些循环强引用。 @@ -477,8 +467,7 @@ paragraph = nil 注意,`HTMLElement` 的析构器中的消息并没有被打印,证明了 `HTMLElement` 实例并没有被销毁。 - -## 解决闭包的循环强引用 +## 解决闭包的循环强引用 {#resolving_strong_reference_cycles_for_closures} 在定义闭包时同时定义捕获列表作为闭包的一部分,通过这种方式可以解决闭包和类实例之间的循环强引用。捕获列表定义了闭包体内捕获一个或者多个引用类型的规则。跟解决两个类实例间的循环强引用一样,声明每个捕获的引用为弱引用或无主引用,而不是强引用。应当根据代码关系来决定使用弱引用还是无主引用。 @@ -486,8 +475,7 @@ paragraph = nil > > Swift 有如下要求:只要在闭包内使用 `self` 的成员,就要用 `self.someProperty` 或者 `self.someMethod()`(而不只是 `someProperty` 或 `someMethod()`)。这提醒你可能会一不小心就捕获了 `self`。 - -### 定义捕获列表 +### 定义捕获列表 {#defining_a_capture_list} 捕获列表中的每一项都由一对元素组成,一个元素是 `weak` 或 `unowned` 关键字,另一个元素是类实例的引用(例如 `self`)或初始化过的变量(如 `delegate = self.delegate!`)。这些项在方括号中用逗号分开。 @@ -509,8 +497,7 @@ lazy var someClosure: () -> String = { } ``` - -### 弱引用和无主引用 +### 弱引用和无主引用 {#weak_and_unowned_references} 在闭包和捕获的实例总是互相引用并且总是同时销毁时,将闭包内的捕获定义为 `无主引用`。 diff --git a/source/chapter2/24_Memory_Safety.md b/source/chapter2/24_Memory_Safety.md index aa8f36f5..03ae1529 100644 --- a/source/chapter2/24_Memory_Safety.md +++ b/source/chapter2/24_Memory_Safety.md @@ -4,8 +4,7 @@ Swift 也保证同时访问同一块内存时不会冲突,通过约束代码里对于存储地址的写操作,去获取那一块内存的访问独占权。因为 Swift 自动管理内存,所以大部分时候你完全不需要考虑内存访问的事情。然而,理解潜在的冲突也是很重要的,可以避免你写出访问冲突的代码。而如果你的代码确实存在冲突,那在编译时或者运行时就会得到错误。 - -## 理解内存访问冲突 +## 理解内存访问冲突 {#understanding_conflicting_access_to_memory} 内存的访问,会发生在你给变量赋值,或者传递参数给函数时。例如,下面的代码就包含了读和写的访问: @@ -33,8 +32,7 @@ print("We're number \(one)!") > > 如果你曾经在单线程代码里有访问冲突,Swift 可以保证你在编译或者运行时会得到错误。对于多线程的代码,可以使用 [Thread Sanitizer](https://developer.apple.com/documentation/code_diagnostics/thread_sanitizer) 去帮助检测多线程的冲突。 - -### 内存访问性质 +### 内存访问性质 {#characteristics_of_memory_access} 内存访问冲突时,要考虑内存访问上下文中的这三个性质:访问是读还是写,访问的时长,以及被访问的存储地址。特别是,冲突会发生在当你有两个访问符合下列的情况: @@ -61,8 +59,7 @@ print(myNumber) 重叠的访问主要出现在使用 in-out 参数的函数和方法或者结构体的 mutating 方法里。Swift 代码里典型的长期访问会在后面进行讨论。 - -## In-Out 参数的访问冲突 +## In-Out 参数的访问冲突 {#conflicting_access_to_in-out_parameters} 一个函数会对它所有的 in-out 参数进行长期写访问。in-out 参数的写访问会在所有非 in-out 参数处理完之后开始,直到函数执行完毕为止。如果有多个 in-out 参数,则写访问开始的顺序与参数的顺序一致。 @@ -118,8 +115,7 @@ balance(&playerOneScore, &playerOneScore) > > 因为操作符也是函数,它们也会对 in-out 参数进行长期访问。例如,假设 `balance(_:_:)` 是一个名为 `<^>` 的操作符函数,那么 `playerOneScore <^> playerOneScore` 也会造成像 `balance(&playerOneScore, &playerOneScore)` 一样的冲突。 - -## 方法里 self 的访问冲突 +## 方法里 self 的访问冲突 {#conflicting_access_to_self_in_methods} 一个结构体的 mutating 方法会在调用期间对 `self` 进行写访问。例如,想象一下这么一个游戏,每一个玩家都有血量,受攻击时血量会下降,并且有敌人的数量,使用特殊技能时会减少敌人数量。 @@ -165,8 +161,7 @@ mutating 方法在调用期间需要对 `self` 发起写访问,而同时 in-ou ![](https://docs.swift.org/swift-book/_images/memory_share_health_oscar_2x.png) - -## 属性的访问冲突 +## 属性的访问冲突 {#conflicting_access_to_properties} 如结构体,元组和枚举的类型都是由多个独立的值组成的,例如结构体的属性或元组的元素。因为它们都是值类型,修改值的任何一部分都是对于整个值的修改,意味着其中一个属性的读或写访问都需要访问整一个值。例如,元组元素的写访问重叠会产生冲突: diff --git a/source/chapter2/25_Access_Control.md b/source/chapter2/25_Access_Control.md index 782f9672..7e90adce 100644 --- a/source/chapter2/25_Access_Control.md +++ b/source/chapter2/25_Access_Control.md @@ -10,8 +10,7 @@ Swift 不仅提供了多种不同的访问级别,还为某些典型场景提 > > 为了简单起见,对于代码中可以设置访问级别的特性(属性、基本类型、函数等),在下面的章节中我们会称之为“实体”。 - -## 模块和源文件 +## 模块和源文件 {#modules_and_source_files} Swift 中的访问控制模型基于模块和源文件这两个概念。 @@ -21,8 +20,7 @@ Swift 中的访问控制模型基于模块和源文件这两个概念。 *源文件*就是 Swift 中的源代码文件,它通常属于一个模块,即一个应用程序或者框架。尽管我们一般会将不同的类型分别定义在不同的源文件中,但是同一个源文件也可以包含多个类型、函数之类的定义。 - -## 访问级别 +## 访问级别 {#access_levels} Swift 为代码中的实体提供了五种不同的*访问级别*。这些访问级别不仅与源文件中定义的实体相关,同时也与源文件所属的模块相关。 @@ -42,8 +40,7 @@ Open 只能作用于类和类的成员,它和 Public 的区别如下: 把一个类标记为 `open`,明确的表示你已经充分考虑过外部模块使用此类作为父类的影响,并且设计好了你的类的代码了。 - -### 访问级别基本原则 +### 访问级别基本原则 {#guiding_principle_of_access_levels} Swift 中的访问级别遵循一个基本原则:*不可以在某个实体中定义访问级别更低(更严格)的实体*。 @@ -54,18 +51,15 @@ Swift 中的访问级别遵循一个基本原则:*不可以在某个实体中 关于此原则在各种情况下的具体表现,将在下文有所体现。 - -### 默认访问级别 +### 默认访问级别 {#default_access_levels} 如果你没有为代码中的实体显式指定访问级别,那么它们默认为 `internal` 级别(有一些例外情况,稍后会进行说明)。因此,在大多数情况下,我们不需要显式指定实体的访问级别。 - -### 单 target 应用程序的访问级别 +### 单 target 应用程序的访问级别 {#access_levels_for_single-target_apps} 当你编写一个单目标应用程序时,应用的所有功能都是为该应用服务,而不需要提供给其他应用或者模块使用,所以我们不需要明确设置访问级别,使用默认的访问级别 Internal 即可。但是,你也可以使用 `fileprivate` 访问或 `private` 访问级别,用于隐藏一些功能的实现细节。 - -### 框架的访问级别 +### 框架的访问级别 {#access_levels_for_frameworks} 当你开发框架时,就需要把一些对外的接口定义为 Open 或 Public,以便使用者导入该框架后可以正常使用其功能。这些被你定义为对外的接口,就是这个框架的 API。 @@ -73,13 +67,11 @@ Swift 中的访问级别遵循一个基本原则:*不可以在某个实体中 > > 框架依然会使用默认的 `internal` ,也可以指定为 `fileprivate` 访问或者 `private` 访问级别。当你想把某个实体作为框架的 API 的时候,需显式为其指定开放访问或公开访问级别。 - -### 单元测试 target 的访问级别 +### 单元测试 target 的访问级别 {#access_levels_for_unit_test_targets} 当你的应用程序包含单元测试 target 时,为了测试,测试模块需要访问应用程序模块中的代码。默认情况下只有 `open` 或 `public` 级别的实体才可以被其他模块访问。然而,如果在导入应用程序模块的语句前使用 `@testable` 特性,然后在允许测试的编译设置(`Build Options -> Enable Testability`)下编译这个应用程序模块,单元测试目标就可以访问应用程序模块中所有内部级别的实体。 - -## 访问控制语法 +## 访问控制语法 {#access_control_syntax} 通过修饰符 `open`、`public`、`internal`、`fileprivate`、`private` 来声明实体的访问级别: @@ -102,8 +94,7 @@ class SomeInternalClass {} // 隐式 internal var someInternalConstant = 0 // 隐式 internal ``` - -## 自定义类型 +## 自定义类型 {#custom_types} 如果想为一个自定义类型指定访问级别,在定义类型时进行指定即可。新类型只能在它的访问级别限制范围内使用。例如,你定义了一个 `fileprivate` 级别的类,那这个类就只能在定义它的源文件中使用,可以作为属性类型、函数参数类型或者返回类型,等等。 @@ -136,8 +127,7 @@ private class SomePrivateClass { // 显式 private 类 func somePrivateMethod() {} // 隐式 private 类成员 } ``` - -### 元组类型 +### 元组类型 {#tuple_types} 元组的访问级别将由元组中访问级别最严格的类型来决定。例如,如果你构建了一个包含两种不同类型的元组,其中一个类型为 `internal`,另一个类型为 `private`,那么这个元组的访问级别为 `private`。 @@ -145,8 +135,7 @@ private class SomePrivateClass { // 显式 private 类 > > 元组不同于类、结构体、枚举、函数那样有单独的定义。元组的访问级别是在它被使用时自动推断出的,而无法明确指定。 - -### 函数类型 +### 函数类型 {#function_types} 函数的访问级别根据访问级别最严格的参数类型或返回类型的访问级别来决定。但是,如果这种访问级别不符合函数定义所在环境的默认访问级别,那么就需要明确地指定该函数的访问级别。 @@ -170,8 +159,7 @@ private func someFunction() -> (SomeInternalClass, SomePrivateClass) { 将该函数指定为 `public` 或 `internal`,或者使用默认的访问级别 `internal` 都是错误的,因为如果把该函数当做 `public` 或 `internal` 级别来使用的话,可能会无法访问 `private` 级别的返回值。 - -### 枚举类型 +### 枚举类型 {#enumeration_types} 枚举成员的访问级别和该枚举类型相同,你不能为枚举成员单独指定不同的访问级别。 @@ -186,18 +174,15 @@ public enum CompassPoint { } ``` - -#### 原始值和关联值 +#### 原始值和关联值 {#raw_values_and_associated_values} 枚举定义中的任何原始值或关联值的类型的访问级别至少不能低于枚举类型的访问级别。例如,你不能在一个 `internal` 的枚举中定义 `private` 的原始值类型。 - -### 嵌套类型 +### 嵌套类型 {#nested_types} 如果在 `private` 的类型中定义嵌套类型,那么该嵌套类型就自动拥有 `private` 访问级别。如果在 `public` 或者 `internal` 级别的类型中定义嵌套类型,那么该嵌套类型自动拥有 `internal` 访问级别。如果想让嵌套类型拥有 `public` 访问级别,那么需要明确指定该嵌套类型的访问级别。 - -## 子类 +## 子类 {#subclassing} 子类的访问级别不得高于父类的访问级别。例如,父类的访问级别是 `internal`,子类的访问级别就不能是 `public`。 @@ -231,8 +216,7 @@ internal class B: A { 因为父类 `A` 和子类 `B` 定义在同一个源文件中,所以在子类 `B` 可以在重写的 `someMethod()` 方法中调用 `super.someMethod()`。 - -## 常量、变量、属性、下标 +## 常量、变量、属性、下标 {#constants_variables_properties_subscripts} 常量、变量、属性不能拥有比它们的类型更高的访问级别。例如,你不能定义一个 `public` 级别的属性,但是它的类型却是 `private` 级别的。同样,下标也不能拥有比索引类型或返回类型更高的访问级别。 @@ -242,8 +226,7 @@ internal class B: A { private var privateInstance = SomePrivateClass() ``` - -### Getter 和 Setter +### Getter 和 Setter {#getters_and_setters} 常量、变量、属性、下标的 `Getters` 和 `Setters` 的访问级别和它们所属类型的访问级别相同。 @@ -297,29 +280,25 @@ public struct TrackedString { } ``` - -## 构造器 +## 构造器 {#initializers} 自定义构造器的访问级别可以低于或等于其所属类型的访问级别。唯一的例外是[必要构造器](./14_Initialization.md#required_initializers),它的访问级别必须和所属类型的访问级别相同。 如同函数或方法的参数,构造器参数的访问级别也不能低于构造器本身的访问级别。 - -### 默认构造器 +### 默认构造器 {#default_initializers} 如[默认构造器](./14_Initialization.md#default_initializers)所述,Swift 会为结构体和类提供一个默认的无参数的构造器,只要它们为所有存储型属性设置了默认初始值,并且未提供自定义的构造器。 默认构造器的访问级别与所属类型的访问级别相同,除非类型的访问级别是 `public`。如果一个类型被指定为 `public` 级别,那么默认构造器的访问级别将为 `internal`。如果你希望一个 `public` 级别的类型也能在其他模块中使用这种无参数的默认构造器,你只能自己提供一个 `public` 访问级别的无参数构造器。 - -### 结构体默认的成员逐一构造器 +### 结构体默认的成员逐一构造器 {#default_memberwise_initializers_for_structure_types} 如果结构体中任意存储型属性的访问级别为 `private`,那么该结构体默认的成员逐一构造器的访问级别就是 `private`。否则,这种构造器的访问级别依然是 `internal`。 如同前面提到的默认构造器,如果你希望一个 `public` 级别的结构体也能在其他模块中使用其默认的成员逐一构造器,你依然只能自己提供一个 `public` 访问级别的成员逐一构造器。 - -## 协议 +## 协议 {#protocols} 如果想为一个协议类型明确地指定访问级别,在定义协议时指定即可。这将限制该协议只能在适当的访问级别范围内被遵循。 @@ -329,13 +308,11 @@ public struct TrackedString { > > 如果你定义了一个 `public` 访问级别的协议,那么该协议的所有实现也会是 `public` 访问级别。这一点不同于其他类型,例如,当类型是 `public` 访问级别时,其成员的访问级别却只是 `internal`。 - -### 协议继承 +### 协议继承 {#protocol_inheritance} 如果定义了一个继承自其他协议的新协议,那么新协议拥有的访问级别最高也只能和被继承协议的访问级别相同。例如,你不能将继承自 `internal` 协议的新协议定义为 `public` 协议。 - -### 协议遵循 +### 协议遵循 {#protocol_conformance} 一个类型可以遵循比它级别更低的协议。例如,你可以定义一个 `public` 级别类型,它能在别的模块中使用,但是如果它遵循一个 `internal` 协议,这个遵循的部分就只能在这个 `internal` 协议所在的模块中使用。 @@ -347,8 +324,7 @@ public struct TrackedString { > > Swift 和 Objective-C 一样,协议遵循是全局的,也就是说,在同一程序中,一个类型不可能用两种不同的方式实现同一个协议。 - -## Extension +## Extension {#extensions} Extension 可以在访问级别允许的情况下对类、结构体、枚举进行扩展。Extension 的成员具有和原始类型成员一致的访问级别。例如,你使用 extension 扩展了一个 `public` 或者 `internal` 类型,extension 中的成员就默认使用 `internal` 访问级别,和原始类型中的成员一致。如果你使用 extension 扩展了一个 `private` 类型,则 extension 的成员默认使用 `private` 访问级别。 @@ -356,8 +332,7 @@ Extension 可以在访问级别允许的情况下对类、结构体、枚举进 如果你使用 extension 来遵循协议的话,就不能显式地声明 extension 的访问级别。extension 每个 protocol 要求的实现都默认使用 protocol 的访问级别。 - -### Extension 的私有成员 +### Extension 的私有成员 {#Private Members in Extensions} 扩展同一文件内的类,结构体或者枚举,extension 里的代码会表现得跟声明在原类型里的一模一样。也就是说你可以这样: @@ -387,13 +362,11 @@ extension SomeStruct: SomeProtocol { } ``` - -## 泛型 +## 泛型 {#generics} 泛型类型或泛型函数的访问级别取决于泛型类型或泛型函数本身的访问级别,还需结合类型参数的类型约束的访问级别,根据这些访问级别中的最低访问级别来确定。 - -## 类型别名 +## 类型别名 {#type_aliases} 你定义的任何类型别名都会被当作不同的类型,以便于进行访问控制。类型别名的访问级别不可高于其表示的类型的访问级别。例如,`private` 级别的类型别名可以作为 `private`、`file-private`、`internal`、`public` 或者 `open` 类型的别名,但是 `public` 级别的类型别名只能作为 `public` 类型的别名,不能作为 `internal`、`file-private` 或 `private` 类型的别名。 diff --git a/source/chapter2/26_Advanced_Operators.md b/source/chapter2/26_Advanced_Operators.md index 88b48272..21dd7ad2 100644 --- a/source/chapter2/26_Advanced_Operators.md +++ b/source/chapter2/26_Advanced_Operators.md @@ -8,15 +8,13 @@ 我们不用被预定义的运算符所限制。在 Swift 中可以自由地定义中缀、前缀、后缀和赋值运算符,它们具有自定义的优先级与关联值。这些运算符在代码中可以像预定义的运算符一样使用,你甚至可以扩展已有的类型以支持自定义运算符。 - -## 位运算符 +## 位运算符 {#bitwise_operators} *位运算符*可以操作数据结构中每个独立的比特位。它们通常被用在底层开发中,比如图形编程和创建设备驱动。位运算符在处理外部资源的原始数据时也十分有用,比如对自定义通信协议传输的数据进行编码和解码。 Swift 支持 C 语言中的全部位运算符,接下来会一一介绍。 - -### Bitwise NOT Operator(按位取反运算符) +### Bitwise NOT Operator(按位取反运算符) {#bitwise_not_operator} *按位取反运算符(`~`)*对一个数值的全部比特位进行取反: @@ -33,8 +31,7 @@ let invertedBits = ~initialBits // 等于 0b11110000 接着使用按位取反运算符创建了一个名为 `invertedBits` 的常量,这个常量的值与全部位取反后的 `initialBits` 相等。即所有的 `0` 都变成了 `1`,同时所有的 `1` 都变成 `0`。`invertedBits` 的二进制值为 `11110000`,等价于无符号十进制数的 `240`。 - -### Bitwise AND Operator(按位与运算符) +### Bitwise AND Operator(按位与运算符) {#bitwise_and_operator} *按位与运算符(`&`)* 对两个数的比特位进行合并。它返回一个新的数,只有当两个数的对应位*都*为 `1` 的时候,新数的对应位才为 `1`: @@ -48,8 +45,7 @@ let lastSixBits: UInt8 = 0b00111111 let middleFourBits = firstSixBits & lastSixBits // 等于 00111100 ``` - -### Bitwise OR Operator(按位或运算符) +### Bitwise OR Operator(按位或运算符) {#bitwise_or_operator} *按位或运算符(`|`)* 可以对两个数的比特位进行比较。它返回一个新的数,只要两个数的对应位中有*任意一个*为 `1` 时,新数的对应位就为 `1`: @@ -63,8 +59,7 @@ let moreBits: UInt8 = 0b01011110 let combinedbits = someBits | moreBits // 等于 11111110 ``` - -### Bitwise XOR Operator(按位异或运算符) +### Bitwise XOR Operator(按位异或运算符) {#bitwise_xor_operator} *按位异或运算符*,或称“排外的或运算符”(`^`),可以对两个数的比特位进行比较。它返回一个新的数,当两个数的对应位不相同时,新数的对应位就为 `1`,并且对应位相同时则为 `0`: @@ -78,15 +73,13 @@ let otherBits: UInt8 = 0b00000101 let outputBits = firstBits ^ otherBits // 等于 00010001 ``` - -### Bitwise Left and Right Shift Operators(按位左移、右移运算符) +### Bitwise Left and Right Shift Operators(按位左移、右移运算符) {#bitwise_left_and_right_shift_operators} *按位左移运算符(`<<`)* 和 *按位右移运算符(`>>`)*可以对一个数的所有位进行指定位数的左移和右移,但是需要遵守下面定义的规则。 对一个数进行按位左移或按位右移,相当于对这个数进行乘以 2 或除以 2 的运算。将一个整数左移一位,等价于将这个数乘以 2,同样地,将一个整数右移一位,等价于将这个数除以 2。 - -#### 无符号整数的移位运算 +#### 无符号整数的移位运算 {#shifting_behavior_for_unsigned_integers} 对无符号整数进行移位的规则如下: @@ -130,8 +123,7 @@ let blueComponent = pink & 0x0000FF // blueComponent 是 0x99,即 153 最后,蓝色部分通过对 `0xCC6699` 和 `0x0000FF` 进行按位与运算得到 `0x000099`。这里不需要再向右移位,而 `0x000099` 也就是 `0x99` ,也就是十进制数值的 `153`。 - -#### 有符号整数的移位运算 +#### 有符号整数的移位运算 {#shifting_behavior_for_signed_integers} 对比无符号整数,有符号整数的移位运算相对复杂得多,这种复杂性源于有符号整数的二进制表现形式。(为了简单起见,以下的示例都是基于 8 比特的有符号整数,但是其中的原理对任何位数的有符号整数都是通用的。) @@ -167,8 +159,7 @@ let blueComponent = pink & 0x0000FF // blueComponent 是 0x99,即 153 由于正数和负数的特殊存储方式,在对它们进行右移的时候,会使它们越来越接近 `0`。在移位的过程中保持符号位不变,意味着负整数在接近 `0` 的过程中会一直保持为负。 - -## 溢出运算符 +## 溢出运算符 {#overflow_operators} 当向一个整数类型的常量或者变量赋予超过它容量的值时,Swift 默认会报错,而不是允许生成一个无效的数。这个行为为我们在运算过大或者过小的数时提供了额外的安全性。 @@ -189,8 +180,7 @@ potentialOverflow += 1 * 溢出减法 `&-` * 溢出乘法 `&*` - -### 数值溢出 +### 数值溢出 {#value_overflow} 数值有可能出现上溢或者下溢。 @@ -235,8 +225,7 @@ signedOverflow = signedOverflow &- 1 对于无符号与有符号整型数值来说,当出现上溢时,它们会从数值所能容纳的最大数变成最小数。同样地,当发生下溢时,它们会从所能容纳的最小数变成最大数。 - -## 优先级和结合性 +## 优先级和结合性 {#precedence_and_associativity} 运算符的*优先级*使得一些运算符优先于其他运算符;它们会先被执行。 @@ -283,8 +272,7 @@ signedOverflow = signedOverflow &- 1 > > 相对 C 语言和 Objective-C 来说,Swift 的运算符优先级和结合性规则更加简洁和可预测。但是,这也意味着它们相较于 C 语言及其衍生语言并不是完全一致。在对现有的代码进行移植的时候,要注意确保运算符的行为仍然符合你的预期。 - -## 运算符函数 +## 运算符函数 {#operator_functions} 类和结构体可以为现有的运算符提供自定义的实现。这通常被称为运算符*重载*。 @@ -321,8 +309,7 @@ let combinedVector = vector + anotherVector ![Art/vectorAddition_2x.png](https://docs.swift.org/swift-book/_images/vectorAddition_2x.png) - -### 前缀和后缀运算符 +### 前缀和后缀运算符 {#prefix_and_postfix_operators} 上个例子演示了一个二元中缀运算符的自定义实现。类与结构体也能提供标准*一元运算符*的实现。一元运算符只运算一个值。当运算符出现在值之前时,它就是*前缀*的(例如 `-a`),而当它出现在值之后时,它就是*后缀*的(例如 `b!`)。 @@ -348,8 +335,7 @@ let alsoPositive = -negative // alsoPositive 是一个值为 (3.0, 4.0) 的 Vector2D 实例 ``` - -### 复合赋值运算符 +### 复合赋值运算符 {#compound_assignment_operators} *复合赋值运算符*将赋值运算符(`=`)与其它运算符进行结合。例如,将加法与赋值结合成加法赋值运算符(`+=`)。在实现的时候,需要把运算符的左参数设置成 `inout` 类型,因为这个参数的值会在运算符函数内直接被修改。 @@ -376,8 +362,7 @@ original += vectorToAdd > > 不能对默认的赋值运算符(`=`)进行重载。只有复合赋值运算符可以被重载。同样地,也无法对三元条件运算符 (`a ? b : c`) 进行重载。 - -### 等价运算符 +### 等价运算符 {#equivalence_operators} 通常情况下,自定义的类和结构体没有对*等价运算符*进行默认实现,等价运算符通常被称为*相等*运算符(`==`)与*不等*运算符(`!=`)。 @@ -427,8 +412,7 @@ if twoThreeFour == anotherTwoThreeFour { // 打印“These two vectors are also equivalent.” ``` - -## 自定义运算符 +## 自定义运算符 {#custom_operators} 除了实现标准运算符,在 Swift 中还可以声明和实现*自定义运算符*。可以用来自定义运算符的字符列表请参考[运算符](../chapter3/02_Lexical_Structure.html#operators)。 @@ -454,8 +438,7 @@ let afterDoubling = +++toBeDoubled // afterDoubling 现在的值也为 (2.0, 8.0) ``` - -### 自定义中缀运算符的优先级 +### 自定义中缀运算符的优先级 {#precedence_and_associativity_for_custom_infix_operators} 每个自定义中缀运算符都属于某个优先级组。优先级组指定了这个运算符相对于其他中缀运算符的优先级和结合性。[优先级和结合性](#precedence_and_associativity)中详细阐述了这两个特性是如何对中缀运算符的运算产生影响的。 diff --git a/source/chapter3/01_About_the_Language_Reference.md b/source/chapter3/01_About_the_Language_Reference.md index fa2d96f2..2c6f5aee 100755 --- a/source/chapter3/01_About_the_Language_Reference.md +++ b/source/chapter3/01_About_the_Language_Reference.md @@ -4,8 +4,7 @@ Swift 语言相对较小,这是由于 Swift 代码中常用的类型、函数以及运算符都已经在 Swift 标准库中定义了。虽然这些类型、函数和运算符并不是 Swift 语言自身的一部分,但是它们被广泛应用于本书的讨论和代码范例中。 - -## 如何阅读语法 +## 如何阅读语法 {#how_to_read_the_grammar} 用来描述 Swift 编程语言形式语法的符号遵循下面几个约定: diff --git a/source/chapter3/02_Lexical_Structure.md b/source/chapter3/02_Lexical_Structure.md index 5a0a412c..c04c0ce7 100755 --- a/source/chapter3/02_Lexical_Structure.md +++ b/source/chapter3/02_Lexical_Structure.md @@ -4,8 +4,7 @@ Swift 的*“词法结构(lexical structure)”* 描述了能构成该语言 通常情况下,通过考虑输入文本当中可能的最长子串,并且在随后将介绍的语法约束之下,根据随后将介绍的语法约束生成的,根据 Swift 源文件当中的字符来生成相应的“符号”。这种方法称为*“最长匹配(longest match)”*,或者*“最大适合(maximal munch)”*。 - -## 空白与注释 +## 空白与注释 {#whitespace_and_comments} 空白(whitespace)有两个用途:分隔源文件中的符号以及帮助区分运算符属于前缀还是后缀(参见 [运算符](#operators)),在其他情况下空白则会被忽略。以下的字符会被当作空白:空格(U+0020)、换行符(U+000A)、回车符(U+000D)、水平制表符(U+0009)、垂直制表符(U+000B)、换页符(U+000C)以及空字符(U+0000)。 @@ -14,7 +13,7 @@ Swift 的*“词法结构(lexical structure)”* 描述了能构成该语言 > 空白语法 > - +whitespace {#whitespace} > *空白* → [*空白项*](#whitespace-item) [*空白*](#whitespace)可选 > > *空白项* → [*断行符*](#line-break) @@ -26,7 +25,7 @@ Swift 的*“词法结构(lexical structure)”* 描述了能构成该语言 > *空白项* → U+0000,U+0009,U+000B,U+000C 或者 U+0020 > - +line-break {#line-break} > *断行符* → U+000A > > *断行符* → U+000D @@ -34,7 +33,7 @@ Swift 的*“词法结构(lexical structure)”* 描述了能构成该语言 > *断行符* → U+000D 接着是 U+000A > - +comment {#comment} > *注释* → // [*注释内容 断行*](#comment-text line-break) > > *多行注释* → `/*` [*多行注释内容*](#multiline-commnet-text) `*/` @@ -54,8 +53,7 @@ Swift 的*“词法结构(lexical structure)”* 描述了能构成该语言 注释可以包含额外的格式和标记,正如 [*Markup Formatting Reference*](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/index.html) 所述。 - -## 标识符 +## 标识符 {#identifiers} *标识符(identifier)* 可以由以下的字符开始:大写或小写的字母 `A` 到 `Z`、下划线(`_`)、基本多文种平面(Basic Multilingual Plane)中非字符数字组合的 Unicode 字符以及基本多文种平面以外的非个人专用区字符。在首字符之后,允许使用数字和组合 Unicode 字符。 @@ -66,7 +64,7 @@ Swift 的*“词法结构(lexical structure)”* 描述了能构成该语言 > 标识符语法 > - +identifier {#identifier} > *标识符* → [*头部标识符*](#identifier-head) [*标识符字符组*](#identifier-characters)可选 > > *标识符* → \`[*头部标识符*](#identifier-head) [*标识符字符组*](#identifier-characters)可选\` @@ -74,11 +72,11 @@ Swift 的*“词法结构(lexical structure)”* 描述了能构成该语言 > *标识符* → [*隐式参数名*](#implicit-parameter-name) > - +identifier-list {#identifier-list} > *标识符列表* → [*标识符*](#identifier) | [*标识符*](#identifier) **,** [*标识符列表*](#identifier-list) > - +identifier-head {#identifier-head} > *头部标识符* → 大写或小写字母 A - Z > > *头部标识符* → _ @@ -112,24 +110,23 @@ Swift 的*“词法结构(lexical structure)”* 描述了能构成该语言 > *头部标识符* → U+D0000–U+DFFFD 或者 U+E0000–U+EFFFD > - +identifier-character {#identifier-character} > *标识符字符* → 数值 0 - 9 > > *标识符字符* → U+0300–U+036F,U+1DC0–U+1DFF,U+20D0–U+20FF,或者 U+FE20–U+FE2F > > *标识符字符* → [*头部标识符*](#identifier-head) > -> +> identifier-characters {#identifier-characters} > > *标识符字符组* → [*标识符字符*](#identifier-character) [*标识符字符组*](#identifier-characters)可选 > - +implicit-parameter-name {#implicit-parameter-name} > *隐式参数名* → **$** [*十进制数字列表*](#decimal-digits) > - -## 关键字和标点符号 +## 关键字和标点符号 {#keywords} 下面这些被保留的关键字不允许用作标识符,除非使用反引号转义,具体描述请参考 [标识符](#identifiers)。除了 `inout`、`var` 以及 `let` 之外的关键字可以用作某个函数声明或者函数调用当中的外部参数名,不用添加反引号转义。 @@ -142,8 +139,7 @@ Swift 的*“词法结构(lexical structure)”* 描述了能构成该语言 以下符号被当作保留符号,不能用于自定义运算符: `(`、`)`、`{`、`}`、`[`、`]`、`.`、`,`、`:`、`;`、`=`、`@`、`#`、`&`(作为前缀运算符)、`->`、`` ` ``、`?`、`!`(作为后缀运算符)。 - -## 字面量 +## 字面量 {#literals} *字面量(literal)* 用来表示源码中某种特定类型的值,比如一个数字或字符串。 @@ -165,20 +161,19 @@ true // 布尔值字面量 > *字面量* → [*数值字面量*](#numeric-literal) | [*字符串字面量*](#string-literal) | [*布尔值字面量*](#boolean-literal) | [*nil 字面量*](#nil-literal) > - +numeric-literal {#numeric-literal} > *数值字面量* → **-**可选 [*整数字面量*](#integer-literal) | **-**可选 [*浮点数字面量*](#floating-point-literal) > -> +> boolean-literal {#boolean-literal} > > *布尔值字面量* → **true** | **false** > -> +> nil-literal {#nil-literal} > > *nil 字面量* → **nil** > - -### 整数字面量 +### 整数字面量 {#integer_literals} *整数字面量(Integer Literals)* 表示未指定精度整数的值。整数字面量默认用十进制表示,可以加前缀来指定其他的进制。二进制字面量加 `0b`,八进制字面量加 `0o`,十六进制字面量加 `0x`。 @@ -192,7 +187,7 @@ true // 布尔值字面量 > 整数字面量语法 > - +integer-literal {#integer-literal} > *整数字面量* → [*二进制字面量*](#binary-literal) > > *整数字面量* → [*八进制字面量*](#octal-literal) @@ -202,76 +197,75 @@ true // 布尔值字面量 > *整数字面量* → [*十六进制字面量*](#hexadecimal-literal) > - +binary-literal {#binary-literal} > *二进制字面量* → **0b** [*二进制数字*](#binary-digit) [*二进制字面量字符组*](#binary-literal-characters)可选 > -> +> binary-digit {#binary-digit} > > *二进制数字* → 数值 0 到 1 > -> +> binary-literal-character {#binary-literal-character} > > *二进制字面量字符* → [*二进制数字*](#binary-digit) | _ > -> +> binary-literal-characters {#binary-literal-characters} > > *二进制字面量字符组* → [*二进制字面量字符*](#binary-literal-character) [*二进制字面量字符组*](#binary-literal-characters)可选 > - +octal-literal {#octal-literal} > *八进制字面量* → **0o** [*八进字数字*](#octal-digit) [*八进制字符组*](#octal-literal-characters)可选 > -> +> octal-digit {#octal-digit} > > *八进字数字* → 数值 0 到 7 > -> +> octal-literal-character {#octal-literal-character} > > *八进制字符* → [*八进字数字*](#octal-digit) | _ > -> +> octal-literal-characters {#octal-literal-characters} > > *八进制字符组* → [*八进制字符*](#octal-literal-character) [*八进制字符组*](#octal-literal-characters)可选 > - +decimal-literal {#decimal-literal} > *十进制字面量* → [*十进制数字*](#decimal-digit) [*十进制字符组*](#decimal-literal-characters)可选 > -> +> decimal-digit {#decimal-digit} > > *十进制数字* → 数值 0 到 9 > -> +> decimal-digits {#decimal-digits} > > *十进制数字组* → [*十进制数字*](#decimal-digit) [*十进制数字组*](#decimal-digits)可选 > -> +> decimal-literal-character {#decimal-literal-character} > > *十进制字符* → [*十进制数字*](#decimal-digit) | _ > -> +> decimal-literal-characters {#decimal-literal-characters} > > *十进制字符组* → [*十进制字符*](#decimal-literal-character) [*十进制字符组*](#decimal-literal-characters)可选 > - +hexadecimal-literal {#hexadecimal-literal} > *十六进制字面量* → **0x** [*十六进制数字*](#hexadecimal-digit) [*十六进制字面量字符组*](#hexadecimal-literal-characters)可选 > -> +> hexadecimal-digit {#hexadecimal-digit} > > *十六进制数字* → 数值 0 到 9, 字母 a 到 f, 或 A 到 F > -> +> hexadecimal-literal-character {#hexadecimal-literal-character} > > *十六进制字符* → [*十六进制数字*](#hexadecimal-digit) | _ > -> +> hexadecimal-literal-characters {#hexadecimal-literal-characters} > > *十六进制字面量字符组* → [*十六进制字符*](#hexadecimal-literal-character) [*十六进制字面量字符组*](#hexadecimal-literal-characters)可选 > - -### 浮点数字面量 +### 浮点数字面量 {#floating_point_literals} *浮点数字面量(Floating-point literals)* 表示未指定精度浮点数的值。 @@ -289,42 +283,41 @@ true // 布尔值字面量 > 浮点数字面量语法 > - +floating-point-literal {#floating-point-literal} > *浮点数字面量* → [*十进制字面量*](#decimal-literal) [*十进制分数*](#decimal-fraction)可选 [*十进制指数*](#decimal-exponent)可选 > > *浮点数字面量* → [*十六进制字面量*](#hexadecimal-literal) [*十六进制分数*](#hexadecimal-fraction)可选 [*十六进制指数*](#hexadecimal-exponent) > - +decimal-fraction {#decimal-fraction} > *十进制分数* → **.** [*十进制字面量*](#decimal-literal) > -> +> decimal-exponent {#decimal-exponent} > > *十进制指数* → [*十进制指数 e*](#floating-point-e) [*正负号*](#sign)可选 [*十进制字面量*](#decimal-literal) > - +hexadecimal-fraction {#hexadecimal-fraction} > *十六进制分数* → **.** [*十六进制数字*](#hexadecimal-digit) [*十六进制字面量字符组*](#hexadecimal-literal-characters)可选 > -> +> hexadecimal-exponent {#hexadecimal-exponent} > > *十六进制指数* → [*十六进制指数 p*](#floating-point-p) [*正负号*](#sign)可选 [*十进制字面量*](#decimal-literal) > - +floating-point-e {#floating-point-e} > *十进制指数 e* → **e** | **E** > -> +> floating-point-p {#floating-point-p} > > *十六进制指数 p* → **p** | **P** > -> +> sign {#sign} > > *正负号* → **+** | **-** > - -### 字符串字面量 +### 字符串字面量 {#string_literals} 字符串字面量是被引号包括的一串字符组成。 单行字符串字面量被包在双引号中的一串字符组成,形式如下: @@ -385,48 +378,47 @@ let textB = "Hello world" > 字符串字面量语法 > - +string-literal {#string-literal} > *字符串字面量* → [*静态字符串字面量*](#static-string-literal) | [*插值字符串字面量*](#interpolated-string-literal) > - +static-string-literal {#static-string-literal} > *静态字符串字面量* → **"**[*引用文本*](#quoted-text)可选**"** > -> +> quoted-text {#quoted-text} > > *引用文本* → [*引用文本项*](#quoted-text-item) [*引用文本*](#quoted-text)可选 > -> +> quoted-text-item {#quoted-text-item} > > *引用文本项* → [*转义字符*](#escaped-character) > > *引用文本项* → 除了 **"**、**\\**、U+000A、U+000D 以外的所有 Unicode 字符 > - +interpolated-string-literal {#interpolated-string-literal} > *插值字符串字面量* → **"**[*插值文本*](#interpolated-text)可选**"** > -> +> interpolated-text {#interpolated-text} > > *插值文本* → [*插值文本项*](#interpolated-text-item) [*插值文本*](#interpolated-text)可选 > -> +> interpolated-text-item {#interpolated-text-item} > > *插值文本项* → **\\****(**[*表达式*](./04_Expressions.md)**)** | [*引用文本项*](#quoted-text-item) > - +escaped-character {#escaped-character} > *转义字符* → **\\****0** | **\\****\\** | **\t** | **\n** | **\r** | **\\"** | **\\'** > > *转义字符* → **\u {** [*unicode 标量数字*](#unicode-scalar-digits) **}** > -> +> unicode-scalar-digits {#unicode-scalar-digits} > > *unicode 标量数字* → 一到八位的十六进制数字 > - -## 运算符 +## 运算符 {#operators} Swift 标准库定义了许多可供使用的运算符,其中大部分在 [基础运算符](../chapter2/02_Basic_Operators.md) 和 [高级运算符](../chapter2/26_Advanced_Operators.md) 中进行了阐述。这一小节将描述哪些字符能用于自定义运算符。 @@ -458,13 +450,13 @@ Swift 标准库定义了许多可供使用的运算符,其中大部分在 [基 > 运算符语法 > - +operator {#operator} > *运算符* → [*头部运算符*](#operator-head) [*运算符字符组*](#operator-characters)可选 > > *运算符* → [*头部点运算符*](#dot-operator-head) [*点运算符字符组*](#dot-operator-characters)可选 > - +operator-head {#operator-head} > *头部运算符* → **/** | **=** | **-** | **+** | **!** | __*__ | **%** | **<** | **>** | **&** | **|** | **^** | **~** | **?** > > *头部运算符* → U+00A1–U+00A7 @@ -496,7 +488,7 @@ Swift 标准库定义了许多可供使用的运算符,其中大部分在 [基 > *头部运算符* → U+3008–U+3030 > - +operator-character {#operator-character} > *运算符字符* → [*头部运算符*](#operator-head) > > *运算符字符* → U+0300–U+036F @@ -511,31 +503,31 @@ Swift 标准库定义了许多可供使用的运算符,其中大部分在 [基 > > *运算符字符* → U+E0100–U+E01EF > -> +> operator-characters {#operator-characters} > > *运算符字符组* → [*运算符字符*](#operator-character) [*运算符字符组*](#operator-characters)可选 > - +dot-operator-head {#dot-operator-head} > *头部点运算符* → **..** > -> +> dot-operator-character {#dot-operator-character} > > *点运算符字符* → **.** | [*运算符字符*](#operator-character) > -> +> dot-operator-characters {#dot-operator-characters} > > *点运算符字符组* → [*点运算符字符*](#dot-operator-character) [*点运算符字符组*](#dot-operator-characters)可选 > - +binary-operator {#binary-operator} > *二元运算符* → [*运算符*](#operator) > -> +> prefix-operator {#prefix-operator} > > *前缀运算符* → [*运算符*](#operator) > -> +> postfix-operator {#postfix-operator} > > *后缀运算符* → [*运算符*](#operator) > diff --git a/source/chapter3/03_Types.md b/source/chapter3/03_Types.md index b5a139d9..0d96571f 100644 --- a/source/chapter3/03_Types.md +++ b/source/chapter3/03_Types.md @@ -27,7 +27,7 @@ Swift 语言存在两种类型:命名型类型和复合型类型。命名型 > 类型语法 > - +type {#type} > *类型* → [*数组类型*](#array-type) > > *类型* → [*字典类型*](#dictionary-type) @@ -53,7 +53,7 @@ Swift 语言存在两种类型:命名型类型和复合型类型。命名型 > *类型* → [*(类型)*](#type) > - +type_annotation {#type_annotation} ## 类型注解 类型注解显式地指定一个变量或表达式的值。类型注解始于冒号 `:` 终于类型,比如下面两个例子: @@ -69,11 +69,11 @@ func someFunction(a: Int) { /* ... */ } > 类型注解语法 > - +type-annotation {#type-annotation} > *类型注解* → **:** [*特性列表*](./07_Attributes.md#attributes)可选 **输入输出参数**可选 [*类型*](#type) > - +type_identifier {#type_identifier} ## 类型标识符 类型标识符引用命名型类型,还可引用命名型或复合型类型的别名。 @@ -95,14 +95,14 @@ var someValue: ExampleModule.MyType > 类型标识符语法 > - +type-identifier {#type-identifier} > *类型标识符* → [*类型名称*](#type-name) [*泛型参数子句*](08_Generic_Parameters_and_Arguments.md#generic_argument_clause)可选 | [*类型名称*](#type-name) [*泛型参数子句*](08_Generic_Parameters_and_Arguments.md#generic_argument_clause)可选 **.** [*类型标识符*](#type-identifier) > - +type-name {#type-name} > *类型名称* → [*标识符*](./02_Lexical_Structure.md#identifier) > - +tuple_type {#tuple_type} ## 元组类型 元组类型是使用括号括起来的零个或多个类型,类型间用逗号隔开。 @@ -122,20 +122,20 @@ someTuple = (left: 5, right: 5) // 错误:命名类型不匹配 > 元组类型语法 > - +tuple-type {#tuple-type} > *元组类型* → **(** [*元组类型元素列表*](#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} > *元组类型元素* → [*元素名*](#element-name) [*类型注解*](#type-annotation) | [*类型*](#type) > - +element-name {#element-name} > *元素名* → [*标识符*](./02_Lexical_Structure.md#identifier) > - +function_type {#function_type} ## 函数类型 函数类型表示一个函数、方法或闭包的类型,它由参数类型和返回值类型组成,中间用箭头(`->`)隔开: @@ -190,7 +190,7 @@ var operation: (Int, Int) -> Int // 正确 函数类型若要抛出错误就必须使用 `throws` 关键字来标记,若要重抛错误则必须使用 `rethrows` 关键字来标记。`throws` 关键字是函数类型的一部分,非抛出函数是抛出函数函数的一个子类型。因此,在使用抛出函数的地方也可以使用不抛出函数。抛出和重抛函数的相关描述见章节 [抛出函数与方法](./06_Declarations.md#throwing_functions_and_methods) 和 [重抛函数与方法](./06_Declarations.md#rethrowing_functions_and_methods)。 - +Restrictions for Nonescaping Closures {#Restrictions for Nonescaping Closures} ### 对非逃逸闭包的限制 非逃逸闭包函数不能作为参数传递到另一个非逃逸闭包函数的参数。这样的限制可以让 Swift 在编译时就完成更多的内存访问冲突检查, 而不是在运行时。举个例子: @@ -219,27 +219,27 @@ func takesTwoFunctions(first: (Any) -> Void, second: (Any) -> Void) { > 函数类型语法 > - +function-type {#function-type} > *函数类型* → [*特性列表*](./07_Attributes.md#attributes)可选 [*函数类型子句*](#function-type-argument-clause) **throws**可选 **->** [*类型*](#type) > > *函数类型* → [*特性列表*](./07_Attributes.md#attributes)可选 [*函数类型子句*](#function-type-argument-clause) **rethrows­** **->** [*类型*](#type) > - +function-type-argument-clause {#function-type-argument-clause} > *函数类型子句* → (­)­ > > *函数类型子句* → ([*函数类型参数列表*](#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} > *函数类型参数* → [*特性列表*](./07_Attributes.md#attributes)可选 **输入输出参数**可选 [*类型*](#type) | [*参数标签*](#argument-label) [*类型注解*](#type-annotation) > - +argument-label {#argument-label} > *参数标签* → [*标识符*](./02_Lexical_Structure.md#identifier) > - +array_type {#array_type} ## 数组类型 Swift 语言为标准库中定义的 `Array` 类型提供了如下语法糖: @@ -269,11 +269,11 @@ var array3D: [[[Int]]] = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]] > 数组类型语法 > - +array-type {#array-type} > *数组类型* → **[** [*类型*](#type) **]** > - +dictionary_type {#dictionary_type} ## 字典类型 Swift 语言为标准库中定义的 `Dictionary` 类型提供了如下语法糖: @@ -299,11 +299,11 @@ let someDictionary: Dictionary = ["Alex": 31, "Paul": 39] > 字典类型语法 > - +dictionary-type {#dictionary-type} > *字典类型* → **[** [*类型*](#type) **:** [*类型*](#type) **]** > - +optional_type {#optional_type} ## 可选类型 Swift 定义后缀 `?` 来作为标准库中的定义的命名型类型 `Optional` 的语法糖。换句话说,下面两个声明是等价的: @@ -332,11 +332,11 @@ optionalInteger! // 42 > 可选类型语法 > - +optional-type {#optional-type} > *可选类型* → [*类型*](#type) **?** > - +implicitly_unwrapped_optional_type {#implicitly_unwrapped_optional_type} ## 隐式解析可选类型 当可以被访问时,Swift 语言定义后缀 `!` 作为标准库中命名类型 `Optional` 的语法糖,来实现自动解包的功能。换句话说,下面两个声明等价: @@ -368,11 +368,11 @@ let implicitlyUnwrappedArray: [Int]! // 正确 > 隐式解析可选类型语法 > - +implicitly-unwrapped-optional-type {#implicitly-unwrapped-optional-type} > *隐式解析可选类型* → [*类型*](#type) **!** > - +protocol_composition_type {#protocol_composition_type} ## 协议合成类型 协议合成类型是一种符合协议列表中每个指定协议的类型。协议合成类型可能会用在类型注解和泛型参数中。 @@ -388,17 +388,17 @@ let implicitlyUnwrappedArray: [Int]! // 正确 > 协议合成类型语法 > - +protocol-composition-type {#protocol-composition-type} > *协议合成类型* → [*协议标识符*](#protocol-identifier) & [*协议合成延续*](#protocol-composition-continuation) > - +protocol-composition-continuation {#protocol-composition-continuation} > *协议合成延续* → [*协议标识符*](#protocol-identifier) | [*协议合成类型*](#protocol-composition-type) > - +protocol-identifier {#protocol-identifier} > *协议标识符* → [*类型标识符*](#type-identifier) > - +metatype_type {#metatype_type} ## 元类型 元类型是指类型的类型,包括类类型、结构体类型、枚举类型和协议类型。 @@ -445,11 +445,11 @@ let anotherInstance = metatype.init(string: "some string") > 元类型语法 > - +metatype-type {#metatype-type} > *元类型* → [*类型*](#type) **.** **Type** | [*类型*](#type) **.** **Protocol** > - +type_inheritance_clause {#type_inheritance_clause} ## 类型继承子句 类型继承子句被用来指定一个命名型类型继承自哪个类、采纳哪些协议。类型继承子句也用来指定一个类类型专属协议。类型继承子句开始于冒号 `:`,其后是所需要的类、类型标识符列表或两者都有。 @@ -462,16 +462,16 @@ let anotherInstance = metatype.init(string: "some string") > 类型继承子句语法 > - +type_inheritance_clause {#type_inheritance_clause} > *类型继承子句* → **:** [*类型继承列表*](#type-inheritance-list) > - +type-inheritance-list {#type-inheritance-list} > *类型继承列表* → [*类型标识符*](#type-identifier) | [*类型标识符*](#type-identifier) **,** [*类型继承列表*](#type-inheritance-list) > - +class-requirement {#class-requirement} - +type_inference {#type_inference} ## 类型推断 Swift 广泛使用类型推断,从而允许你省略代码中很多变量和表达式的类型或部分类型。比如,对于 `var x: Int = 0`,你可以完全省略类型而简写成 `var x = 0`,编译器会正确推断出 `x` 的类型 `Int`。类似的,当完整的类型可以从上下文推断出来时,你也可以省略类型的一部分。比如,如果你写了 `let dict: Dictionary = ["A" : 1]`,编译器能推断出 `dict` 的类型是 `Dictionary`。 diff --git a/source/chapter3/04_Expressions.md b/source/chapter3/04_Expressions.md index 35c3c58b..e80943a3 100644 --- a/source/chapter3/04_Expressions.md +++ b/source/chapter3/04_Expressions.md @@ -6,16 +6,14 @@ Swift 中存在四种表达式:前缀表达式,二元表达式,基本表 > 表达式语法 > - +expression {#expression} > *表达式* → [*try 运算符*](#try-operator)可选 [*前缀表达式*](#prefix-expression) [*二元表达式列表*](#binary-expressions)可选 > - +expression-list {#expression-list} > *表达式列表* → [*表达式*](#expression) | [*表达式*](#expression) **,** [*表达式列表*](#expression-list) > - -## 前缀表达式 - +## 前缀表达式 {#prefix_expressions} 前缀表达式由可选的前缀运算符和表达式组成。前缀运算符只接收一个参数,表达式则紧随其后。 关于这些运算符的更多信息,请参阅 [基本运算符](../chapter2/02_Basic_Operators.md) 和 [高级运算符](../chapter2/26_Advanced_Operators.md)。 @@ -26,18 +24,16 @@ Swift 中存在四种表达式:前缀表达式,二元表达式,基本表 > 前缀表达式语法 > - +prefix-expression {#prefix-expression} > *前缀表达式* → [*前缀运算符*](./02_Lexical_Structure.md#prefix-operator)可选 [*后缀表达式*](#postfix-expression) > > *前缀表达式* → [*输入输出表达式*](#in-out-expression) > - +in-out-expression {#in-out-expression} > *输入输出表达式* → **&** [*标识符*](./02_Lexical_Structure.md#identifier) > - -### Try 运算符 - +### Try 运算符 {#try_operator} try 表达式由 `try` 运算符加上紧随其后的可抛出错误的表达式组成,形式如下: > try `可抛出错误的表达式` @@ -70,13 +66,11 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误:try 关于 `try`、`try?` 和 `try!` 的更多信息,以及该如何使用的例子,请参阅 [错误处理](../chapter2/17_Error_Handling.md)。 > Try 表达式语法 > - +try-operator {#try-operator} > *try 运算符* → **try** | **try?** | **try!** > - -## 二元表达式 - +## 二元表达式 {#binary_expressions} *二元表达式*由中缀运算符和左右参数表达式组成。形式如下: > `左侧参数` `二元运算符` `右侧参数` @@ -91,7 +85,7 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误:try > 在解析时,一个二元表达式将作为一个扁平列表表示,然后根据运算符的优先级,再进一步进行组合。例如,`2 + 3 * 5` 首先被看作具有五个元素的列表,即 `2`、`+`、`3`、`*`、`5`,随后根据运算符优先级组合为 `(2 + (3 * 5))`。 > - +binary-expression {#binary-expression} > 二元表达式语法 > > *二元表达式* → [*二元运算符*](./02_Lexical_Structure.md#binary-operator) [*前缀表达式*](#prefix-expression) @@ -102,13 +96,11 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误:try > > *二元表达式* → [*类型转换运算符*](#type-casting-operator) > - +binary-expressions {#binary-expressions} > *二元表达式列表* → [*二元表达式*](#binary-expression) [*二元表达式列表*](#binary-expressions)可选 > - -### 赋值表达式 - +### 赋值表达式 {#assignment_operator} 赋值表达式会为某个给定的表达式赋值,形式如下; > `表达式` = `值` @@ -125,13 +117,11 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误:try > 赋值运算符语法 > - +assignment-operator {#assignment-operator} > *赋值运算符* → **=** > - -### 三元条件运算符 - +### 三元条件运算符 {#ternary_conditional_operator} *三元条件运算符*会根据条件来对两个给定表达式中的一个进行求值,形式如下: > `条件` ? `表达式(条件为真则使用)` : `表达式(条件为假则使用)` @@ -143,13 +133,11 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误:try > 三元条件运算符语法 > - +conditional-operator {#conditional-operator} > *三元条件运算符* → **?** [*表达式*](#expression) **:** > - -### 类型转换运算符 - +### 类型转换运算符 {#type-casting_operators} 有 4 种类型转换运算符:`is`、`as`、`as? ` 和 `as!`。它们有如下的形式: > `表达式` is `类型` @@ -188,7 +176,7 @@ f(x as Any) 关于类型转换的更多内容和例子,请参阅 [类型转换](../chapter2/18_Type_Casting.md)。 - +type-casting-operator {#type-casting-operator} > 类型转换运算符语法 > > *类型转换运算符* → **is** [*类型*](./03_Types.md#type) @@ -200,14 +188,12 @@ f(x as Any) > *类型转换运算符* → **as** **!** [*类型*](./03_Types.md#type) > - -## 基本表达式 - +## 基本表达式 {#primary_expressions} *基本表达式*是最基本的表达式。它们可以单独使用,也可以跟前缀表达式、二元表达式、后缀表达式组合使用。 > 基本表达式语法 > - +primary-expression {#primary-expression} > *基本表达式* → [*标识符*](./02_Lexical_Structure.md#identifier) [*泛型实参子句*](./09_Generic_Parameters_and_Arguments.md#generic-argument-clause)可选 > > *基本表达式* → [*字面量表达式*](#literal-expression) @@ -229,9 +215,7 @@ f(x as Any) > *基本表达式* → [*key-path字符串表达式*](#key-patch-string-expression) > - -### 字面量表达式 - +### 字面量表达式 {#literal_expression} *字面量表达式*可由普通字面量(例如字符串或者数字),字典或者数组字面量,或者下面列表中的特殊字面量组成: 字面量 | 类型 | 值 @@ -285,7 +269,7 @@ Xcode 使用 playground 字面量对程序编辑器中的颜色、文件或者 > 字面量表达式语法 > > -> +> literal-expression {#literal-expression} > > *字面量表达式* → [*字面量*](./02_Lexical_Structure.md#literal) > @@ -295,38 +279,38 @@ Xcode 使用 playground 字面量对程序编辑器中的颜色、文件或者 > -> +> array-literal {#array-literal} > > *数组字面量* → [[*数组字面量项列表*](#array-literal-items)可选 **]** > -> +> array-literal-items {#array-literal-items} > > *数组字面量项列表* → [*数组字面量项*](#array-literal-item) **,**可选 | [*数组字面量项*](#array-literal-item) **,** [*数组字面量项列表*](#array-literal-items) > -> +> array-literal-item {#array-literal-item} > > *数组字面量项* → [*表达式*](#expression) > > -> +> dictionary-literal {#dictionary-literal} > > *字典字面量* → [[*字典字面量项列表*](#dictionary-literal-items) **]** | **[** **:** **]** > -> +> dictionary-literal-items {#dictionary-literal-items} > > *字典字面量项列表* → [*字典字面量项*](#dictionary-literal-item) **,**可选 | [*字典字面量项*](#dictionary-literal-item) **,** [*字典字面量项列表*](#dictionary-literal-items) > -> +> dictionary-literal-item {#dictionary-literal-item} > > *字典字面量项* → [*表达式*](#expression) **:** [*表达式*](#expression)。 > -> +> playground-literal {#playground-literal} > > *playground 字面量* → **#colorLiteral ( red : [*表达式*](#expression) , green :[*表达式*](#expression) [*表达式*](#e[*表达式*](#expression) xpression) , blue :[*表达式*](#expression) , alpha : [*表达式*](#expression) )** > > *playground 字面量* → **#fileLiteral ( resourceName : [*表达式*](#expression) )** > -> playground 字面量* → **#imageLiteral ( resourceName : [*表达式*](#expression) )** +> playground 字面量* → **#imageLiteral ( resourceName : [*表达式*](#expression) )**self_expression {#self_expression} > ### Self 表达式 @@ -370,23 +354,21 @@ struct Point { > Self 表达式语法 > - +self-expression {#self-expression} > *self 表达式* → **self** | [*self 方法表达式*](#self-method-expression) | [*self 下标表达式*](#self-subscript-expression) | [*self 构造器表达式*](#self-initializer-expression) > > - +self-method-expression {#self-method-expression} > *self 方法表达式* → **self** **.** [*标识符*](./02_Lexical_Structure.md#identifier) > - +self-subscript-expression {#self-subscript-expression} > *self 下标表达式* → **self** **[** [*函数调用参数表*](#function-call-argument-list­) **]** > - +self-initializer-expression {#self-initializer-expression} > *self 构造器表达式* → **self** **.** **init** > - -### 父类表达式 - +### 父类表达式 {#superclass_expression} *父类*表达式可以使我们在某个类中访问它的父类,它有如下形式: > super.`成员名称` @@ -402,22 +384,20 @@ struct Point { > 父类表达式语法 > - +superclass-expression {#superclass-expression} > *父类表达式* → [*父类方法表达式*](#superclass-method-expression) | [*父类下标表达式*](#superclass-subscript-expression) | [*父类构造器表达式*](#superclass-initializer-expression) > - +superclass-method-expression {#superclass-method-expression} > *父类方法表达式* → **super** **.** [*标识符*](./02_Lexical_Structure.md#identifier) > - +superclass-subscript-expression {#superclass-subscript-expression} > *父类下标表达式* → **super** [[*函数调用参数表*](#function-call-argument-list­) **]** > - +superclass-initializer-expression {#superclass-initializer-expression} > *父类构造器表达式* → **super** **.** **init** > - -### 闭包表达式 - +### 闭包表达式 {#closure_expression} *闭包表达式*会创建一个闭包,在其他语言中也叫 *lambda* 或*匿名*函数。跟函数一样,闭包包含了待执行的代码,不同的是闭包还会捕获所在环境中的常量和变量。它的形式如下: ```swift @@ -460,10 +440,7 @@ myFunction { $0 + $1 } 关于逃逸闭包的内容,请参阅[逃逸闭包](./chapter2/07_Closures.md#escaping_closures) - - -## 捕获列表 - +## 捕获列表 {#capture-lists} 默认情况下,闭包会捕获附近作用域中的常量和变量,并使用强引用指向它们。你可以通过一个*捕获列表*来显式指定它的捕获行为。 捕获列表在参数列表之前,由中括号括起来,里面是由逗号分隔的一系列表达式。一旦使用了捕获列表,就必须使用 `in` 关键字,即使省略了参数名、参数类型和返回类型。 @@ -524,12 +501,12 @@ myFunction { [weak parent = self.parent] in print(parent!.title) } > 闭包表达式语法 > > -> +> closure-expression {#closure-expression} > > *闭包表达式* → **{** [*闭包签名*](#closure-signature)可选 [*语句*](#statements) **}** > > -> +> closure-signature {#closure-signature} > > > 闭包签名* → [*参数子句*](#parameter-clause) [*函数结果*](05_Declarations.md#function-result)可选 **in** @@ -543,27 +520,25 @@ myFunction { [weak parent = self.parent] in print(parent!.title) } > *闭包签名* → [*捕获列表*](#capture-list) **in** > > -> +> capture-list {#capture-list} > > > 捕获列表* → [ [*捕获列表项列表*](#capture-list-items) **]** > -> +> capture-list-items {#capture-list-items} > > *捕获列表项列表* → [*捕获列表项*](#capture-list-item) | [*捕获列表项*](#capture-list-item) **,** [*捕获列表项列表*](#capture-list-items) > -> +> capture-list-item {#capture-list-item} > > *捕获列表项* → [*捕获说明符*](#capture-specifier)可选 [*表达式*](#expression) > -> +> capture-specifier {#capture-specifier} > > *捕获说明符* → **weak** | **unowned** | **unowned(safe)** | **unowned(unsafe)** > - -### 隐式成员表达式 - +### 隐式成员表达式 {#implicit_member_expression} 若类型可被推断出来,可以使用*隐式成员表达式*来访问某个类型的成员(例如某个枚举成员或某个类型方法),形式如下: > .`成员名称` @@ -578,24 +553,20 @@ x = .AnotherValue > 隐式成员表达式语法 > - +implicit-member-expression {#implicit-member-expression} > *隐式成员表达式* → **.** [*标识符*](./02_Lexical_Structure.md#identifier) > - -### 圆括号表达式 - +### 圆括号表达式 {#parenthesized_expression} *圆括号表达式*是由圆括号包围的表达式。你可以用圆括号说明成组的表达式的先后操作。成组的圆括号不会改变表达式的类型 - 例如 `(1)` 的类型就是简单的 `Int`。 > 圆括号表达式语法 > - +parenthesized-expression {#parenthesized-expression} > *圆括号表达式* → **( [*表达式*](#expression) )** > - -### 元组表达式 - +### 元组表达式 {#Tuple_Expression} *元组表达式*由圆括号和其中多个逗号分隔的子表达式组成。每个子表达式前面可以有一个标识符,用冒号隔开。元组表达式形式如下: > (`标识符 1` : `表达式 1`, `标识符 2` : `表达式 2`, `...`) @@ -612,20 +583,17 @@ x = .AnotherValue > 元组表达式语法 > - +tuple-expression {#tuple-expression} > *元组表达式* → **( )** | **(**[*元组元素*](#tuple-element), [*元组元素列表*](#tuple-element-list) **)** > - +tuple-element-list {#tuple-element-list} > *元组元素列表* → [*元组元素*](#tuple-element) | [*元组元素*](#tuple-element) **,** [*元组元素列表*](#tuple-element-list) > - +tuple-element {#tuple-element} > *元组元素* → [*表达式*](#expression) | [*标识符*](identifier) **:** [*表达式*](#expression) > - - -### 通配符表达式 - +### 通配符表达式 {#wildcard_expression} *通配符表达式*可以在赋值过程中显式忽略某个值。例如下面的代码中,`10` 被赋值给 `x`,而 `20` 则被忽略: ```swift @@ -635,14 +603,12 @@ x = .AnotherValue > 通配符表达式语法 > - +wildcard-expression {#wildcard-expression} > *通配符表达式* → **_** > - -### Key-path 表达式 - +### Key-path 表达式 {#key-path_expression} Key-path 表达式引用一个类型的属性或下标。在动态语言中使场景可以使用 Key-path 表达式,例如观察键值对。格式为: > **\类型名.路径** @@ -774,31 +740,28 @@ print(interestingNumbers[keyPath: \[String: [Int]].["hexagonal"]!.count.bitWidth > key-path 表达式语法 > > -> +> key-path-expression {#key-path-expression} > > *key-path 表达式* → **\\** [类型](./03_Types.md#type)可选 **.** [多个 key-path 组件](#key-path-components) > -> +> key-path-components {#key-path-components} > > *多个 key-path 组件* → [key-path 组件](#key-path-component) | [key-path 组件](#key-path-component) **.** [多个 key-path 组件](#key-path-components) > -> +> key-path-component {#key-path-component} > > *key-path 组件* → [标识符](./02_Lexical_Structure.md#identifier) [多个 key-path 后缀](#key-path-postfixes)可选 | [多个 key-path 后缀](#key-path-postfixes) > -> +> key-path-postfixes {#key-path-postfixes} > -> *多个 key-path 后缀* → [key-path 后缀](#key-path-postfix) [多个 key-path 后缀](#key-path-postfixes)可选 +> *多个 key-path 后缀* → [key-path 后缀](#key-path-postfix) [多个 key-path 后缀](#key-path-postfixes)可选 key-path-postfixes {#key-path-postfixes} > > *key-path 后缀* → **?** | **!** | **self** | **\[** [函数调用参数表](#function-call-argument-list) **\]** > - - -### 选择器表达式 - +### 选择器表达式 {#selector_expression} *选择器表达式*可以让你通过选择器来引用在 Objective-C 中方法(method)和属性(property)的 setter 和 getter 方法。 > \#selector(方法名) @@ -846,7 +809,7 @@ let anotherSelector = #selector(SomeClass.doSomething(_:) as (SomeClass) -> (Str > 选择器表达式语法 > - +selector-expression {#selector-expression} > *选择器表达式* → __#selector__ **(** [*表达式*](#expression) **)** > > *选择器表达式* → __#selector__ **(** [*getter:表达式*](#expression) **)** @@ -854,10 +817,7 @@ let anotherSelector = #selector(SomeClass.doSomething(_:) as (SomeClass) -> (Str > *选择器表达式* → __#selector__ **(** [*setter:表达式*](#expression) **)** > - - -## Key-path 字符串表达式 - +## Key-path 字符串表达式 {#key-path_string_expressions} key-path 字符串表达式可以访问一个引用 Objective-C 属性的字符串,通常在 key-value 编程和 key-value 观察 APIs 中使用。其格式如下: > `#keyPath` ( `属性名` ) @@ -907,15 +867,12 @@ print(keyPath == c.getSomeKeyPath()) > key-path 字符串表达式语法 > -> +> key-path-string-expression {#key-path-string-expression} > > *key-path 字符串表达式* → **#keyPath (** [表达式](#expression) **)** > - - -## 后缀表达式 - +## 后缀表达式 {#postfix_expressions} *后缀表达式*就是在某个表达式的后面运用后缀运算符或其他后缀语法。从语法构成上来看,基本表达式也是后缀表达式。 关于这些运算符的更多信息,请参阅 [基本运算符](../chapter2/02_Basic_Operators.md) 和 [高级运算符](../chapter2/26_Advanced_Operators.md)。 @@ -924,7 +881,7 @@ print(keyPath == c.getSomeKeyPath()) > 后缀表达式语法 > - +postfix-expression {#postfix-expression} > *后缀表达式* → [*基本表达式*](#primary-expression) > > *后缀表达式* → [*后缀表达式*](#postfix-expression) [*后缀运算符*](02_Lexical_Structure.md#postfix-operator) @@ -946,9 +903,7 @@ print(keyPath == c.getSomeKeyPath()) > *后缀表达式* → [*可选链表达式*](#optional-chaining-expression) > - -### 函数调用表达式 - +### 函数调用表达式 {#function_call_expression} *函数调用表达式*由函数名和参数列表组成,形式如下: > `函数名`(`参数 1`, `参数 2`) @@ -979,36 +934,34 @@ myData.someMethod {$0 == 13} > 函数调用表达式语法 > -> +> function-call-expression {#function-call-expression} > > *函数调用表达式* → [*后缀表达式*](#postfix-expression) [*函数调用参数子句*](#function-call-argument-clause) > > *函数调用表达式* → [*后缀表达式*](#postfix-expression) [*函数调用参数子句*](#function-call-argument-clause)可选 [*尾随闭包*](#trailing-closure) > > -> +> function-call-argument-clause {#function-call-argument-clause} > > *函数调用参数子句* → **(** **)** | **(** [*函数调用参数表*](#function-call-argument-list) **)** > -> +> function-call-argument-list {#function-call-argument-list} > > *函数调用参数表* → [函数调用参数](#function-call-argument) | [函数调用参数](#function-call-argument) **,** [*函数调用参数表*](#function-call-argument-list) > -> +> function-call-argument {#function-call-argument} > > *函数调用参数* → [表达式](#expression) | [标识符](02_Lexical_Structure.md#identifier) **:** [*表达式*](#expression) > > *函数调用参数* → [运算符](./02_Lexical_Structure.md#operator) | [标识符](./02_Lexical_Structure.md#identifier) **:** [*运算符*](./02_Lexical_Structure.md#operator) > > -> +> trailing-closure {#trailing-closure} > > *尾随闭包* → [*闭包表达式*](#closure-expression) > - -### 构造器表达式 - +### 构造器表达式 {#initializer_expression} *构造器表达式*用于访问某个类型的构造器,形式如下: > `表达式`.init(`构造器参数`) @@ -1048,15 +1001,13 @@ let s3 = someValue.dynamicType.init(data: 7) // 有效 > 构造器表达式语法 > - +initializer-expression {#initializer-expression} > *构造器表达式* → [*后缀表达式*](#postfix-expression) **.** **init** > > *构造器表达式* → [*后缀表达式*](#postfix-expression) **.** **init** **(** [*参数名称*](#argument-names) **)** > - -### 显式成员表达式 - +### 显式成员表达式 {#explicit_member_expression} *显式成员表达式*允许我们访问命名类型、元组或者模块的成员,其形式如下: > `表达式`.`成员名` @@ -1116,23 +1067,21 @@ let x = [10, 3, 20, 15, 4] > 显式成员表达式语法 > - +explicit-member-expression {#explicit-member-expression} > *显式成员表达式* → [*后缀表达式*](#postfix-expression) **.** [*十进制数字*] (02_Lexical_Structure.md#decimal-digit) > > *显式成员表达式* → [*后缀表达式*](#postfix-expression) **.** [*标识符*](02_Lexical_Structure.md#identifier) [*泛型实参子句*](./09_Generic_Parameters_and_Arguments.md#generic-argument-clause)可选
> > *显式成员表达式* → [*后缀表达式*](#postfix-expression) **.** [*标识符*] (02_Lexical_Structure.md#identifier) **(** [*参数名称*](#argument-names) **)** > - +argument-names {#argument-names} > *参数名称* → [*参数名*](#argument-name) [*参数名称*](#argument-names)可选
> - +argument-name {#argument-name} > *参数名* → [*标识符*](./02_Lexical_Structure.md#identifier) **:** > - -### 后缀 self 表达式 - +### 后缀 self 表达式 {#postfix_self_expression} 后缀 `self` 表达式由某个表达式或类型名紧跟 `.self` 组成,其形式如下: > `表达式`.self @@ -1146,14 +1095,12 @@ let x = [10, 3, 20, 15, 4] > 后缀 self 表达式语法 > - +postfix-self-expression {#postfix-self-expression} > *后缀 self 表达式* → [*后缀表达式*](#postfix-expression) **.** **self** > - -### 下标表达式 - +### 下标表达式 {#subscript_expression} 可通过*下标表达式*访问相应的下标,形式如下: > `表达式`[`索引表达式`] @@ -1165,13 +1112,11 @@ let x = [10, 3, 20, 15, 4] > 下标表达式语法 > - +subscript-expression {#subscript-expression} > *下标表达式* → [*后缀表达式*](#postfix-expression) **[** [*表达式列表*](#expression-list) **]** > - -### 强制取值表达式 - +### 强制取值表达式 {#forced-Value_expression} 当你确定可选值不是 `nil` 时,可以使用*强制取值表达式*来强制解包,形式如下: > `表达式`! @@ -1193,13 +1138,11 @@ someDictionary["a"]![0] = 100 > 强制取值语法 > - +forced-value-expression {#forced-value-expression} > *强制取值表达式* → [*后缀表达式*](#postfix-expression) **!** > - -### 可选链表达式 - +### 可选链表达式 {#optional-chaining_expression} *可选链表达式*提供了一种使用可选值的便捷方法,形式如下: > `表达式`? @@ -1247,6 +1190,6 @@ someDictionary["a"]?[0] = someFunctionWithSideEffects() > 可选链表达式语法 > - +optional-chaining-expression {#optional-chaining-expression} > *可选链表达式* → [*后缀表达式*](#postfix-expression) **?** > diff --git a/source/chapter3/05_Statements.md b/source/chapter3/05_Statements.md index 97d91deb..59b0b262 100755 --- a/source/chapter3/05_Statements.md +++ b/source/chapter3/05_Statements.md @@ -1,6 +1,4 @@ - -# 语句(Statements) - +# 语句(Statements) {#statement_statements} 在 Swift 中,有三种类型的语句:简单语句、编译器控制语句和控制流语句。简单语句是最常见的,用于构造表达式或者声明。编译器控制语句允许程序改变编译器的行为,包含编译配置语句和行控制语句。 控制流语句则用于控制程序执行的流程,Swift 中有多种类型的控制流语句:循环语句、分支语句和控制转移语句。循环语句用于重复执行代码块;分支语句用于执行满足特定条件的代码块;控制转移语句则用于改变代码的执行顺序。另外,Swift 提供了 `do` 语句,用于构建局部作用域,还用于错误的捕获和处理;还提供了 `defer` 语句,用于退出当前作用域之前执行清理操作。 @@ -9,7 +7,7 @@ > 语句语法 > - +statement {#statement} > *语句* → [*表达式*](./04_Expressions.md#expression) **;**可选 > > *语句* → [*声明*](./06_Declarations.md#declaration) **;**可选 @@ -28,13 +26,11 @@ > > *语句* → [*编译器控制语句*](#compiler-control-statement) > - +statements {#statements} > *多条语句* → [*语句*](#statement) [*多条语句*](#statements)可选 > - -## 循环语句 - +## 循环语句 {#loop_statements} 循环语句会根据特定的循环条件来重复执行代码块。Swift 提供三种类型的循环语句:`for-in` 语句、`while` 语句和 `repeat-while` 语句。 通过 `break` 语句和 `continue` 语句可以改变循环语句的控制流。有关这两条语句,详情参见 [Break 语句](#break_statement) 和 [Continue 语句](#continue_statement)。 @@ -42,7 +38,7 @@ > 循环语句语法 > > - +loop-statement {#loop-statement} > *循环语句* → [*for-in 语句*](#for-in-statement) > > *循环语句* → [*while 语句*](#while-statement) @@ -50,9 +46,7 @@ > *循环语句* → [*repeat-while 语句*](#repeat-while-statement) > - -### For-In 语句 - +### For-In 语句 {#for-in_statements} `for-in` 语句会为集合(或实现了 `SequenceType` 协议的任意类型)中的每一项执行一次代码块。 `for-in` 语句的形式如下: @@ -68,13 +62,11 @@ for item in collection { > for-in 语句语法 > > - +for-in-statement {#for-in-statement} > *for-in 语句* → **for** **case**可选 [*模式*](./08_Patterns.md#pattern) **in** [*表达式*](./04_Expressions.md#expression) [*where 子句*](#where-clause)可选 [*代码块*](05_Declarations.md#code-block) > - -### While 语句 - +### While 语句 {#while_statements} 只要循环条件为真,`while` 语句就会重复执行代码块。 `while` 语句的形式如下: @@ -97,27 +89,25 @@ while condition { > while 语句语法 > > - +while-statement {#while-statement} > *while 语句* → **while** [*条件子句*](#condition-clause) [*代码块*](05_Declarations.md#code-block) > - +condition-clause {#condition-clause} > *条件子句* → [*表达式*](./04_Expressions.md#expression) | [*表达式*](./04_Expressions.md#expression) **,** [*条件列表*](#condition-list) > - +condition {#condition} > *条件* → [*表达式*](./04_Expressions.md#expression) |[*可用性条件*](#availability-condition) | [*case 条件*](#case-condition) | [*可选绑定条件*](#optional-binding-condition) > > - +case-condition {#case-condition} > *case 条件* → **case** [*模式*](./08_Patterns.md#pattern) [*构造器*](./06_Declarations.md#initializer) > - +optional-binding-condition {#optional-binding-condition} > *可选绑定条件* → **let** [*模式*](./08_Patterns.md#pattern) [*构造器*](./06_Declarations.md#initializer) | **var** [*模式*](./08_Patterns.md#pattern) [*构造器*](./06_Declarations.md#initializer) > - -### Repeat-While 语句 - +### Repeat-While 语句 {#repeat-while_statements} `repeat-while` 语句至少执行一次代码块,之后只要循环条件为真,就会重复执行代码块。 `repeat-while` 语句的形式如下: @@ -140,13 +130,11 @@ repeat { > repeat-while 语句语法 > > - +repeat-while-statement {#repeat-while-statement} > *repeat-while 语句* → **repeat** [*代码块*](./06_Declarations.md#code-block) **while** [*表达式*](./04_Expressions.md#expression) > - -## 分支语句 - +## 分支语句 {#branch_statements} 分支语句会根据一个或者多个条件来执行指定部分的代码。分支语句中的条件将会决定程序如何分支以及执行哪部分代码。Swift 提供三种类型的分支语句:`if` 语句、 `guard` 语句和 `switch` 语句。 `if` 语句和 `switch` 语句中的控制流可以用 `break` 语句改变,请参阅 [Break 语句](#break_statement)。 @@ -154,7 +142,7 @@ repeat { > 分支语句语法 > > - +branch-statement {#branch-statement} > *分支语句* → [*if 语句*](#if-statement) > > *分支语句* → [*guard 语句*](#guard-statement) @@ -162,9 +150,7 @@ repeat { > *分支语句* → [*switch 语句*](#switch-statement) > - -### If 语句 - +### If 语句 {#if_statements} `if` 语句会根据一个或多个条件来决定执行哪一块代码。 `if` 语句有两种基本形式,无论哪种形式,都必须有花括号。 @@ -204,16 +190,14 @@ if condition 1 { > if 语句语法 > > - +if-statement {#if-statement} > *if 语句* → **if** [*条件子句*](#condition-clause) [*代码块*](05_Declarations.md#code-block) [*else 子句*](#else-clause)可选 > - +else-clause {#else-clause} > *else 子句* → **else** [*代码块*](./06_Declarations.md#code-block) | **else** [*if 语句*](#if-statement) > - -### Guard 语句 - +### Guard 语句 {#guard_statements} 如果一个或者多个条件不成立,可用 `guard` 语句来退出当前作用域。 `guard` 语句的格式如下: @@ -240,13 +224,11 @@ guard condition else { > guard 语句语法 > > - +guard-statement {#guard-statement} > *guard 语句* → **guard** [*条件子句*](#condition-clause) **else** [*代码块*] (05_Declarations.md#code-block) > - -### Switch 语句 - +### Switch 语句 {#switch_statements} `switch` 语句会根据控制表达式的值来决定执行哪部分代码。 `switch` 语句的形式如下: @@ -285,9 +267,7 @@ case let (x, y) where x == y: 在 Swift 中,`switch` 语句中控制表达式的每一个可能的值都必须至少有一个 `case` 与之对应。在某些无法面面俱到的情况下(例如,表达式的类型是 `Int`),你可以使用 `default` 分支满足该要求。 - -#### 对未来枚举的 `case` 进行 `switch` - +#### 对未来枚举的 `case` 进行 `switch` {#future-case} 非冻结枚举(`nonfronzen enumeration`)是一种特殊的枚举类型,它可能在未来会增加新的枚举 `case`,即使这时候你已经编译并且发布了你的应用,所以在 switch 非冻结枚举前需要深思熟虑。当一个库的作者们把一个枚举标记为非冻结的,这意味着他们保留了增加新的枚举 `case` 的权利,并且任何和这个枚举交互的代码都要在不需要重新编译的条件下能够处理那些未来可能新加入的 `case` 。只有那些标准库,比如用 Swift 实现的苹果的一些框架,C 以及 Objective-C 代码才能够声明非冻结枚举。你在 Swift 中声明的枚举不能是非冻结的。 当你对未来枚举进行 switch 时,你总是需要有一个 `default case`,即使每种枚举类型都已经有对应的 `case` 了。你可以在 default 前标注 `@unknown` ,意思是这个 `case` 应该只匹配未来加入的枚举 `case` 。如果你的 `default case` 中匹配了任何在编译时就能确定的枚举 `case` ,Swift 会抛出一个警告。这可以很好地提醒你库的作者已经新增了一种 `case` ,并且你还没有去处理。 @@ -316,52 +296,50 @@ case .suppressed: > switch 语句语法 > > - +switch-statement {#switch-statement} > *switch 语句* → **switch** [*表达式*](./04_Expressions.md#expression) **{** [*switch-case 列表*](#switch-cases)可选 **}** > - +switch-cases {#switch-cases} > *switch case 列表* → [*switch-case*](#switch-case) [*switch-case 列表*](#switch-cases)可选 > - +switch-case {#switch-case} > *switch case* → [*case 标签*](#case-label) [*多条语句*](#statements) | [*default 标签*](#default-label) [*多条语句*](#statements) | [*conditional-switch-case*](#conditional-switch-case-label) > - +case-label {#case-label} > *case 标签* → [*属性*](#switch-case-attributes-label)可选 **case** [*case 项列表*](#case-item-list) **:** > - +case-item-list {#case-item-list} > *case 项列表* → [*模式*](./08_Patterns.md#pattern) [*where 子句*](#where-clause)可选 | [*模式*](07_Patterns.md#pattern) [*where 子句*](#where-clause)可选 **,** [*case 项列表*](#case-item-list) > - +default-label {#default-label} > *default 标签* → [*属性*](#switch-case-attributes-label)可选 **default** **:** > > - +where-clause {#where-clause} > *where-clause* → **where** [*where 表达式*](#where-expression) > - +where-expression {#where-expression} > *where-expression* → [*表达式*](./04_Expressions.md#expression) > > - +grammar_conditional-switch-case {#grammar_conditional-switch-case} > *conditional-switch-case* → [*switch-if-directive-clause*](#switch-case-attributes-label) [*switch-elseif-directive-clauses*](#switch-case-attributes-label) 可选 [*switch-else-directive-clause*](#switch-case-attributes-label) 可选 [*endif-directive*](#switch-case-attributes-label) > - +grammar_switch-if-directive-clause {#grammar_switch-if-directive-clause} > *switch-if-directive 语句* → [*if-directive*](#switch-case-attributes-label) [*compilation-condition*](#switch-case-attributes-label) [*switch-cases*](#switch-case-attributes-label) 可选 > - +grammar_switch-elseif-directive-clauses {#grammar_switch-elseif-directive-clauses} > *switch-elseif-directive 语句(复数)* → [*elseif-directive-clause*](#switch-case-attributes-label) [*switch-elseif-directive-clauses*](#switch-case-attributes-label)可选 > - +grammar_switch-elseif-directive-clause {#grammar_switch-elseif-directive-clause} > *switch-elseif-directive 语句* → [*elseif-directive*](#switch-case-attributes-label) [*compilation-condition*](#switch-case-attributes-label) [*switch-cases*](#switch-case-attributes-label)可选 > - +grammar_switch-else-directive-clause {#grammar_switch-else-directive-clause} > *switch-else-directive 语句* → [*else-directive*](#switch-case-attributes-label) [*switch-cases*](#switch-case-attributes-label) 可选 > - -## 带标签的语句 - +## 带标签的语句 {#labeled_statements} 你可以在循环语句或 `switch` 语句前面加上标签,它由标签名和紧随其后的冒号(`:`)组成。在 `break` 和 `continue` 后面跟上标签名可以显式地在循环语句或 `switch` 语句中改变相应的控制流。关于这两条语句用法,请参阅 [Break 语句](#break_statement) 和 [Continue 语句](#continue_statement)。 标签的作用域在该标签所标记的语句内。可以嵌套使用带标签的语句,但标签名必须唯一。 @@ -371,7 +349,7 @@ case .suppressed: > 带标签的语句语法 > > - +labeled-statement {#labeled-statement} > *带标签的语句* → [*语句标签*](#statement-label) [*循环语句*](#grammar_loop-statement) > > *带标签的语句* → [*语句标签*](#statement-label) [*if 语句*](#if-statement) @@ -380,22 +358,20 @@ case .suppressed: > > > *带标签的语句* → [*语句标签*](#statement-label) [*do 语句*](#sdo-statement) > - +statement-label {#statement-label} > *语句标签* → [*标签名称*](#label-name) **:** > - +label-name {#label-name} > *标签名称* → [*标识符*](./02_Lexical_Structure.md#identifier) > - -## 控制转移语句 - +## 控制转移语句 {#control_transfer_statements} 控制转移语句能够无条件地把控制权从一片代码转移到另一片代码,从而改变代码执行的顺序。Swift 提供五种类型的控制转移语句:`break` 语句、`continue` 语句、`fallthrough` 语句、`return` 语句和 `throw` 语句。 > 控制转移语句语法 > > - +control-transfer-statement {#control-transfer-statement} > *控制转移语句* → [*break 语句*](#break-statement) > > *控制转移语句* → [*continue 语句*](#continue-statement) @@ -407,9 +383,7 @@ case .suppressed: > *控制转移语句* → [*throw 语句*](#throw-statement) > - -### Break 语句 - +### Break 语句 {#break_statement} `break` 语句用于终止循环语句、`if` 语句或 `switch` 语句的执行。使用 `break` 语句时,可以只写 `break` 这个关键词,也可以在 `break` 后面跟上标签名,像下面这样: > break @@ -428,13 +402,11 @@ case .suppressed: > break 语句语法 > > - +break-statement {#break-statement} > *break 语句* → **break** [*标签名称*](#label-name)可选 > - -### Continue 语句 - +### Continue 语句 {#continue_statement} `continue` 语句用于终止循环中当前迭代的执行,但不会终止该循环的执行。使用 `continue` 语句时,可以只写 `continue` 这个关键词,也可以在 `continue` 后面跟上标签名,像下面这样: > continue @@ -455,13 +427,11 @@ case .suppressed: > continue 语句语法 > > - +continue-statement {#continue-statement} > *continue 语句* → **continue** [*标签名称*](#label-name)可选 > - -### Fallthrough 语句 - +### Fallthrough 语句 {#fallthrough_statements} `fallthrough` 语句用于在 `switch` 语句中转移控制权。`fallthrough` 语句会把控制权从 `switch` 语句中的一个 `case` 转移到下一个 `case`。这种控制权转移是无条件的,即使下一个 `case` 的模式与 `switch` 语句的控制表达式的值不匹配。 `fallthrough` 语句可出现在 `switch` 语句中的任意 `case` 中,但不能出现在最后一个 `case` 中。同时,`fallthrough` 语句也不能把控制权转移到使用了值绑定的 `case`。 @@ -471,13 +441,11 @@ case .suppressed: > fallthrough 语句语法 > > - +fallthrough-statement {#fallthrough-statement} > *fallthrough 语句* → **fallthrough** > - -### Return 语句 - +### Return 语句 {#return_statements} `return` 语句用于在函数或方法的实现中将控制权转移到调用函数或方法,接着程序将会从调用位置继续向下执行。 使用 `return` 语句时,可以只写 `return` 这个关键词,也可以在 `return` 后面跟上表达式,像下面这样: @@ -500,13 +468,11 @@ case .suppressed: > return 语句语法 > > - +return-statement {#return-statement} > *return 语句* → **return** [*表达式*](./04_Expressions.md#expression)可选 > - -### Throw 语句 - +### Throw 语句 {#throw_statements} `throw` 语句出现在抛出函数或者抛出方法体内,或者类型被 `throws` 关键字标记的闭包表达式体内。 `throw` 语句使程序在当前作用域结束执行,并向外围作用域传播错误。抛出的错误会一直传递,直到被 `do` 语句的 `catch` 子句处理掉。 @@ -523,13 +489,11 @@ case .suppressed: > throw 语句语法 > > - +throw-statement {#throw-statement} > *throw 语句* → **throw** [*表达式*](./04_Expressions.md#expression) > - -## Defer 语句 - +## Defer 语句 {#defer_statements} `defer` 语句用于在退出当前作用域之前执行代码。 `defer` 语句形式如下: @@ -561,13 +525,11 @@ f() > defer 语句语法 > > - +defer-statement {#defer-statement} > *延迟语句* → **defer** [*代码块*](./06_Declarations.md#code-block) > - -## Do 语句 - +## Do 语句 {#do_statements} `do` 语句用于引入一个新的作用域,该作用域中可以含有一个或多个 `catch` 子句,`catch` 子句中定义了一些匹配错误条件的模式。`do` 语句作用域内定义的常量和变量只能在 `do` 语句作用域内使用。 Swift 中的 `do` 语句与 C 中限定代码块界限的大括号(`{}`)很相似,也并不会降低程序运行时的性能。 @@ -594,25 +556,23 @@ do { > do 语句语法 > > - +do-statement {#do-statement} > *do 语句* → **do** [*代码块*](./06_Declarations.md#code-block) [*多条 catch 子句*](#catch-clauses)可选 > - +catch-clauses {#catch-clauses} > *多条 catch 子句* → [*catch 子句*](#catch-clause) [*多条 catch 子句*](#catch-clauses)可选 > - +catch-clause {#catch-clause} > *catch 子句* → **catch** [*模式*](./08_Patterns.md#pattern)可选 [*where 子句*](#where-clause)可选 [*代码块*](05_Declarations.md#code-block) > - -## 编译器控制语句 - +## 编译器控制语句 {#compiler_control_statements} 编译器控制语句允许程序改变编译器的行为。Swift 有三种编译器控制语句:条件编译语句、线路控制语句和编译时诊断语句。 > 编译器控制语句语法 > > - +compiler-control-statement {#compiler-control-statement} > *编译器控制语句* → [*条件编译语句*](#grammar_conditional-compilation-block) > > *编译器控制语句* → [*线路控制语句*](#line-control-statement) @@ -620,9 +580,7 @@ do { > *编译器控制语句* → [*诊断语句*](#grammar_diagnostic-statement) > - -### 条件编译代码块 - +### 条件编译代码块 {#Conditional_Compilation_Block} 条件编译代码块可以根据一个或多个配置来有条件地编译代码。 每一个条件编译代码块都以 `#if` 开始,`#endif` 结束。如下: @@ -693,23 +651,23 @@ statements to compile if both compilation conditions are false > 即使没有被编译,编译配置中的语句仍然会被解析。然而,唯一的例外是编译配置语句中包含语言版本检测函数:仅当 `Swift` 编译器版本和语言版本检测函数中指定的版本号匹配时,语句才会被解析。这种设定能确保旧的编译器不会尝试去解析新 Swift 版本的语法。 > - +build-config-statement {#build-config-statement} > 条件编译代码块语法 > > - +grammar_conditional-compilation-block {#grammar_conditional-compilation-block} > *条件编译代码块* → [*if-directive 语句*](#grammar_if-directive-clause) [*elseif-directive 语句(复数)*](#grammar_elseif-directive-clauses)可选 [*else-directive 语句*](#grammar_else-directive-clause)可选 [*endif-directive*](#grammar_endif-directive) > - +grammar_if-directive-clause {#grammar_if-directive-clause} > *if-directive 语句* → [*if-directive*](#grammar_if-directive) [*编译条件*](#compilation-condition) [*语句(复数)*](#statements)可选 > - +grammar_elseif-directive-clauses {#grammar_elseif-directive-clauses} > *elseif-directive 语句(复数)* → [*elseif-directive 语句*](#grammar_elseif-directive-clause) [*elseif-directive 语句(复数)*](#grammar_elseif-directive-clauses) > - +grammar_elseif-directive-clauses {#grammar_elseif-directive-clauses} > *elseif-directive 语句* → [*elseif-directive*](#grammar_elseif-directive) [*编译条件*](#compilation-condition) [*语句(复数)*](#statements)可选 > - +grammar_else-directive-clause {#grammar_else-directive-clause} > *else-directive 语句* → [*else-directive*](#grammar_else-directive) [*语句(复数)*](#statements)可选 > @@ -723,7 +681,7 @@ statements to compile if both compilation conditions are false > *endif-directive* → **#endif** > - +compilation-condition {#compilation-condition} > *编译条件* → [*平台条件*](#grammar_platform-condition) > > *编译条件* → [*标识符*](./02_Lexical_Structure.md#identifier) @@ -739,47 +697,45 @@ statements to compile if both compilation conditions are false > *编译条件* → [*编译条件*](#compilation-condition) **||** [*编译条件*](#compilation-condition) > - - +grammar_platform-condition {#grammar_platform-condition} +grammar_platform-condition-os {#grammar_platform-condition-os} > *平台条件* → **os ( [*操作系统*](#operating-system) )** > - +grammar_platform-condition-arch {#grammar_platform-condition-arch} > *平台条件* → **arch ( [*架构*](#architecture) )** > - +grammar_platform-condition-swift {#grammar_platform-condition-swift} > *平台条件* → **swift ( >= [*swift 版本*](#swift-version) )** | **swift ( < [*swift 版本*](#swift-version) )** > - +grammar_platform-condition-compiler {#grammar_platform-condition-compiler} > *平台条件* → **compiler ( >= [*swift 版本*](#swift-version) )** | **compiler ( < [*swift 版本*](#swift-version) )** > - +grammar_platform-condition-canImport {#grammar_platform-condition-canImport} > *平台条件* → **canImport ( [*模块名*](#grammar_module-name) )** > - +grammar_platform-condition-targetEnvironment {#grammar_platform-condition-targetEnvironment} > *平台条件* → **targetEnvironment ( [*环境*](#grammar_environment) )** > - +operating-system {#operating-system} > *操作系统* → **macOS** | **iOS** | **watchOS** | **tvOS** > - +architecture {#architecture} > *架构* → **i386** | **x86_64** | **arm** | **arm64** > - +swift-version {#swift-version} > *swift 版本* → [*十进制数字*](./02_Lexical_Structure.md#decimal-digit) ­**.** ­[*swift 版本延续*](#grammar_swift-version-continuation) 可选 > - +grammar_swift-version-continuation {#grammar_swift-version-continuation} > *swift 版本延续* → **.** [*十进制数字*](./02_Lexical_Structure.md#decimal-digit) [*swift 版本延续*](#grammar_swift-version-continuation) 可选 > - +grammar_module-name {#grammar_module-name} > *模块名* → [*identifier*](./02_Lexical_Structure.md#identifier) > - +grammar_environment {#grammar_environment} > *环境* → **模拟器** > - -### 行控制语句 - +### 行控制语句 {#line_control_statements} 行控制语句可以为被编译的源代码指定行号和文件名,从而改变源代码的定位信息,以便进行分析和调试。 行控制语句形式如下: @@ -794,7 +750,7 @@ statements to compile if both compilation conditions are false 第二种的行控制语句,`#sourceLocation()`,会将源代码的定位信息重置回默认的行号和文件名。 - +line-control-statement {#line-control-statement} > 行控制语句语法 > > @@ -802,10 +758,10 @@ statements to compile if both compilation conditions are false > > *行控制语句* → **#sourceLocation()** > - +line-number {#line-number} > *行号* → 大于 0 的十进制整数 > - +file-name {#file-name} > *文件名* → [*静态字符串字面量*](./02_Lexical_Structure.md#static-string-literal) > @@ -823,18 +779,16 @@ statements to compile if both compilation conditions are false > 编译时诊断语句语法 > > - +grammar_compile-time-diagnostic-statement {#grammar_compile-time-diagnostic-statement} > *诊断语句* → **#error** **(** [*diagnostic-message*](#grammar_diagnostic-message) **)** > > *诊断语句* → **#warning** **(** [*diagnostic-message*](#grammar_diagnostic-message) **)** > - +grammar_diagnostic-message {#grammar_diagnostic-message} > *诊断语句* → [*静态字符串字面量*](./02_Lexical_Structure.md#static-string-literal) > - -## 可用性条件 - +## 可用性条件 {#availability_condition} 可用性条件可作为 `if`,`while`,`guard` 语句的条件,可以在运行时基于特定的平台参数来查询 API 的可用性。 可用性条件的形式如下: @@ -856,19 +810,19 @@ if #available(platform name version, ..., *) { > 可用性条件语法 > > - +availability-condition {#availability-condition} > *可用性条件* → **#available** **(** [*可用性参数列表*](#availability-arguments) **)** > - +availability-arguments {#availability-arguments} > *可用性参数列表* → [*可用性参数*](#availability-argument) | [*可用性参数*](#availability-argument) **,** [*可用性参数列表*](#availability-arguments) > - +availability-argument {#availability-argument} > *可用性参数* → [平台名称](#platform-name) [平台版本](#platform-version) > > *可用性条件* → __*__ > > - +platform-name {#platform-name} > *平台名称* → **iOS** | **iOSApplicationExtension** > > *平台名称* → **OSX** | **macOSApplicationExtension** @@ -877,7 +831,7 @@ if #available(platform name version, ..., *) { > > *平台名称* → **tvOS** > - +platform-version {#platform-version} > *平台版本* → [十进制数字](./02_Lexical_Structure.md#decimal-digits) > > *平台版本* → [十进制数字](./02_Lexical_Structure.md#decimal-digits) **.** [十进制数字](./02_Lexical_Structure.md#decimal-digits) diff --git a/source/chapter3/06_Declarations.md b/source/chapter3/06_Declarations.md index 88e14806..0950d826 100755 --- a/source/chapter3/06_Declarations.md +++ b/source/chapter3/06_Declarations.md @@ -1,14 +1,11 @@ - - -# 声明(Declarations) - +# 声明(Declarations) {#declarations} *声明(declaration)* 用以向程序里引入新的名字或者结构。举例来说,可以使用声明来引入函数和方法,变量和常量,或者定义新的具有命名的枚举、结构体、类和协议类型。还可以使用声明来扩展一个既有的具有命名的类型的行为,或者在程序里引入在其它地方声明的符号。 在 Swift 中,大多数声明在某种意义上讲也是定义,因为它们在声明时往往伴随着实现或初始化。由于协议并不提供实现,大多数协议成员仅仅只是声明而已。为了方便起见,也是因为这些区别在 Swift 中并不是很重要,“声明”这个术语同时包含了声明和定义两种含义。 > 声明语法 > -> +> declaration {#declaration} > > *声明* → [*导入声明*](#import-declaration) > @@ -38,14 +35,12 @@ > > *声明* → [*运算符声明*](#operator-declaration) > -> +> declarations {#declarations} > > *多条声明* → [*声明*](#declaration) [*多条声明*](#declarations)可选 > - -## 顶级代码 - +## 顶级代码 {#top-level_code} Swift 的源文件中的顶级代码(top-level code)由零个或多个语句、声明和表达式组成。默认情况下,在一个源文件的顶层声明的变量,常量和其他具有命名的声明可以被同模块中的每一个源文件中的代码访问。可以使用一个访问级别修饰符来标记声明来覆盖这种默认行为,请参阅 [访问控制级别](#access_control_levels)。 > 顶级声明语法 @@ -53,9 +48,7 @@ Swift 的源文件中的顶级代码(top-level code)由零个或多个语句 > *顶级声明* → [*多条语句*](./05_Statements.md#statements)可选 > - -## 代码块 - +## 代码块 {#code_blocks} *代码块(code block)* 可以将一些声明和控制结构体组织在一起。它有如下的形式: ```swift @@ -68,14 +61,12 @@ Swift 的源文件中的顶级代码(top-level code)由零个或多个语句 > 代码块语法 > -> +> code-block {#code-block} > > *代码块* → **{** [*多条语句*](./05_Statements.md#statements)可选 **}** > - -## 导入声明 - +## 导入声明 {#import_declaration} *导入声明(import declaration)* 让你可以使用在其他文件中声明的内容。导入语句的基本形式是导入整个模块,它由 `import` 关键字和紧随其后的模块名组成: ```swift @@ -89,29 +80,27 @@ import 导入类型 模块.符号名 import 模块.子模块 ``` - +grammer_of_an_import_declaration {#grammer_of_an_import_declaration} > 导入声明语法 > -> +> import-declaration {#import-declaration} > > *导入声明* → [*特性列表*](./07_Attributes.md#attributes)可选 **import** [*导入类型*](#import-kind)可选 [*导入路径*](#import-path) > -> +> import-kind {#import-kind} > > *导入类型* → **typealias** | **struct** | **class** | **enum** | **protocol** | **let** | **var** | **func** > -> +> import-path {#import-path} > > *导入路径* → [*导入路径标识符*](#import-path-identifier) | [*导入路径标识符*](#import-path-identifier) **.** [*导入路径*](#import-path) > -> +> import-path-identifier {#import-path-identifier} > > *导入路径标识符* → [*标识符*](./02_Lexical_Structure.md#identifier) | [*运算符*](./02_Lexical_Structure.md#operator) > - -## 常量声明 - +## 常量声明 {#constant_declaration} *常量声明(constant declaration)* 可以在程序中引入一个具有命名的常量。常量以关键字 `let` 来声明,遵循如下格式: ```swift @@ -143,30 +132,27 @@ print("The second number is \(secondNumber).") 如果还想获得更多关于常量的信息或者想在使用中获得帮助,请参阅 [常量和变量](../chapter2/01_The_Basics.md#constants_and_variables) 和 [存储属性](../chapter2/10_Properties.md#stored_properties)。 - +grammer_of_a_constant_declaration {#grammer_of_a_constant_declaration} > 常量声明语法 > -> +> constant-declaration {#constant-declaration} > > *常量声明* → [*特性列表*](./07_Attributes.md#attributes)可选 [*声明修饰符列表*](#declaration-modifiers)可选 **let** [*模式构造器列表*](pattern-initializer-list) > -> +> pattern-initializer-list {#pattern-initializer-list} > > *模式构造器列表* → [*模式构造器*](#pattern-initializer) | [*模式构造器*](#pattern-initializer) **,** [*模式构造器列表*](#pattern-initializer-list) > -> +> pattern-initializer {#pattern-initializer} > > *模式构造器* → [*模式*](./08_Patterns.md#pattern) [*构造器*](#initializer)可选 > -> +> initializer {#initializer} > > *构造器* → **=** [*表达式*](./04_Expressions.md#expression) > - - -## 变量声明 - +## 变量声明 {#variable_declaration} *变量声明(variable declaration)* 可以在程序中引入一个具有命名的变量,它以关键字 `var` 来声明。 变量声明有几种不同的形式,可以声明不同种类的命名值和可变值,如存储型和计算型变量和属性,属性观察器,以及静态变量属性。所使用的声明形式取决于变量声明的适用范围和打算声明的变量类型。 @@ -178,9 +164,7 @@ print("The second number is \(secondNumber).") 可以在子类中重写继承来的变量属性,使用 `override` 声明修饰符标记属性的声明即可,详情请参阅 [重写](../chapter2/13_Inheritance.md#overriding)。 - -### 存储型变量和存储型变量属性 - +### 存储型变量和存储型变量属性 {#stored_variables_and_stored_variable_properties} 使用如下形式声明一个存储型变量或存储型变量属性: ```swift @@ -195,9 +179,7 @@ var 变量名称: 类型 = 表达式 正如名字所示,存储型变量和存储型变量属性的值会存储在内存中。 - -### 计算型变量和计算型属性 - +### 计算型变量和计算型属性 {#computed_variables_and_computed_properties} 使用如下形式声明一个计算型变量或计算型属性: ```swift @@ -221,9 +203,7 @@ setter 的圆括号以及 setter 名称是可选的。如果提供了 setter 名 要获得更多关于计算型属性的信息和例子,请参阅 [计算型属性](../chapter2/10_Properties.md#computed_properties)。 - -### 存储型变量和属性的观察器 - +### 存储型变量和属性的观察器 {#stored_variable_observers_and_property_observers} 可以在声明存储型变量或属性时提供 `willSet` 和 `didSet` 观察器。一个包含观察器的存储型变量或属性以如下形式声明: ```swift @@ -254,9 +234,7 @@ var 变量名称: 类型 = 表达式 { 要获得更多信息以及查看如何使用属性观察器的例子,请参阅 [属性观察器](../chapter2/10_Properties.md#property_observers)。 - -### 类型变量属性 - +### 类型变量属性 {#type_variable_properties} 要声明一个类型变量属性,用 `static` 声明修饰符标记该声明。类可以改用 `class` 声明修饰符标记类的类型计算型属性从而允许子类重写超类的实现。类型属性在 [类型属性](../chapter2/10_Properties.md#type_properties) 章节有详细讨论。 > 注意 @@ -264,10 +242,10 @@ var 变量名称: 类型 = 表达式 { > 在一个类声明中,使用关键字 `static` 与同时使用 `class` 和 `final` 去标记一个声明的效果相同。 > - +grammer_of_a_variable_declaration {#grammer_of_a_variable_declaration} > 变量声明语法 > - +variable-declaration {#variable-declaration} > *变量声明* → [*变量声明头*](#variable-declaration-head) [*模式构造器列表*](#pattern-initializer-list) > > *变量声明* → [*变量声明头*](#variable-declaration-head) [*变量名称*](#variable-name) [*类型标注*](03_Types.md#type-annotation) [*代码块*](#code-block) @@ -281,65 +259,63 @@ var 变量名称: 类型 = 表达式 { > *变量声明* → [*变量声明头*](#variable-declaration-head) [*变量名称*](#variable-name) [*类型标注*](03_Types.md#type-annotation) [*构造器*](#initializer)可选 [*willSet-didSet 代码块*](#willSet-didSet-block) > - +variable-declaration-head {#variable-declaration-head} > *变量声明头* → [*特性列表*](./07_Attributes.md#attributes)可选 [*声明修饰符列表*](#declaration-modifiers)可选 **var** > -> +> variable-name {#variable-name} > > *变量名称* → [*标识符*](./02_Lexical_Structure.md#identifier) > - +getter-setter-block {#getter-setter-block} > *getter-setter 代码块* → [*代码块*](#code-block) > > *getter-setter 代码块* → **{** [*getter 子句*](#getter-clause) [*setter 子句*](#setter-clause)可选 **}** > > *getter-setter 代码块* → **{** [*setter 子句*](#setter-clause) [*getter 子句*](#getter-clause) **}** > -> +> getter-clause {#getter-clause} > > *getter 子句* → [*特性列表*](./07_Attributes.md#attributes)可选 **get** [*代码块*](#code-block) > -> +> setter-clause {#setter-clause} > > *setter 子句* → [*特性列表*](./07_Attributes.md#attributes)可选 **set** [*setter 名称*](#setter-name)可选 [*代码块*](#code-block) > -> +> setter-name {#setter-name} > > *setter 名称* → **(** [*标识符*](./02_Lexical_Structure.md#identifier) **)** > - +getter-setter-keyword-block {#getter-setter-keyword-block} > *getter-setter 关键字代码块* → **{** [*getter 关键字子句*](#getter-keyword-clause) [*setter 关键字子句*](#setter-keyword-clause)可选 **}** > > *getter-setter 关键字代码块* → **{** [*setter 关键字子句*](#setter-keyword-clause) [*getter 关键字子句*](#getter-keyword-clause) **}** > -> +> getter-keyword-clause {#getter-keyword-clause} > > *getter 关键字子句* → [*特性列表*](./07_Attributes.md#attributes)可选 **get** > -> +> setter-keyword-clause {#setter-keyword-clause} > > *setter 关键字子句* → [*特性列表*](./07_Attributes.md#attributes)可选 **set** > - +willSet-didSet-block {#willSet-didSet-block} > *willSet-didSet 代码块* → **{** [*willSet 子句*](#willSet-clause) [*didSet 子句*](#didSet-clause)可选 **}** > > *willSet-didSet 代码块* → **{** [*didSet 子句*](#didSet-clause) [*willSet 子句*](#willSet-clause)可选 **}** > -> +> willSet-clause {#willSet-clause} > > *willSet 子句* → [*特性列表*](./07_Attributes.md#attributes)可选 **willSet** [*setter 名称*](#setter-name)可选 [*代码块*](#code-block) > -> +> didSet-clause {#didSet-clause} > > *didSet 子句* → [*特性列表*](./07_Attributes.md#attributes)可选 **didSet** [*setter 名称*](#setter-name)可选 [*代码块*](#code-block) > - -## 类型别名声明 - +## 类型别名声明 {#type_alias_declaration} *类型别名(type alias)* 声明可以在程序中为一个既有类型声明一个别名。类型别名声明语句使用关键字 `typealias` 声明,遵循如下的形式: ```swift @@ -394,26 +370,24 @@ func sum(_ sequence: T) -> Int where T.Element == Int { 另请参阅 [协议关联类型声明](#protocol_associated_type_declaration)。 - +grammer_of_a_type_alias_declaration {#grammer_of_a_type_alias_declaration} > 类型别名声明语法 > > -> +> typealias-declaration {#typealias-declaration} > > *类型别名声明* → [*特性列表*](./07_Attributes.md#attributes)可选 [*访问级别修饰符*](#access-level-modifier)可选 **typealias** [*类型别名名称*](#typealias-name) [*类型别子句*](#typealias-clause) [*类型别名赋值*](#typealias-assignment) > -> +> typealias-name {#typealias-name} > > *类型别名名称* → [*标识符*](./02_Lexical_Structure.md#identifier) > -> +> typealias-assignment {#typealias-assignment} > > *类型别名赋值* → **=** [*类型*](./03_Types.md#type) > - -## 函数声明 - +## 函数声明 {#function_declaration} 使用*函数声明(function declaration)* 在程序中引入新的函数或者方法。在类、结构体、枚举,或者协议中声明的函数会作为方法。函数声明使用关键字 `func`,遵循如下的形式: ```swift @@ -441,9 +415,7 @@ func 函数名称(参数列表) { 更多关于嵌套函数的讨论,请参阅 [嵌套函数](../chapter2/06_Functions.md#Nested_Functions)。 - -### 参数名 - +### 参数名 {#parameter_names} 函数的参数列表由一个或多个函数参数组成,参数间以逗号分隔。函数调用时的参数顺序必须和函数声明时的参数顺序一致。最简单的参数列表有着如下的形式: `参数名称`: `参数类型` @@ -470,9 +442,7 @@ func repeatGreeting(_ greeting: String, count n: Int) { /* Greet n times */ } repeatGreeting("Hello, world!", count: 2) // count 有标签, greeting 没有 ``` - -### 输入输出参数 - +### 输入输出参数 {#in-out_parameters} 输入输出参数被传递时遵循如下规则: 1. 函数调用时,参数的值被拷贝。 @@ -516,10 +486,7 @@ func multithreadedFunction(queue: DispatchQueue, x: inout Int) { 关于输入输出参数的详细讨论,请参阅 [输入输出参数](../chapter2/06_Functions.md#in_out_parameters)。 - - -### 特殊参数 - +### 特殊参数 {#special_kinds_of_parameters} 参数可以被忽略,数量可以不固定,还可以为其提供默认值,使用形式如下: ```swift @@ -542,18 +509,14 @@ f(7) // 有效,提供了值 f(x: 7) // 无效,该参数没有外部名称 ``` - -### 特殊方法 - +### 特殊方法 {#special_kinds_of_methods} 枚举或结构体的方法如果会修改 `self`,则必须以 `mutating` 声明修饰符标记。 子类重写超类中的方法必须以 `override` 声明修饰符标记。重写方法时不使用 `override` 修饰符,或者被 `override` 修饰符修饰的方法并未对超类方法构成重写,都会导致编译错误。 枚举或者结构体中的类型方法,要以 `static` 声明修饰符标记,而对于类中的类型方法,除了使用 `static`,还可使用 `class` 声明修饰符标记。类中使用 `class` 声明修饰的方法可以被子类实现重写;类中使用 `static` 声明修饰的方法不可被重写。 - -### 抛出错误的函数和方法 - +### 抛出错误的函数和方法 {#throwing_functions_and_methods} 可以抛出错误的函数或方法必须使用 `throws` 关键字标记。这类函数和方法被称为抛出函数和抛出方法。它们有着下面的形式: ```swift @@ -571,9 +534,7 @@ func 函数名称(参数列表) throws -> 返回类型 { 抛出方法不能重写非抛出方法,而且抛出方法不能满足协议对于非抛出方法的要求。也就是说,非抛出方法可以重写抛出方法,而且非抛出方法可以满足协议对于抛出方法的要求。 - -### 重抛错误的函数和方法 - +### 重抛错误的函数和方法 {#rethrowing_functions_and_methods} 函数或方法可以使用 `rethrows` 关键字来声明,从而表明仅当该函数或方法的一个函数类型的参数抛出错误时,该函数或方法才抛出错误。这类函数和方法被称为重抛函数和重抛方法。重新抛出错误的函数或方法必须至少有一个参数的类型为抛出函数。 ```swift @@ -602,55 +563,53 @@ func someFunction(callback: () throws -> Void) rethrows { 抛出方法不能重写重抛方法,而且抛出方法不能满足协议对于重抛方法的要求。也就是说,重抛方法可以重写抛出方法,而且重抛方法可以满足协议对于抛出方法的要求。 - -### 永不返回的函数 - +### 永不返回的函数 {#functions_that_never_return} Swift 定义了 `Never` 类型,它表示函数或者方法不会返回给它的调用者。`Never` 返回类型的函数或方法可以称为不归,不归函数、方法要么引发不可恢复的错误,要么永远不停地运作,这会使调用后本应执行得代码就不再执行了。但即使是不归函数、方法,抛错函数和重抛出函数也可以将程序控制转移到合适的 `catch` 代码块。 不归函数、方法可以在 guard 语句的 else 字句中调用,具体讨论在[*Guard 语句*](./05_Statements.md#guard_statements)。 你可以重写一个不归方法,但是新的方法必须保持原有的返回类型和没有返回的行为。 - +grammer_of_a_function_declaration {#grammer_of_a_function_declaration} > 函数声明语法 > - +function-declaration {#function-declaration} > *函数声明* → [*函数头*](#function-head) [*函数名*](#function-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)可选 [*函数签名*](#function-signature) [*泛型 where 子句*](08_Generic_Parameters_and_Arguments.md#generic-where-clause) [*函数体*](#function-body)可选 > - +function-head {#function-head} > *函数头* → [*特性列表*](./07_Attributes.md#attributes)可选 [*声明修饰符列表*](#declaration-modifiers)可选 **func** > -> +> function-name {#function-name} > > *函数名* → [*标识符*](./02_Lexical_Structure.md#identifier) | [*运算符*](./02_Lexical_Structure.md#operator) > > -> +> function-signature {#function-signature} > > > *函数签名* → [*参数子句列表*](#parameter-clauses) **throws**可选 [*函数结果*](#function-result)可选 > > *函数签名* → [*参数子句列表*](#parameter-clauses) **rethrows** [*函数结果*](#function-result)可选 > -> +> function-result {#function-result} > > *函数结果* → **->** [*特性列表*](./07_Attributes.md#attributes)可选 [*类型*](./03_Types.md#type) > -> +> function-body {#function-body} > > *函数体* → [*代码块*](#code-block) > -> +> parameter-clause {#parameter-clause} > > *参数子句* → **(** **)** | **(** [*参数列表*](#parameter-list) **)** > -> +> parameter-list {#parameter-list} > > *参数列表* → [*参数*](#parameter) | [*参数*](#parameter) **,** [*参数列表*](#parameter-list) > -> +> parameter {#parameter} > > *参数* → [*外部参数名*](#external-parameter-name)可选 [*内部参数名*](#local-parameter-name) [*类型标注*](03_Types.md#type-annotation) [*默认参数子句*](#default-argument-clause)可选 > @@ -658,23 +617,20 @@ Swift 定义了 `Never` 类型,它表示函数或者方法不会返回给它 > > *参数* → [*外部参数名*](#external-parameter-name)可选 [*内部参数名*](#local-parameter-name) [*类型标注*](03_Types.md#type-annotation) **...** > -> +> external-parameter-name {#external-parameter-name} > > *外部参数名* → [*标识符*](./02_Lexical_Structure.md#identifier) | **_** > -> +> local-parameter-name {#local-parameter-name} > > *内部参数名* → [*标识符*](./02_Lexical_Structure.md#identifier) | **_** > -> +> default-argument-clause {#default-argument-clause} > > *默认参数子句* → **=** [*表达式*](./04_Expressions.md#expression) > - - -## 枚举声明 - +## 枚举声明 {#enumeration_declaration} 在程序中使用*枚举声明(enumeration declaration)* 来引入一个枚举类型。 枚举声明有两种基本形式,使用关键字 `enum` 来声明。枚举声明体包含零个或多个值,称为枚举用例,还可包含任意数量的声明,包括计算型属性、实例方法、类型方法、构造器、类型别名,甚至其他枚举、结构体和类。枚举声明不能包含析构器或者协议声明。 @@ -687,9 +643,7 @@ Swift 定义了 `Never` 类型,它表示函数或者方法不会返回给它 可以扩展枚举类型,正如在 [扩展声明](#extension_declaration) 中讨论的一样。 - -### 任意类型的枚举用例 - +### 任意类型的枚举用例 {#enumerations_with_cases_of_any_type} 如下的形式声明了一个包含任意类型枚举用例的枚举变量: ```swift @@ -721,9 +675,7 @@ let evenInts: [Number] = [0, 2, 4, 6].map(f) 要获得更多关于具有关联值的枚举用例的信息和例子,请参阅 [关联值](../chapter2/08_Enumerations.md#associated_values)。 - -#### 递归枚举 - +#### 递归枚举 {#enumerations_with_indirection} 枚举类型可以具有递归结构,就是说,枚举用例的关联值类型可以是枚举类型自身。然而,枚举类型的实例具有值语义,这意味着它们在内存中有固定布局。为了支持递归,编译器必须插入一个间接层。 要让某个枚举用例支持递归,使用 `indirect` 声明修饰符标记该用例。 @@ -740,9 +692,7 @@ enum Tree { 被 `indirect` 修饰符标记的枚举用例必须有一个关联值。使用 `indirect` 修饰符标记的枚举类型可以既包含有关联值的用例,同时还可包含没有关联值的用例。但是,它不能再单独使用 `indirect` 修饰符来标记某个用例。 - -### 拥有原始值的枚举用例 - +### 拥有原始值的枚举用例 {#enumerations_with_cases_of_a_raw-value_type} 以下形式声明了一种枚举类型,其中各个枚举用例的类型均为同一种基本类型: ```swift @@ -776,18 +726,16 @@ enum GamePlayMode: String { 枚举用例具有原始值的枚举类型隐式地符合定义在 Swift 标准库中的 `RawRepresentable` 协议。所以,它们拥有一个 `rawValue` 属性和一个可失败构造器 `init?(rawValue: RawValue)`。可以使用 `rawValue` 属性去获取枚举用例的原始值,例如 `ExampleEnum.b.rawValue`。还可以根据原始值来创建一个相对应的枚举用例,只需调用枚举的可失败构造器,例如 `ExampleEnum(rawValue: 5)`,这个可失败构造器返回一个可选类型的用例。要获得更多关于具有原始值的枚举用例的信息和例子,请参阅 [原始值](../chapter2/08_Enumerations.md#raw_values)。 - -### 访问枚举用例 - +### 访问枚举用例 {#accessing_enumeration_cases} 使用点语法(`.`)来引用枚举类型的枚举用例,例如 `EnumerationType.enumerationCase`。当枚举类型可以由上下文推断而出时,可以省略它(但是 `.` 仍然需要),正如 [枚举语法](../chapter2/08_Enumerations.md#enumeration_syntax) 和 [显式成员表达式](./04_Expressions.md#explicit_member_expression) 所述。 可以使用 `switch` 语句来检验枚举用例的值,正如 [使用 switch 语句匹配枚举值](../chapter2/08_Enumerations.md#matching_enumeration_values_with_a_switch_statement) 所述。枚举类型是模式匹配的,依靠 `switch` 语句 `case` 块中的枚举用例模式,正如 [枚举用例模式](./08_Patterns.md#enumeration_case_pattern) 所述。 - +grammer_of_an_enumeration_declaration {#grammer_of_an_enumeration_declaration} > 枚举声明语法 > > -> +> enum-declaration {#enum-declaration} > > *枚举声明* → [*特性列表*](./07_Attributes.md#attributes)可选 [*访问级别修饰符*](#access-level-modifier)可选 [*联合风格枚举*](#union-style-enum) > @@ -796,72 +744,70 @@ enum GamePlayMode: String { > > *联合风格枚举* → **indirect**可选 **enum** [*枚举名称*](#enum-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)可选 [类型继承子句](./03_Types.md#type-inheritance-clause)可选 **{** [*多个联合风格枚举成员*](#union-style-enum-members)可选 **}** > -> +> union-style-enum-members {#union-style-enum-members} > > *多个联合风格枚举成员* → [*联合风格枚举成员*](#union-style-enum-member) [*多个联合风格枚举成员*](#union-style-enum-members)可选 > -> +> union-style-enum-member {#union-style-enum-member} > > *联合风格枚举成员* → [*声明*](#declaration) | [*联合风格枚举用例子句*](#union-style-enum-case-clause) | [*编译控制流语句*](05_Statements.md#compiler-control-statement) > -> +> union-style-enum-case-clause {#union-style-enum-case-clause} > > *联合风格枚举用例子句* → [*特性列表*](./07_Attributes.md#attributes)可选 **indirect**可选 **case** [*联合风格枚举用例列表*](#union-style-enum-case-list) > -> +> union-style-enum-case-list {#union-style-enum-case-list} > > *联合风格枚举用例列表* → [*联合风格枚举用例*](#union-style-enum-case) | [*联合风格枚举用例*](#union-style-enum-case) **,** [*联合风格枚举用例列表*](#union-style-enum-case-list) > -> +> union-style-enum-case {#union-style-enum-case} > > *联合风格枚举用例* → [*枚举用例名称*](#enum-case-name) [*元组类型*](03_Types.md#tuple-type)可选 > -> +> enum-name {#enum-name} > > *枚举名称* → [*标识符*](./02_Lexical_Structure.md#identifier) > -> +> enum-case-name {#enum-case-name} > > *枚举用例名称* → [*标识符*](./02_Lexical_Structure.md#identifier) > > -> +> raw-value-style-enum {#raw-value-style-enum} > > > *原始值风格枚举* → **enum** [*枚举名称*](#enum-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)可选 [*类型继承子句*](./03_Types.md#type-inheritance-clause) [*泛型 where 子句*](./09_Generic_Parameters_and_Arguments.md#generic-where-clause) **{** [*多个原始值风格枚举成员*](#raw-value-style-enum-members) **}** > -> +> raw-value-style-enum-members {#raw-value-style-enum-members} > > *多个原始值风格枚举成员* → [*原始值风格枚举成员*](#raw-value-style-enum-member) [*多个原始值风格枚举成员*](#raw-value-style-enum-members)可选 > -> +> raw-value-style-enum-member {#raw-value-style-enum-member} > > *原始值风格枚举成员* → [*声明*](#declaration) | [*原始值风格枚举用例子句*](#raw-value-style-enum-case-clause) | [*编译控制流语句*](05_Statements.md#compiler-control-statement) > -> +> raw-value-style-enum-case-clause {#raw-value-style-enum-case-clause} > > *原始值风格枚举用例子句* → [*特性列表*](./07_Attributes.md#attributes)可选 **case** [*原始值风格枚举用例列表*](#raw-value-style-enum-case-list) > -> +> raw-value-style-enum-case-list {#raw-value-style-enum-case-list} > > *原始值风格枚举用例列表* → [*原始值风格枚举用例*](#raw-value-style-enum-case) | [*原始值风格枚举用例*](#raw-value-style-enum-case) **,** [*原始值风格枚举用例列表*](#raw-value-style-enum-case-list) > -> +> raw-value-style-enum-case {#raw-value-style-enum-case} > > *原始值风格枚举用例* → [*枚举用例名称*](#enum-case-name) [*原始值赋值*](#raw-value-assignment)可选 > -> +> raw-value-assignment {#raw-value-assignment} > > *原始值赋值* → **=** [*原始值字面量*](#raw-value-literal) > -> +> raw-value-literal {#raw-value-literal} > > *原始值字面量* → [数字型字面量](./02_Lexical_Structure.md#numeric-literal) | [字符串型字面量](./02_Lexical_Structure.md#static-string-literal) | [布尔型字面量](./02_Lexical_Structure.md#boolean-literal) > - -## 结构体声明 - +## 结构体声明 {#structure_declaration} 使用*结构体声明(structure declaration)* 可以在程序中引入一个结构体类型。结构体声明使用 `struct` 关键字,遵循如下的形式: ```swift @@ -891,36 +837,33 @@ struct 结构体名称: 采纳的协议 { 可以使用扩展声明来扩展结构体类型的行为,请参阅 [扩展声明](#extension_declaration)。 - +grammer_of_a_structure_declaration {#grammer_of_a_structure_declaration} > 结构体声明语法 > > -> +> struct-declaration {#struct-declaration} > > *结构体声明* → [*特性列表*](./07_Attributes.md#attributes)可选 [*访问级别修饰符*](#access-level-modifier) 可选 **struct** [*结构体名称*](#struct-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)可选 [*类型继承子句*](./03_Types.md#type-inheritance-clause)可选 [*泛型 where 子句*](./09_Generic_Parameters_and_Arguments.md#generic-where-clause)可选 [*结构体主体*](#struct-body) > -> +> struct-name {#struct-name} > > *结构体名称* → [*标识符*](./02_Lexical_Structure.md#identifier) > -> +> struct-body {#struct-body} > > *结构体主体* → **{** [*多条声明*](#declarations)可选 **}** > -> +> struct-name {#struct-name} > > > *结构体多个成员* → [*结构体成员*](#struct-member) [*结构体多个成员*](#struct-members)可选 > -> +> struct-member {#struct-member} > > *结构体成员* → [*声明*](#declaration) | [*编译控制流语句*](05_Statements.md#compiler-control-statement) > - - -## 类声明 - +## 类声明 {#class_declaration} 可以在程序中使用*类声明(class declaration)* 来引入一个类。类声明使用关键字 `class`,遵循如下的形式: ```swift @@ -953,35 +896,33 @@ class 类名: 超类, 采纳的协议 { 可以使用扩展声明来扩展类的行为,请参阅 [扩展声明](#extension_declaration)。 - +grammer_of_a_class_declaration {#grammer_of_a_class_declaration} > 类声明语法 > > -> +> class-declaration {#class-declaration} > > *类声明* → [*特性列表*](./07_Attributes.md#attributes)可选 [访问级别修饰符](#access-level-modifier)可选 **final**可选 **class** [*类名*](#class-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)可选 [*类型继承子句*](./03_Types.md#type-inheritance-clause)可选 [*泛型 where 子句*](./09_Generic_Parameters_and_Arguments.md#generic-where-clause)可选 [*类主体*](#class-body) > > *类声明* → [*特性列表*](./07_Attributes.md#attributes)可选 **final** [访问级别修饰符](#access-level-modifier)可选 **class** [*类名*](#class-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)可选 [*类型继承子句*](./03_Types.md#type-inheritance-clause)可选 [*泛型 where 子句*](./09_Generic_Parameters_and_Arguments.md#generic-where-clause)可选 [*类主体*](#class-body) > -> +> class-name {#class-name} > > *类名* → [*标识符*](./02_Lexical_Structure.md#identifier) > -> +> class-body {#class-body} > > *类主体* → **{** [*多条声明*](#declarations)可选 **}** > > > *类多个成员* → [*类成员*](#class-member) [*类多个成员*](#class-members)可选 > -> +> class-member {#class-member} > > *类成员* → [*声明*](#declaration) | [*编译控制流语句*](05_Statements.md#compiler-control-statement) > - -## 协议声明 - +## 协议声明 {#protocol_declaration} *协议声明(protocol declaration)* 可以为程序引入一个命名的协议类型。协议声明只能在全局区域使用 `protocol` 关键字来进行声明,并遵循如下形式: ```swift @@ -1022,31 +963,31 @@ protocol SomeProtocol: AnyObject { 可以使用协议来声明作为代理的类或者结构体应该实现的方法,正如 [委托(代理)模式](../chapter2/21_Protocols.md#delegation) 中所述。 - +grammer_of_a_protocol_declaration {#grammer_of_a_protocol_declaration} > 协议声明语法 > > -> +> protocol-declaration {#protocol-declaration} > > *协议声明* → [*特性列表*](./07_Attributes.md#attributes)可选 [*访问级别修饰符*](#access-level-modifier)可选 **protocol** [*协议名称*](#protocol-name) [*类型继承子句*](03_Types.md#type-inheritance-clause)可选 [*泛型 where 子句*](./09_Generic_Parameters_and_Arguments.md#generic-where-clause)可选 [*协议主体*](#protocol-body) > -> +> protocol-name {#protocol-name} > > *协议名称* → [*标识符*](./02_Lexical_Structure.md#identifier) > -> +> protocol-body {#protocol-body} > > *协议主体* → **{** [*协议成员声明列表*](#protocol-member-declarations)可选 **}** > > > *协议多个成员* → [*协议成员*](#protocol-member) [*协议多个成员*](#protocol-members)可选 > -> +> protocol-member {#protocol-member} > > *协议成员* → [*协议成员声明*](#protocol-member-declaration) | [*编译控制流语句*](05_Statements.md#compiler-control-statement) > > -> +> protocol-member-declaration {#protocol-member-declaration} > > *协议成员声明* → [*协议属性声明*](#protocol-property-declaration) > @@ -1058,14 +999,12 @@ protocol SomeProtocol: AnyObject { > > *协议成员声明* → [*协议关联类型声明*](#protocol-associated-type-declaration) > -> +> protocol-member-declarations {#protocol-member-declarations} > > *协议成员声明列表* → [*协议成员声明*](#protocol-member-declaration) [*协议成员声明列表*](#protocol-member-declarations)可选 > - -### 协议属性声明 - +### 协议属性声明 {#protocol_property_declaration} 协议可以通过在协议声明主体中引入一个协议属性声明,来声明符合的类型必须实现的属性。协议属性声明有一种特殊的变量声明形式: ```swift @@ -1078,35 +1017,30 @@ var 属性名: 类型 { get set } 另请参阅 [变量声明](#variable_declaration)。 - +grammer_of_an_import_declaration {#grammer_of_an_import_declaration} > 协议属性声明语法 > -> +> protocol-property-declaration {#protocol-property-declaration} > > *协议属性声明* → [*变量声明头*](#variable-declaration-head) [*变量名称*](#variable-name) [*类型标注*](03_Types.md#type-annotation) [*getter-setter 关键字代码块*](#getter-setter-keyword-block) > - -### 协议方法声明 - +### 协议方法声明 {#protocol_method_declaration} 协议可以通过在协议声明主体中引入一个协议方法声明,来声明符合的类型必须实现的方法。协议方法声明和函数方法声明有着相同的形式,但有两项例外:它们不包括函数体,也不能包含默认参数。关于如何实现协议中的方法要求的例子,请参阅 [方法要求](../chapter2/21_Protocols.md#method_requirements)。 使用 `static` 声明修饰符可以在协议声明中声明一个类型方法。类在实现这些方法时使用 `class` 声明修饰符。结构体实现这些方法时必须使用 `static` 声明修饰符。通过扩展实现时亦是如此(类的扩展中使用 `class` 声明修饰符,结构体的扩展中使用 `static` 声明修饰符)。 另请参阅 [函数声明](#function_declaration)。 - +grammer_of_a_protocol_declaration {#grammer_of_a_protocol_declaration} > 协议方法声明语法 > -> +> protocol-method-declaration {#protocol-method-declaration} > > *协议方法声明* → [*函数头*](#function-head) [*函数名*](#function-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)可选 [*函数签名*](#function-signature) [*泛型 where 子句*](08_Generic_Parameters_and_Arguments.md#generic-where-clause)可选 > - - -### 协议构造器声明 - +### 协议构造器声明 {#protocol_initializer_declaration} 协议可以通过在协议声明主体中引入一个协议构造器声明,来声明符合的类型必须实现的构造器。协议构造器声明 除了不包含实现主体外,和构造器声明有着相同的形式。 @@ -1116,19 +1050,17 @@ var 属性名: 类型 { get set } 另请参阅 [构造器声明](#initializer_declaration)。 - +grammer_of_a_protocol_initializer_declaration {#grammer_of_a_protocol_initializer_declaration} > 协议构造器声明语法 > -> +> protocol-initializer-declaration {#protocol-initializer-declaration} > > *协议构造器声明* → [*构造器头*](#initializer-head) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)可选 [*参数子句*](#parameter-clause) **throws**可选 [*泛型 where 子句*](08_Generic_Parameters_and_Arguments.md#generic-where-clause)可选 > > *协议构造器声明* → [*构造器头*](#initializer-head) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)可选 [*参数子句*](#parameter-clause) **rethrows** [*泛型 where 子句*](08_Generic_Parameters_and_Arguments.md#generic-where-clause)可选 > - -### 协议下标声明 - +### 协议下标声明 {#protocol_subscript_declaration} 协议可以通过在协议声明主体中引入一个协议下标声明,来声明符合的类型必须实现的下标。协议下标声明有一个特殊的下标声明形式: ```swift @@ -1140,18 +1072,15 @@ subscript (参数列表) -> 返回类型 { get set } 另请参阅 [下标声明](#subscript_declaration)。 - +grammer_of_a_protocol_subscript_declaration {#grammer_of_a_protocol_subscript_declaration} > 协议下标声明语法 > -> +> protocol-subscript-declaration {#protocol-subscript-declaration} > > *协议下标声明* → [*下标头*](#subscript-head) [*下标结果*](#subscript-result) [*泛型 where 子句*](08_Generic_Parameters_and_Arguments.md#generic-where-clause)可选 [*getter-setter 关键字代码块*](#getter-setter-keyword-block) > - - -### 协议关联类型声明 - +### 协议关联类型声明 {#protocol_associated_type_declaration} 使用关键字 `associatedtype` 来声明协议关联类型。关联类型为作为协议声明的一部分,为某种类型提供了一个别名。关联类型和泛型参数子句中的类型参数很相似,但是它们和 `Self` 一样,用于协议中。`Self` 指代采纳协议的类型。要获得更多信息和例子,请参阅 [关联类型](../chapter2/22_Generics.md#associated_types)。 在协议声明中使用泛型 `where` 子句来为继承的协议关联类型添加约束,且不需要重新声明关联类型。例如下面代码中的 `SubProtocol` 声明。 @@ -1174,17 +1103,15 @@ protocol SubProtocolB: SomeProtocol where SomeType: Equatable { } 另请参阅 [类型别名声明](#type_alias_declaration)。 - +grammer_of_a_protocol_associated_type_declaration {#grammer_of_a_protocol_associated_type_declaration} > 协议关联类型声明语法 > -> +> protocol-associated-type-declaration {#protocol-associated-type-declaration} > > *协议关联类型声明* → [*特性列表*](./07_Attributes.md#attributes)可选 [*访问级别修饰符*](#access-level-modifier)可选 **associatedtype** [*类型别名头*](#typealias-head) [*类型继承子句*](03_Types.md#type-inheritance-clause)可选 [*类型别名赋值*](#typealias-assignment)可选 [*泛型 where 子句*](08_Generic_Parameters_and_Arguments.md#generic-where-clause)可选 > - -## 构造器声明 - +## 构造器声明 {#initializer_declaration} 构造器声明会为程序中的类、结构体或枚举引入构造器。构造器使用关键字 `init` 来声明,有两种基本形式。 结构体、枚举、类可以有任意数量的构造器,但是类的构造器具有不同的规则和行为。不同于结构体和枚举,类有两种构造器,即指定构造器和便利构造器,请参阅 [构造过程](../chapter2/14_Initialization.md)。 @@ -1228,9 +1155,7 @@ convenience init(参数列表) { 关于在不同类型中声明构造器的例子,请参阅 [构造过程](../chapter2/14_Initialization.md)。 - -### 可失败构造器 - +### 可失败构造器 {#failable_initializers} 可失败构造器可以生成所属类型的可选实例或者隐式解包可选实例,因此,这种构造器通过返回 `nil` 来指明构造过程失败。 声明生成可选实例的可失败构造器时,在构造器声明的 `init` 关键字后加追加一个问号(`init?`)。声明生成隐式解包可选实例的可失败构造器时,在构造器声明后追加一个叹号(`init!`)。使用 `init?` 可失败构造器生成结构体的一个可选实例的例子如下。 @@ -1269,16 +1194,16 @@ if let actualInstance = SomeStruct(input: "Hello") { 更多关于可失败构造器的信息和例子,请参阅 [可失败构造器](../chapter2/14_Initialization.md#failable_initializers)。 - +grammer_of_an_initializer_declaration {#grammer_of_an_initializer_declaration} > 构造器声明语法 > -> +> initializer-declaration {#initializer-declaration} > > *构造器声明* → [*构造器头*](#initializer-head) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)可选 [*参数子句*](#parameter-clause) **throws**可选 [*泛型 where 子句*](08_Generic_Parameters_and_Arguments.md#generic-where-clause)可选 [*构造器主体*](#initializer-body) > > *构造器声明* → [*构造器头*](#initializer-head) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)可选 [*参数子句*](#parameter-clause) **rethrows**可选 [*泛型 where 子句*](08_Generic_Parameters_and_Arguments.md#generic-where-clause)可选 [*构造器主体*](#initializer-body) > -> +> initializer-head {#initializer-head} > > *构造器头* → [*特性列表*](./07_Attributes.md#attributes)可选 [*声明修饰符列表*](#declaration-modifiers)可选 **init** > @@ -1286,14 +1211,12 @@ if let actualInstance = SomeStruct(input: "Hello") { > > *构造器头* → [*特性列表*](./07_Attributes.md#attributes)可选 [*声明修饰符列表*](#declaration-modifiers)可选 **init** **!** > -> +> initializer-body {#initializer-body} > > *构造器主体* → [*代码块*](#code-block) > - -## 析构器声明 - +## 析构器声明 {#deinitializer_declaration} *析构器声明(deinitializer declaration)* 可以为类声明一个析构器。析构器没有参数,遵循如下格式: ```swift @@ -1310,17 +1233,15 @@ deinit { 关于如何在类声明中使用析构器的例子,请参阅 [析构过程](../chapter2/15_Deinitialization.md)。 - +grammer_of_a_deinitializer_declaration {#grammer_of_a_deinitializer_declaration} > 析构器声明语法 > -> +> deinitializer-declaration {#deinitializer-declaration} > > *析构器声明* → [*特性列表*](./07_Attributes.md#attributes)可选 **deinit** [*代码块*](#code-block) > - -## 扩展声明 - +## 扩展声明 {#extension_declaration} *扩展声明(extension declaration)* 可以扩展一个现存的类型的行为。扩展声明使用关键字 `extension`,遵循如下格式: ```swift @@ -1349,16 +1270,10 @@ extension 类型名称: 采纳的协议 where 约束条件 { 协议声明不能为现有的类添加类的继承关系,因此你只能在 “类型名称” 的冒号后面添加一系列协议。 - - -### 条件遵循 - +### 条件遵循 {#conditional_conformance} 你可以扩展一个泛型类型并使其有条件地遵循某协议,此后此类型的实例只有在特定的限制条件满足时才遵循此协议。在扩展声明中加入限制条件来为协议添加条件遵循。 - - -## 已重写的限制条件会在某些泛型上下文中失效 - +## 已重写的限制条件会在某些泛型上下文中失效 {#overridden_requirements_aren't_Used_in_some_generic_contexts} 对于一些通过条件遵循获得了特定行为的类型,在某些泛型上下文中,并不能够确保能够使用协议限制中的特定实现。为了说明这个行为,下面的例子中定义了两个协议以及一个有条件地遵循两个协议的泛型类型。 ```swift @@ -1422,16 +1337,10 @@ doSomething(with: oneAndTwo) 当传入 `doSomething(_:)` 的实例调用 `log()` 时,打印结果省略了自定义标题。 - - -### 协议遵循决不能冗余 - +### 协议遵循决不能冗余 {#protocol_conformance_must_not_be_redundant} 一个具体的类型只能够遵循某特定协议一次。Swift 会把冗余的协议遵循标记为错误。你会在两种场景中遇到这种错误。第一种场景是,使用不同的限制条件来多次显式地遵循同一协议。第二种场景是,多次隐式地继承同一协议。以上两种场景会在下面章节中讨论。 - - -## 解决显式冗余 - +## 解决显式冗余 {#resolving_explicit_redundancy} 对同一具体类型的多个扩展不能遵循同一协议,即便这些扩展有不同的显式限制条件。这个限制的具体示例在下面的例子中。两个扩展声明都试图添加对 `Serializable` 的条件遵循,一个为 `Int` 类型元素的数组,另一个为 `String` 类型元素的数组。 ```swift @@ -1470,10 +1379,7 @@ extension Array: Serializable where Element: SerializableInArray { } ``` - - -## 解决隐式冗余 - +## 解决隐式冗余 {#resolving_implicit_redundancy} 当一个具体类型有条件地遵循某协议,此类型会隐式地使用相同的条件遵循任一父协议。 如果你需要让一个类型有条件地遵循两个继承自同一父协议的协议,请显式地声明对父协议的遵循。这可以避免使用不同的限制条件隐式遵循同一父协议两次。 @@ -1510,15 +1416,15 @@ extension Array: Loggable where Element: MarkedLoggable { } // 报错: redundant conformance of 'Array' to protocol 'Loggable' ``` - +grammer_of_an_extension_declaration {#grammer_of_an_extension_declaration} > 扩展声明语法 > > -> +> extension-declaration {#extension-declaration} > > *扩展声明* → [特性](./07_Attributes.md#type_attributes)可选 [访问级别修饰符](#access-level-modifier)可选 **extension** [*类型标识符*](03_Types.md#type-identifier) [*类型-继承-子句*](./03_Types.md#type-inheritance-clause)可选 [*泛型 where 子句*](./09_Generic_Parameters_and_Arguments.md#generic-where-clause)可选 [*扩展主体*](#extension-body) > -> +> extension-body {#extension-body} > > *扩展主体* → **{** [*多条声明*](#declarations)可选 **}** > @@ -1527,10 +1433,7 @@ extension Array: Loggable where Element: MarkedLoggable { } > *单条声明* → [声明语句](#declarations) | [*编译控制流语句*](05_Statements.md#compiler-control-statement) > - - -## 下标声明 - +## 下标声明 {#subscript_declaration} *下标声明(subscript declaration)* 用于为特定类型的对象添加下标支持,通常也用于为访问集合、列表和序列中的元素提供语法便利。下标声明使用关键字 `subscript`,形式如下: ```swift @@ -1561,11 +1464,11 @@ subscript (参数列表) -> 返回类型 { 更多关于下标的信息和例子,请参阅 [下标](../chapter2/12_Subscripts.md)。 - +grammer_of_a_subscript_declaration {#grammer_of_a_subscript_declaration} > 下标声明语法 > > -> +> subscript-declaration {#subscript-declaration} > > *下标声明* → [*下标头*](#subscript-head) [*下标结果*](#subscript-result) [*泛型 where 子句*](08_Generic_Parameters_and_Arguments.md#generic-where-clause)可选 [*代码块*](#code-block) > @@ -1573,18 +1476,16 @@ subscript (参数列表) -> 返回类型 { > > *下标声明* → [*下标头*](#subscript-head) [*下标结果*](#subscript-result) [*泛型 where 子句*](08_Generic_Parameters_and_Arguments.md#generic-where-clause)可选 [*getter-setter 关键字代码块*](#getter-setter-keyword-block) > -> +> subscript-head {#subscript-head} > > *下标头* → [*特性列表*](./07_Attributes.md#attributes)可选 [*声明修饰符列表*](#declaration-modifiers)可选 **subscript** [*泛型参数子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)可选 [*参数子句*](#parameter-clause) > -> +> subscript-result {#subscript-result} > > *下标结果* → **->** [*特性列表*](./07_Attributes.md#attributes)可选 [*类型*](./03_Types.md#type) > - -## 运算符声明 - +## 运算符声明 {#operator_declaration} *运算符声明(operator declaration)* 会向程序中引入中缀、前缀或后缀运算符,使用关键字 `operator` 来声明。 可以声明三种不同的缀性:中缀、前缀和后缀。运算符的缀性指定了运算符与其运算对象的相对位置。 @@ -1623,30 +1524,28 @@ postfix operator 运算符名称 {} 声明了一个新的运算符以后,需要实现一个跟这个运算符同名的函数来实现这个运算符。如果是实现一个前缀或者后缀运算符,也必须使用相符的 `prefix` 或者 `postfix` 声明修饰符标记函数声明。如果是实现中缀运算符,则不需要使用 `infix` 声明修饰符标记函数声明。关于如何实现一个新的运算符的例子,请参阅 [自定义运算符](../chapter2/26_Advanced_Operators.md#custom_operators)。 - +grammer_of_an_operator_declaration {#grammer_of_an_operator_declaration} > 运算符声明语法 > - +operator-declaration {#operator-declaration} > *运算符声明* → [*前缀运算符声明*](#prefix-operator-declaration) | [*后缀运算符声明*](#postfix-operator-declaration) | [*中缀运算符声明*](#infix-operator-declaration) > - +prefix-operator-declaration {#prefix-operator-declaration} > *前缀运算符声明* → **prefix** **运算符** [*运算符*](./02_Lexical_Structure.md#operator) **{** **}** > -> +> postfix-operator-declaration {#postfix-operator-declaration} > > *后缀运算符声明* → **postfix** **运算符** [*运算符*](./02_Lexical_Structure.html#operator) **{** **}** > -> +> infix-operator-declaration {#infix-operator-declaration} > > *中缀运算符声明* → **infix** **运算符** [*运算符*](./02_Lexical_Structure.md#operator) **{** [*中缀运算符属性*](#infix-operator-attributes)可选 **}** > - +infix-operator-group {#infix-operator-group} > *中缀运算符组* → [*优先级组名称*](#precedence-group-name) > - -## 优先级组声明 - +## 优先级组声明 {#precedence_group_declaration_modifiers} *优先级组声明(A precedence group declaration)* 会向程序的中缀运算符引入一个全新的优先级组。当没有用圆括号分组时,运算符优先级反应了运算符与它的操作数的关系的紧密程度。 优先级组的声明如下所示: @@ -1672,49 +1571,47 @@ Swift 定义了大量的优先级组来与标准库的运算符配合使用, 优先级组的赋值性表示在包含可选链操作时的运算符优先级。当设为 true 时,与优先级组对应的运算符在可选链操作中使用和标准库中赋值运算符同样的分组规则,当设为 false 或者不设置,该优先级组的运算符与不赋值的运算符遵循同样的可选链规则。 - +grammer_of_a_precedence_group_declaration {#grammer_of_a_precedence_group_declaration} > 优先级组声明语法 > - +precedence-group-declaration {#precedence-group-declaration} > *优先级组声明* → **precedence**[*优先级组名称*](#precedence-group-name){[*多优先级组属性*](#precedence-group-attributes)可选 } > - +precedence-group-attributes {#precedence-group-attributes} > *优先级组属性* → [*优先级组属性*](#precedence-group-attribute)[*多优先级组属性*](#precedence-group-attributes)可选 **{** **}** > - +precedence-group-attribute {#precedence-group-attribute} > *优先级组属性* → [*优先级组关系*](#precedence-group-relation) > > *优先级组属性* → [*优先级组赋值性*](#precedence-group-assignment) > > *优先级组属性* → [*优先级组相关性*](#precedence-group-associativity) > -> +> precedence-group-relation {#precedence-group-relation} > > *优先级组关系* → **higherThan:**[*多优先级组名称*](#precedence-group-names) > > *优先级组关系* → **lowerThan:**[*多优先级组名称*](#precedence-group-names) > -> +> precedence-group-assignment {#precedence-group-assignment} > > *优先级组赋值* → **assignment:**[*布尔字面值*](./02_Lexical_Structure.md#boolean-literal) > - +precedence-group-associativity {#precedence-group-associativity} > *优先级组结合性* → **associativity:left** > > *优先级组结合性* → **associativity:right** > > *优先级组结合性* → **associativity:none** > - +precedence-group-names {#precedence-group-names} > *多优先级组名称* → [*优先级组名称*](#precedence-group-name) | [*优先级组名称*](#precedence-group-name) | [*优先级组名称*](#precedence-group-name) > - +precedence-group-name {#precedence-group-name} > *优先级组名称* →[*标识符*](./02_Lexical_Structure.md#identifier) > - -## 声明修饰符 - +## 声明修饰符 {#Declaration_Modifiers} 声明修饰符都是关键字或上下文相关的关键字,可以修改一个声明的行为或者含义。可以在声明的特性(如果存在)和引入该声明的关键字之间,利用声明修饰符的关键字或上下文相关的关键字指定一个声明修饰符。 `dynamic` @@ -1757,9 +1654,7 @@ Swift 定义了大量的优先级组来与标准库的运算符配合使用, 该修饰符用于修饰变量或存储型变量属性,表示该变量或属性持有其存储的对象的弱引用。这种变量或属性的类型必须是可选的类类型。使用 `weak` 修饰符可避免强引用循环。关于 `weak` 修饰符的更多信息和例子,请参阅 [弱引用](../chapter2/23_Automatic_Reference_Counting.md#resolving_strong_reference_cycles_between_class_instances)。 - -### 访问控制级别 - +### 访问控制级别 {#access_control_levels} Swift 提供了三个级别的访问控制:`public`、`internal` 和 `private`。可以使用以下任意一种访问级别修饰符来指定声明的访问级别。访问控制在 [访问控制](../chapter2/25_Access_Control.md) 中有详细讨论。 `public` @@ -1776,19 +1671,19 @@ Swift 提供了三个级别的访问控制:`public`、`internal` 和 `private` 以上访问级别修饰符都可以选择带上一个参数,该参数由一对圆括号和其中的 `set` 关键字组成(例如,`private(set)`)。使用这种形式的访问级别修饰符来限制某个属性或下标的 setter 的访问级别低于其本身的访问级别,正如 [Getter 和 Setter](../chapter2/25_Access_Control.md#getters_and_setters) 中所讨论的。 - +grammer_of_a_declaration_modifier {#grammer_of_a_declaration_modifier} > 声明修饰符的语法 > - +declaration-modifier {#declaration-modifier} > *声明修饰符* → **class** | **convenience**| **dynamic** | **final** | **infix** | **lazy** | **mutating** | **nonmutating** | **optional** | **override** | **postfix** | **prefix** | **required** | **static** | **unowned** | **unowned ( safe )** | **unowned ( unsafe )** | **weak** > > 声明修饰符 → [*访问级别修饰符*](#access-level-modifier) > -> +> declaration-modifiers {#declaration-modifiers} > > *声明修饰符列表* → [*声明修饰符*](#declaration-modifier) [*声明修饰符列表*](#declaration-modifiers)可选 > - +access-level-modifier {#access-level-modifier} > 访问级别修饰符 → **internal** | **internal ( set )** > > 访问级别修饰符 → **private** | **private ( set )** diff --git a/source/chapter3/07_Attributes.md b/source/chapter3/07_Attributes.md index d4ee1edc..cabde055 100755 --- a/source/chapter3/07_Attributes.md +++ b/source/chapter3/07_Attributes.md @@ -10,13 +10,11 @@ 有些声明特性通过接收参数来指定特性的更多信息以及它是如何修饰某个特定的声明的。这些_特性的参数_写在圆括号内,它们的格式由它们所属的特性来定义。 - -## 声明特性 +## 声明特性 {#declaration_attributes} 声明特性只能应用于声明。 - -### `available` +### `available` {#available} 将 `available` 特性用于声明时,表示该声明的生命周期是相对于特定的平台和操作系统版本。 @@ -112,13 +110,11 @@ struct MyStruct { } ``` - -### `discardableResult` +### `discardableResult` {#discardableresult} 该特性用于的函数或方法声明,以抑制编译器中函数或方法的返回值被调而没有使用其结果的警告。 - -### `dynamicCallable` +### `dynamicCallable` {#dynamiccallable} 该特性用于类、结构体、枚举或协议,以将该类型的实例视为可调用的函数。该类型必须实现 `dynamicallyCall(withArguments:)`、`dynamicallyCall(withKeywordArguments:)` 方法之一,或两者同时实现。 @@ -183,8 +179,7 @@ print(repeatLabels(a: 1, b: 2, c: 3, b: 2, a: 1)) repeatLabels(a: "four") // Error ``` - -### `dynamicMemberLookup` +### `dynamicMemberLookup` {#dynamicmemberlookup} 该特性用于类、结构体、枚举或协议,让其能在运行时查找成员。该类型必须实现 `subscript(dynamicMemberLookup:)` 下标。 @@ -213,13 +208,11 @@ print(dynamic == equivalent) // 打印“true” ``` - -### `GKInspectable` +### `GKInspectable` {#gkinspectable} 应用此属性,暴露一个自定义 GameplayKit 组件属性给 SpriteKit 编辑器 UI。 - -### `inlinable` +### `inlinable` {#inlinable} 该特性用于函数、方法、计算属性、下标、便利构造器或析构器的声明,以将该声明的实现公开为模块公开接口的一部分。编译器允许在调用处把 `inlinable` 标记的符号替换为符号实现的副本。 @@ -227,8 +220,7 @@ print(dynamic == equivalent) 该特性不能用于嵌套在函数内的声明,也不能用于 `fileprivate` 或 `private` 访问级别的声明。在内联函数定义的函数和闭包是隐式非内联的,即使他们不能标记该特性。 - -### `nonobjc` +### `nonobjc` {#nonobjc} 该特性用于方法、属性、下标、或构造器的声明,这些声明本可以在 Objective-C 代码中使用,而使用 `nonobjc` 特性则告诉编译器这个声明不能在 Objective-C 代码中使用。 @@ -238,8 +230,7 @@ print(dynamic == equivalent) 标有 `nonobjc` 特性的方法不能重写标有 `objc` 特性的方法。然而,标有 `objc` 特性的方法可以重写标有 `nonobjc` 特性的方法。同样,标有 `nonobjc` 特性的方法不能满足标有 `@objc` 特性的协议中的方法要求。 - -### `NSApplicationMain` +### `NSApplicationMain` {#nsapplicationmain} 在类上使用该特性表示该类是应用程序委托类,使用该特性与调用 `NSApplicationMain(_:_:)` 函数并且把该类的名字作为委托类的名字传递给函数的效果相同。 @@ -250,20 +241,17 @@ import AppKit NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv) ``` - -### `NSCopying` +### `NSCopying` {#nscopying} 该特性用于修饰一个类的存储型变量属性。该特性将使属性的设值方法使用传入值的副本进行赋值,这个值由传入值的 `copyWithZone(_:)` 方法返回。该属性的类型必需符合 `NSCopying` 协议。 `NSCopying` 特性的行为与 Objective-C 中的 `copy` 特性相似。 - -### `NSManaged` +### `NSManaged` {#nsmanaged} 该特性用于修饰 `NSManagedObject` 子类中的实例方法或存储型变量属性,表明它们的实现由 `Core Data` 在运行时基于相关实体描述动态提供。对于标记了 `NSManaged` 特性的属性,`Core Data` 也会在运行时为其提供存储。应用这个特性也意味着 `objc` 特性。 - -### `objc` +### `objc` {#objc} 该特性用于修饰任何可以在 Objective-C 中表示的声明。比如,非嵌套类、协议、非泛型枚举(仅限原始值为整型的枚举)、类和协议中的属性和方法(包括存取方法)、构造器、析构器以及下标运算符。`objc` 特性告诉编译器这个声明可以在 Objective-C 代码中使用。 @@ -291,32 +279,27 @@ class ExampleClass: NSObject { } ``` - -### `objcMembers` +### `objcMembers` {#objcmembers} 该特性用于类声明,以将 `objc` 特性应用于该类、扩展、子类以及子类的扩展的所有 Objective-C 兼容成员。 大多数代码应该使用 `objc` 特性,以暴露所需的声明。如果需要暴露多个声明,可以将其分组到添加 `objc` 特性的扩展中。`objcMembers` 特性为大量使用 Objective-C 运行时的内省工具的库提供了便利。添加不必要的 `objc` 特性会增加二进制体积并影响性能。 - -### `requires_stored_property_inits` +### `requires_stored_property_inits` {#requires_stored_property_inits} 该特性用于类声明,以要求类中所有存储属性提供默认值作为其定义的一部分。对于从中继承的任何类都推断出 `NSManagedObject` 特性。 - -### `testable` +### `testable` {#testable} 在导入允许测试的编译模块时,该特性用于修饰 `import` 声明,这样就能访问被导入模块中的任何标有 `internal` 访问级别修饰符的实体,犹如它们被标记了 `public` 访问级别修饰符。测试也可以访问使用 `internal` 或者 `public` 访问级别修饰符标记的类和类成员,就像它们是 `open` 访问修饰符声明的。 - -### `UIApplicationMain` +### `UIApplicationMain` {#uiapplicationmain} 在类上使用该特性表示该类是应用程序委托类,使用该特性与调用 `UIApplicationMain` 函数并且把该类的名字作为委托类的名字传递给函数的效果相同。 如果你不想使用这个特性,可以提供一个 `main.swift` 文件,并在代码顶层调用 `UIApplicationMain(_:_:_:_:)` 函数。比如,如果你的应用程序使用一个继承于 UIApplication 的自定义子类作为主要类,你可以调用 `UIApplicationMain(_:_:_:_:)` 函数而不是使用该特性。 - -### `usableFromInline` +### `usableFromInline` {#usablefrominline} 该特性用于函数、方法、计算属性、下标、构造器或析构器的声明,以在同一模块中允许该符号用于内联代码的声明。声明必须具有 `internal` 访问级别修饰符。 @@ -324,16 +307,13 @@ class ExampleClass: NSObject { 标记为 `inlinable` 特性的声明,在内联代码中可以隐式使用。虽然 `inlinable` 或 `usableFromInline` 可以用于 `internal` 声明,但这两者不能同时使用。 - -### `warn_unqualified_access` +### `warn_unqualified_access` {#warn_unqualified_access} 该特性应用于顶级函数、实例方法、类方法或静态方法,以在没有前置限定符(例如模块名称、类型名称、实例变量或常量)的情况下使用该函数或方法时触发警告。使用该特性可以帮助减少在同一作用于访问同名函数之间的歧义。 例如,Swift 标准库包含 [`min(_:_:)`](https://developer.apple.com/documentation/swift/1538339-min/) 顶级函数和用于序列比较元素的 [`min()`](https://developer.apple.com/documentation/swift/sequence/1641174-min) 方法。序列方法声明使用了 `warn_unqualified_access`,以减少在 `Sequence` 扩展中使用它们的歧义。 - - -### Interface Builder 使用的声明特性 +### Interface Builder 使用的声明特性 {#declaration_attributes_used_by_interface_builder} `Interface Builder` 特性是 `Interface Builder` 用来与 Xcode 同步的声明特性。`Swift` 提供了以下的 `Interface Builder` 特性:`IBAction`,`IBOutlet`,`IBDesignable`,以及 `IBInspectable` 。这些特性与 Objective-C 中对应的特性在概念上是相同的。 @@ -341,18 +321,15 @@ class ExampleClass: NSObject { 应用 `IBAction`、`IBOutlet`、`IBDesignable` 或者 `IBInspectable` 特性都意味着同时应用 `objc` 特性。 - -## 类型特性 +## 类型特性 {#type_attributes} 类型特性只能用于修饰类型。 - -### `autoclosure` +### `autoclosure` {#autoclosure} 这个特性通过把表达式自动封装成无参数的闭包来延迟表达式的计算。它可以修饰类型为返回表达式结果类型的无参数函数类型的函数参数。关于如何使用 autoclosure 特性的例子,请参阅 [自动闭包](../chapter2/07_Closures.md#autoclosures) 和 [函数类型](./03_Types.md#function_type)。 - -### `convention` +### `convention` {#convention} 该特性用于修饰函数类型,它指出了函数调用的约定。 @@ -366,13 +343,11 @@ convention 特性总是与下面的参数之一一起出现。 使用 C 函数调用约定的函数也可用作使用 Objective-C 块调用约定的函数,同时使用 Objective-C 块调用约定的函数也可用作使用 Swift 函数调用约定的函数。然而,只有非泛型的全局函数、局部函数以及未捕获任何局部变量的闭包,才可以被用作使用 C 函数调用约定的函数。 - -### `escaping` +### `escaping` {#escaping} 在函数或者方法声明上使用该特性,它表示参数将不会被存储以供延迟执行,这将确保参数不会超出函数调用的生命周期。在使用 `escaping` 声明特性的函数类型中访问属性和方法时不需要显式地使用 `self.`。关于如何使用 `escaping` 特性的例子,请参阅 [逃逸闭包](../chapter2/07_Closures.md#escaping_closures)。 - -## Switch Case 特性 +## Switch Case 特性 {#switch_case_attributes} 你只能在 switch cases 中使用 switch case 特性。 @@ -383,28 +358,28 @@ convention 特性总是与下面的参数之一一起出现。 > 特性语法 > > -> +> attribute {#attribute} > > *特性*→ [特性名](#attribute_name) [特性参数子句](#atribute_argument_clause)可选 > -> +> attribute_name {#attribute_name} > > *特性名* → [标识符](./02_Lexical_Structure.md#identifier) > -> +> atribute_argument_clause {#atribute_argument_clause} > > *特性参数子句* → **(** [均衡令牌列表](#balanced_tokens)可选 **)** > -> +> attributes {#attributes} > > *特性列表* → [特性](#attribute) [特性列表](#attributes)可选 > > -> +> balanced_tokens {#balanced_tokens} > > *均衡令牌列表* → [均衡令牌](#balanced_token) [均衡令牌列表](#balanced_tokens)可选 > -> +> balanced_token {#balanced_token} > > *均衡令牌* → **(** [均衡令牌列表](#balanced_tokens)可选 **)** > diff --git a/source/chapter3/08_Patterns.md b/source/chapter3/08_Patterns.md index 76c0b2de..64e5512a 100755 --- a/source/chapter3/08_Patterns.md +++ b/source/chapter3/08_Patterns.md @@ -10,7 +10,7 @@ Swift 中的模式分为两类:一种能成功匹配任何类型的值,另 > 模式语法 > - +pattern {#pattern} > *模式* → [*通配符模式*](#wildcard_pattern) [*类型标注*](03_Types.md#type-annotation)可选 > > *模式* → [*标识符模式*](#identifier_pattern) [*类型标注*](03_Types.md#type-annotation)可选 @@ -40,13 +40,11 @@ for _ in 1...3 { > 通配符模式语法 > - +wildcard-pattern {#wildcard-pattern} > *通配符模式* → **_** > - -## 标识符模式(Identifier Pattern) - +## 标识符模式(Identifier Pattern) {#identifier_pattern} *标识符模式*匹配任何值,并将匹配的值和一个变量或常量绑定起来。例如,在下面的常量声明中,`someValue` 是一个标识符模式,匹配了 `Int` 类型的 `42`: ```swift @@ -59,13 +57,11 @@ let someValue = 42 > 标识符模式语法 > - +identifier-pattern {#identifier-pattern} > *标识符模式* → [*标识符*](./02_Lexical_Structure.md#identifier) > - -## 值绑定模式(Value-Binding Pattern) - +## 值绑定模式(Value-Binding Pattern) {#value-binding_pattern} *值绑定模式*把匹配到的值绑定给一个变量或常量。把匹配到的值绑定给常量时,用关键字 `let`,绑定给变量时,用关键字 `var`。 在值绑定模式中的标识符模式会把新命名的变量或常量与匹配到的值做绑定。例如,你可以拆开一个元组,然后把每个元素绑定到相应的标识符模式中。 @@ -84,13 +80,11 @@ case let (x, y): > 值绑定模式语法 > - +value-binding-pattern {#value-binding-pattern} > *值绑定模式* → **var** [*模式*](#pattern) | **let** [*模式*](#pattern) > - -## 元组模式 - +## 元组模式 {#tuple_pattern} *元组模式*是由逗号分隔的,具有零个或多个模式的列表,并由一对圆括号括起来。元组模式匹配相应元组类型的值。 你可以使用类型标注去限制一个元组模式能匹配哪种元组类型。例如,在常量声明 `let (x, y): (Int, Int) = (1, 2)` 中的元组模式 `(x, y): (Int, Int)` 只匹配两个元素都是 `Int` 类型的元组。 @@ -115,32 +109,28 @@ let (a): Int = 2 // a: Int = 2 > 元组模式语法 > - +tuple-pattern {#tuple-pattern} > *元组模式* → **(** [*元组模式元素列表*](#tuple-pattern-element-list)可选 **)** > - +tuple-pattern-element-list {#tuple-pattern-element-list} > *元组模式元素列表* → [*元组模式元素*](#tuple-pattern-element) | [*元组模式元素*](#tuple-pattern-element) **,** [*元组模式元素列表*](#tuple-pattern-element-list) > - +tuple-pattern-element {#tuple-pattern-element} > *元组模式元素* → [*模式*](#pattern) > - -## 枚举用例模式(Enumeration Case Pattern) - +## 枚举用例模式(Enumeration Case Pattern) {#enumeration_case_pattern} *枚举用例模式*匹配现有的某个枚举类型的某个用例。枚举用例模式出现在 `switch` 语句中的 `case` 标签中,以及 `if`、`while`、`guard` 和 `for-in` 语句的 `case` 条件中。 如果你准备匹配的枚举用例有任何关联的值,则相应的枚举用例模式必须指定一个包含每个关联值元素的元组模式。关于使用 `switch` 语句来匹配包含关联值的枚举用例的例子,请参阅 [关联值](../chapter2/08_Enumerations.md#associated_values)。 > 枚举用例模式语法 > - +enum-case-pattern {#enum-case-pattern} > *枚举用例模式* → [*类型标识*](./03_Types.md#type-identifier)可选 **.** [*枚举用例名*](./06_Declarations.md#enum-case-name) [*元组模式*](#tuple-pattern)可选 > - -## 可选模式(Optional Pattern) - +## 可选模式(Optional Pattern) {#optional_pattern} *可选模式*匹配包装在一个 `Optional(Wrapped)` 或者 `ExplicitlyUnwrappedOptional(Wrapped)` 枚举中的 `Some(Wrapped)` 用例中的值。可选模式由一个标识符模式和紧随其后的一个问号组成,可以像枚举用例模式一样使用。 由于可选模式是 `Optional` 和 `ImplicitlyUnwrappedOptional` 枚举用例模式的语法糖,下面两种写法是等效的: @@ -173,13 +163,11 @@ for case let number? in arrayOfOptinalInts { > 可选模式语法 > - +optional-pattern {#optional-pattern} > *可选模式* → [*标识符模式*](./03_Types.md#type-identifier) **?** > - -## 类型转换模式(Type-Casting Patterns) - +## 类型转换模式(Type-Casting Patterns) {#type-casting_patterns} 有两种类型转换模式,`is` 模式和 `as` 模式。`is` 模式只出现在 `switch` 语句中的 `case` 标签中。`is` 模式和 `as` 模式形式如下: > is `类型` @@ -195,19 +183,17 @@ for case let number? in arrayOfOptinalInts { > 类型转换模式语法 > - +type-casting-pattern {#type-casting-pattern} > *类型转换模式* → [*is 模式*](#is-pattern) | [*as 模式*](#as-pattern) > - +is-pattern {#is-pattern} > *is 模式* → **is** [*类型*](./03_Types.md#type) > - +as-pattern {#as-pattern} > *as 模式* → [*模式*](#pattern) **as** [*类型*](03_Types.md#type) > - -## 表达式模式(Expression Pattern) - +## 表达式模式(Expression Pattern) {#expression_pattern} *表达式模式*代表表达式的值。表达式模式只出现在 `switch` 语句中的 `case` 标签中。 表达式模式代表的表达式会使用 Swift 标准库中的 `~=` 运算符与输入表达式的值进行比较。如果 `~=` 运算符返回 `true`,则匹配成功。默认情况下,`~=` 运算符使用 `==` 运算符来比较两个相同类型的值。它也可以将一个整型数值与一个 `Range` 实例中的一段整数区间做匹配,正如下面这个例子所示: @@ -245,6 +231,6 @@ default: > 表达式模式语法 > - +expression-pattern {#expression-pattern} > *表达式模式* → [*表达式*](./04_Expressions.md#expression) > diff --git a/source/chapter3/09_Generic_Parameters_and_Arguments.md b/source/chapter3/09_Generic_Parameters_and_Arguments.md index ed0cac15..374157ce 100755 --- a/source/chapter3/09_Generic_Parameters_and_Arguments.md +++ b/source/chapter3/09_Generic_Parameters_and_Arguments.md @@ -4,9 +4,7 @@ 关于 Swift 语言的泛型概述,请参阅 [泛型](../chapter2/22_Generics.md)。 - -## 泛型形参子句 - +## 泛型形参子句 {#generic_parameter} *泛型形参子句*指定泛型类型或函数的类型形参,以及这些参数相关的约束和要求。泛型形参子句用尖括号(`<>`)包住,形式如下: > <`泛型形参列表`> @@ -38,9 +36,7 @@ simpleMax(17, 42) // T 被推断为 Int 类型 simpleMax(3.14159, 2.71828) // T 被推断为 Double 类型 ``` - -### Where 子句 - +### Where 子句 {#where_clauses} 要想对类型形参及其关联类型指定额外要求,可以在函数体或者类型的大括号之前添加 `where` 子句。`where` 子句由关键字 `where` 及其后的用逗号分隔的一个或多个要求组成。 > `where` : `类型要求` @@ -60,42 +56,40 @@ simpleMax(3.14159, 2.71828) // T 被推断为 Double 类型 > 泛型形参子句语法 > - +generic-parameter-clause {#generic-parameter-clause} > *泛型形参子句* → **<** [*泛型形参列表*](#generic-parameter-list) [*约束子句*](#requirement-clause)可选 **>** > - +generic-parameter-list {#generic-parameter-list} > *泛型形参列表* → [*泛形形参*](#generic-parameter) | [*泛形形参*](#generic-parameter) **,** [*泛型形参列表*](#generic-parameter-list) > - +generic-parameter {#generic-parameter} > *泛形形参* → [*类型名称*](./03_Types.md#type-name) > > *泛形形参* → [*类型名称*](./03_Types.md#type-name) **:** [*类型标识符*](./03_Types.md#type-identifier) > > *泛形形参* → [*类型名称*](./03_Types.md#type-name) **:** [*协议合成类型*](./03_Types.md#protocol-composition-type) > -> +> requirement-clause {#requirement-clause} > > *约束子句* → **where** [*约束列表*](#requirement-list) > - +requirement-list {#requirement-list} > *约束列表* → [*约束*](#requirement) | [*约束*](#requirement) **,** [*约束列表*](#requirement-list) > - +requirement {#requirement} > *约束* → [*一致性约束*](#conformance-requirement) | [*同类型约束*](#same-type-requirement) > -> +> conformance-requirement {#conformance-requirement} > > *一致性约束* → [*类型标识符*](./03_Types.md#type-identifier) **:** [*类型标识符*](./03_Types.md#type-identifier) > > *一致性约束* → [*类型标识符*](./03_Types.md#type-identifier) **:** [*协议合成类型*](./03_Types.md#protocol-composition-type) > - +same-type-requirement {#same-type-requirement} > *同类型约束* → [*类型标识符*](./03_Types.md#type-identifier) **==** [*类型*](./03_Types.md#type) > - -## 泛型实参子句 - +## 泛型实参子句 {#generic_argument} *泛型实参子句*指定泛型类型的类型实参。泛型实参子句用尖括号(`<>`)包住,形式如下: > <`泛型实参列表`> @@ -122,12 +116,12 @@ let arrayOfArrays: Array> = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] > 泛型实参子句语法 > - +generic-argument-clause {#generic-argument-clause} > *泛型实参子句* → **<** [*泛型实参列表*](#generic-argument-list) **>** > - +generic-argument-list {#generic-argument-list} > *泛型实参列表* → [*泛型实参*](#generic-argument) | [*泛型实参*](#generic-argument) **,** [*泛型实参列表*](#generic-argument-list) > - +generic-argument {#generic-argument} > *泛型实参* → [*类型*](./03_Types.md#type) > diff --git a/source/chapter3/10_Summary_of_the_Grammar.md b/source/chapter3/10_Summary_of_the_Grammar.md index d8ccc305..279ca9c8 100755 --- a/source/chapter3/10_Summary_of_the_Grammar.md +++ b/source/chapter3/10_Summary_of_the_Grammar.md @@ -1,7 +1,6 @@ # 语法总结(Summary of the Grammar) - -## 语句 +## 语句 {#statements} > 语句语法 > @@ -243,8 +242,7 @@ > *catch-clauses* → **catch** [*模式(pattern)*](TODO) _可选_ [*where-clause*](TODO) _可选_ [*代码块(code-block)*](TODO) _可选_ > - -## 泛型参数 +## 泛型参数 {#generic_parameters_and_arguments} > 泛型形参从句(Generic Parameter Clause)语法 > @@ -283,8 +281,7 @@ > *泛型参数* → [*类型*](./03_Types.md#type) > - -## 声明(Declarations) +## 声明(Declarations) {#declarations} > 声明语法 > @@ -666,8 +663,7 @@ > *访问级别修改器集* →[*访问级别修改器*](TODO) [*访问级别修改器集*](TODO) _可选_ > - -## 模式 +## 模式 {#patterns} > 模式(Patterns)语法 > @@ -751,8 +747,7 @@ > *表达式模式* → [*表达式*](./04_Expressions.md#expression) > - -## 属性 +## 属性 {#attributes} > 属性语法 > @@ -777,8 +772,7 @@ > *平衡令牌* → **任意标点除了(、)、[、]、{ 或 }** > - -## 表达式 +## 表达式 {#expressions} > 表达式语法 > @@ -1049,8 +1043,7 @@ > *可选链表达式* → [*后置表达式*](./04_Expressions.md#postfix_expression) **?** > - -## 词法结构 +## 词法结构 {#lexical_structure} > 标识符语法 > @@ -1277,8 +1270,7 @@ > *后置运算符* → [*运算符*](./02_Lexical_Structure.md#operator) > - -## 类型 +## 类型 {#types} > 类型语法 > diff --git a/source/chapter4/02_Type_Custom.md b/source/chapter4/02_Type_Custom.md index 9ae1c210..3b82da08 100644 --- a/source/chapter4/02_Type_Custom.md +++ b/source/chapter4/02_Type_Custom.md @@ -15,8 +15,7 @@ 小伙伴们,Swift 中的 Bool 类型有着非常重要的语法功能,并支撑起了整个 Swift 体系中的逻辑判断体系,经过老码的研究和学习, Bool 类型本身其实是对基础 Boolean 类型封装,小伙伴们可能咬着手指头问老码,怎么一会 Bool 类型,一会 Boolean 类型,其区别在于,前者是基于枚举的组合类型,而后者则是基本类型,只有两种 true 和 false。 - -####自定义原型 +####自定义原型 {#prefix_expressions} 接下老码根据 Bool 的思想来创建一个 OCBool 类型,来让小伙伴们了解一下 Swift 中到底是怎么玩儿的。 来我们先看一下 OCBool 的定义。 @@ -35,8 +34,7 @@ case ocFalse - 代码中第2行和第3行,可以合并到一行写,如苹果官方 Blog 所写的一样 - 代码中命名需要注意:OCBool 是类型名,所以首字母必须大写,而 case 中的 ocTrue 和 ocFalse 是小类型则需要首字母小写。 - -####实现默认值 +####实现默认值 {#imp-default} 行,我们给了一个漂亮的定义,不过按照传统语言的经验,Bool 值默认情况下是假, 所以我们的 OCBool 也应该如此,我们使用类型扩展技术增加这个默认特性: @@ -61,8 +59,7 @@ var result:OCBool = OCBool() var result1:OCBool = .ocTrue ``` - -####支持基本布尔型初始化 +####支持基本布尔型初始化 {#init-by-bool} 正如上述代码所述,我们只能通过类型或者枚举项目赋值,这是组合类型的用法,但是编码的日子里,我们总是希望和 true,false 直接打交道,也就是说,我们希望这么做, 代码示例如下: @@ -113,8 +110,7 @@ protocol BooleanLiteralConvertible { - 这个定义中有个类方法 convertFromBooleanLiteral,它的参数为 BooleanLiteralType 类型,也就是我传入的 Bool 类型, 且返回值为实现这个协议的类型本身,在我们的 OCBool 类型中,其返回值就是 OCBool 本身。经过这个定义,我们可以直接对 OCBool 类型直接进行布尔字面量初始化了。 - -####支持 Bool 类型判断 +####支持 Bool 类型判断 {#condition-by-bool} 小伙伴们不安分, 肯定想着我怎么用它实现逻辑判断,所以如果你这么写, @@ -188,9 +184,7 @@ Program ended with exit code: 0 - 如果小伙伴们现在用的是 Beta 版的 Xcode,注意苹果官方 Blog 中,在代码第17行如果在 Xcode Beta4下是错误的,这里的协议是,LogicValue 而不是 BooleanVue,所以记得看错误提示才是好习惯。 - 注意代码第34行,完美支持 if 判断,且输出结果为“老码请你吃火锅”,老码也是说说而已,请不要当真。 - - -####支持兼容各们各派的类型 +####支持兼容各们各派的类型 {#support-all-type} 小伙伴们,江湖风险,门派众多,老码有自己的 OCBool 类型,可能嵩山少林有自己的 SSBool 类型,甚至连郭美美都可能有自己的 MMBool 类型,所以 OCBool 必须能够识别这些类型,这些各门各派的类型,只要支持 LogicValue 协议,就应该可以被识别,看老码怎么做, @@ -232,8 +226,7 @@ Program ended with exit code: 0 - 代码中第2行:“_”下横杠的用法,这是一个功能强大的小强,在此的目的是屏蔽外部参数名,所以小伙伴们可以直接:var ocResult:OCBool = OCBool(mmResult)而不是:var ocResult:OCBool = OCBool(v: mmResult),小伙伴们惊呆了!这个 init 函数中本来就没有外部参数名啊,还记得老码在书里说过没,Swift 的初始化函数会默认使用内部参数名,作为外部参数名。 - -####完善 OCBool 的布尔基因体系: +####完善 OCBool 的布尔基因体系: {#make-up-type} 小伙伴们,bool 类型的价值就是在于各种判断,诸如==,!=, &,|,^,!,以及各种组合逻辑运算,我们 OCBool 也要具备这些功能,否则就会基因缺陷,且看老码如何实现: diff --git a/source/chapter4/05_Value_and_Reference_Types.md b/source/chapter4/05_Value_and_Reference_Types.md index 25d5c473..df24e210 100644 --- a/source/chapter4/05_Value_and_Reference_Types.md +++ b/source/chapter4/05_Value_and_Reference_Types.md @@ -17,8 +17,7 @@ 在这篇博文里面,我们会介绍两种类型各自的优点,以及应该怎么选择使用。 - -#### 值类型与引用类型的区别 +#### 值类型与引用类型的区别 {#difference-two} 值类型和引用类型最基本的分别在复制之后的结果。当一个值类型被复制的时候,相当于创造了一个完全独立的实例,这个实例保有属于自己的独有数据,数据不会受到其他实例的数据变化影响: @@ -44,8 +43,7 @@ println("\(x.data), \(y.data)") // 输出结果 "42, 42" ``` - -#### Mutation(修改)在安全中扮演的角色 +#### Mutation(修改)在安全中扮演的角色 {#act-in=mutation} 值类型较引用类型来说,会让你更容易在大量代码中理清状况。如果你总是得到一个独立的拷贝出来的实例,你就可以放心它不会被你 app 里面的其他部分代码默默地修改。这在多线程的环境里面是尤为重要的,因为另外一个线程可能会在暗地里修改你的数据。因此可能会造成严重的程序错误,这在调试过程中非常难以排除。 @@ -53,8 +51,7 @@ 你可能在想,有的时候我可能也需要一个完全不变的类。这样使用 `Cocoa NSObject` 对象的时候会比较容易,又可以保留值语义的好处。在今天,你可以通过只使用不可变的存储属性,和避开任何可以修改状态的 API,用 Swift 写出一个不可变类 `(immutable class)`。实际上,很多基本的 Cocoa 类,例如 `NSURL`,都是设计成不可变类的。然而,Swift 语言目前只强制 `struct` 和 `enum` 这种值类型的不可变性,对类这种引用类型则没有。(例如还不支持强制将子类的限制为不可变类) - -#### 如何选择类型? +#### 如何选择类型? {#how-to-choose} 所以当我们想要建立一个新的类型的时候,怎么决定用值类型还是引用类型呢?当你使用 Cocoa 框架的时候,很多 API 都要通过 NSObject 的子类使用,所以这时候必须要用到引用类型 class。在其他情况下,有下面几个准则: diff --git a/source/chapter4/07_Optional_Case_Study.md b/source/chapter4/07_Optional_Case_Study.md index 4a38ee93..d54a68db 100644 --- a/source/chapter4/07_Optional_Case_Study.md +++ b/source/chapter4/07_Optional_Case_Study.md @@ -13,10 +13,10 @@ 可选类型是 Swift 中新引入的,功能很强大。在这篇博文里讨论的,是在 Swift 里,如何通过可选类型来保证强类型的安全性。作为例子,我们来创建一个 Objective-C API 的 Swift 版本,但实际上 Swift 本身并不需要这样的 API。 - -#### 为 Dictionary 增加 objectsForKeys 函数 +#### 为 Dictionary 增加 objectsForKeys 函数 {#add-function} -在 Objective-C 中,`NSDictionary` 有一个方法 `-objectsForKeys:NoFoundMarker:`, 这个方法需要一个 `NSArray` 数组作为键值参数,然后返回一个包含相关值的数组。文档里写到:“返回数组中的第 N 个值,和输入数组中的第 N 个值相对应”,那如果有某个键值在字典里不存在呢?于是就有了 `notFoundMarker` 作为返回提示。比如第三个键值没有找到,那么在返回数组中第三个值就是这个 `notFoundMarker`,而不是字典中的第三个值,但是这个值只是用来提醒原字典中没有找到对应值,但在返回数组中该元素存在,且用 `notFoundMarker` 作为占位符,因为这个对象不能直接使用,所以在 Foundation 框架中有个专门的类处理这个情况:`NSNull`。 +在 Objective-C 中,`NSDictionary` 有一个方法 `-objectsForKeys:NoFoundMarker:`, 这个方法需要一个 `NSArray` 数组作为键值参数,然后返回一个包含相关值的数组。文档里写到:“返回数组中的第 N 个值, +和输入数组中的第 N 个值相对应”,那如果有某个键值在字典里不存在呢?于是就有了 `notFoundMarker` 作为返回提示。比如第三个键值没有找到,那么在返回数组中第三个值就是这个 `notFoundMarker`,而不是字典中的第三个值,但是这个值只是用来提醒原字典中没有找到对应值,但在返回数组中该元素存在,且用 `notFoundMarker` 作为占位符,因为这个对象不能直接使用,所以在 Foundation 框架中有个专门的类处理这个情况:`NSNull`。 在 Swift 中,`Dictionary` 类没有类似 `objectsForKeys` 的函数,为了说明问题,我们动手加一个,并且使其成为操作字典值的通用方法。我们可以用 `extension` 来实现: @@ -43,8 +43,7 @@ extension Dictionary{ } ``` - -#### Swift 中更简便的方法 +#### Swift 中更简便的方法 {#easy-function} 小伙伴们可能会问,为什么 Swift 中不需要实现这么一个 API 呢?其实其有更简单的实现,如下面代码所示: @@ -73,8 +72,7 @@ t = dic.valuesForKeys([]) //结果为:[] ``` - -#### 内嵌可选类型 +#### 内嵌可选类型 {#nested-optional} 现在,如果我们为每一个结果调用 `last` 方法,看下结果如何? @@ -114,8 +112,7 @@ var last:T? { get } 不管是 Swift 版本还是 Objective-C 版本,返回值为 `nil` 都意味数组是空的,所以它就没有最后一个元素。 但是如果返回是 `Optional(nil)` 或者 Objective-C 中的 `NSNull` 都表示数组中的最后一个元素存在,但是元素的内容是空的。在 Objective-C 中只能借助 `NSNull` 作为占位符来达到这个目的,但是 Swift 却可以语言系统类型的角度的实现。 - -#### 提供一个默认值 +#### 提供一个默认值 {#provide-default} 进一步封装,如果我字典中的某个或某些元素不存在,我们想提供一个默认值怎么办呢?实现方法很简单: