From a104f980b4908a4121d7d821f36ddd7df13f48f0 Mon Sep 17 00:00:00 2001 From: "mynightelfplayer@hotmail.com" Date: Fri, 1 Sep 2017 10:42:52 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E2=80=9C=E5=A4=9A=E8=A1=8C=E5=AD=97?= =?UTF-8?q?=E7=AC=A6=E4=B8=B2=E5=AD=97=E9=9D=A2=E9=87=8F=E2=80=9D=20?= =?UTF-8?q?=E7=9A=84=E8=BF=BD=E5=8A=A0=20Swift=204.0=20=E2=80=9C=E5=A4=9A?= =?UTF-8?q?=E8=A1=8C=E5=AD=97=E7=AC=A6=E4=B8=B2=E5=AD=97=E9=9D=A2=E9=87=8F?= =?UTF-8?q?=E2=80=9D=E7=9A=84=E5=AF=B9=E5=BA=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/chapter2/03_Strings_and_Characters.md | 54 ++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/source/chapter2/03_Strings_and_Characters.md b/source/chapter2/03_Strings_and_Characters.md index 30a761f6..f806680b 100755 --- a/source/chapter2/03_Strings_and_Characters.md +++ b/source/chapter2/03_Strings_and_Characters.md @@ -22,6 +22,7 @@ 本页包含内容: - [字符串字面量](#string_literals) +- [多行字符串字面量](#multiline_string_literals) - [初始化空字符串](#initializing_an_empty_string) - [字符串可变性](#string_mutability) - [字符串是值类型](#strings_are_value_types) @@ -63,6 +64,59 @@ let someString = "Some string literal value" > 注意: 更多关于在字符串字面量中使用特殊字符的信息,请查看 [字符串字面量的特殊字符](#special_characters_in_string_literals) 。 + +## 多行字符串字面量 + +如果你需要一个字符串是跨越多行的,你可以使用多行字符串字面量,它是由三个双引号(`"""`)包裹着的具有固定顺序的文本字符集。 + +```swift +let quotation = """ +The White Rabbit put on his spectacles. "Where shall I begin, +please your Majesty?" he asked. + +"Begin at the beginning," the King said gravely, "and go on +till you come to the end; then stop." +""" +``` + +一个多行字符串字面量包含了所有的在开启和关闭引号(`"""`)中的行。这个字符从开启引号(`"""`)之后的第一行开始,到关闭引号(`"""`)之前为止。这就意味着字符串开启引号之后(`"""`)或者结束引号(`"""`)之前都没有一个换行符号。(译者:下面两个字符串其实是一样的,虽然第二个使用了多行字符串的形势) + +```swift +let singleLineString = "These are the same." +let multilineString = """ +These are the same. +""" +``` + +如果你的代码中,多行字符串字面量包含换行符的话,则多行字符串字面量中也会包含换行符。如果你想使用换行,使得你的代码获得好的可读性,但是你又不想在你的多行字符串字面量中出现换行符的话,你可以用在行尾写一个反斜杠(`\`)作为续行符。 + +```swift +let softWrappedQuotation = """ +The White Rabbit put on his spectacles. "Where shall I begin, \ +please your Majesty?" he asked. + +"Begin at the beginning," the King said gravely, "and go on \ +till you come to the end; then stop." +""" +``` + +为了让一个多行字符串字面量开始和结束于换行符,请将换行写在第一行和最后一行,例如: + +```swift +let lineBreaks = """ + +This string starts with a line break. +It also ends with a line break. + +""" +``` + +一个多行字符串字面量能够缩进来匹配周围的代码。关闭引号(`"""`)之前的空白字符串告诉Swift编译器其他各行多少空白字符串需要忽略。然而,如果你在某行的前面写的空白字符串超出了关闭引号(`"""`)之前的空白字符串,则超出部分将被包含在多行字符串字面量中。 + +![](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Art/multilineStringWhitespace_2x.png) + +在上面的例子中,尽管整个多行字符串字面量都是缩进的(源代码缩进),第一行和最后一行没有以空白字符串开始(实际的变量值)。中间一行的缩进用空白字符串(源代码缩进)比关闭引号(`"""`)之前的空白字符串多,所以,它的行首将有4个空白字符串(实际的变量值)。 + ## 初始化空字符串 From 2db98b316ecbf94ae705504ec3d7f2fc412286ea Mon Sep 17 00:00:00 2001 From: "mynightelfplayer@hotmail.com" Date: Fri, 1 Sep 2017 11:47:47 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E5=85=B7=E6=9C=89=E6=B3=9B=E5=9E=8B=20Wher?= =?UTF-8?q?e=20=E5=AD=90=E5=8F=A5=E7=9A=84=E5=85=B3=E8=81=94=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=20=E6=B3=9B=E5=9E=8B=E4=B8=8B=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/chapter2/23_Generics.md | 56 +++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/source/chapter2/23_Generics.md b/source/chapter2/23_Generics.md index da678988..34a25a93 100644 --- a/source/chapter2/23_Generics.md +++ b/source/chapter2/23_Generics.md @@ -31,6 +31,8 @@ - [关联类型](#associated_types) - [泛型 Where 语句](#where_clauses) - [具有泛型 where 子句的扩展](#extensions_with_a_generic_where_clause) +- [具有泛型 Where 子句的关联类型](#associated_types_with_a_generic_where_clause) +- [泛型下标](#generic_subscripts) *泛型代码*让你能够根据自定义的需求,编写出适用于任意类型、灵活可重用的函数及类型。它能让你避免代码的重复,用一种清晰和抽象的方式来表达代码的意图。 @@ -618,4 +620,56 @@ print([1260.0, 1200.0, 98.6, 37.0].average()) 此示例将一个 `average()` 方法添加到 `Item` 类型为 `Double` 的容器中。此方法遍历容器中的元素将其累加,并除以容器的数量计算平均值。它将数量从 `Int` 转换为 `Double` 确保能够进行浮点除法。 -就像可以在其他地方写泛型 `where` 子句一样,你可以在一个泛型 `where` 子句中包含多个条件作为扩展的一部分。用逗号分隔列表中的每个条件。 +就像可以在其他地方写泛型 `where` 子句一样,你可以在一个泛型 `where` 子句中包含多个条件作为扩展的一部分。用逗号分隔列表中的每个条件。 + + + +## 具有泛型 Where 子句的关联类型 + +你能够包含一个具有泛型 Where 子句的关联类型。例如建立一个包含迭代器(Iterator)的容器,就像是标准库中使用的Sequence协议那样。下面是你所写的代码: + +```swift +protocol Container { + associatedtype Item + mutating func append(_ item: Item) + var count: Int { get } + subscript(i: Int) -> Item { get } + + associatedtype Iterator: IteratorProtocol where Iterator.Element == Item + func makeIterator() -> Iterator +} +``` + +迭代器(Iterator)的泛型Where子句要求:无论迭代器是什么类型,迭代器中的元素类型,必须和容器项目的类型保持一致。makeIterator()则提供了容器的迭代器的访问接口。 + +一个协议继承了另一个协议,你通过在协议声明的时候,包含泛型Where子句,来添加了一个约束到被继承协议的关联类型。例如,下面的代码声明了一个ComparableContainer协议,它要求所有的Item必须是Comparable的。 + +```swift +protocol ComparableContainer: Container where Item: Comparable { } +``` + + +##泛型下标 + +下标能够是泛型的,他们能够包含泛型Where子句。你可以在subscript后面的尖括号中,写下占位符(placeholder)类型名称,在下标代码体开始的标志的花括号之前写下泛型Where子句。例如: + +```swift +extension Container { + subscript(indices: Indices) -> [Item] + where Indices.Iterator.Element == Int { + var result = [Item]() + for index in indices { + result.append(self[index]) + } + return result + } +} +``` + +这个Container协议的扩展添加了一个下标:下标是一个序列的索引,返回的则是索引所在的项目的值所构成的数组。这个泛型下标的约束如下: +- 在尖括号中的泛型参数Indices,必须是符合标准库中的Sequence协议的类型。 +- 下标使用的单一的参数,indices,必须是Indices的实例。 +- 泛型Where子句要求Sequence(Indices)的迭代器,其所有的元素都是Int类型。这样就能确保在序列(Sequence)中的索引和容器(Container)里面的索引类型是一致的。 + +综合一下,这些约束意味着,传入到indices下标,是一个整数的序列. +(译者:原来的Container协议,subscript必须是Int型的,扩展中新的subscript,允许下标是一个的序列,而非单一的值。) \ No newline at end of file From 9c0a744c478c013de0206ab3dc87698bea6de1b1 Mon Sep 17 00:00:00 2001 From: "mynightelfplayer@hotmail.com" Date: Fri, 1 Sep 2017 14:44:41 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E6=96=AD=E8=A8=80=E5=92=8C=E5=85=88?= =?UTF-8?q?=E5=86=B3=E6=9D=A1=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/chapter2/01_The_Basics.md | 55 +++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/source/chapter2/01_The_Basics.md b/source/chapter2/01_The_Basics.md index 578c69ec..b1b7c5a9 100755 --- a/source/chapter2/01_The_Basics.md +++ b/source/chapter2/01_The_Basics.md @@ -16,8 +16,8 @@ > 校对:[SketchK](https://github.com/SketchK) > > 3.0 -> 校对:[CMB](https://github.com/chenmingbiao),版本时间2016-09-13 -> +> 校对:[CMB](https://github.com/chenmingbiao),版本时间2016-09-13 +> > 3.0.1, 2016-11-11,shanks 本页包含内容: @@ -48,7 +48,7 @@ - [可选绑定](#optional_binding) - [隐式解析可选类型](#implicityly_unwrapped_optionals) - [错误处理](#error_handling) -- [断言](#assertions) +- [断言和先决条件](#assertions_and_Preconditions) Swift 是一门开发 iOS, macOS, watchOS 和 tvOS 应用的新语言。然而,如果你有 C 或者 Objective-C 开发经验的话,你会发现 Swift 的很多内容都是你熟悉的。 @@ -772,17 +772,21 @@ do { 抛出,捕捉,以及传播错误会在[错误处理](./18_Error_Handling.html)章节详细说明。 - -## 断言 + +## 断言和先决条件 -可选类型可以让你判断值是否存在,你可以在代码中优雅地处理值缺失的情况。然而,在某些情况下,如果值缺失或者值并不满足特定的条件,你的代码可能没办法继续执行。这时,你可以在你的代码中触发一个 *断言(assertion)* 来结束代码运行并通过调试来找到值缺失的原因。 +断言和先决条件是在运行时所做的检查。你可以用他们来检查在执行后续代码之前是否一个必要的条件已经被满足了。如果断言或者先决条件中的布尔条件评估的结果为 true(真),则代码像往常一样继续执行。如果布尔条件评估结果为false(假),程序的当前状态是无效的,则代码执行结束,应用程序中止。 + +你使用断言和先决条件来表达你所做的假设和你在编码时候的期望。你可以将这些包含在你的代码中。断言帮助你在开发阶段找到错误和不正确的假设,先决条件帮助你在生产环境中探测到存在的问题。 + +除了在运行时验证你的期望值,断言和先决条件也变成了一个在你的代码中的有用的文档形式。和在上面讨论过的[错误处理](./18_Error_Handling.html)不同,断言和先决条件并不是用来处理可以恢复的或者可预期的错误。因为一个断言失败表明了程序正处于一个无效的状态,没有办法去捕获一个失败的断言。 + +使用断言和先决条件不是一个能够避免出现程序出现无效状态的编码方法。然而,如果一个无效状态程序产生了,断言和先决条件可以强制检查你的数据和程序状态,使得你的程序可预测的中止(译者:不是系统强制的,被动的中止),并帮助使这个问题更容易调试。一旦探测到无效的状态,执行则被中止,防止无效的状态导致的进一步对于系统的伤害。 + +断言和先决条件的不同点是,他们什么时候进行状态检测:断言仅在调试环境运行,而先决条件则在调试环境和生产环境中运行。在生产环境中,断言的条件将不会进行评估。这个意味着你可以使用很多断言在你的开发阶段,但是这些断言在生产环境中不会产生任何影响。 ### 使用断言进行调试 -断言会在运行时判断一个逻辑条件是否为 `true`。从字面意思来说,断言“断言”一个条件是否为真。你可以使用断言来保证在运行其他代码之前,某些重要的条件已经被满足。如果条件判断为 `true`,代码运行会继续进行;如果条件判断为 `false`,代码执行结束,你的应用被终止。 - -如果你的代码在调试环境下触发了一个断言,比如你在 Xcode 中构建并运行一个应用,你可以清楚地看到不合法的状态发生在哪里并检查断言被触发时你的应用的状态。此外,断言允许你附加一条调试信息。 - 你可以使用全局 `assert(_:_:file:line:)` 函数来写一个断言。向这个函数传入一个结果为 `true` 或者 `false` 的表达式以及一条信息,当表达式的结果为 `false` 的时候这条信息会被显示: ```swift @@ -797,20 +801,33 @@ assert(age >= 0, "A person's age cannot be less than zero") ```swift assert(age >= 0) +``` + +如果代码已经检查了条件,你可以使用 `assertionFailure(_:file:line:)`函数来表明断言失败了,例如: + +```swift +if age > 10 { + print("You can ride the roller-coaster or the ferris wheel.") +} else if age > 0 { + print("You can ride the ferris wheel.") +} else { + assertionFailure("A person's age can't be less than zero.") +} ``` -> 注意: -> 当代码使用优化编译的时候,断言将会被禁用,例如在 Xcode 中,使用默认的 target Release 配置选项来 build 时,断言会被禁用。 +### 强制执行先决条件 -### 何时使用断言 +当一个条件可能为false(假),但是继续执行代码要求条件必须为true(真)的时候,需要使用先决条件。例如使用先决条件来检查是否下标越界,或者来检查是否将一个正确的参数传给函数。 -当条件可能为假时使用断言,但是最终一定要_保证_条件为真,这样你的代码才能继续运行。断言的适用情景: +你可以使用全局 `precondition(_:_:file:line:)` 函数来写一个先决条件。向这个函数传入一个结果为 `true` 或者 `false` 的表达式以及一条信息,当表达式的结果为 `false` 的时候这条信息会被显示: -* 整数类型的下标索引被传入一个自定义下标实现,但是下标索引值可能太小或者太大。 -* 需要给函数传入一个值,但是非法的值可能导致函数不能正常执行。 -* 一个可选值现在是 `nil`,但是后面的代码运行需要一个非 `nil` 值。 +```swift +// In the implementation of a subscript... +precondition(index > 0, "Index must be greater than zero.") +``` -请参考[下标](./12_Subscripts.html)和[函数](./06_Functions.html)。 +你可以调用 `precondition(_:_:file:line:)`方法来表明出现了一个错误,例如,switch进入了default分支,但是所有的有效值应该被任意一个其他分支(非default分支)处理。 > 注意: -> 断言可能导致你的应用终止运行,所以你应当仔细设计你的代码来让非法条件不会出现。然而,在你的应用发布之前,有时候非法条件可能出现,这时使用断言可以快速发现问题。 +> 如果你使用unchecked模式(-Ounchecked)编译代码,先决条件将不会进行检查。编译器假设所有的先决条件总是为true(真),他将优化你的代码。然而,`fatalError(_:file:line:)`函数总是中断执行,无论你怎么进行优化设定。 +>你能使用 `fatalError(_:file:line:)`函数在设计原型和早期开发阶段,这个阶段只有方法的声明,但是没有具体实现,你可以在方法体中写上fatalError("Unimplemented")作为具体实现。因为fatalError不会像断言和先决条件那样被优化掉,所以你可以确保当代码执行到一个没有被实现的方法时,程序会被中断。 \ No newline at end of file From 0c18c51c8a24a54a220df988179c2afa08c5d07d Mon Sep 17 00:00:00 2001 From: "mynightelfplayer@hotmail.com" Date: Fri, 1 Sep 2017 14:59:22 +0800 Subject: [PATCH 4/4] Protocol Composition --- source/chapter2/22_Protocols.md | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/source/chapter2/22_Protocols.md b/source/chapter2/22_Protocols.md index 8498c30b..154cc205 100644 --- a/source/chapter2/22_Protocols.md +++ b/source/chapter2/22_Protocols.md @@ -646,8 +646,36 @@ wishHappyBirthday(to: birthdayPerson) 上面的例子创建了一个名为 `birthdayPerson` 的 `Person` 的实例,作为参数传递给了 `wishHappyBirthday(to:)` 函数。因为 `Person` 同时符合这两个协议,所以这个参数合法,函数将打印生日问候语。 -> 注意 -> 协议合成并不会生成新的、永久的协议类型,而是将多个协议中的要求合成到一个只在局部作用域有效的临时协议中。 +这里有一个例子:将Location类和前面的Named协议进行组合: + +```swift +class Location { + var latitude: Double + var longitude: Double + init(latitude: Double, longitude: Double) { + self.latitude = latitude + self.longitude = longitude + } +} +class City: Location, Named { + var name: String + init(name: String, latitude: Double, longitude: Double) { + self.name = name + super.init(latitude: latitude, longitude: longitude) + } +} +func beginConcert(in location: Location & Named) { + print("Hello, \(location.name)!") +} + +let seattle = City(name: "Seattle", latitude: 47.6, longitude: -122.3) +beginConcert(in: seattle) +// Prints "Hello, Seattle!" +``` + +`beginConcert(in:)`方法接受一个类型为 `Location & Named` 的参数,这意味着"任何Location的子类,并且遵循Named协议"。例如,City就满足这样的条件。 + +将 birthdayPerson 传入`beginConcert(in:)`函数是不合法的,因为 Person不是一个Location的子类。就像,如果你新建一个类继承与Location,但是没有遵循Named协议,你用这个类的实例去调用`beginConcert(in:)`函数也是不合法的。 ## 检查协议一致性