From 2e699265cda54420536d02ee63f42eec42511e39 Mon Sep 17 00:00:00 2001 From: 949478479 <949478479@qq.com> Date: Sat, 6 Feb 2016 04:45:24 +0800 Subject: [PATCH 01/61] =?UTF-8?q?=E6=A0=B9=E6=8D=AE=E8=AF=84=E8=AE=BA?= =?UTF-8?q?=E5=8F=8D=E9=A6=88=E4=BF=AE=E6=94=B9=E4=B8=AA=E5=88=AB=E5=AD=97?= =?UTF-8?q?=E8=AF=8D=E7=9A=84=E7=BF=BB=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/chapter2/22_Protocols.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/source/chapter2/22_Protocols.md b/source/chapter2/22_Protocols.md index 6014ddac..f10dc3f3 100644 --- a/source/chapter2/22_Protocols.md +++ b/source/chapter2/22_Protocols.md @@ -65,11 +65,11 @@ class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol { ## 属性要求 -协议可以要求采纳协议的类型提供特定名称和类型的实例属性或类型属性。协议不指定属性是存储型属性还是计算型属性,它只指定属性的名称和类型。此外,协议还指定属性是只读的还是可读可写的。 +协议可以要求采纳协议的类型提供特定名称和类型的实例属性或类型属性。协议不指定属性是存储型属性还是计算型属性,它只指定属性的名称和类型。此外,协议还指定属性是可读的还是可读可写的。 -如果协议要求属性是可读可写的,那么该属性不能是常量属性或只读的计算型属性。如果协议只要求属性是只读的,那么该属性不仅可以是只读的,如果代码需要的话,还可以是可写的。 +如果协议要求属性是可读可写的,那么该属性不能是常量属性或只读的计算型属性。如果协议只要求属性是可读的,那么该属性不仅可以是可读的,如果代码需要的话,还可以是可写的。 -协议通常用 `var` 关键字来声明变量属性,在类型声明后加上 `{ set get }` 来表示属性是可读可写的,只读属性则用 `{ get }` 来表示: +协议总是用 `var` 关键字来声明变量属性,在类型声明后加上 `{ set get }` 来表示属性是可读可写的,可读属性则用 `{ get }` 来表示: ```swift protocol SomeProtocol { @@ -94,7 +94,7 @@ protocol FullyNamed { } ``` -`FullyNamed` 协议除了要求采纳协议的类型提供 `fullName` 属性外,并没有其他特别的要求。这个协议表示,任何采纳 `FullyNamed` 的类型,都必须有一个只读的 `String` 类型的实例属性 `fullName`。 +`FullyNamed` 协议除了要求采纳协议的类型提供 `fullName` 属性外,并没有其他特别的要求。这个协议表示,任何采纳 `FullyNamed` 的类型,都必须有一个可读的 `String` 类型的实例属性 `fullName`。 下面是一个采纳 `FullyNamed` 协议的简单结构体: @@ -155,7 +155,7 @@ protocol RandomNumberGenerator { `RandomNumberGenerator` 协议并不关心每一个随机数是怎样生成的,它只要求必须提供一个随机数生成器。 -如下所示,下边是一个采纳了 `RandomNumberGenerator` 协议的类。该类实现了一个叫做 *线性同余生成器(linear congruential generator)* 的伪随机数算法。 +如下所示,下边是一个采纳并符合 `RandomNumberGenerator` 协议的类。该类实现了一个叫做 *线性同余生成器(linear congruential generator)* 的伪随机数算法。 ```swift class LinearCongruentialGenerator: RandomNumberGenerator { @@ -385,7 +385,7 @@ class SnakesAndLadders: DiceGame { 关于这个蛇梯棋游戏的详细描述请参阅 [Control Flow](./05_Control_Flow.html) 章节中的 [Break](./05_Control_Flow.html#break) 部分。 -这个版本的游戏封装到了 `SnakesAndLadders` 类中,该类采纳了 `DiceGame` 协议,并且提供了相应的只读的 `dice` 属性和 `play()` 方法。( `dice` 属性在构造之后就不再改变,且协议只要求 `dice` 为只读的,因此将 `dice` 声明为常量属性。) +这个版本的游戏封装到了 `SnakesAndLadders` 类中,该类采纳了 `DiceGame` 协议,并且提供了相应的可读的 `dice` 属性和 `play()` 方法。( `dice` 属性在构造之后就不再改变,且协议只要求 `dice` 为可读的,因此将 `dice` 声明为常量属性。) 游戏使用 `SnakesAndLadders` 类的 `init()` 构造器来初始化游戏。所有的游戏逻辑被转移到了协议中的 `play()` 方法,`play()` 方法使用协议要求的 `dice` 属性提供骰子摇出的值。 @@ -470,8 +470,8 @@ extension Dice: TextRepresentable { 现在所有 `Dice` 的实例都可以看做 `TextRepresentable` 类型: ```swift -let d12 = Dice(sides: 12,generator: LinearCongruentialGenerator()) -print(d12. textualDescription) +let d12 = Dice(sides: 12, generator: LinearCongruentialGenerator()) +print(d12.textualDescription) // 打印 “A 12-sided dice” ``` @@ -652,7 +652,7 @@ wishHappyBirthday(birthdayPerson) * `as?` 返回一个可选值,当实例符合某个协议时,返回类型为协议类型的可选值,否则返回 `nil`。 * `as!` 将实例强制向下转换到某个协议类型,如果强转失败,会引发运行时错误。 -下面的例子定义了一个 `HasArea` 协议,该协议定义了一个 `Double` 类型的只读属性 `area`: +下面的例子定义了一个 `HasArea` 协议,该协议定义了一个 `Double` 类型的可读属性 `area`: ```swift protocol HasArea { @@ -877,7 +877,7 @@ extension PrettyTextRepresentable { ### 为协议扩展添加限制条件 -在扩展协议的时候,可以指定一些限制条件,只有采纳协议的类型满足这些限制条件时,才能获得协议扩展提供的默认实现。这些限制条件写在协议名之后,使用 `where` 子句来描述,正如[Where子句](./23_Generics.html#where_clauses))中所描述的。 +在扩展协议的时候,可以指定一些限制条件,只有采纳协议的类型满足这些限制条件时,才能获得协议扩展提供的默认实现。这些限制条件写在协议名之后,使用 `where` 子句来描述,正如[Where子句](./23_Generics.html#where_clauses)中所描述的。 例如,你可以扩展 `CollectionType` 协议,但是只适用于集合中的元素采纳了 `TextRepresentable` 协议的情况: From a4d4563a3edf7045a5a452b0943ddaa364896f31 Mon Sep 17 00:00:00 2001 From: numbbbbb Date: Sun, 21 Feb 2016 18:56:59 +0800 Subject: [PATCH 02/61] Update README.md --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 376e29dd..0c8dbb3d 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ 《The Swift Programming Language》in Chinese ============================================= -中文版Apple官方Swift教程《The Swift Programming Language》 +中文版 Apple 官方 Swift 教程《The Swift Programming Language》 [英文原版](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/index.html#//apple_ref/doc/uid/TP40014097-CH3-ID0) @@ -9,17 +9,17 @@ # 在线阅读 -使用Gitbook制作,可以直接[在线阅读](http://swiftguide.cn/)。 +使用 Gitbook 制作,可以直接[在线阅读](http://swiftguide.cn/)。 # 当前阶段 -已经更新到Swift 2.1。 +已经更新到 Swift 2.2。 -# 2.1译者记录 +# 2.1 & 2.2 译者记录 详见各章节开头位置。 -# 2.0译者记录 +# 2.0 译者记录 - About Swift ([xtymichael](https://github.com/xtymichael)) - A Swift Tour([xtymichael](https://github.com/xtymichael)) @@ -59,7 +59,7 @@ - Generic Parameters and Arguments([wardenNScaiyi](https://github.com/wardenNScaiyi)) - Summary of the Grammar([miaosiqi](https://github.com/miaosiqi)) -# 1.0译者记录 +# 1.0 译者记录 * 欢迎使用 Swift * 关于 Swift ([numbbbbb]) From 673a930825374d05d6781555ca920fbd3e073a21 Mon Sep 17 00:00:00 2001 From: numbbbbb Date: Tue, 8 Mar 2016 15:19:43 +0800 Subject: [PATCH 03/61] Update CNAME --- CNAME | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CNAME b/CNAME index eb06dc03..23971755 100755 --- a/CNAME +++ b/CNAME @@ -1 +1 @@ -www.swiftguide.cn \ No newline at end of file +gg.swiftguide.cn From 8038c724fd55bcbd9d4f13e991efdc2ac5975878 Mon Sep 17 00:00:00 2001 From: xinqiu Date: Sun, 13 Mar 2016 23:12:57 +0800 Subject: [PATCH 04/61] =?UTF-8?q?=E8=BF=99=E4=B8=AA=E5=9C=B0=E6=96=B9?= =?UTF-8?q?=E7=94=A8=E8=AE=B8=E5=A4=9A=E6=AF=94=E8=BE=83=E5=A5=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The contents of a String can be accessed in various ways 一个`String`的内容可以用许多方式读取. way加了s,说明前面的various指的是多种。 --- source/chapter2/03_Strings_and_Characters.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/chapter2/03_Strings_and_Characters.md b/source/chapter2/03_Strings_and_Characters.md index ece7412b..20704884 100755 --- a/source/chapter2/03_Strings_and_Characters.md +++ b/source/chapter2/03_Strings_and_Characters.md @@ -29,7 +29,7 @@ `String`是例如"hello, world","albatross"这样的有序的`Character`(字符)类型的值的集合。通过`String`类型来表示。 -一个`String`的内容可以用变量的方式读取,它包括一个`Character`值的集合。 +一个`String`的内容可以用许多方式读取,它包括一个`Character`值的集合。 创建和操作字符串的语法与 C 语言中字符串操作相似,轻量并且易读。 字符串连接操作只需要简单地通过`+`符号将两个字符串相连即可。与 Swift 中其他值一样,能否更改字符串的值,取决于其被定义为常量还是变量。你也可以在字符串内插过程中使用字符串插入常量、变量、字面量表达成更长的字符串,这样可以很容易的创建自定义的字符串值,进行展示、存储以及打印。 尽管语法简易,但`String`类型是一种快速、现代化的字符串实现。 @@ -248,7 +248,7 @@ let sparklingHeart = "\u{1F496}" // 💖, Unicode 标量 U+1F496 ```swift let eAcute: Character = "\u{E9}" // é let combinedEAcute: Character = "\u{65}\u{301}" // e 后面加上 ́ -// eAcute 是 é, combinedEAcute 是 é +// eAcute 是 é, combinedEAcute 是 é ``` 可扩展的字符群集是一个灵活的方法,用许多复杂的脚本字符表示单一的`Character`值。 @@ -259,7 +259,7 @@ let combinedEAcute: Character = "\u{65}\u{301}" // e 后面加上 ́ ```swift let precomposed: Character = "\u{D55C}" // 한 let decomposed: Character = "\u{1112}\u{1161}\u{11AB}" // ᄒ, ᅡ, ᆫ -// precomposed 是 한, decomposed 是 한 +// precomposed 是 한, decomposed 是 한 ``` 可拓展的字符群集可以使包围记号(例如`COMBINING ENCLOSING CIRCLE`或者`U+20DD`)的标量包围其他 Unicode 标量,作为一个单一的`Character`值: @@ -290,7 +290,7 @@ print("unusualMenagerie has \(unusualMenagerie.characters.count) characters") 注意在 Swift 中,使用可拓展的字符群集作为`Character`值来连接或改变字符串时,并不一定会更改字符串的字符数量。 -例如,如果你用四个字符的单词`cafe`初始化一个新的字符串,然后添加一个`COMBINING ACTUE ACCENT`(`U+0301`)作为字符串的结尾。最终这个字符串的字符数量仍然是`4`,因为第四个字符是`é`,而不是`e`: +例如,如果你用四个字符的单词`cafe`初始化一个新的字符串,然后添加一个`COMBINING ACTUE ACCENT`(`U+0301`)作为字符串的结尾。最终这个字符串的字符数量仍然是`4`,因为第四个字符是`é`,而不是`e`: ```swift var word = "cafe" @@ -417,7 +417,7 @@ if quotation == sameQuotation { // "Voulez-vous un café?" 使用 LATIN SMALL LETTER E WITH ACUTE let eAcuteQuestion = "Voulez-vous un caf\u{E9}?" -// "Voulez-vous un café?" 使用 LATIN SMALL LETTER E and COMBINING ACUTE ACCENT +// "Voulez-vous un café?" 使用 LATIN SMALL LETTER E and COMBINING ACUTE ACCENT let combinedEAcuteQuestion = "Voulez-vous un caf\u{65}\u{301}?" if eAcuteQuestion == combinedEAcuteQuestion { From 94e9ce8896482280c29423b483889cd4a0993213 Mon Sep 17 00:00:00 2001 From: numbbbbb Date: Mon, 14 Mar 2016 20:55:53 +0800 Subject: [PATCH 05/61] Update CNAME --- CNAME | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CNAME b/CNAME index eb06dc03..23971755 100755 --- a/CNAME +++ b/CNAME @@ -1 +1 @@ -www.swiftguide.cn \ No newline at end of file +gg.swiftguide.cn From 3984a8eba02bda6e43432f7ee40eeb85a03d6c4b Mon Sep 17 00:00:00 2001 From: colourful987 Date: Sat, 2 Apr 2016 23:35:39 +0800 Subject: [PATCH 06/61] update initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit delete 类的可失败构造器小节;update 构造失败的传递小节。 --- source/chapter2/14_Initialization.md | 80 ++++++++-------------------- 1 file changed, 23 insertions(+), 57 deletions(-) diff --git a/source/chapter2/14_Initialization.md b/source/chapter2/14_Initialization.md index 65ac194d..04712d26 100755 --- a/source/chapter2/14_Initialization.md +++ b/source/chapter2/14_Initialization.md @@ -12,6 +12,10 @@ > 翻译:[Channe](https://github.com/Channe),[Realank](https://github.com/Realank) > 校对:[shanks](http://codebuild.me),2016-1-23 +> 2.2 +> 翻译:[pmst](https://github.com/colourful987) +> 校对:[]() + 本页包含内容: - [存储属性的初始赋值](#setting_initial_values_for_stored_properties) @@ -807,46 +811,6 @@ if unknownUnit == nil { // 打印 "This is not a defined temperature unit, so initialization failed." ``` - -### 类的可失败构造器 - -值类型(也就是结构体或枚举)的可失败构造器,可以在构造过程中的任意时间点触发构造失败。比如在前面的例子中,结构体`Animal`的可失败构造器在构造过程一开始就触发了构造失败,甚至在`species`属性被初始化前。 - -而对类而言,可失败构造器只能在类引入的所有存储型属性被初始化后,以及构造器代理调用完成后,才能触发构造失败。 - -下面例子展示了如何在类的可失败构造器中使用隐式解包可选类型来满足上述要求: - -```swift -class Product { - let name: String! - init?(name: String) { - self.name = name - if name.isEmpty { return nil } - } -} -``` - -上面定义的`Product`类和之前的`Animal`结构体很相似。`Product`类有一个不能为空字符串的常量属性`name`。为了强制这个要求,`Product`类使用了可失败构造器确保这个属性的值不是空字符串后,才允许构造成功。 - -毕竟,`Product`是一个类而不是结构体,这意味着不同于`Animal`,`Product`类的所有可失败构造器必须给`name`属性一个初始值,然后才能触发构造失败。 - -上面的例子中,`Product`类的`name`属性被定义为隐式解包可选字符串类型(`String!`)。因为它是一个可选类型,所以它在构造过程中被赋值前,具有默认值`nil`。这个默认值`nil`意味着`Product`类引入的所有存储型属性都有一个有效的初始值。因此,一旦传入一个空字符串,该可失败构造器可以在`name`属性被赋值前触发构造失败。 - -> 译者注 -> 上面的示例代码和描述并不相符,根据描述,`if name.isEmpty { return nil }`这句代码应该在`self.name = name`之前,而这却会导致编译错误`error: all stored properties of a class instance must be initialized before returning nil from an initializer`,除非将`let name: String!`改为`var name: String!`。 -> -> 根据前面的介绍和实测,常量存储属性,只能被初始化一次。上面的代码,在构造器中有初始化常量属性`name`的过程,所以在此之前不会被赋予默认值`nil`,而在构造器的初始化`name`过程之前,因为并不是所有存储型属性都被初始化,是不可以返回`nil`的;而如果将`name`声明为变量存储属性,那么就可以被多次赋值,所以在执行构造器方法之前,就会获得默认值`nil`,和构造器中的再次赋值并不冲突,所以此时,`if name.isEmpty { return nil }`才可以放在构造器前面。英文原文在这里表述有误 - -因为`name`属性是一个常量,所以一旦构造成功,`name`属性肯定有一个非`nil`的值。即使它被定义为隐式解包可选类型,也完全可以放心大胆地直接访问,而不用检查`name`属性的值是否为`nil`: - -```swift -if let bowTie = Product(name: "bow tie") { - // 不需要检查 bowTie.name 是否为 nil - print("The product's name is \(bowTie.name)") -} -// 打印 "The product's name is bow tie" -``` - ### 构造失败的传递 @@ -859,36 +823,38 @@ if let bowTie = Product(name: "bow tie") { 下面这个例子,定义了一个名为`CartItem`的`Product`类的子类。这个类建立了一个在线购物车中的物品的模型,它有一个名为`quantity`的常量存储型属性,并确保该属性的值至少为`1`: + ```swift +class Product { + let name: String + init?(name: String) { + if name.isEmpty { return nil } + self.name = name + } +} + class CartItem: Product { - let quantity: Int! + let quantity: Int init?(name: String, quantity: Int) { + if quantity < 1 { return nil } self.quantity = quantity super.init(name: name) - if quantity < 1 { return nil } } } ``` -和`Product`类中的`name`属性类似,`CartItem`类中的`quantity`属性也是隐式解包可选类型。这意味着在构造过程中,该属性在被赋予特定的值之前能有一个默认的初始值`nil`。 +`CartItem` 可失败构造器首先验证接收的 `quantity` 值是否大于等于 1 。倘若 `quantity` 值无效,则立即终止整个构造过程,返回失败结果,且不再执行余下代码。同样地,`Product` 的可失败构造器首先检查 `name` 值,假如 `name` 值为空字符串,则构造器立即执行失败。 -该可失败构造器以向上代理到父类的可失败构造器`init(name:)`开始。这满足了可失败构造器在触发构造失败前必须总是完成构造器代理调用这个条件。 - -如果由于`name`的值为空字符串而导致父类的可失败构造器构造失败,则`CartIem`类的整个构造过程都将立即失败,之后的构造代码将不会再被执行。如果父类构造成功,`CartIem`的可失败构造器会进一步验证`quantity`的值是否不小于`1`。 - -> 译者注 -> 上面的示例代码和描述也不相符,`name`属性在被赋予特定的值之前不能获得一个默认的初始值`nil`,并且根据描述,`self.quantity = quantity`这句代码应该放在最后一行,而这却会导致编译错误`error: property 'self.quantity' not initialized at super.init call`,除非将`let quantity: Int!`改为`var quantity: Int!`。 - -如果你构造一个`name`的值为非空字符串,`quantity`的值不小于`1`的`CartItem`实例,则可成功构造: +如果你通过传入一个非空字符串 `name` 以及一个值大于等于 1 的 `quantity` 来创建一个 `CartItem` 实例,那么构造方法能够成功被执行: ```swift if let twoSocks = CartItem(name: "sock", quantity: 2) { print("Item: \(twoSocks.name), quantity: \(twoSocks.quantity)") } -// 打印 "Item: sock, quantity: 2" +// 打印 "Item: sock, quantity: 2” ``` -如果你试图构造一个`quantity`的值为`0`的`CartItem`实例, 则`CartItem`的可失败构造器会触发构造失败: +倘若你以一个值为 0 的 `quantity` 来创建一个 `CartItem` 实例,那么将导致 `CartItem` 构造器失败: ```swift if let zeroShirts = CartItem(name: "shirt", quantity: 0) { @@ -896,10 +862,10 @@ if let zeroShirts = CartItem(name: "shirt", quantity: 0) { } else { print("Unable to initialize zero shirts") } -// 打印 "Unable to initialize zero shirts" -``` +// 打印 "Unable to initialize zero shirts” +``` -类似的,如果你试图构造一个`name`的值为空字符串的`CartItem`实例,则父类`Product`的可失败构造器会触发构造失败: +同样地,如果你尝试传入一个值为空字符串的 `name`来创建一个 `CartItem` 实例,那么将导致父类 `Product` 的构造过程失败: ```swift if let oneUnnamed = CartItem(name: "", quantity: 1) { @@ -907,7 +873,7 @@ if let oneUnnamed = CartItem(name: "", quantity: 1) { } else { print("Unable to initialize one unnamed product") } -// 打印 "Unable to initialize one unnamed product" +// 打印 "Unable to initialize one unnamed product” ``` From 8063e80cd415a0c6847c6aa9ed2e353e115705bc Mon Sep 17 00:00:00 2001 From: Linus <360725966@qq.com> Date: Sun, 3 Apr 2016 14:14:15 +0800 Subject: [PATCH 07/61] update Control Flow --- source/chapter2/05_Control_Flow.md | 140 +++++++++-------------------- 1 file changed, 42 insertions(+), 98 deletions(-) diff --git a/source/chapter2/05_Control_Flow.md b/source/chapter2/05_Control_Flow.md index e6658d3d..012d6920 100755 --- a/source/chapter2/05_Control_Flow.md +++ b/source/chapter2/05_Control_Flow.md @@ -12,31 +12,27 @@ > 翻译:[Prayer](https://github.com/futantan) > 校对:[shanks](http://codebuild.me) +> 2.2 +> 翻译:[LinusLing](https://github.com/linusling) +> 校对:[]() + 本页包含内容: -- [For 循环](#for_loops) +- [For-In 循环](#for_in_loops) - [While 循环](#while_loops) - [条件语句](#conditional_statement) - [控制转移语句(Control Transfer Statements)](#control_transfer_statements) - [提前退出](#early_exit) -- [检测API可用性](#checking_api_availability) +- [检测 API 可用性](#checking_api_availability) -Swift 提供了类似 C 语言的流程控制结构,包括可以多次执行任务的`for`和`while`循环,基于特定条件选择执行不同代码分支的`if`、`guard`和`switch`语句,还有控制流程跳转到其他代码的`break`和`continue`语句。 +Swift提供了多种流程控制结构,包括可以多次执行任务的`while`循环,基于特定条件选择执行不同代码分支的`if`、`guard`和`switch`语句,还有控制流程跳转到其他代码的`break`和`continue`语句。 -除了 C 语言里面传统的 for 循环,Swift 还增加了`for-in`循环,用来更简单地遍历数组(array),字典(dictionary),区间(range),字符串(string)和其他序列类型。 +Swift 还增加了`for-in`循环,用来更简单地遍历数组(array),字典(dictionary),区间(range),字符串(string)和其他序列类型。 Swift 的`switch`语句比 C 语言中更加强大。在 C 语言中,如果某个 case 不小心漏写了`break`,这个 case 就会贯穿至下一个 case,Swift 无需写`break`,所以不会发生这种贯穿的情况。case 还可以匹配更多的类型模式,包括区间匹配(range matching),元组(tuple)和特定类型的描述。`switch`的 case 语句中匹配的值可以是由 case 体内部临时的常量或者变量决定,也可以由`where`分句描述更复杂的匹配条件。 - -## For 循环 - -Swift 提供两种`for`循环形式来按照指定的次数执行一系列语句: - -* `for-in`循环对一个集合里面的每个元素执行一系列语句。 -* for 循环,用来重复执行一系列语句直到达成特定条件达成,一般通过在每次循环完成后增加计数器的值来实现。 - - -### For-In + +## For-In 循环 你可以使用`for-in`循环来遍历一个集合里面的所有元素,例如由数字表示的区间、数组中的元素、字符串中的字符。 @@ -99,53 +95,6 @@ for (animalName, legCount) in numberOfLegs { 字典元素的遍历顺序和插入顺序可能不同,字典的内容在内部是无序的,所以遍历元素时不能保证顺序。关于数组和字典,详情参见[集合类型](./04_Collection_Types.html)。 - -### For - -除了`for-in`循环,Swift 提供使用条件判断和递增方法的标准 C 样式`for`循环: - -```swift -for var index = 0; index < 3; ++index { - print("index is \(index)") -} -// index is 0 -// index is 1 -// index is 2 -``` - -下面是一般情况下这种循环方式的格式: - -```swift -for initialization; condition; increment { - statements -} -``` - -和 C 语言中一样,分号将循环的定义分为 3 个部分,不同的是,Swift 不需要使用圆括号将“initialization; condition; increment”包括起来。 - -这个循环执行流程如下: - -1. 循环首次启动时,*初始化表达式( initialization expression )*被调用一次,用来初始化循环所需的所有常量和变量。 -2. *条件表达式(condition expression)*被调用,如果表达式调用结果为`false`,循环结束,继续执行`for`循环关闭大括号(`}`)之后的代码。如果表达式调用结果为`true`,则会执行大括号内部的代码。 -3. 执行所有语句之后,执行*递增表达式(increment expression)*。通常会增加或减少计数器的值,或者根据语句输出来修改某一个初始化的变量。当递增表达式运行完成后,重复执行第 2 步,条件表达式会再次执行。 - - -在初始化表达式中声明的常量和变量(比如`var index = 0`)只在`for`循环的生命周期里有效。如果想在循环结束后访问`index`的值,你必须要在循环生命周期开始前声明`index`。 - -```swift -var index: Int -for index = 0; index < 3; ++index { - print("index is \(index)") -} -// index is 0 -// index is 1 -// index is 2 -print("The loop statements were executed \(index) times") -// 输出 "The loop statements were executed 3 times -``` - -注意`index`在循环结束后最终的值是`3`而不是`2`。最后一次调用递增表达式`++index`会将`index`设置为`3`,从而导致`index < 3`条件为`false`,并终止循环。 - ## While 循环 @@ -169,7 +118,7 @@ while condition { 下面的例子来玩一个叫做蛇和梯子的小游戏,也叫做滑道和梯子: -![image](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/snakesAndLadders_2x.png) +![image](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/snakesAndLadders_2x.png) 游戏的规则如下: @@ -212,11 +161,12 @@ while square < finalSquare { print("Game over!") ``` -本例中使用了最简单的方法来模拟掷骰子。 `diceRoll`的值并不是一个随机数,而是以`0`为初始值,之后每一次`while`循环,`diceRoll`的值使用前置自增操作符(`++i`)来自增 1 ,然后检测是否超出了最大值。`++diceRoll`调用完成_后_,返回值等于`diceRoll`自增后的值。任何时候如果`diceRoll`的值等于7时,就超过了骰子的最大值,会被重置为`1`。所以`diceRoll`的取值顺序会一直是`1`,`2`,`3`,`4`,`5`,`6`,`1`,`2`。 +本例中使用了最简单的方法来模拟掷骰子。 `diceRoll`的值并不是一个随机数,而是以`0`为初始值,之后每一次`while`循环,`diceRoll`的值使用前置自增操作符(`++i`)来自增 1 ,然后检测是否超出了最大值。任何时候如果`diceRoll`的值等于 7 时,就超过了骰子的最大值,会被重置为`1`。所以`diceRoll`的取值顺序会一直是`1`。因此,`diceRoll` 所有的值只可能是 `1` ,`2`,`3`,`4`,`5`,`6`,`1`,`2` 等。 掷完骰子后,玩家向前移动`diceRoll`个方格,如果玩家移动超过了第 25 个方格,这个时候游戏结束,相应地,代码会在`square`增加`board[square]`的值向前或向后移动(遇到了梯子或者蛇)之前,检测`square`的值是否小于`board`的`count`属性。 -如果没有这个检测(`square < board.count`),`board[square]`可能会越界访问`board`数组,导致错误。例如如果`square`等于`26`, 代码会去尝试访问`board[26]`,超过数组的长度。 +> 注意: +> 如果没有这个检测(`square < board.count`),`board[square]`可能会越界访问`board`数组,导致错误。例如如果`square`等于`26`, 代码会去尝试访问`board[26]`,超过数组的长度。 当本轮`while`循环运行完毕,会再检测循环条件是否需要再运行一次循环。如果玩家移动到或者超过第 25 个方格,循环条件结果为`false`,此时游戏结束。 @@ -325,7 +275,7 @@ if temperatureInFahrenheit <= 32 { 实际上,最后的`else`语句是可选的: ```swift -temperatureInFahrenheit = 72 +temperatureInFahrenheit = 90 if temperatureInFahrenheit <= 32 { print("It's very cold. Consider wearing a scarf.") } else if temperatureInFahrenheit >= 86 { @@ -346,7 +296,8 @@ if temperatureInFahrenheit <= 32 { switch some value to consider { case value 1: respond to value 1 -case value 2, value 3: +case value 2, + value 3: respond to value 2 or 3 default: otherwise, do something else @@ -407,7 +358,8 @@ default: ```swift switch some value to consider { -case value 1, value 2: +case value 1, + value 2: statements } ``` @@ -442,7 +394,7 @@ print("There are \(naturalCount) \(countedThings).") // 输出 "There are dozens of moons orbiting Saturn." ``` -在上例中,`approximateCount`在一个`switch`声明中被估值。每一个`case`都与之进行比较。因为`approximateCount`落在了12到100的区间,所以`naturalCount`等于`"dozens of"`值,并且此后这段执行跳出了`switch`声明。 +在上例中,`approximateCount`在一个`switch`声明中被估值。每一个`case`都与之进行比较。因为`approximateCount`落在了 12 到 100 的区间,所以`naturalCount`等于`"dozens of"`值,并且此后这段执行跳出了`switch`声明。 > 注意: > 闭区间操作符(`...`)以及半开区间操作符(`..<`)功能被重载去返回`IntervalType`或`Range`。一个区间可以决定他是否包含特定的元素,就像当匹配一个`switch`声明的`case`一样。区间是一个连续值的集合,可以用`for-in`语句遍历它。 @@ -471,7 +423,7 @@ default: // 输出 "(1, 1) is inside the box" ``` -![image](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/coordinateGraphSimple_2x.png) +![image](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/coordinateGraphSimple_2x.png) 在上面的例子中,`switch`语句会判断某个点是否是原点(0, 0),是否在红色的x轴上,是否在黄色y轴上,是否在一个以原点为中心的4x4的矩形里,或者在这个矩形外面。 @@ -498,7 +450,7 @@ case let (x, y): // 输出 "on the x-axis with an x value of 2" ``` -![image](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/coordinateGraphMedium_2x.png) +![image](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/coordinateGraphMedium_2x.png) 在上面的例子中,`switch`语句会判断某个点是否在红色的x轴上,是否在黄色y轴上,或者不在坐标轴上。 @@ -508,8 +460,6 @@ case let (x, y): 请注意,这个`switch`语句不包含默认分支。这是因为最后一个 case ——`case let(x, y)`声明了一个可以匹配余下所有值的元组。这使得`switch`语句已经完备了,因此不需要再书写默认分支。 -在上面的例子中,`x`和`y`是常量,这是因为没有必要在其对应的 case 分支中修改它们的值。然而,它们也可以是变量——程序将会创建临时变量,并用相应的值初始化它。修改这些变量只会影响其对应的 case 分支。 - #### Where @@ -530,7 +480,7 @@ case let (x, y): // 输出 "(1, -1) is on the line x == -y" ``` -![image](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/coordinateGraphComplex_2x.png) +![image](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/coordinateGraphComplex_2x.png) 在上面的例子中,`switch`语句会判断某个点是否在绿色的对角线`x == y`上,是否在紫色的对角线`x == -y`上,或者不在对角线上。 @@ -557,7 +507,7 @@ case let (x, y): `continue`语句告诉一个循环体立刻停止本次循环迭代,重新开始下次循环迭代。就好像在说“本次循环迭代我已经执行完了”,但是并不会离开整个循环体。 > 注意: -> 在一个带有条件和递增的for循环体中,调用`continue`语句后,迭代增量仍然会被计算求值。循环体继续像往常一样工作,仅仅只是循环体中的执行代码会被跳过。 +> 在一个带有条件和递增的 for 循环体中,调用`continue`语句后,迭代增量仍然会被计算求值。循环体继续像往常一样工作,仅仅只是循环体中的执行代码会被跳过。 下面的例子把一个小写字符串中的元音字母和空格字符移除,生成了一个含义模糊的短句: @@ -573,7 +523,7 @@ for character in puzzleInput.characters { } } print(puzzleOutput) -// 输出 "grtmndsthnklk" + // 输出 "grtmndsthnklk" ``` 在上面的代码中,只要匹配到元音字母或者空格字符,就调用`continue`语句,使本次循环迭代结束,从新开始下次循环迭代。这种行为使`switch`匹配到元音字母和空格字符时不做处理,而不是让每一个匹配到的字符都被打印。 @@ -668,11 +618,9 @@ print(description) 产生一个带标签的语句是通过在该语句的关键词的同一行前面放置一个标签,并且该标签后面还需带着一个冒号。下面是一个`while`循环体的语法,同样的规则适用于所有的循环体和`switch`代码块。 -```swift -label name: while condition { - statements -} -``` +> `label name`: while `condition` { +> `statements` +> } 下面的例子是在一个带有标签的`while`循环体中调用`break`和`continue`语句,该循环体是前面章节中*蛇和梯子*的改编版本。这次,游戏增加了一条额外的规则: @@ -682,7 +630,7 @@ label name: while condition { 游戏的棋盘和之前一样: -![image](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/snakesAndLadders_2x.png) +![image](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/snakesAndLadders_2x.png) `finalSquare`、`board`、`square`和`diceRoll`值被和之前一样的方式初始化: @@ -724,8 +672,8 @@ print("Game over!") - 如果骰子数将会使玩家的移动超出最后的方格,那么这种移动是不合法的,玩家需要重新掷骰子。`continue gameLoop`语句结束本次`while`循环的迭代,开始下一次循环迭代。 - 在剩余的所有情况中,骰子数产生的都是合法的移动。玩家向前移动骰子数个方格,然后游戏逻辑再处理玩家当前是否处于蛇头或者梯子的底部。本次循环迭代结束,控制跳转到`while`循环体的条件判断语句处,再决定是否能够继续执行下次循环迭代。 -> 注意: -> 如果上述的`break`语句没有使用`gameLoop`标签,那么它将会中断`switch`代码块而不是`while`循环体。使用`gameLoop`标签清晰的表明了`break`想要中断的是哪个代码块。 +>注意: +如果上述的`break`语句没有使用`gameLoop`标签,那么它将会中断`switch`代码块而不是`while`循环体。使用`gameLoop`标签清晰的表明了`break`想要中断的是哪个代码块。 同时请注意,当调用`continue gameLoop`去跳转到下一次循环迭代时,这里使用`gameLoop`标签并不是严格必须的。因为在这个游戏中,只有一个循环体,所以`continue`语句会影响到哪个循环体是没有歧义的。然而,`continue`语句使用`gameLoop`标签也是没有危害的。这样做符合标签的使用规则,同时参照旁边的`break gameLoop`,能够使游戏的逻辑更加清晰和易于理解。 @@ -738,38 +686,34 @@ func greet(person: [String: String]) { guard let name = person["name"] else { return } - print("Hello \(name)") guard let location = person["location"] else { print("I hope the weather is nice near you.") return } - print("I hope the weather is nice in \(location).") } - greet(["name": "John"]) -// prints "Hello John!" -// prints "I hope the weather is nice near you." +// 输出 "Hello John!" +// 输出 "I hope the weather is nice near you." greet(["name": "Jane", "location": "Cupertino"]) -// prints "Hello Jane!" -// prints "I hope the weather is nice in Cupertino." +// 输出 "Hello Jane!" +// 输出 "I hope the weather is nice in Cupertino." ``` 如果`guard`语句的条件被满足,则在保护语句的封闭大括号结束后继续执行代码。任何使用了可选绑定作为条件的一部分并被分配了值的变量或常量对于剩下的保护语句出现的代码段是可用的。 如果条件不被满足,在`else`分支上的代码就会被执行。这个分支必须转移控制以退出`guard`语句出现的代码段。它可以用控制转移语句如`return`,`break`,`continue`或者`throw`做这件事,或者调用一个不返回的方法或函数,例如`fatalError()`。 -相比于可以实现同样功能的`if`语句,按需使用`guard`语句会提升我们代码的可靠性。 -它可以使你的代码连贯的被执行而不需要将它包在`else`块中,它可以使你处理违反要求的代码使其接近要求。 +相比于可以实现同样功能的`if`语句,按需使用`guard`语句会提升我们代码的可靠性。它可以使你的代码连贯的被执行而不需要将它包在`else`块中,它可以使你处理违反要求的代码接近要求。 ## 检测 API 可用性 Swift 有检查 API 可用性的内置支持,这可以确保我们不会不小心地使用对于当前部署目标不可用的 API。 -编译器使用 SDK 中的可用信息来验证我们的代码中使用的所有 API 在项目指定的部署目标上是否可用。如果我们尝试使用一个不可用的 API,Swift 会在编译期报错。 +编译器使用 SDK 中的可用信息来验证我们的代码中使用的所有 API 在项目指定的部署目标上是否可用。如果我们尝试使用一个不可用的 API,Swift 会在编译时报错。 我们使用一个可用性条件在一个`if`或`guard`语句中去有条件的执行一段代码,这取决于我们想要使用的 API 是否在运行时是可用的。编译器使用从可用性条件语句中获取的信息去验证在代码块中调用的 API 是否都可用。 @@ -781,14 +725,14 @@ if #available(iOS 9, OSX 10.10, *) { } ``` -以上可用性条件指定了在 iOS 系统上,`if`段的代码仅会在 iOS 9 及更高版本的系统上执行;在 OS X,仅会在 OS X v10.10 及更高版本的系统上执行。最后一个参数,`*`,是必须写的,用于处理未来潜在的平台。 +以上可用性条件指定在 iOS,`if`段的代码仅仅在 iOS 9 及更高可运行;在 OS X,仅在 OS X v10.10 及更高可运行。最后一个参数,`*`,是必须的并且指定在任何其他平台上,`if`段的代码在最小可用部署目标指定项目中执行。 -在它的一般形式中,可用性条件获取了一系列平台名字和版本。平台名字可以是`iOS`,`OSX`或`watchOS`。除了特定的主板本号像 iOS 8,我们可以指定较小的版本号像 iOS 8.3 以及 OS X v10.10.3。 +在它普遍的形式中,可用性条件获取了平台名字和版本的清单。平台名字可以是`iOS`,`OSX`或`watchOS`。除了特定的主板本号像 iOS 8,我们可以指定较小的版本号像iOS 8.3 以及 OS X v10.10.3。 ```swift -if #available(platform name version, ..., *) { - statements to execute if the APIs are available +if #available(`platform name` `version`, `...`, *) { + `statements to execute if the APIs are available` } else { - fallback statements to execute if the APIs are unavailable + `fallback statements to execute if the APIs are unavailable` } ``` From 13ba7b263a31f803f0d5f7eeaec07308e6a68a9c Mon Sep 17 00:00:00 2001 From: chenmingbiao Date: Sun, 3 Apr 2016 16:55:44 +0800 Subject: [PATCH 08/61] update Language Reference - Statements --- source/chapter3/10_Statements.md | 134 +++++++++++++------------------ 1 file changed, 54 insertions(+), 80 deletions(-) diff --git a/source/chapter3/10_Statements.md b/source/chapter3/10_Statements.md index a3a8f356..166d6b28 100755 --- a/source/chapter3/10_Statements.md +++ b/source/chapter3/10_Statements.md @@ -9,10 +9,13 @@ > 2.0 > 翻译+校对:[littledogboy](https://github.com/littledogboy) +> 2.2 +> 翻译:[chenmingbiao](https://github.com/chenmingbiao) +> 校对:[]() + 本页包含内容: - [循环语句](#loop_statements) - - [For 语句](#for_statements) - [For-In 语句](#for-in_statements) - [While 语句](#while_statements) - [Repeat-While 语句](#repeat-while_statements) @@ -26,13 +29,13 @@ - [Continue 语句](#continue_statement) - [Fallthrough 语句](#fallthrough_statements) - [Return 语句](#return_statements) - - [Available 语句](#availability_statements) - [Throw 语句](#throw_statements) - [Defer 语句](#defer_statements) - [Do 语句](#do_statements) - [编译器控制语句](#compiler_control_statements) - [编译配置语句](#build_config_statements) - [源代码控制语句](#line_control_statements) +- [可用性语句](#availability_condition) 在 Swift 中,有三种类型的语句:简单语句、编译器控制语句和控制流语句。简单语句是最常见的,用于构造表达式或者声明。编译器控制语句允许程序改变编译器的行为,包含编译配置语句和线路控制语句。 @@ -57,53 +60,20 @@ ## 循环语句 -循环语句会根据特定的循环条件来重复执行代码块。Swift 提供四种类型的循环语句:`for` 语句、`for-in` 语句、`while` 语句和 `repeat-while` 语句。 +循环语句会根据特定的循环条件来重复执行代码块。Swift 提供三种类型的循环语句:`for-in` 语句、`while` 语句和 `repeat-while` 语句。 通过 `break` 语句和 `continue` 语句可以改变循环语句的控制流。有关这两条语句,详情参见 [Break 语句](#break_statement) 和 [Continue 语句](#continue_statement)。 > 循环语句语法 -> *循环语句* → [*for 语句*](#for-statement) > *循环语句* → [*for-in 语句*](#for-in-statement) > *循环语句* → [*while 语句*](#while-statement) > *循环语句* → [*repeat-while 语句*](#repeat-while-statement) - -### For 语句 - -`for` 语句只有在循环条件为真时重复执行代码块,同时计数器递增。 - -`for` 语句的形式如下: - -```swift -for 初始化; 条件; 增量 { - 语句 -} -``` - -初始化、条件和增量语句之间必须以分号相隔,循环体中的语句必须以花括号包裹。 - -`for` 语句的执行流程如下: - -1. 初始化只会被执行一次,通常用于声明和初始化在接下来的循环中需要使用的变量。 -2. 判断条件的值。如果为 `true`,循环体中的语句将会被执行,然后转到第 3 步;如果为 `false`,循环体中的语句以及增量语句都不会被执行,`for` 语句至此执行完毕。 -3. 执行增量语句,然后重复第 2 步。 - -在初始化语句中定义的变量仅在 `for` 循环的作用域内有效。 - -条件的结果必须符合 `BooleanType` 协议。 - -> for 语句语法 - -> *for 语句* → **for** [*for初始条件*](#for-init)可选 **;** [*表达式*](04_Expressions.md#expression)可选 **;** [*表达式*](04_Expressions.md#expression)可选 [*代码块*](05_Declarations.md#code-block) -> *for语句* → **for** **(** [*for初始条件*](#for-init)可选 **;** [*表达式*](04_Expressions.md#expression)可选 **;** [*表达式*](04_Expressions.md#expression)可选 **)** [*代码块*](05_Declarations.md#code-block) - -> *for 初始条件* → [*变量声明*](05_Declarations.md#variable-declaration) | [*表达式列表*](04_Expressions.md#expression-list) - ### For-In 语句 -`for-in` 语句会为集合(或符合 `Sequence` 协议的任意类型)中的每一项执行一次代码块。 +`for-in` 语句会为集合(或实现了 `SequenceType` 协议的任意类型)中的每一项执行一次代码块。 `for-in` 语句的形式如下: @@ -113,7 +83,7 @@ for 项 in 集合 { } ``` -`for-in` 语句在循环开始前会调用集合表达式的 `generate()` 方法来获取一个符合 `Generator` 协议的类型的值。接下来循环开始,反复调用该值的 `next()` 方法。如果其返回值不是 `None`,它将会被赋给“项”,然后执行循环体语句,执行完毕后回到循环开始处,继续重复这一过程;否则,既不会赋值也不会执行循环体语句,`for-in` 语句至此执行完毕。 +`for-in` 语句在循环开始前会调用集合表达式的 `generate()` 方法来获取一个符合 `GeneratorType` 协议的类型的值。接下来循环开始,反复调用该值的 `next()` 方法。如果其返回值不是 `None`,它将会被赋给“项”,然后执行循环体语句,执行完毕后回到循环开始处,继续重复这一过程;否则,既不会赋值也不会执行循环体语句,`for-in` 语句至此执行完毕。 > for-in 语句语法 @@ -452,46 +422,6 @@ case let (x, y) where x == y: > return 语句语法 > *return 语句* → **return** [*表达式*](04_Expressions.html#expression)可选 - - -### Available 语句 - -可用性条件可作为 `if`,`while`,`guard` 语句的条件,可以在运行时基于特定的平台参数来查询 API 的可用性。 - -可用性条件的形式如下: - -```swift -if #available(平台名称 版本, ..., *) { - 如果 API 可用,则执行这部分语句 -} else { - 如果 API 不可用,则执行这部分语句 -} -``` - -使用可用性条件来执行一个代码块时,取决于使用的接口在运行时是否可用。编译器会根据可用性条件提供的信息以及运行时的平台来决定是否执行相应的代码块。 - -可用性条件使用一系列逗号分隔的平台名称和版本。使用 `iOS`,`OSX`,以及 `watchOS` 等作为平台名称,并写上相应的版本号。`*` 参数是必须写的,用于处理未来的潜在平台。可用性条件确保了运行时的平台不低于条件中指定的平台版本时才执行代码块。 - -与布尔类型的条件不同,不能用逻辑运算符 `&&` 和 `||` 合并可用性条件。 - -> 可用性条件语法 - - -> *可用性条件* → **#available** **(** [*可用性参数列表*](#availability-arguments) **)** - -> *可用性参数列表* → [*可用性参数*](#availability-argument) | [*可用性参数*](#availability-argument) **,** [*可用性参数列表*](#availability-arguments) - -> *可用性参数* → [平台名称](#platform-name) [平台版本](#platform-version) -> *可用性条件* → __*__ - - -> *平台名称* → **iOS** | **iOSApplicationExtension** -> *平台名称* → **OSX** | **OSXApplicationExtension** -> *平台名称* → **watchOS** - -> *平台版本* → [十进制数字](02_Lexical_Structure.md#decimal-digits) -> *平台版本* → [十进制数字](02_Lexical_Structure.md#decimal-digits) **.** [十进制数字](02_Lexical_Structure.md#decimal-digits) -> *平台版本* → [十进制数字](02_Lexical_Structure.md#decimal-digits) **.** [十进制数字](02_Lexical_Structure.md#decimal-digits) **.** [十进制数字](02_Lexical_Structure.md#decimal-digits) ### Throw 语句 @@ -612,6 +542,9 @@ do { | --- | --- | | `os()` | `OSX`, `iOS`, `watchOS`, `tvOS` | | `arch()` | `i386`, `x86_64`, `arm`, `arm64` | +| `swift()` | >= followed by a version number | + +`swift()` (语言版本检测函数)的版本号参数主要由主版本号和次版本号组成并且使用点号(`.`)分割开,`>=` 和版本号之间不能有空白符。 > 注意 > `arch(arm)` 编译配置在 ARM 64位设备上不会返回 `true`。如果代码在 32 位的 iOS 模拟器上编译,`arch(i386)` 编译配置返回 `true`。 @@ -631,7 +564,7 @@ do { ``` > 注意 -> 即使没有被编译,编译配置中的语句仍然会被解析。 +> 即使没有被编译,编译配置中的语句仍然会被解析。但是,在编译配置语句中如果包含语言版本检测函数则是例外:仅当 `Swift` 编译器版本和语言版本检测函数中指定的版本号匹配时语句才能被解析。这种设定能确保旧的编译器不会尝试去解析新 `Swift` 版本的语法。 > 编译配置语句语法 @@ -660,7 +593,8 @@ do { > *操作系统* → **OSX** | **iOS** | **watchOS** | **tvOS** -> *架构* → **i386** | **x86_64** | **arm** | **arm64** +> *架构* → **i386** | **x86_64** | **arm** | **arm64** +> *swift 版本* → [*十进制数字*](02_Lexical_Structure.md#decimal-digit) ­**.** ­[*十进制数字*](02_Lexical_Structure.md#decimal-digit) ### 线路控制语句 @@ -685,3 +619,43 @@ do { > *行号* → 大于 0 的十进制整数 > *文件名* → [*静态字符串字面量*](02_Lexical_Structure.md#static-string-literal) + + +### 可用性条件 + +可用性条件可作为 `if`,`while`,`guard` 语句的条件,可以在运行时基于特定的平台参数来查询 API 的可用性。 + +可用性条件的形式如下: + +```swift +if #available(平台名称 版本, ..., *) { + 如果 API 可用,则执行这部分语句 +} else { + 如果 API 不可用,则执行这部分语句 +} +``` + +使用可用性条件来执行一个代码块时,取决于使用的接口在运行时是否可用。编译器会根据可用性条件提供的信息以及运行时的平台来决定是否执行相应的代码块。 + +可用性条件使用一系列逗号分隔的平台名称和版本。使用 `iOS`,`OSX`,以及 `watchOS` 等作为平台名称,并写上相应的版本号。`*` 参数是必须写的,用于处理未来的潜在平台。可用性条件确保了运行时的平台不低于条件中指定的平台版本时才执行代码块。 + +与布尔类型的条件不同,不能用逻辑运算符 `&&` 和 `||` 合并可用性条件。 + +> 可用性条件语法 + + +> *可用性条件* → **#available** **(** [*可用性参数列表*](#availability-arguments) **)** + +> *可用性参数列表* → [*可用性参数*](#availability-argument) | [*可用性参数*](#availability-argument) **,** [*可用性参数列表*](#availability-arguments) + +> *可用性参数* → [平台名称](#platform-name) [平台版本](#platform-version) +> *可用性条件* → __*__ + + +> *平台名称* → **iOS** | **iOSApplicationExtension** +> *平台名称* → **OSX** | **OSXApplicationExtension** +> *平台名称* → **watchOS** + +> *平台版本* → [十进制数字](02_Lexical_Structure.md#decimal-digits) +> *平台版本* → [十进制数字](02_Lexical_Structure.md#decimal-digits) **.** [十进制数字](02_Lexical_Structure.md#decimal-digits) +> *平台版本* → [十进制数字](02_Lexical_Structure.md#decimal-digits) **.** [十进制数字](02_Lexical_Structure.md#decimal-digits) **.** [十进制数字](02_Lexical_Structure.md#decimal-digits) From 63baf590446ff33816ccee8c8ff086e6187e18d5 Mon Sep 17 00:00:00 2001 From: chenmingbiao Date: Sun, 3 Apr 2016 17:02:09 +0800 Subject: [PATCH 09/61] update Language Reference - Statements --- source/chapter3/10_Statements.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/chapter3/10_Statements.md b/source/chapter3/10_Statements.md index 166d6b28..36e899b5 100755 --- a/source/chapter3/10_Statements.md +++ b/source/chapter3/10_Statements.md @@ -35,7 +35,7 @@ - [编译器控制语句](#compiler_control_statements) - [编译配置语句](#build_config_statements) - [源代码控制语句](#line_control_statements) -- [可用性语句](#availability_condition) +- [可用性条件](#availability_condition) 在 Swift 中,有三种类型的语句:简单语句、编译器控制语句和控制流语句。简单语句是最常见的,用于构造表达式或者声明。编译器控制语句允许程序改变编译器的行为,包含编译配置语句和线路控制语句。 From 2aece1573bdc7d0e6ea77a2831a73b7a12bd1cec Mon Sep 17 00:00:00 2001 From: Cee Date: Mon, 4 Apr 2016 15:55:07 +0800 Subject: [PATCH 10/61] Added information about comparing tuples. --- source/chapter2/02_Basic_Operators.md | 178 ++++++++++++++------------ 1 file changed, 96 insertions(+), 82 deletions(-) diff --git a/source/chapter2/02_Basic_Operators.md b/source/chapter2/02_Basic_Operators.md index e95c48fa..d034f68b 100755 --- a/source/chapter2/02_Basic_Operators.md +++ b/source/chapter2/02_Basic_Operators.md @@ -6,46 +6,49 @@ > 校对:[EvilCome](https://github.com/Evilcome) > 2.0 -> 翻译+校对:[JackAlan](https://github.com/AlanMelody) - +> 翻译+校对:[JackAlan](https://github.com/AlanMelody) + > 2.1 > 校对:[shanks](http://codebuild.me) +> 2.2 +> 翻译+校对:[Cee](https://github.com/Cee) + 本页包含内容: - [术语](#terminology) - [赋值运算符](#assignment_operator) - [算术运算符](#arithmetic_operators) -- [组合赋值运算符(Compound Assignment Operators)](#compound_assignment_operators) +- [组合赋值运算符](#compound_assignment_operators) - [比较运算符](#comparison_operators) -- [三目运算符(Ternary Conditional Operator)](#ternary_conditional_operator) +- [三目运算符](#ternary_conditional_operator) - [空合运算符](#nil_coalescing_operator) - [区间运算符](#range_operators) - [逻辑运算符](#logical_operators) -运算符是检查、改变、合并值的特殊符号或短语。例如,加号`+`将两个数相加(如`let i = 1 + 2`)。更复杂的运算例子包括逻辑与运算符`&&`(如`if enteredDoorCode && passedRetinaScan`),或让 i 值加1的便捷自增运算符`++i`等。 +运算符是检查、改变、合并值的特殊符号或短语。例如,加号(`+`)将两个数相加(如 `let i = 1 + 2`)。更复杂的运算例子包括逻辑与运算符 `&&`(如 `if enteredDoorCode && passedRetinaScan`),或让 i 值加 1 的便捷自增运算符 `++i` 等。 Swift 支持大部分标准 C 语言的运算符,且改进许多特性来减少常规编码错误。如:赋值符(`=`)不返回值,以防止把想要判断相等运算符(`==`)的地方写成赋值符导致的错误。算术运算符(`+`,`-`,`*`,`/`,`%`等)会检测并不允许值溢出,以此来避免保存变量时由于变量大于或小于其类型所能承载的范围时导致的异常结果。当然允许你使用 Swift 的溢出运算符来实现溢出。详情参见[溢出运算符](../chapter2/25_Advanced_Operators.html#overflow_operators)。 -区别于 C 语言,在 Swift 中你可以对浮点数进行取余运算(`%`),Swift 还提供了 C 语言没有的表达两数之间的值的区间运算符(`a.. ## 术语 -运算符有一元、二元和三元运算符。 +运算符分为一元、二元和三元运算符。 -- 一元运算符对单一操作对象操作(如`-a`)。一元运算符分前置运算符和后置运算符,前置运算符需紧跟在操作对象之前(如`!b`),后置运算符需紧跟在操作对象之后(如`i++`)。 -- 二元运算符操作两个操作对象(如`2 + 3`),是中置的,因为它们出现在两个操作对象之间。 +- 一元运算符对单一操作对象操作(如 `-a`)。一元运算符分前置运算符和后置运算符,前置运算符需紧跟在操作对象之前(如 `!b`),后置运算符需紧跟在操作对象之后(如 `i++`)。 +- 二元运算符操作两个操作对象(如 `2 + 3`),是中置的,因为它们出现在两个操作对象之间。 - 三元运算符操作三个操作对象,和 C 语言一样,Swift 只有一个三元运算符,就是三目运算符(`a ? b : c`)。 -受运算符影响的值叫操作数,在表达式`1 + 2`中,加号`+`是二元运算符,它的两个操作数是值`1`和`2`。 +受运算符影响的值叫操作数,在表达式 `1 + 2` 中,加号 `+` 是二元运算符,它的两个操作数是值 `1` 和 `2`。 ## 赋值运算符 -赋值运算(`a = b`),表示用`b`的值来初始化或更新`a`的值: +赋值运算(`a = b`),表示用 `b` 的值来初始化或更新 `a` 的值: ```swift let b = 10 @@ -58,7 +61,7 @@ a = b ```swift let (x, y) = (1, 2) -// 现在 x 等于 1, y 等于 2 +// 现在 x 等于 1,y 等于 2 ``` 与 C 语言和 Objective-C 不同,Swift 的赋值操作并不返回任何值。所以以下代码是错误的: @@ -69,7 +72,7 @@ if x = y { } ``` -这个特性使你无法把(`==`)错写成(`=`),由于`if x = y`是错误代码,Swift帮你避免此类错误的的发生。 +这个特性使你无法把(`==`)错写成(`=`),由于 `if x = y` 是错误代码,Swift 能帮你避免此类错误发生。 ## 算术运算符 @@ -88,9 +91,9 @@ Swift 中所有数值类型都支持了基本的四则算术运算: 10.0 / 2.5 // 等于 4.0 ``` -与 C 语言和 Objective-C 不同的是,Swift 默认情况下不允许在数值运算中出现溢出情况。但是你可以使用 Swift 的溢出运算符来实现溢出运算(如`a &+ b`)。详情参见[溢出运算符](../chapter2/25_Advanced_Operators.html#overflow_operators)。 +与 C 语言和 Objective-C 不同的是,Swift 默认情况下不允许在数值运算中出现溢出情况。但是你可以使用 Swift 的溢出运算符来实现溢出运算(如 `a &+ b`)。详情参见[溢出运算符](../chapter2/25_Advanced_Operators.html#overflow_operators)。 -加法运算符也可用于`String`的拼接: +加法运算符也可用于 `String` 的拼接: ```swift "hello, " + "world" // 等于 "hello, world" @@ -98,16 +101,16 @@ Swift 中所有数值类型都支持了基本的四则算术运算: ### 求余运算符 -求余运算(`a % b`)是计算`b`的多少倍刚刚好可以容入`a`,返回多出来的那部分(余数)。 +求余运算(`a % b`)是计算 `b` 的多少倍刚刚好可以容入`a`,返回多出来的那部分(余数)。 ->注意: -求余运算(`%`)在其他语言也叫取模运算。然而严格说来,我们看该运算符对负数的操作结果,"求余"比"取模"更合适些。 +> 注意: +求余运算(`%`)在其他语言也叫取模运算。然而严格说来,我们看该运算符对负数的操作结果,「求余」比「取模」更合适些。 -我们来谈谈取余是怎么回事,计算`9 % 4`,你先计算出`4`的多少倍会刚好可以容入`9`中: +我们来谈谈取余是怎么回事,计算 `9 % 4`,你先计算出 `4` 的多少倍会刚好可以容入 `9` 中: ![Art/remainderInteger_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/remainderInteger_2x.png "Art/remainderInteger_2x.png") -2倍,非常好,那余数是1(用橙色标出) +你可以在 `9` 中放入两个 `4`,那余数是 1(用橙色标出)。 在 Swift 中可以表达为: @@ -115,13 +118,13 @@ Swift 中所有数值类型都支持了基本的四则算术运算: 9 % 4 // 等于 1 ``` -为了得到`a % b`的结果,`%`计算了以下等式,并输出`余数`作为结果: +为了得到 `a % b` 的结果,`%` 计算了以下等式,并输出`余数`作为结果: a = (b × 倍数) + 余数 -当`倍数`取最大值的时候,就会刚好可以容入`a`中。 +当`倍数`取最大值的时候,就会刚好可以容入 `a` 中。 -把`9`和`4`代入等式中,我们得`1`: +把 `9` 和 `4` 代入等式中,我们得 `1`: 9 = (4 × 2) + 1 @@ -131,13 +134,13 @@ Swift 中所有数值类型都支持了基本的四则算术运算: -9 % 4 // 等于 -1 ``` -把`-9`和`4`代入等式,`-2`是取到的最大整数: +把 `-9` 和 `4` 代入等式,`-2` 是取到的最大整数: -9 = (4 × -2) + -1 -余数是`-1`。 +余数是 `-1`。 -在对负数`b`求余时,`b`的符号会被忽略。这意味着 `a % b` 和 `a % -b`的结果是相同的。 +在对负数 `b` 求余时,`b` 的符号会被忽略。这意味着 `a % b` 和 `a % -b` 的结果是相同的。 ### 浮点数求余计算 @@ -147,27 +150,27 @@ Swift 中所有数值类型都支持了基本的四则算术运算: 8 % 2.5 // 等于 0.5 ``` -这个例子中,`8`除于`2.5`等于`3`余`0.5`,所以结果是一个`Double`值`0.5`。 +这个例子中,`8` 除以 `2.5` 等于 `3` 余 `0.5`,所以结果是一个 `Double` 型的值为 `0.5`。 ![Art/remainderFloat_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/remainderFloat_2x.png "Art/remainderFloat_2x.png") ### 自增和自减运算 -和 C 语言一样,Swift 也提供了对变量本身加1或减1的自增(`++`)和自减(`--`)的缩略算符。其操作对象可以是整形和浮点型。 +和 C 语言一样,Swift 也提供了对变量本身加 1 或减 1 的自增(`++`)和自减(`--`)的缩略算符。其操作对象可以是整形和浮点型。 ```swift var i = 0 ++i // 现在 i = 1 ``` -每调用一次`++i`,`i`的值就会加1。实际上,`++i`是`i = i + 1`的简写,而`--i`是`i = i - 1`的简写。 +每调用一次 `++i`,`i` 的值就会加 1。实际上,`++i` 是 `i = i + 1` 的简写,而 `--i` 是 `i = i - 1` 的简写。 -`++`和`--`既可以用作前置运算又可以用作后置运算。`++i`,`i++`,`--i`和`i--`都是有效的写法。 +`++` 和 `--` 既可以用作前置运算又可以用作后置运算。`++i`、`i++` 都是有效的自增的写法;相类似对应自减的写法则是 `--i` 和 `i--`。 -我们需要注意的是这些运算符即可修改了`i`的值也可以返回`i`的值。如果你只想修改`i`的值,那你就可以忽略这个返回值。但如果你想使用返回值,你就需要留意前置和后置操作的返回值是不同的,她们遵循以下原则: +我们需要注意的是这些运算符即可修改了 `i` 的值也可以返回 `i` 的值。如果你只想修改 `i` 的值,那你就可以忽略这个返回值。但如果你想使用返回值,你就需要留意前置和后置操作的返回值是不同的,它们遵循以下原则: -- 当`++`前置的时候,先自増再返回。 -- 当`++`后置的时候,先返回再自增。 +- 当 `++` 前置的时候,先自増再返回。 +- 当 `++` 后置的时候,先返回再自增。 例如: @@ -177,16 +180,16 @@ let b = ++a // a 和 b 现在都是 1 let c = a++ // a 现在 2, 但 c 是 a 自增前的值 1 ``` -上述例子,`let b = ++a`先把`a`加1了再返回`a`的值。所以`a`和`b`都是新值`1`。 +上述例子,`let b = ++a` 先把 `a` 加 1 了再返回 `a` 的值。所以 `a` 和 `b` 都是新值 `1`。 -而`let c = a++`,是先返回了`a`的值,然后`a`才加1。所以`c`得到了`a`的旧值1,而`a`加1后变成2。 +而 `let c = a++`,是先返回了 `a` 的值,然后 `a` 才加 1。所以 `c` 得到了 `a` 的旧值 1,而 `a` 加 1 后变成 2。 -除非你需要使用`i++`的特性,不然推荐你使用`++i`和`--i`,因为先修改后返回这样的行为更符合我们的逻辑。 +除非你需要使用 `i++` 的特性,不然推荐你使用 `++i` 和 `--i`,因为先修改后返回这样的行为更符合我们的逻辑。 ### 一元负号运算符 -数值的正负号可以使用前缀`-`(即一元负号)来切换: +数值的正负号可以使用前缀 `-`(即一元负号)来切换: ```swift let three = 3 @@ -205,28 +208,29 @@ let minusSix = -6 let alsoMinusSix = +minusSix // alsoMinusSix 等于 -6 ``` -虽然一元`+`什么都不会改变,但当你在使用一元负号来表达负数时,你可以使用一元正号来表达正数,如此你的代码会具有对称美。 +虽然一元 `+` 什么都不会改变,但当你在使用一元负号来表达负数时,你可以使用一元正号来表达正数,如此你的代码会具有对称美。 -## 组合赋值运算符(Compound Assignment Operators) +## 组合赋值运算符 如同 C 语言,Swift 也提供把其他运算符和赋值运算(`=`)组合的组合赋值运算符,组合加运算(`+=`)是其中一个例子: ```swift var a = 1 -a += 2 // a 现在是 3 +a += 2 +// a 现在是 3 ``` -表达式`a += 2`是`a = a + 2`的简写,一个组合加运算就是把加法运算和赋值运算组合成进一个运算符里,同时完成两个运算任务。 +表达式 `a += 2` 是 `a = a + 2` 的简写,一个组合加运算就是把加法运算和赋值运算组合成进一个运算符里,同时完成两个运算任务。 ->注意: +> 注意: 复合赋值运算没有返回值,`let b = a += 2`这类代码是错误。这不同于上面提到的自增和自减运算符。 在[表达式](../chapter3/04_Expressions.html)章节里有复合运算符的完整列表。 ‌ -## 比较运算符 +## 比较运算符(Comparison Operators) 所有标准 C 语言中的比较运算都可以在 Swift 中使用: @@ -238,7 +242,7 @@ a += 2 // a 现在是 3 - 小于等于(`a <= b`) > 注意: -Swift 也提供恒等`===`和不恒等`!==`这两个比较符来判断两个对象是否引用同一个对象实例。更多细节在[类与结构](../chapter2/09_Classes_and_Structures.html)。 +Swift 也提供恒等(`===`)和不恒等(`!==`)这两个比较符来判断两个对象是否引用同一个对象实例。更多细节在[类与结构](../chapter2/09_Classes_and_Structures.html)。 每个比较运算都返回了一个标识表达式是否成立的布尔值: @@ -263,12 +267,25 @@ if name == "world" { // 输出 "hello, world", 因为 `name` 就是等于 "world" ``` -关于`if`语句,请看[控制流](../chapter2/05_Control_Flow.html)。 +关于 `if` 语句,请看[控制流](../chapter2/05_Control_Flow.html)。 + +当元组中的值可以比较时,你也可以使用这些运算符来比较它们的大小。例如,因为 `Int` 和 `String` 类型的值可以比较,所以类型为 `(Int, String)` 的元组也可以被比较。相反,`Bool` 不能被比较,也意味着存有布尔类型的元组不能被比较。 + +比较元组大小会按照从左到右、逐值比较的方式,直到发现有两个值不等时停止。如果所有的值都相等,那么这一对元组我们就称它们是相等的。例如: + +```swift +(1, "zebra") < (2, "apple") // true,因为 1 小于 2 +(3, "apple") < (3, "bird") // true,因为 3 等于 3,但是 apple 小于 bird +(4, "dog") == (4, "dog") // true,因为 4 等于 4,dog 等于 dog +``` + +> 注意: +Swift 标准库只能比较七个以内元素的元组比较函数。如果你的元组元素超过七个时,你需要自己实现比较运算符。 -## 三目运算符(Ternary Conditional Operator) +## 三目运算符(Ternary Conditional Operator) -三目运算符的特殊在于它是有三个操作数的运算符,它的原型是 `问题 ? 答案1 : 答案2`。它简洁地表达根据`问题`成立与否作出二选一的操作。如果`问题`成立,返回`答案1`的结果; 如果不成立,返回`答案2`的结果。 +三目运算符的特殊在于它是有三个操作数的运算符,它的形式是 `问题 ? 答案 1 : 答案 2`。它简洁地表达根据 `问题`成立与否作出二选一的操作。如果 `问题` 成立,返回 `答案 1` 的结果;反之返回 `答案 2` 的结果。 三目运算符是以下代码的缩写形式: @@ -280,7 +297,7 @@ if question { } ``` -这里有个计算表格行高的例子。如果有表头,那行高应比内容高度要高出50点;如果没有表头,只需高出20点: +这里有个计算表格行高的例子。如果有表头,那行高应比内容高度要高出 50 点;如果没有表头,只需高出 20 点: ```swift let contentHeight = 40 @@ -303,28 +320,25 @@ if hasHeader { // rowHeight 现在是 90 ``` -第一段代码例子使用了三目运算,所以一行代码就能让我们得到正确答案。这比第二段代码简洁得多,无需将`rowHeight`定义成变量,因为它的值无需在`if`语句中改变。 +第一段代码例子使用了三目运算,所以一行代码就能让我们得到正确答案。这比第二段代码简洁得多,无需将 `rowHeight` 定义成变量,因为它的值无需在 `if` 语句中改变。 三目运算提供有效率且便捷的方式来表达二选一的选择。需要注意的事,过度使用三目运算符会使简洁的代码变的难懂。我们应避免在一个组合语句中使用多个三目运算符。 -## 空合运算符(Nil Coalescing Operator) +## 空合运算符(Nil Coalescing Operator) -空合运算符(`a ?? b`)将对可选类型`a`进行空判断,如果`a`包含一个值就进行解封,否则就返回一个默认值`b`.这个运算符有两个条件: +空合运算符(`a ?? b`)将对可选类型 `a` 进行空判断,如果 `a` 包含一个值就进行解封,否则就返回一个默认值 `b`。表达式 `a` 必须是 Optional 类型。默认值 `b` 的类型必须要和 `a` 存储值的类型保持一致。 -- 表达式`a`必须是Optional类型 -- 默认值`b`的类型必须要和`a`存储值的类型保持一致 - -空合运算符是对以下代码的简短表达方法 +空合运算符是对以下代码的简短表达方法: ```swift a != nil ? a! : b ``` -上述代码使用了三目运算符。当可选类型`a`的值不为空时,进行强制解封(`a!`)访问`a`中值,反之当`a`中值为空时,返回默认值b。无疑空合运算符(`??`)提供了一种更为优雅的方式去封装条件判断和解封两种行为,显得简洁以及更具可读性。 +上述代码使用了三目运算符。当可选类型 `a` 的值不为空时,进行强制解封(`a!`),访问 `a` 中的值;反之返回默认值 `b`。无疑空合运算符(`??`)提供了一种更为优雅的方式去封装条件判断和解封两种行为,显得简洁以及更具可读性。 > 注意: -如果`a`为非空值(`non-nil`),那么值`b`将不会被估值。这也就是所谓的短路求值。 +如果 `a` 为非空值(`non-nil`),那么值 `b` 将不会被计算。这也就是所谓的短路求值。 下文例子采用空合运算符,实现了在默认颜色名和可选自定义颜色名之间抉择: @@ -336,10 +350,10 @@ var colorNameToUse = userDefinedColorName ?? defaultColorName // userDefinedColorName 的值为空,所以 colorNameToUse 的值为 "red" ``` -`userDefinedColorName`变量被定义为一个可选`String`类型,默认值为`nil`。由于`userDefinedColorName`是一个可选类型,我们可以使用空合运算符去判断其值。在上一个例子中,通过空合运算符为一个名为`colorNameToUse`的变量赋予一个字符串类型初始值。 -由于`userDefinedColorName`值为空,因此表达式`userDefinedColorName ?? defaultColorName`返回`defaultColorName`的值,即`red`。 +`userDefinedColorName` 变量被定义为一个可选的 `String` 类型,默认值为 `nil`。由于 `userDefinedColorName` 是一个可选类型,我们可以使用空合运算符去判断其值。在上一个例子中,通过空合运算符为一个名为 `colorNameToUse` 的变量赋予一个字符串类型初始值。 +由于 `userDefinedColorName` 值为空,因此表达式 `userDefinedColorName ?? defaultColorName` 返回 `defaultColorName` 的值,即 `red`。 -另一种情况,分配一个非空值(`non-nil`)给`userDefinedColorName`,再次执行空合运算,运算结果为封包在`userDefaultColorName`中的值,而非默认值。 +另一种情况,分配一个非空值(`non-nil`)给 `userDefinedColorName`,再次执行空合运算,运算结果为封包在 `userDefaultColorName` 中的值,而非默认值。 ```swift userDefinedColorName = "green" @@ -348,14 +362,14 @@ colorNameToUse = userDefinedColorName ?? defaultColorName ``` -## 区间运算符 +## 区间运算符(Range Operators) Swift 提供了两个方便表达一个区间的值的运算符。 ### 闭区间运算符 -闭区间运算符(`a...b`)定义一个包含从`a`到`b`(包括`a`和`b`)的所有值的区间,`b`必须大于等于`a`。 +闭区间运算符(`a...b`)定义一个包含从 `a` 到 `b`(包括 `a` 和 `b`)的所有值的区间。`a` 的值不能超过 `b`。 ‌ -闭区间运算符在迭代一个区间的所有值时是非常有用的,如在`for-in`循环中: +闭区间运算符在迭代一个区间的所有值时是非常有用的,如在 `for-in` 循环中: ```swift for index in 1...5 { @@ -368,14 +382,14 @@ for index in 1...5 { // 5 * 5 = 25 ``` -关于`for-in`,请看[控制流](../chapter2/05_Control_Flow.html)。 +关于 `for-in`,请看[控制流](../chapter2/05_Control_Flow.html)。 ### 半开区间运算符 -半开区间(`a.. -## 逻辑运算 +## 逻辑运算(Logical Operators) 逻辑运算的操作对象是逻辑布尔值。Swift 支持基于 C 语言的三个标准逻辑运算。 @@ -402,9 +416,9 @@ for i in 0..注意: -Swift 逻辑操作符`&&`和`||`是左结合的,这意味着拥有多元逻辑操作符的复合表达式优先计算最左边的子表达式。 +> 注意: +Swift 逻辑操作符 `&&` 和 `||` 是左结合的,这意味着拥有多元逻辑操作符的复合表达式优先计算最左边的子表达式。 ### 使用括号来明确优先级 From 1e58639fd48444a4e5c7aa86b48b4e65efa3e58e Mon Sep 17 00:00:00 2001 From: futantan Date: Mon, 4 Apr 2016 19:37:58 +0800 Subject: [PATCH 11/61] Updated the discussion of the @objc attribute in the Declaration Attributes section to note that enumerations and enumeration cases can use this attribute. --- source/chapter3/06_Attributes.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/chapter3/06_Attributes.md b/source/chapter3/06_Attributes.md index 49f8c63f..e1f48024 100755 --- a/source/chapter3/06_Attributes.md +++ b/source/chapter3/06_Attributes.md @@ -100,7 +100,7 @@ protocol MyProtocol { protocol MyRenamedProtocol { // 这里是协议定义 } - + @available(*, unavailable, renamed="MyRenamedProtocol") typealias MyProtocol = MyRenamedProtocol ``` @@ -109,7 +109,7 @@ typealias MyProtocol = MyRenamedProtocol 如果 `available` 特性除了平台名称参数外,只指定了一个 `introduced` 参数,那么可以使用以下简写语法代替: -> @available(`平台名称` `版本号`, *) +> @available(`平台名称` `版本号`, * ) `available` 特性的简写语法可以简明地表达出声明在多个平台上的可用性。尽管这两种形式在功能上是相同的,但请尽可能地使用简写语法形式。 @@ -122,13 +122,13 @@ class MyClass { `objc` -该特性用于修饰任何可以在 Objective-C 中表示的声明。比如,非嵌套类、协议、非泛型枚举(仅限原始值为整型的枚举)、类和协议中的属性和方法(包括存取方法)、构造器、析构器以及下标。`objc` 特性告诉编译器这个声明可以在 Objective-C 代码中使用。 +该特性用于修饰任何可以在 Objective-C 中表示的声明。比如,非嵌套类、协议、非泛型枚举(仅限原始值为整型的枚举)、类和协议中的属性和方法(包括存取方法)、构造器、析构器以及下标运算符。`objc` 特性告诉编译器这个声明可以在 Objective-C 代码中使用。 标有 `objc` 特性的类必须继承自 Objective-C 中定义的类。如果你将 `objc` 特性应用于一个类或协议,它也会隐式地应用于类或协议中兼容 Objective-C 的成员。对于标记了 `objc` 特性的类,编译器会隐式地为它的子类添加 `objc` 特性。标记了 `objc` 特性的协议不能继承没有标记 `objc` 的协议。 如果你将 `objc` 特性应用于枚举,每一个枚举用例都会以枚举名称和用例名称组合的方式暴露在 Objective-C 代码中。例如,在 `Planet` 枚举中有一个名为 `Venus` 的用例,该用例暴露在 Objective-C 代码中时叫做 `PlanetVenus`。 -`objc` 特性有一个可选的参数,由标识符构成。当你想把 `objc` 所修饰的实体以一个不同的名字暴露给 Objective-C 时,你就可以使用这个特性参数。你可以使用这个参数来命名类、协议、方法、存取方法以及构造器。下面的例子把 `ExampleClass` 中的 `enabled` 属性的取值方法暴露给 Objective-C,名字是 `isEnabled`,而不是它原来的属性名。 +`objc` 特性有一个可选的参数,由标识符构成。当你想把 `objc` 所修饰的实体以一个不同的名字暴露给 Objective-C 时,你就可以使用这个特性参数。你可以使用这个参数来命名类、枚举类型、枚举用例、协议、方法、存取方法以及构造器。下面的例子把 `ExampleClass` 中的 `enabled` 属性的取值方法暴露给 Objective-C,名字是 `isEnabled`,而不是它原来的属性名。 ```swift @objc From 8aa9f9c59183bf95bd43854fd9ec62b6b61985f7 Mon Sep 17 00:00:00 2001 From: semper_idem Date: Wed, 6 Apr 2016 12:10:26 +0800 Subject: [PATCH 12/61] Language Reference - Lexical Structure Update to Swift 2.2 --- source/chapter3/02_Lexical_Structure.md | 140 +++++++++++++----------- 1 file changed, 77 insertions(+), 63 deletions(-) diff --git a/source/chapter3/02_Lexical_Structure.md b/source/chapter3/02_Lexical_Structure.md index 5eb3aa20..b258617d 100755 --- a/source/chapter3/02_Lexical_Structure.md +++ b/source/chapter3/02_Lexical_Structure.md @@ -5,32 +5,41 @@ > 翻译:[superkam](https://github.com/superkam) > 校对:[numbbbbb](https://github.com/numbbbbb) + + > 2.0 > 翻译+校对:[buginux](https://github.com/buginux) + + > 2.1 > 翻译:[mmoaay](https://github.com/mmoaay) + +> 2.2 +> 翻译+校对:[星夜暮晨](https://github.com/semperidem) + + 本页包含内容: - [空白与注释](#whitespace_and_comments) - [标识符](#identifiers) - [关键字和标点符号](#keywords) - [字面量](#literals) - - [整数字面量](#integer_literals) - - [浮点数字面量](#floating_point_literals) - - [字符串字面量](#string_literals) + - [整数字面量](#integer_literals) + - [浮点数字面量](#floating_point_literals) + - [字符串字面量](#string_literals) - [运算符](#operators) -Swift 的“词法结构”描述了能构成该语言中合法符号的字符序列。这些合法符号组成了语言中最底层的构建基块,并在之后的章节中用于描述语言的其他部分。一个合法符号由一个标识符、关键字、标点符号、字面量或运算符组成。 +Swift 的*“词法结构 (lexical structure)”* 描述了能构成该语言中合法符号 (token) 的字符序列。这些合法符号组成了语言中最底层的构建基块,并在之后的章节中用于描述语言的其他部分。一个合法符号由一个标识符 (identifier)、关键字 (keyword)、标点符号 (punctuation)、字面量 (literal) 或运算符 (operator) 组成。 -通常情况下,符号是根据随后将介绍的语法约束,由 Swift 源文件的输入文本中提取可能的最长子串生成。这种方法称为“最长匹配”,或者“最大适合”。 +通常情况下,通过考虑输入文本当中可能的最长子串,并且在随后将介绍的语法约束之下,根据随后将介绍的语法约束生成的,根据 Swift 源文件当中的字符来生成相应的“符号”。这种方法称为*“最长匹配 (longest match)”*,或者*“最大适合(maximal munch)”*。 ## 空白与注释 -空白有两个用途:分隔源文件中的符号以及帮助区分运算符属于前缀还是后缀(参见 [运算符](#operators)),在其他情况下则会被忽略。以下的字符会被当作空白:空格(U+0020)、换行符(U+000A)、回车符(U+000D)、水平制表符(U+0009)、垂直制表符(U+000B)、换页符(U+000C)以及空(U+0000)。 +空白 (whitespace) 有两个用途:分隔源文件中的符号以及帮助区分运算符属于前缀还是后缀(参见 [运算符](#operators)),在其他情况下空白则会被忽略。以下的字符会被当作空白:空格(U+0020)、换行符(U+000A)、回车符(U+000D)、水平制表符(U+0009)、垂直制表符(U+000B)、换页符(U+000C)以及空字符(U+0000)。 注释被编译器当作空白处理。单行注释由 `//` 开始直至遇到换行符(U+000A)或者回车符(U+000D)。多行注释由 `/*` 开始,以 `*/` 结束。注释允许嵌套,但注释标记必须匹配。 @@ -39,9 +48,9 @@ Swift 的“词法结构”描述了能构成该语言中合法符号的字符 ## 标识符 -标识符可以由以下的字符开始:大写或小写的字母 `A` 到 `Z`、下划线 `_`、基本多文种平面中的 Unicode 非组合字符以及基本多文种平面以外的非个人专用区字符。首字符之后,允许使用数字和 Unicode 字符组合。 +*标识符(identifier)* 可以由以下的字符开始:大写或小写的字母 `A` 到 `Z`、下划线 (`_`)、基本多文种平面 (Basic Multilingual Plane) 中非字符数字组合的 Unicode 字符以及基本多文种平面以外的非个人专用区字符。在首字符之后,允许使用数字和组合 Unicode 字符。 -使用保留字作为标识符,需要在其前后增加反引号 `` ` ``。例如,`class` 不是合法的标识符,但可以使用 `` `class` ``。反引号不属于标识符的一部分,`` `x` `` 和 `x` 表示同一标识符。 +使用保留字作为标识符,需要在其前后增加反引号 (`` ` ``)。例如,`class` 不是合法的标识符,但可以使用 `` `class` ``。反引号不属于标识符的一部分,`` `x` `` 和 `x` 表示同一标识符。 闭包中如果没有明确指定参数名称,参数将被隐式命名为 `$0`、`$1`、`$2` 等等。这些命名在闭包作用域范围内是合法的标识符。 @@ -51,6 +60,7 @@ Swift 的“词法结构”描述了能构成该语言中合法符号的字符 > *标识符* → [*头部标识符*](#identifier-head) [*标识符字符组*](#identifier-characters)可选 > *标识符* → \`[*头部标识符*](#identifier-head) [*标识符字符组*](#identifier-characters)可选\` > *标识符* → [*隐式参数名*](#implicit-parameter-name) + > *标识符列表* → [*标识符*](#identifier) | [*标识符*](#identifier) **,** [*标识符列表*](#identifier-list) @@ -76,7 +86,7 @@ Swift 的“词法结构”描述了能构成该语言中合法符号的字符 > *标识符字符* → 数值 0 - 9 > *标识符字符* → U+0300–U+036F,U+1DC0–U+1DFF,U+20D0–U+20FF,或者 U+FE20–U+FE2F > *标识符字符* → [*头部标识符*](#identifier-head) - +> > *标识符字符组* → [*标识符字符*](#identifier-character) [*标识符字符组*](#identifier-characters)可选 @@ -85,20 +95,21 @@ Swift 的“词法结构”描述了能构成该语言中合法符号的字符 ## 关键字和标点符号 -下面这些被保留的关键字不允许用作标识符,除非被反引号转义,具体描述请参考 [标识符](#identifiers)。 +下面这些被保留的关键字不允许用作标识符,除非使用反引号转义,具体描述请参考 [标识符](#identifiers)。除了 `inout`、`var` 以及 `let` 之外的关键字可以用作某个函数声明或者函数调用当中的外部参数名,不用添加反引号转义。 + +* 用在声明中的关键字: `associatedtype`、`class`、`deinit`、`enum`、`extension`、`func`、`import`、`init`、`inout`、`internal`、`let`、`operator`、`private`、`protocol`、`public`、`static`、`struct`、`subscript`、`typealias` 以及 `var`。 +* 用在语句中的关键字:`break`、`case`、`continue`、`default`、`defer`、`do`、`else`、`fallthrough`、`for`、`guard`、`if`、`in`、`repeat`、`return`、`switch`、`where` 以及 `while`。 +* 用在表达式和类型中的关键字:`as`、`catch`、`dynamicType`、`false`、`is`、`nil`、`rethrows`、`super`、`self`、`Self`、`throw`、`throws`、`true`、`try`、`#column`、`#file`、`#function` 以及 `#line`。 +* 用在模式中的关键字:`_`。 +* 以井字号 (`#`) 开头的关键字:`#available`、`#column`、`#else#elseif`、`#endif`、`#file`、`#function`、`#if`、`#line` 以及 `#selector`。 +* 特定上下文中被保留的关键字: `associativity`、`convenience`、`dynamic`、`didSet`、`final`、`get`、`infix`、`indirect`、`lazy`、`left`、`mutating`、`none`、`nonmutating`、`optional`、`override`、`postfix`、`precedence`、`prefix`、`Protocol`、`required`、`right`、`set`、`Type`、`unowned`、`weak` 以及 `willSet`。这些关键字在特定上下文之外可以被用做标识符。 -* 用在声明中的关键字: `class`、`deinit`、`enum`、`extension`、`func`、`import`、`init`、`inout`、`internal`、`let`、`operator`、`private`、`protocol`、`public`、`static`、`struct`、`subscript`、`typealias`、`var`。 -* 用在语句中的关键字: `break`、`case`、`continue`、`default`、`defer`、`do`、`else`、`fallthrough`、`for`、`guard`、`if`、`in`、`repeat`、`return`、`switch`、`where`、`while`。 -* 用在表达式和类型中的关键字: `as`、`catch`、`dynamicType`、`false`、`is`、`nil`、`rethrows`、`super`、`self`、`Self`、`throw`、`throws`、`true`、`try`、`__COLUMN__`、`__FILE__`、`__FUNCTION__`、`__LINE__`。 -* 用在模式中的关键字:`_` -* 特定上下文中被保留的关键字: `associativity`、`convenience`、`dynamic`、`didSet`、`final`、`get`、`infix`、`indirect`、`lazy`、`left`、`mutating`、`none`、`nonmutating`、`optional`、`override`、`postfix`、`precedence`、`prefix`、`Protocol`、`required`、`right`、`set`、`Type`、`unowned`、`weak`、`willSet`,这些关键字在特定上下文之外可以被用做标识符。 - 以下符号被当作保留符号,不能用于自定义运算符: `(`、`)`、`{`、`}`、`[`、`]`、`.`、`,`、`:`、`;`、`=`、`@`、`#`、`&`(作为前缀运算符)、`->`、`` ` ``、`?`、`!`(作为后缀运算符)。 ## 字面量 -字面量是用来表示源码中某种特定类型的值,比如一个数字或字符串。 +*字面量 (literal)* 用来表示源码中某种特定类型的值,比如一个数字或字符串。 下面是字面量的一些示例: @@ -106,7 +117,7 @@ Swift 的“词法结构”描述了能构成该语言中合法符号的字符 42 // 整数字面量 3.14159 // 浮点数字面量 "Hello, world!" // 字符串字面量 -true // 布尔值字面量 +true // 布尔值字面量 ``` 字面量本身并不包含类型信息。事实上,一个字面量会被解析为拥有无限的精度,然后 Swift 的类型推导会尝试去推导出这个字面量的类型。比如,在 `let x: Int8 = 42` 这个声明中,Swift 使用了显式类型标注(`: Int8`)来推导出 `42` 这个整数字面量的类型是 `Int8`。如果没有可用的类型信息, Swift 则会从标准库中定义的字面量类型中推导出一个默认的类型。整数字面量的默认类型是 `Int`,浮点数字面量的默认类型是 `Double`,字符串字面量的默认类型是 `String`,布尔值字面量的默认类型是 `Bool`。比如,在 `let str = "Hello, world"` 这个声明中,字符串 `"Hello, world"` 的默认推导类型就是 `String`。 @@ -114,26 +125,25 @@ true // 布尔值字面量 当为一个字面量值指定了类型标注的时候,这个标注的类型必须能通过这个字面量值实例化。也就是说,这个类型必须符合这些 Swift 标准库协议中的一个:整数字面量的 `IntegerLiteralConvertible` 协议、浮点数字面量的 `FloatingPointLiteralConvertible` 协议、字符串字面量的 `StringLiteralConvertible` 协议以及布尔值字面量的 `BooleanLiteralConvertible` 协议。比如,`Int8` 符合 `IntegerLiteralConvertible` 协议,因此它能在 `let x: Int8 = 42` 这个声明中作为整数字面量 `42` 的类型标注。 > 字面量语法 - > *字面量* → [*数值字面量*](#numeric-literal) | [*字符串字面量*](#string-literal) | [*布尔值字面量*](#boolean-literal) | [*nil 字面量*](#nil-literal) > *数值字面量* → **-**可选 [*整数字面量*](#integer-literal) | **-**可选 [*浮点数字面量*](#floating-point-literal) - +> > *布尔值字面量* → **true** | **false** - +> > *nil 字面量* → **nil** ### 整数字面量 -整数字面量表示未指定精度整数的值。整数字面量默认用十进制表示,可以加前缀来指定其他的进制,二进制字面量加 `0b`,八进制字面量加 `0o`,十六进制字面量加 `0x`。 +*整数字面量 (Integer Literals)* 表示未指定精度整数的值。整数字面量默认用十进制表示,可以加前缀来指定其他的进制。二进制字面量加 `0b`,八进制字面量加 `0o`,十六进制字面量加 `0x`。 十进制字面量包含数字 `0` 至 `9`。二进制字面量只包含 `0` 或 `1`,八进制字面量包含数字 `0` 至 `7`,十六进制字面量包含数字 `0` 至 `9` 以及字母 `A` 至 `F`(大小写均可)。 负整数的字面量在整数字面量前加负号 `-`,比如 `-42`。 -整型字面面可以使用下划线 `_` 来增加数字的可读性,下划线会被系统忽略,因此不会影响字面量的值。同样地,也可以在数字前加 `0`,并不会影响字面量的值。 +整型字面面可以使用下划线 (`_`) 来增加数字的可读性,下划线会被系统忽略,因此不会影响字面量的值。同样地,也可以在数字前加 `0`,这同样也会被系统所忽略,并不会影响字面量的值。 除非特别指定,整数字面量的默认推导类型为 Swift 标准库类型中的 `Int`。Swift 标准库还定义了其他不同长度以及是否带符号的整数类型,请参考 [整数](../chapter2/01_The_Basics.html#integers)。 @@ -149,54 +159,54 @@ true // 布尔值字面量 > *二进制字面量* → **0b** [*二进制数字*](#binary-digit) [*二进制字面量字符组*](#binary-literal-characters)可选 > > *二进制数字* → 数值 0 到 1 - +> > *二进制字面量字符* → [*二进制数字*](#binary-digit) | _ - +> > *二进制字面量字符组* → [*二进制字面量字符*](#binary-literal-character) [*二进制字面量字符组*](#binary-literal-characters)可选 > *八进制字面量* → **0o** [*八进字数字*](#octal-digit) [*八进制字符组*](#octal-literal-characters)可选 - +> > *八进字数字* → 数值 0 到 7 - +> > *八进制字符* → [*八进字数字*](#octal-digit) | _ - +> > *八进制字符组* → [*八进制字符*](#octal-literal-character) [*八进制字符组*](#octal-literal-characters)可选 > *十进制字面量* → [*十进制数字*](#decimal-digit) [*十进制字符组*](#decimal-literal-characters)可选 - +> > *十进制数字* → 数值 0 到 9 - +> > *十进制数字组* → [*十进制数字*](#decimal-digit) [*十进制数字组*](#decimal-digits)可选 - +> > *十进制字符* → [*十进制数字*](#decimal-digit) | _ - +> > *十进制字符组* → [*十进制字符*](#decimal-literal-character) [*十进制字符组*](#decimal-literal-characters)可选 > *十六进制字面量* → **0x** [*十六进制数字*](#hexadecimal-digit) [*十六进制字面量字符组*](#hexadecimal-literal-characters)可选 - +> > *十六进制数字* → 数值 0 到 9, 字母 a 到 f, 或 A 到 F - +> > *十六进制字符* → [*十六进制数字*](#hexadecimal-digit) | _ - +> > *十六进制字面量字符组* → [*十六进制字符*](#hexadecimal-literal-character) [*十六进制字面量字符组*](#hexadecimal-literal-characters)可选 ### 浮点数字面量 -浮点数字面量表示未指定精度浮点数的值。 +*浮点数字面量 (Floating-point literals)* 表示未指定精度浮点数的值。 浮点数字面量默认用十进制表示(无前缀),也可以用十六进制表示(加前缀 `0x`)。 -十进制浮点数字面量由十进制数字串后跟小数部分或指数部分(或两者皆有)组成。十进制小数部分由小数点 `.` 后跟十进制数字串组成。指数部分由大写或小写字母 `e` 为前缀后跟十进制数字串组成,这串数字表示 `e` 之前的数量乘以 10 的几次方。例如:`1.25e2` 表示 `1.25 ⨉ 10^2`,也就是 `125.0`;同样,`1.25e-2` 表示 `1.25 ⨉ 10^-2`,也就是 `0.0125`。 +十进制浮点数字面量由十进制数字串后跟小数部分或指数部分(或两者皆有)组成。十进制小数部分由小数点 (`.`) 后跟十进制数字串组成。指数部分由大写或小写字母 `e` 为前缀后跟十进制数字串组成,这串数字表示 `e` 之前的数量乘以 10 的几次方。例如:`1.25e2` 表示 1.25 x 10²,也就是 `125.0`;同样,`1.25e-2` 表示 1.25 x 10¯²,也就是 `0.0125`。 -十六进制浮点数字面量由前缀 `0x` 后跟可选的十六进制小数部分以及十六进制指数部分组成。十六进制小数部分由小数点后跟十六进制数字串组成。指数部分由大写或小写字母 `p` 为前缀后跟十进制数字串组成,这串数字表示 `p` 之前的数量乘以 2 的几次方。例如:`0xFp2` 表示 `15 ⨉ 2^2`,也就是 `60`;同样,`0xFp-2` 表示 `15 ⨉ 2^-2`,也就是 `3.75`。 +十六进制浮点数字面量由前缀 `0x` 后跟可选的十六进制小数部分以及十六进制指数部分组成。十六进制小数部分由小数点后跟十六进制数字串组成。指数部分由大写或小写字母 `p` 为前缀后跟十进制数字串组成,这串数字表示 `p` 之前的数量乘以 2 的几次方。例如:`0xFp2` 表示 15 x 2²,也就是 `60`;同样,`0xFp-2` 表示 15 x 2¯²,也就是 `3.75`。 -负数的浮点数字面量由负号 `-` 和浮点数字面量组成,例如 `-42.5`。 +负数的浮点数字面量由负号 (`-`) 和浮点数字面量组成,例如 `-42.5`。 -浮点数字面量允许使用下划线 `_` 来增强数字的可读性,下划线会被系统忽略,因此不会影响字面量的值。同样地,也可以在数字前加 `0`,并不会影响字面量的值。 +浮点数字面量允许使用下划线 (`_`) 来增强数字的可读性,下划线会被系统忽略,因此不会影响字面量的值。同样地,也可以在数字前加 `0`,并不会影响字面量的值。 除非特别指定,浮点数字面量的默认推导类型为 Swift 标准库类型中的 `Double`,表示 64 位浮点数。Swift 标准库也定义了 `Float` 类型,表示 32 位浮点数。 @@ -208,19 +218,19 @@ true // 布尔值字面量 > *十进制分数* → **.** [*十进制字面量*](#decimal-literal) - +> > *十进制指数* → [*十进制指数 e*](#floating-point-e) [*正负号*](#sign)可选 [*十进制字面量*](#decimal-literal) > *十六进制分数* → **.** [*十六进制数字*](#hexadecimal-digit) [*十六进制字面量字符组*](#hexadecimal-literal-characters)可选 - +> > *十六进制指数* → [*十六进制指数 p*](#floating-point-p) [*正负号*](#sign)可选 [*十进制字面量*](#decimal-literal) > *十进制指数 e* → **e** | **E** - +> > *十六进制指数 p* → **p** | **P** - +> > *正负号* → **+** | **-** @@ -243,7 +253,7 @@ true // 布尔值字面量 * 单引号 `\'` * Unicode 标量 `\u{`n`}`,n 为一到八位的十六进制数字 -字符串字面量允许在反斜杠 `\` 后的括号 `()` 中插入表达式的值。插入表达式可以包含字符串字面量,但不能包含未转义的双引号 `"`、未转义的反斜线 `\`、回车符、换行符。 +字符串字面量允许在反斜杠 (`\`) 后的括号 `()` 中插入表达式的值。插入表达式可以包含字符串字面量,但不能包含未转义的双引号 (`"`)、未转义的反斜线 (`\`)、回车符以及换行符。 例如,以下所有字符串字面量的值都是相同的: @@ -271,23 +281,23 @@ let textB = "Hello world" > *静态字符串字面量* → **"**[*引用文本*](#quoted-text)可选**"** - +> > *引用文本* → [*引用文本项*](#quoted-text-item) [*引用文本*](#quoted-text)可选 - +> > *引用文本项* → [*转义字符*](#escaped-character) > *引用文本项* → 除了 **"**、**\\**、U+000A、U+000D 以外的所有 Unicode 字符 > *插值字符串字面量* → **"**[*插值文本*](#interpolated-text)可选**"** - +> > *插值文本* → [*插值文本项*](#interpolated-text-item) [*插值文本*](#interpolated-text)可选 - +> > *插值文本项* → **\\****(**[*表达式*](./04_Expressions.html)**)** | [*引用文本项*](#quoted-text-item) > *转义字符* → **\\****0** | **\\****\\** | **\t** | **\n** | **\r** | **\\"** | **\\'** > *转义字符* → **\u {** [*unicode 标量数字*](#unicode-scalar-digits) **}** - +> > *unicode 标量数字* → 一到八位的十六进制数字 @@ -295,17 +305,21 @@ let textB = "Hello world" Swift 标准库定义了许多可供使用的运算符,其中大部分在 [基础运算符](../chapter2/02_Basic_Operators.html) 和 [高级运算符](../chapter2/25_Advanced_Operators.html) 中进行了阐述。这一小节将描述哪些字符能用于自定义运算符。 -自定义运算符可以由以下其中之一的 ASCII 字符 `/`、`=`、 `-`、`+`、`!`、`*`、`%`、`<`、`>`、`&`、`|`、`^`、`?` 以及 `~`,或者后面语法中规定的任一个 Unicode 字符开始。在第一个字符之后,允许使用组合型 Unicode 字符。也可以使用两个或者多个的点号来自定义运算符(比如,`....`)。虽然可以自定义包含问号 `?` 的运算符,但是这个运算符不能只包含单独的一个问号。 +自定义运算符可以由以下其中之一的 ASCII 字符 `/`、`=`、 `-`、`+`、`!`、`*`、`%`、`<`、`>`、`&`、`|`、`^`、`?` 以及 `~`,或者后面语法中规定的任一个 Unicode 字符(其中包含了*数学运算符*、*零散符号(Miscellaneous Symbols)* 以及印刷符号 (Dingbats) 之类的 Unicode 块)开始。在第一个字符之后,允许使用组合型 Unicode 字符。 + +您也可以以点号 (`.`) 开头来定义自定义运算符。这些运算符可以包含额外的点,例如 `.+.`。如果某个运算符不是以点号开头的,那么它就无法再包含另外的点号了。例如,`+.+` 就会被看作为一个 `+` 运算符后面跟着一个 `.+` 运算符。 + +虽然您可以用问号 `?` 来自定义运算符,但是这个运算符不能只包含单独的一个问号。此外,虽然运算符可以包含一个惊叹号 `!`,但是前缀运算符不能够以问号或者惊叹号开头。 > 注意 -以下这些标记 `=`、`->`、`//`、`/*`、`*/`、`.`、`<`(前缀运算符)、`&`、`?`、`?`(中缀运算符)、`>`(后缀运算符)、`!` 、`?` 是被系统保留的。这些符号不能被重载,也不能用于自定义运算符。 +> 以下这些标记 `=`、`->`、`//`、`/*`、`*/`、`.`、`<`(前缀运算符)、`&`、`?`、`?`(中缀运算符)、`>`(后缀运算符)、`!` 、`?` 是被系统保留的。这些符号不能被重载,也不能用于自定义运算符。 运算符两侧的空白被用来区分该运算符是否为前缀运算符、后缀运算符或二元运算符。规则总结如下: -* 如果运算符两侧都有空白或两侧都无空白,将被看作二元运算符。例如:`a+b` 和 `a + b` 中的运算符 `+` 被看作二元运算符。 -* 如果运算符只有左侧空白,将被看作前缀一元运算符。例如 `a ++b` 中的 `++` 被看作前缀一元运算符。 -* 如果运算符只有右侧空白,将被看作后缀一元运算符。例如 `a++ b` 中的 `++` 被看作后缀一元运算符。 -* 如果运算符左侧没有空白并紧跟 `.`,将被看作后缀一元运算符。例如 `a++.b` 中的 `++` 被看作后缀一元运算符(即上式被视为 `a++ .b` 而非 `a ++ .b`)。 +* 如果运算符两侧都有空白或两侧都无空白,将被看作二元运算符。例如:`a+++b` 和 `a +++ b` 当中的 `+++` 运算符会被看作二元运算符。 +* 如果运算符只有左侧空白,将被看作一元前缀运算符。例如 `a +++b` 中的 `+++` 运算符会被看做是一元前缀运算符。 +* 如果运算符只有右侧空白,将被看作一元后缀运算符。例如 `a+++ b` 中的 `+++` 运算符会被看作是一元后缀运算符。 +* 如果运算符左侧没有空白并紧跟 `.`,将被看作一元后缀运算符。例如 `a+++.b` 中的 `+++` 运算符会被视为一元后缀运算符(即上式被视为 `a+++ .b` 而不是 `a +++ .b`)。 鉴于这些规则,运算符前的字符 `(`、`[` 和 `{`,运算符后的字符 `)`、`]` 和 `}`,以及字符 `,`、`;` 和 `:` 都被视为空白。 @@ -346,19 +360,19 @@ Swift 标准库定义了许多可供使用的运算符,其中大部分在 [基 > *运算符字符* → U+FE00–U+FE0F > *运算符字符* → U+FE20–U+FE2F > *运算符字符* → U+E0100–U+E01EF - +> > *运算符字符组* → [*运算符字符*](#operator-character) [*运算符字符组*](#operator-characters)可选 > *头部点运算符* → **..** - +> > *点运算符字符* → **.** | [*运算符字符*](#operator-character) - +> > *点运算符字符组* → [*点运算符字符*](#dot-operator-character) [*点运算符字符组*](#dot-operator-characters)可选 - + > *二元运算符* → [*运算符*](#operator) - +> > *前缀运算符* → [*运算符*](#operator) - -> *后缀运算符* → [*运算符*](#operator) +> +> *后缀运算符* → [*运算符*](#operator) \ No newline at end of file From d6fd41d42d28c1049d272540e81d1f49d53f9567 Mon Sep 17 00:00:00 2001 From: Cai Linfeng Date: Fri, 8 Apr 2016 14:18:39 +0800 Subject: [PATCH 13/61] =?UTF-8?q?=E6=A0=A1=E5=AF=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/chapter2/23_Generics.md | 88 +++++++++++++++++----------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/source/chapter2/23_Generics.md b/source/chapter2/23_Generics.md index 2f40acf9..e73b062a 100644 --- a/source/chapter2/23_Generics.md +++ b/source/chapter2/23_Generics.md @@ -24,9 +24,9 @@ - [关联类型](#associated_types) - [Where 子句](#where_clauses) -泛型代码可以让你编写适用自定义需求以及任意类型的灵活可重用的函数和类型。它的可以让你避免重复的代码,用一种清晰和抽象的方式来表达代码的意图。 +泛型代码让你能够根据自定义的需求,编写出适用于任意类型、灵活可重用的函数及类型。它能让你避免代码的重复,用一种清晰和抽象的方式来表达代码的意图。 -泛型是 Swift 的强大特性之一,许多 Swift 标准库是通过泛型代码构建的。事实上,泛型的使用贯穿了整本语言手册,只是你可能没有发现而已。例如,Swift 的 `Array` 和 `Dictionary` 都是泛型集合。你可以创建一个 `Int` 数组,也可创建一个 `String` 数组,甚至可以是任意其他 Swift 类型的数组。同样的,你也可以创建存储任意指定类型的字典。 +泛型是 Swift 最强大的特性之一,许多 Swift 标准库是通过泛型代码构建的。事实上,泛型的使用贯穿了整本语言手册,只是你可能没有发现而已。例如,Swift 的 `Array` 和 `Dictionary` 都是泛型集合。你可以创建一个 `Int` 数组,也可创建一个 `String` 数组,甚至可以是任意其他 Swift 类型的数组。同样的,你也可以创建存储任意指定类型的字典。 ## 泛型所解决的问题 @@ -96,11 +96,11 @@ func swapTwoInts(inout a: Int, inout _ b: Int) func swapTwoValues(inout a: T, inout _ b: T) ``` -这个函数的泛型版本使用了占位类型名(在这里用字母 `T` 来表示)来代替实际类型名(例如 `Int`、`String` 或 `Double`)。占位类型名没有指明 `T` 必须是什么类型,但是它指明了 `a` 和 `b` 必须是同一类型 `T`,而无论 `T` 代表什么类型。只有 `swapTwoValues(_:_:)` 函数在调用时,才能根据所传入的实际类型决定 `T` 所代表的类型。 +这个函数的泛型版本使用了占位类型名(在这里用字母 `T` 来表示)来代替实际类型名(例如 `Int`、`String` 或 `Double`)。占位类型名没有指明 `T` 必须是什么类型,但是它指明了 `a` 和 `b` 必须是同一类型 `T`,无论 `T` 代表什么类型。只有 `swapTwoValues(_:_:)` 函数在调用时,才能根据所传入的实际类型决定 `T` 所代表的类型。 -另外一个不同之处在于这个泛型函数名后面跟着占位类型名(`T`),而且是用尖括号括起来的(``)。这个尖括号告诉 Swift 那个 `T` 是 `swapTwoValues(_:_:)` 函数定义的一个占位类型名,因此 Swift 不会去查找名为 `T` 的实际类型。 +另外一个不同之处在于这个泛型函数名(`swapTwoValues(_:_:)`)后面跟着占位类型名(`T`),并用尖括号括起来(``)。这个尖括号告诉 Swift 那个 `T` 是 `swapTwoValues(_:_:)` 函数定义内的一个占位类型名,因此 Swift 不会去查找名为 `T` 的实际类型。 -`swapTwoValues(_:_:)` 函数现在可以像 `swapTwoInts(_:_:)` 那样调用,可以传入任意类型的值,只要两个值的类型相同。`swapTwoValues(_:_:)` 函数被调用时,`T` 所代表的类型都会由传入的值的类型推断出来。 +`swapTwoValues(_:_:)` 函数现在可以像 `swapTwoInts(_:_:)` 那样调用,不同的是它能接受两个任意类型的值,条件是这两个值有着相同的类型。`swapTwoValues(_:_:)` 函数被调用时,`T` 所代表的类型都会由传入的值的类型推断出来。 在下面的两个例子中,`T` 分别代表 `Int` 和 `String`: @@ -131,22 +131,22 @@ swapTwoValues(&someString, &anotherString) ## 命名类型参数 -在大多数情况下,类型参数具有一个描述性名字,例如 `Dictionary` 中的 `Key` 和 `Value`,以及 `Array` 中的 `Element`,这可以告诉阅读代码的人这些类型参数和泛型函数之间的关系。然而,当它们之间的关系没有意义时,通常使用单一的字母来命名,例如 `T`、`U`、`V`,正如上面演示的 `swapTwoValues(_:_:)` 函数中的 `T` 一样。 +在大多数情况下,类型参数具有一个描述性名字,例如 `Dictionary` 中的 `Key` 和 `Value`,以及 `Array` 中的 `Element`,这可以告诉阅读代码的人这些类型参数和泛型函数之间的关系。然而,当它们之间没有有意义的关系时,通常使用单个字母来命名,例如 `T`、`U`、`V`,正如上面演示的 `swapTwoValues(_:_:)` 函数中的 `T` 一样。 > 注意 -请始终使用大写字母开头的驼峰式命名法(例如 `T` 和 `MyTypeParameter`)来为类型参数命名,以表明它们是占位类型,而不是一个值。 +请始终使用大写字母开头的驼峰命名法(例如 `T` 和 `MyTypeParameter`)来为类型参数命名,以表明它们是占位类型,而不是一个值。 ## 泛型类型 -除了泛型函数,Swift 还允许你定义泛型类型。这些自定义类、结构体和枚举可以适用于任何类型,如同 `Array` 和 `Dictionary` 的用法。 +除了泛型函数,Swift 还允许你定义泛型类型。这些自定义类、结构体和枚举可以适用于任何类型,类似于 `Array` 和 `Dictionary`。 -这部分内容将向你展示如何编写一个名为 `Stack` (栈)的泛型集合类型。栈是一系列值的有序集合,和 `Array` 类似,但它相比 Swift 的 `Array` 类型有更多的操作限制。数组允许对其中任意位置的元素执行插入或删除操作。而栈,只允许在集合的末端添加新的元素(称之为入栈)。同样的,栈也只能从末端移除元素(称之为出栈)。 +这部分内容将向你展示如何编写一个名为 `Stack` (栈)的泛型集合类型。栈是一系列值的有序集合,和 `Array` 类似,但它相比 Swift 的 `Array` 类型有更多的操作限制。数组允许在数组的任意位置插入新元素或是删除其中任意位置的元素。而栈只允许在集合的末端添加新的元素(称之为入栈)。类似的,栈也只能从末端移除元素(称之为出栈)。 > 注意 -栈的概念已被 `UINavigationController` 类用来模拟视图控制器的导航结构。你通过调用 `UINavigationController` 的 `pushViewController(_:animated:)` 方法来添加新的视图控制器到导航栈,通过 `popViewControllerAnimated(_:)` 方法来从导航栈中移除某个视图控制器。每当你需要一个严格的“后进先出”方式来管理集合,栈都是最实用的模型。 +栈的概念已被 `UINavigationController` 类用来构造视图控制器的导航结构。你通过调用 `UINavigationController` 的 `pushViewController(_:animated:)` 方法来添加新的视图控制器到导航栈,通过 `popViewControllerAnimated(_:)` 方法来从导航栈中移除视图控制器。每当你需要一个严格的“后进先出”方式来管理集合,栈都是最实用的模型。 -下图展示了一个栈的压栈(push)和出栈(pop)的行为: +下图展示了一个栈的入栈(push)和出栈(pop)的行为: ![此处输入图片的描述](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/stackPushPop_2x.png) @@ -154,9 +154,9 @@ swapTwoValues(&someString, &anotherString) 2. 第四个值被压入到栈的顶部。 3. 现在有四个值在栈中,最近入栈的那个值在顶部。 4. 栈中最顶部的那个值被移除,或称之为出栈。 -5. 移除掉一个值后,现在栈再一次只有三个值。 +5. 移除掉一个值后,现在栈又只有三个值了。 -下面展示了如何编写一个非泛型版本的栈,在这种情况下是 `Int` 型的栈: +下面展示了如何编写一个非泛型版本的栈,以 `Int` 型的栈为例: ```swift struct IntStack { @@ -188,15 +188,15 @@ struct Stack { } ``` -注意,`Stack` 基本上和 `IntStack` 相同,只是用占位类型参数 `Element` 代替了实际的 `Int` 类型。这种类型参数包裹在一对尖括号里(``),紧跟在结构体名后面。 +注意,`Stack` 基本上和 `IntStack` 相同,只是用占位类型参数 `Element` 代替了实际的 `Int` 类型。这个类型参数包裹在紧随结构体名的一对尖括号里(``)。 -`Element` 为尚未提供的类型定义了一个占位名。这种尚未提供的类型可以在结构体的定义中通过 `Element` 来引用。在这种情况下,`Element` 在如下三个地方被用作占位符: +`Element` 为待提供的类型定义了一个占位名。这种待提供的类型可以在结构体的定义中通过 `Element` 来引用。在这个例子中,`Element` 在如下三个地方被用作占位符: - 创建 `items` 属性,使用 `Element` 类型的空数组对其进行初始化。 -- 指定 `push(_:)` 方法的单一参数 `item` 的类型必须是 `Element` 类型。 +- 指定 `push(_:)` 方法的唯一参数 `item` 的类型必须是 `Element` 类型。 - 指定 `pop()` 方法的返回值类型必须是 `Element` 类型。 -由于 `Stack` 是泛型类型,因此可以用来创建 Swift 中任意有效类型的栈,如同 `Array` 和 `Dictionary`。 +由于 `Stack` 是泛型类型,因此可以用来创建 Swift 中任意有效类型的栈,就像 `Array` 和 `Dictionary` 那样。 你可以通过在尖括号中写出栈中需要存储的数据类型来创建并初始化一个 `Stack` 实例。例如,要创建一个 `String` 类型的栈,可以写成 `Stack()`: @@ -227,7 +227,7 @@ let fromTheTop = stackOfStrings.pop() ## 扩展一个泛型类型 -当你扩展一个泛型类型的时候,你并不需要在扩展的定义中提供类型参数列表。更加方便的是,原始类型定义中声明的类型参数列表在扩展中可以直接使用,并且这些来自原始类型中的参数名称会被用作原始定义中类型参数的引用。 +当你扩展一个泛型类型的时候,你并不需要在扩展的定义中提供类型参数列表。原始类型定义中声明的类型参数列表在扩展中可以直接使用,并且这些来自原始类型中的参数名称会被用作原始定义中类型参数的引用。 下面的例子扩展了泛型类型 `Stack`,为其添加了一个名为 `topItem` 的只读计算型属性,它将会返回当前栈顶端的元素而不会将其从栈中移除: @@ -243,7 +243,7 @@ extension Stack { 注意,这个扩展并没有定义一个类型参数列表。相反的,`Stack` 类型已有的类型参数名称 `Element`,被用在扩展中来表示计算型属性 `topItem` 的可选类型。 -计算型属性 `topItem` 现在可以用来访问任意 `Stack` 实例的顶端元素而不是移除它: +计算型属性 `topItem` 现在可以用来访问任意 `Stack` 实例的顶端元素且不移除它: ```swift if let topItem = stackOfStrings.topItem { @@ -255,18 +255,18 @@ if let topItem = stackOfStrings.topItem { ## 类型约束 -`swapTwoValues(_:_:)` 函数和 `Stack` 类型可以作用于任何类型。不过,有的时候如果能将使用在泛型函数和泛型类型中的类型,强制约束为某种特定类型,将会是非常有用的。类型约束可以指定一个类型参数必须继承自指定类,或者符合一个特定的协议或协议组合。 +`swapTwoValues(_:_:)` 函数和 `Stack` 类型可以作用于任何类型。不过,有的时候如果能将使用在泛型函数和泛型类型中的类型添加一个特定的类型约束,将会是非常有用的。类型约束可以指定一个类型参数必须继承自指定类,或者符合一个特定的协议或协议组合。 -例如,Swift 的 `Dictionary` 类型对字典的键的类型做了些限制。在[字典](./04_Collection_Types.html#dictionaries)的描述中,字典的键的类型必须是可哈希的。也就是说,必须有一种方法能作为其唯一的表示。`Dictionary` 之所以需要其键是可哈希的,是为了便于检查字典是否已经包含某个特定键的值。如无此要求,`Dictionary` 将无法判断是否可以插入或者替换某个指定键的值,也不能查找到已经存储在字典中的指定键的值。 +例如,Swift 的 `Dictionary` 类型对字典的键的类型做了些限制。在[字典](./04_Collection_Types.html#dictionaries)的描述中,字典的键的类型必须是可哈希(`hashable`)的。也就是说,必须有一种方法能够唯一地表示它。`Dictionary` 的键之所以要是可哈希的,是为了便于检查字典是否已经包含某个特定键的值。若没有这个要求,`Dictionary` 将无法判断是否可以插入或者替换某个指定键的值,也不能查找到已经存储在字典中的指定键的值。 -这个要求强制加上了一个类型约束作用于 `Dictionary` 的键类型上,其键类型必须符合 `Hashable` 协议,这是 Swift 标准库中定义的一个特定协议。所有的 Swift 基本类型(例如 `String`、`Int`、`Double` 和 `Bool`)默认都是可哈希的。 +为了实现这个要求,一个类型约束被强制加到 `Dictionary` 的键类型上,要求其键类型必须符合 `Hashable` 协议,这是 Swift 标准库中定义的一个特定协议。所有的 Swift 基本类型(例如 `String`、`Int`、`Double` 和 `Bool`)默认都是可哈希的。 当你创建自定义泛型类型时,你可以定义你自己的类型约束,这些约束将提供更为强大的泛型编程能力。抽象概念,例如可哈希的,描述的是类型在概念上的特征,而不是它们的显式类型。 ### 类型约束语法 -你可以在一个类型参数名后面放置一个类名或者协议名,通过冒号分隔,从而定义类型约束,它们将作为类型参数列表的一部分。这种基本的类型约束作用于泛型函数时的语法如下所示(作用于泛型类型时的语法与之相同): +你可以在一个类型参数名后面放置一个类名或者协议名,并用冒号进行分隔,来定义类型约束,它们将成为类型参数列表的一部分。对泛型函数添加类型约束的基本语法如下所示(作用于泛型类型时的语法与之相同): ```swift func someFunction(someT: T, someU: U) { @@ -279,7 +279,7 @@ func someFunction(someT: T, someU: U) { ### 类型约束实践 -这里有个名为 `findStringIndex` 的非泛型函数,该函数的功能是在 `String` 值的数组中查找给定 `String` 值的索引。若查找到匹配的字符串,`findStringIndex(_:_:)` 函数返回该字符串在数组中的索引值,反之则返回 `nil`: +这里有个名为 `findStringIndex` 的非泛型函数,该函数的功能是在一个 `String` 数组中查找给定 `String` 值的索引。若查找到匹配的字符串,`findStringIndex(_:_:)` 函数返回该字符串在数组中的索引值,否则返回 `nil`: ```swift func findStringIndex(array: [String], _ valueToFind: String) -> Int? { @@ -302,9 +302,9 @@ if let foundIndex = findStringIndex(strings, "llama") { // 打印 “The index of llama is 2” ``` -如果只能查找字符串在数组中的索引,用处不是很大。不过,你可以写出相同功能的泛型函数 `findIndex(_:_:)`,用占位类型 `T` 替换 `String` 类型。 +如果只能查找字符串在数组中的索引,用处不是很大。不过,你可以用占位类型 `T` 替换 `String` 类型来写出具有相同功能的泛型函数 `findIndex(_:_:)`。 -下面展示了 `findStringIndex(_:_:)` 函数的泛型版本 `findIndex(_:_:)`。请注意这个函数仍然返回 `Int?`,那是因为函数返回的是一个可选的索引数,而不是从数组中得到的一个可选值。需要提醒的是,这个函数无法通过编译,原因在例子后面会说明: +下面展示了 `findStringIndex(_:_:)` 函数的泛型版本 `findIndex(_:_:)`。请注意这个函数返回值的类型仍然是 `Int?`,这是因为函数返回的是一个可选的索引数,而不是从数组中得到的一个可选值。需要提醒的是,这个函数无法通过编译,原因会在例子后面说明: ```swift func findIndex(array: [T], _ valueToFind: T) -> Int? { @@ -317,9 +317,9 @@ func findIndex(array: [T], _ valueToFind: T) -> Int? { } ``` -上面所写的函数无法通过编译。这个问题出在相等性检查上,即 `“if value == valueToFind”`。不是所有的 Swift 类型都可以用等式符(`==`)进行比较。例如,如果你创建一个你自己的类或结构体来表示一个复杂的数据模型,那么 Swift 无法猜到对于这个类或结构体而言“相等”意味着什么。正因如此,这部分代码无法保证适用于每个可能的类型 `T`,当你试图编译这部分代码时会出现相应的错误。 +上面所写的函数无法通过编译。问题出在相等性检查上,即 "`if value == valueToFind`"。不是所有的 Swift 类型都可以用等式符(`==`)进行比较。比如说,如果你创建一个自定义的类或结构体来表示一个复杂的数据模型,那么 Swift 无法猜到对于这个类或结构体而言“相等”意味着什么。正因如此,这部分代码无法保证适用于每个可能的类型 `T`,当你试图编译这部分代码时会出现相应的错误。 -不过,所有的这些并不会让我们无从下手。Swift 标准库中定义了一个 `Equatable` 协议,该协议要求任何符合该协议的类型必须实现等式符(`==`),从而能对符合该协议的类型的任意两个值进行比较。所有的 Swift 标准类型自动支持 `Equatable` 协议。 +不过,所有的这些并不会让我们无从下手。Swift 标准库中定义了一个 `Equatable` 协议,该协议要求任何遵循该协议的类型必须实现等式符(`==`)及不等符(`!=`),从而能对该类型的任意两个值进行比较。所有的 Swift 标准类型自动支持 `Equatable` 协议。 任何 `Equatable` 类型都可以安全地使用在 `findIndex(_:_:)` 函数中,因为其保证支持等式操作符。为了说明这个事实,当你定义一个函数时,你可以定义一个 `Equatable` 类型约束作为类型参数定义的一部分: @@ -334,7 +334,7 @@ func findIndex(array: [T], _ valueToFind: T) -> Int? { } ``` -`findIndex(_:_:)` 中的这个单一类型参数写做 `T: Equatable`,也就意味着“任何符合 `Equatable` 协议的 `T` 类型”。 +`findIndex(_:_:)` 唯一的类型参数写做 `T: Equatable`,也就意味着“任何符合 `Equatable` 协议的类型 `T` ”。 `findIndex(_:_:)` 函数现在可以成功编译了,并且可以作用于任何符合 `Equatable` 的类型,如 `Double` 或 `String`: @@ -348,7 +348,7 @@ let stringIndex = findIndex(["Mike", "Malcolm", "Andrea"], "Andrea") ## 关联类型 -定义一个协议时,有的时候声明一个或多个关联类型作为协议定义的一部分将会非常有用。关联类型作为协议的一部分,为某个类型提供了一个占位名(或者说别名),其代表的实际类型在协议被采纳时才会被指定。你可以通过 `typealias` 关键字来指定关联类型。 +定义一个协议时,有的时候声明一个或多个关联类型作为协议定义的一部分将会非常有用。关联类型为协议中的某个类型提供了一个占位名(或者说别名),其代表的实际类型在协议被采纳时才会被指定。你可以通过 `associatedtype` 关键字来指定关联类型。 ### 关联类型实践 @@ -357,26 +357,26 @@ let stringIndex = findIndex(["Mike", "Malcolm", "Andrea"], "Andrea") ```swift protocol Container { - typealias ItemType + associatedtype ItemType mutating func append(item: ItemType) var count: Int { get } subscript(i: Int) -> ItemType { get } } ``` -`Container` 协议定义了三个任何采纳协议的类型必须提供的功能: +`Container` 协议定义了三个任何采纳了该协议的类型(即容器)必须提供的功能: - 必须可以通过 `append(_:)` 方法添加一个新元素到容器里。 - 必须可以通过 `count` 属性获取容器中元素的数量,并返回一个 `Int` 值。 -- 必须可以通过接受 `Int` 索引值的下标检索到每一个元素。 +- 必须可以通过索引值类型为 `Int` 的下标检索到容器中的每一个元素。 -这个协议没有指定容器中元素该如何存储,以及元素必须是何种类型。这个协议只指定了三个任何采纳 `Container` 协议的类型必须提供的功能。采纳协议的类型在满足这三个条件的情况下也可以提供其他额外的功能。 +这个协议没有指定容器中元素该如何存储,以及元素必须是何种类型。这个协议只指定了三个任何遵从 `Container` 协议的类型必须提供的功能。遵从协议的类型在满足这三个条件的情况下也可以提供其他额外的功能。 -任何采纳 `Container` 协议的类型必须能够指定其存储的元素的类型,必须保证只有正确类型的元素可以加进容器中,必须明确通过其下标返回的元素的类型。 +任何遵从 `Container` 协议的类型必须能够指定其存储的元素的类型,必须保证只有正确类型的元素可以加进容器中,必须明确通过其下标返回的元素的类型。 为了定义这三个条件,`Container` 协议需要在不知道容器中元素的具体类型的情况下引用这种类型。`Container` 协议需要指定任何通过 `append(_:)` 方法添加到容器中的元素和容器中的元素是相同类型,并且通过容器下标返回的元素的类型也是这种类型。 -为了达到此目的,`Container` 协议声明了一个关联类型 `ItemType`,写作 `typealias ItemType`。这个协议无法定义 `ItemType` 是什么类型的别名,这个信息将留给采纳协议的类型来提供。尽管如此,`ItemType` 别名提供了一种方式来引用 `Container` 中元素的类型,并将之用于 `append(_:)` 方法和下标,从而保证任何 `Container` 的预期行为都能够被执行。 +为了达到这个目的,`Container` 协议声明了一个关联类型 `ItemType`,写作 `associatedtype ItemType`。这个协议无法定义 `ItemType` 是什么类型的别名,这个信息将留给遵从协议的类型来提供。尽管如此,`ItemType` 别名提供了一种方式来引用 `Container` 中元素的类型,并将之用于 `append(_:)` 方法和下标,从而保证任何 `Container` 的行为都能够正如预期地被执行。 下面是先前的非泛型的 `IntStack` 类型,这一版本采纳并符合了 `Container` 协议: @@ -406,11 +406,11 @@ struct IntStack: Container { `IntStack` 结构体实现了 `Container` 协议的三个要求,其原有功能也不会和这些要求相冲突。 -此外,`IntStack` 指定 `ItemType` 为 `Int` 类型,即 `typealias ItemType = Int`,从而将 `Container` 协议中抽象的 `ItemType` 类型转换为具体的 `Int` 类型。 +此外,`IntStack` 在实现 `Container` 的要求时,指定 `ItemType` 为 `Int` 类型,即 `typealias ItemType = Int`,从而将 `Container` 协议中抽象的 `ItemType` 类型转换为具体的 `Int` 类型。 -由于 Swift 的类型推断,你实际上不用在 `IntStack` 的定义中声明 `ItemType` 为 `Int`。因为 `IntStack` 符合 `Container` 协议的所有要求,Swift 只需通过 `append(_:)` 方法的 `item` 参数类型和下标返回值的类型,就可以推断出 `ItemType` 的具体类型。事实上,如果你在上面的代码中删除了 `typealias ItemType = Int` 这一行,这一切仍旧可以正常工作,因为 Swift 清楚地知道 `ItemType` 应该是何种类型。 +由于 Swift 的类型推断,你实际上不用在 `IntStack` 的定义中声明 `ItemType` 为 `Int`。因为 `IntStack` 符合 `Container` 协议的所有要求,Swift 只需通过 `append(_:)` 方法的 `item` 参数类型和下标返回值的类型,就可以推断出 `ItemType` 的具体类型。事实上,如果你在上面的代码中删除了 `typealias ItemType = Int` 这一行,一切仍旧可以正常工作,因为 Swift 清楚地知道 `ItemType` 应该是哪种类型。 -你也可以令泛型 `Stack` 结构体符合 `Container` 协议: +你也可以让泛型 `Stack` 结构体遵从 `Container` 协议: ```swift struct Stack: Container { @@ -442,7 +442,7 @@ struct Stack: Container { [通过扩展添加协议一致性](./22_Protocols.html#adding_protocol_conformance_with_an_extension)中描述了如何利用扩展让一个已存在的类型符合一个协议,这包括使用了关联类型的协议。 -Swift 的 `Array` 已经提供 `append(_:)` 方法,一个 `count` 属性,以及一个接受 `Int` 型索引值的可用来检索数组元素的下标。这三个功能都符合 `Container` 协议的要求,也就意味着你可以扩展 `Array` 去符合 `Container` 协议,只需简单地声明 `Array` 采纳该协议即可。你可以通过一个空扩展来实现这点,正如[通过扩展采纳协议](./22_Protocols.html#declaring_protocol_adoption_with_an_extension)中的描述: +Swift 的 `Array` 类型已经提供 `append(_:)` 方法,一个 `count` 属性,以及一个接受 `Int` 类型索引值的下标用以检索其元素。这三个功能都符合 `Container` 协议的要求,也就意味着你只需简单地声明 `Array` 采纳该协议就可以扩展 `Array`,使其遵从 `Container` 协议。你可以通过一个空扩展来实现这点,正如[通过扩展采纳协议](./22_Protocols.html#declaring_protocol_adoption_with_an_extension)中的描述: ```swift extension Array: Container {} @@ -455,7 +455,7 @@ extension Array: Container {} [类型约束](#type_constraints)让你能够为泛型函数或泛型类型的类型参数定义一些强制要求。 -为关联类型定义约束也是非常有用的。你可以在参数列表中通过 `where` 子句为关联类型定义约束。一个 `where` 子句能够使一个关联类型符合某个特定的协议,以及某个特定的类型参数和关联类型必须类型相同。你可以通过将 `where` 关键字紧跟在类型参数列表后面来定义 `where` 子句,`where` 子句后跟一个或者多个针对关联类型的约束,以及一个或多个类型参数和关联类型间的相等关系。 +为关联类型定义约束也是非常有用的。你可以在参数列表中通过 `where` 子句为关联类型定义约束。你能通过 `where` 子句要求一个关联类型遵从某个特定的协议,以及某个特定的类型参数和关联类型必须类型相同。你可以通过将 `where` 关键字紧跟在类型参数列表后面来定义 `where` 子句,`where` 子句后跟一个或者多个针对关联类型的约束,以及一个或多个类型参数和关联类型间的相等关系。 下面的例子定义了一个名为 `allItemsMatch` 的泛型函数,用来检查两个 `Container` 实例是否包含相同顺序的相同元素。如果所有的元素能够匹配,那么返回 `true`,否则返回 `false`。 @@ -486,7 +486,7 @@ func allItemsMatch< 这个函数接受 `someContainer` 和 `anotherContainer` 两个参数。参数 `someContainer` 的类型为 `C1`,参数 `anotherContainer` 的类型为 `C2`。`C1` 和 `C2` 是容器的两个占位类型参数,函数被调用时才能确定它们的具体类型。 -这个函数的类型参数列表还定义了两个类型参数的要求: +这个函数的类型参数列表还定义了对两个类型参数的要求: - `C1` 必须符合 `Container` 协议(写作 `C1: Container`)。 - `C2` 必须符合 `Container` 协议(写作 `C2: Container`)。 @@ -504,7 +504,7 @@ func allItemsMatch< 第三个和第四个要求结合起来意味着 `anotherContainer` 中的元素也可以通过 `!=` 操作符来比较,因为它们和 `someContainer` 中的元素类型相同。 -这些要求让 `allItemsMatch(_:_:)` 函数能够比较两个容器,即使它们是不同的容器类型。 +这些要求让 `allItemsMatch(_:_:)` 函数能够比较两个容器,即使它们的容器类型不同。 `allItemsMatch(_:_:)` 函数首先检查两个容器是否拥有相同数量的元素,如果它们的元素数量不同,那么一定不匹配,函数就会返回 `false`。 @@ -530,4 +530,4 @@ if allItemsMatch(stackOfStrings, arrayOfStrings) { // 打印 “All items match.” ``` -上面的例子创建一个 `Stack` 实例来存储一些 `String` 值,然后将三个字符串压入栈中。这个例子还通过数组字面量创建了一个 `Array` 实例,数组中包含三个同栈中一样的字符串。即使栈和数组是不同的类型,但它们都符合 `Container` 协议,而且它们都包含相同类型的值。因此你可以用这两个容器作为参数来调用 `allItemsMatch(_:_:)` 函数。在上面的例子中,`allItemsMatch(_:_:)` 函数正确地显示了这两个容器中的所有元素都是相互匹配的。 +上面的例子创建了一个 `Stack` 实例来存储一些 `String` 值,然后将三个字符串压入栈中。这个例子还通过数组字面量创建了一个 `Array` 实例,数组中包含同栈中一样的三个字符串。即使栈和数组是不同的类型,但它们都遵从 `Container` 协议,而且它们都包含相同类型的值。因此你可以用这两个容器作为参数来调用 `allItemsMatch(_:_:)` 函数。在上面的例子中,`allItemsMatch(_:_:)` 函数正确地显示了这两个容器中的所有元素都是相互匹配的。 From cdf4de78d6d1e1b5c2004032b058890436c81c16 Mon Sep 17 00:00:00 2001 From: shanks Date: Sat, 9 Apr 2016 09:26:42 +0800 Subject: [PATCH 14/61] Update 23_Generics.md update 2.2 history log --- source/chapter2/23_Generics.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/chapter2/23_Generics.md b/source/chapter2/23_Generics.md index e73b062a..3fde82a8 100644 --- a/source/chapter2/23_Generics.md +++ b/source/chapter2/23_Generics.md @@ -12,6 +12,8 @@ > 2.1 > 校对:[shanks](http://codebuild.me),2015-11-01 +> 2.2:翻译+校对:[Lanford](https://github.com/LanfordCai),2016-04-08 + 本页包含内容: - [泛型所解决的问题](#the_problem_that_generics_solve) From 93239d892707de4972d0fff161b4b247cd8cc1cb Mon Sep 17 00:00:00 2001 From: shanks Date: Sat, 9 Apr 2016 09:29:50 +0800 Subject: [PATCH 15/61] update 2.2 history log date --- source/chapter3/02_Lexical_Structure.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/chapter3/02_Lexical_Structure.md b/source/chapter3/02_Lexical_Structure.md index b258617d..3f7ee53b 100755 --- a/source/chapter3/02_Lexical_Structure.md +++ b/source/chapter3/02_Lexical_Structure.md @@ -18,7 +18,7 @@ > 2.2 -> 翻译+校对:[星夜暮晨](https://github.com/semperidem) +> 翻译+校对:[星夜暮晨](https://github.com/semperidem),2016-04-06 本页包含内容: @@ -375,4 +375,4 @@ Swift 标准库定义了许多可供使用的运算符,其中大部分在 [基 > > *前缀运算符* → [*运算符*](#operator) > -> *后缀运算符* → [*运算符*](#operator) \ No newline at end of file +> *后缀运算符* → [*运算符*](#operator) From 556458dcb769fd4ec1c9a0fcdf946fac2250cf35 Mon Sep 17 00:00:00 2001 From: Brian175 Date: Sat, 9 Apr 2016 23:43:37 +0800 Subject: [PATCH 16/61] Update 02_a_swift_tour.md --- source/chapter1/02_a_swift_tour.md | 113 ++++++++++++++++++++++++----- 1 file changed, 96 insertions(+), 17 deletions(-) diff --git a/source/chapter1/02_a_swift_tour.md b/source/chapter1/02_a_swift_tour.md index ef27ee96..547061f6 100755 --- a/source/chapter1/02_a_swift_tour.md +++ b/source/chapter1/02_a_swift_tour.md @@ -9,6 +9,9 @@ > 2.0 > 翻译+校对:[xtymichael](https://github.com/xtymichael) +> 2.2 +> 翻译:[175](https://github.com/Brian175) + 本页内容包括: - [简单值(Simple Values)](#simple_values) @@ -17,6 +20,7 @@ - [对象和类(Objects and Classes)](#objects_and_classes) - [枚举和结构体(Enumerations and Structures)](#enumerations_and_structures) - [协议和扩展(Protocols and Extensions)](#protocols_and_extensions) +- [错误处理(Error Handling)](#error_handling) - [泛型(Generics)](#generics) 通常来说,编程语言教程中的第一个程序应该在屏幕上打印“Hello, world”。在 Swift 中,可以用一行代码实现: @@ -30,8 +34,7 @@ print("Hello, world!") 这个教程会通过一系列编程例子来让你对 Swift 有初步了解,如果你有什么不理解的地方也不用担心——任何本章介绍的内容都会在后面的章节中详细讲解。 > 注意: -> 为了获得最好的体验,在 Xcode 当中使用代码预览功能。代码预览功能可以让你编辑代码并实时看到运行结果。 -> 下载Playground +> 在 Mac 上,下载 Playground 并双击文件在 Xcode 里打开:[https://developer.apple.com/go/?id=swift-tour](https://developer.apple.com/go/?id=swift-tour) ## 简单值 @@ -212,20 +215,14 @@ repeat { print(m) ``` -你可以在循环中使用`..<`来表示范围,也可以使用传统的写法,两者是等价的: +你可以在循环中使用`..<`来表示范围。 ```swift -var firstForLoop = 0 +var total = 0 for i in 0..<4 { - firstForLoop += i + total += i } -print(firstForLoop) - -var secondForLoop = 0 -for var i = 0; i < 4; ++i { - secondForLoop += i -} -print(secondForLoop) +print(total) ``` 使用`..<`创建的范围不包含上界,如果想包含的话需要使用`...`。 @@ -540,7 +537,7 @@ let aceRawValue = ace.rawValue > 练习: > 写一个函数,通过比较它们的原始值来比较两个`Rank`值。 -在上面的例子中,枚举原始值的类型是`Int`,所以你只需要设置第一个原始值。剩下的原始值会按照顺序赋值。你也可以使用字符串或者浮点数作为枚举的原始值。使用`rawValue`属性来访问一个枚举成员的原始值。 +默认情况下,Swift 按照从 0 开始每次加 1 的方式为原始值进行赋值,不过你可以通过显式赋值进行改变。在上面的例子中,`Ace`被显式赋值为 1,并且剩下的原始值会按照顺序赋值。你也可以使用字符串或者浮点数作为枚举的原始值。使用`rawValue`属性来访问一个枚举成员的原始值。 使用`init?(rawValue:)`初始化构造器在原始值和枚举值之间进行转换。 @@ -601,17 +598,17 @@ let threeOfSpadesDescription = threeOfSpades.simpleDescription() ```swift enum ServerResponse { case Result(String, String) - case Error(String) + case Failure(String) } let success = ServerResponse.Result("6:00 am", "8:09 pm") -let failure = ServerResponse.Error("Out of cheese.") +let failure = ServerResponse.Failure("Out of cheese.") switch success { case let .Result(sunrise, sunset): let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)." -case let .Error(error): - let serverResponse = "Failure... \(error)" +case let .Failure(message): + print("Failure... \(message)") } ``` @@ -689,6 +686,88 @@ print(protocolValue.simpleDescription) 即使`protocolValue`变量运行时的类型是`simpleClass`,编译器会把它的类型当做`ExampleProtocol`。这表示你不能调用类在它实现的协议之外实现的方法或者属性。 + +## 错误处理 + +使用采用`ErrorType`协议的类型来表示错误。 + +```swift +enum PrinterError: ErrorType { + case OutOfPaper + case NoToner + case OnFire +} +``` + +使用`throw`来抛出一个错误并使用`throws`来表示一个可以抛出错误的函数。如果在函数中抛出一个错误,这个函数会立刻返回并且调用该函数的代码会进行错误处理。 + +```swift +func sendToPrinter(printerName: String) throws -> String { + if printerName == "Never Has Toner" { + throw PrinterError.NoToner + } + return "Job sent" +} +``` + +有多种方式可以用来进行错误处理。一种方式是使用`do-catch`。在`do`代码块中,使用`try`来标记可以抛出错误的代码。在`catch`代码块中,除非你另外命名,否则错误会自动命名为`error`。 + +```swift +do{ + let printerResponse = try sendToPrinter("Bi Sheng") + print(printerResponse) +} catch { + print(error) +} +``` + +> 练习: +> 将 printer name 改为`"Never Has Toner"`使`sendToPrinter(_:)`函数抛出错误。 + +可以使用多个`catch`块来处理特定的错误。参照 switch 中的`case`风格来写`catch`。 + +```swift +do { + let printerResponse = try sendToPrinter("Gutenberg") + print(printerResponse) +} catch PrinterError.OnFire { + print("I'll just put this over here, with the rest of the fire.") +} catch let printerError as PrinterError { + print("Printer error: \(printerError).") +} catch { + print(error) +} +``` + +> 练习: +> 在`do`代码块中添加抛出错误的代码。你需要抛出哪种错误来使第一个`catch`块进行接收?怎么使第二个和第三个`catch`进行接收呢? + +另一种处理错误的方式使用`try?`将结果转换为可选的。如果函数抛出错误,该错误会被抛弃并且结果为`nil`。否则的话,结果会是一个包含函数返回值的可选值。 + +```swift +let printerSuccess = try? sendToPrinter("Mergenthaler") +let printerFailure = try? sendToPrinter("Never Has Toner") +``` + +使用`defer`代码块来表示在函数返回前,函数中最后执行的代码。无论函数是否会抛出错误,这段代码都将执行。使用`defer`,可以把函数调用之初就要执行的代码和函数调用结束时的扫尾代码写在一起,虽然这两者的执行时机截然不同。 + +```swift +var fridgeIsOpen = false +let fridgeContent = ["milk", "eggs", "leftovers"] + +func fridgeContains(itemName: String) -> Bool { + fridgeIsOpen = true + defer { + fridgeIsOpen = false + } + + let result = fridgeContent.contains(itemName) + return result +} +fridgeContains("banana") +print(fridgeIsOpen) +``` + ## 泛型 From 9522480e318e07e082970ab4d97928836d9127cb Mon Sep 17 00:00:00 2001 From: TangJR Date: Sun, 10 Apr 2016 19:11:30 +0800 Subject: [PATCH 17/61] update to 2.2 --- source/chapter2/10_Properties.md | 870 ++++++++++++++++--------------- 1 file changed, 439 insertions(+), 431 deletions(-) diff --git a/source/chapter2/10_Properties.md b/source/chapter2/10_Properties.md index a0b62444..5bbd9e07 100755 --- a/source/chapter2/10_Properties.md +++ b/source/chapter2/10_Properties.md @@ -1,431 +1,439 @@ -# 属性 (Properties) ---- - -> 1.0 -> 翻译:[shinyzhu](https://github.com/shinyzhu) -> 校对:[pp-prog](https://github.com/pp-prog) [yangsiy](https://github.com/yangsiy) - -> 2.0 -> 翻译+校对:[yangsiy](https://github.com/yangsiy) - -> 2.1 -> 翻译:[buginux](https://github.com/buginux) -> 校对:[shanks](http://codebuild.me),2015-10-29 - -本页包含内容: - -- [存储属性(Stored Properties)](#stored_properties) -- [计算属性(Computed Properties)](#computed_properties) -- [属性观察器(Property Observers)](#property_observers) -- [全局变量和局部变量(Global and Local Variables)](#global_and_local_variables) -- [类型属性(Type Properties)](#type_properties) - -*属性*将值跟特定的类、结构或枚举关联。存储属性存储常量或变量作为实例的一部分,而计算属性计算(不是存储)一个值。计算属性可以用于类、结构体和枚举,存储属性只能用于类和结构体。 - -存储属性和计算属性通常与特定类型的实例关联。但是,属性也可以直接作用于类型本身,这种属性称为类型属性。 - -另外,还可以定义属性观察器来监控属性值的变化,以此来触发一个自定义的操作。属性观察器可以添加到自己定义的存储属性上,也可以添加到从父类继承的属性上。 - - -## 存储属性 - -简单来说,一个存储属性就是存储在特定类或结构体的实例里的一个常量或变量。存储属性可以是*变量存储属性*(用关键字`var`定义),也可以是*常量存储属性*(用关键字`let`定义)。 - -可以在定义存储属性的时候指定默认值,请参考[默认构造器](./14_Initialization.html#default_initializers)一节。也可以在构造过程中设置或修改存储属性的值,甚至修改常量存储属性的值,请参考[构造过程中常量属性的修改](./14_Initialization.html#assigning_constant_properties_during_initialization)一节。 - -下面的例子定义了一个名为`FixedLengthRange`的结构体,它描述了一个在创建后无法修改值域宽度的区间: - -```swift -struct FixedLengthRange { - var firstValue: Int - let length: Int -} -var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3) -// 该区间表示整数0,1,2 -rangeOfThreeItems.firstValue = 6 -// 该区间现在表示整数6,7,8 -``` - -`FixedLengthRange`的实例包含一个名为`firstValue`的变量存储属性和一个名为`length`的常量存储属性。在上面的例子中,`length`在创建实例的时候被初始化,因为它是一个常量存储属性,所以之后无法修改它的值。 - - -### 常量结构体的存储属性 - -如果创建了一个结构体的实例并将其赋值给一个常量,则无法修改该实例的任何属性,即使定义了变量存储属性: - -```swift -let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4) -// 该区间表示整数0,1,2,3 -rangeOfFourItems.firstValue = 6 -// 尽管 firstValue 是个变量属性,这里还是会报错 -``` - -因为`rangeOfFourItems`被声明成了常量(用`let`关键字),即使`firstValue`是一个变量属性,也无法再修改它了。 - -这种行为是由于结构体(struct)属于*值类型*。当值类型的实例被声明为常量的时候,它的所有属性也就成了常量。 - -属于*引用类型*的类(class)则不一样。把一个引用类型的实例赋给一个常量后,仍然可以修改该实例的变量属性。 - - -### 延迟存储属性 - -延迟存储属性是指当第一次被调用的时候才会计算其初始值的属性。在属性声明前使用`lazy`来标示一个延迟存储属性。 - -> 注意 -> 必须将延迟存储属性声明成变量(使用`var`关键字),因为属性的初始值可能在实例构造完成之后才会得到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟属性。 - -延迟属性很有用,当属性的值依赖于在实例的构造过程结束后才会知道具体值的外部因素时,或者当获得属性的初始值需要复杂或大量计算时,可以只在需要的时候计算它。 - -下面的例子使用了延迟存储属性来避免复杂类中不必要的初始化。例子中定义了`DataImporter`和`DataManager`两个类,下面是部分代码: - -```swift -class DataImporter { - /* - DataImporter 是一个负责将外部文件中的数据导入的类。 - 这个类的初始化会消耗不少时间。 - */ - var fileName = "data.txt" - // 这里会提供数据导入功能 -} - -class DataManager { - lazy var importer = DataImporter() - var data = [String]() - // 这里会提供数据管理功能 -} - -let manager = DataManager() -manager.data.append("Some data") -manager.data.append("Some more data") -// DataImporter 实例的 importer 属性还没有被创建 -``` - -`DataManager`类包含一个名为`data`的存储属性,初始值是一个空的字符串(`String`)数组。这里没有给出全部代码,只需知道`DataManager`类的目的是管理和提供对这个字符串数组的访问即可。 - -`DataManager`的一个功能是从文件导入数据。该功能由`DataImporter`类提供,`DataImporter`完成初始化需要消耗不少时间:因为它的实例在初始化时可能要打开文件,还要读取文件内容到内存。 - -`DataManager`管理数据时也可能不从文件中导入数据。所以当`DataManager`的实例被创建时,没必要创建一个`DataImporter`的实例,更明智的做法是第一次用到`DataImporter`的时候才去创建它。 - -由于使用了`lazy`,`importer`属性只有在第一次被访问的时候才被创建。比如访问它的属性`fileName`时: - -```swift -print(manager.importer.fileName) -// DataImporter 实例的 importer 属性现在被创建了 -// 输出 "data.txt” -``` - -> 注意 -> 如果一个被标记为`lazy`的属性在没有初始化时就同时被多个线程访问,则无法保证该属性只会被初始化一次。 - - -### 存储属性和实例变量 - -如果您有过 Objective-C 经验,应该知道 Objective-C 为类实例存储值和引用提供两种方法。除了属性之外,还可以使用实例变量作为属性值的后端存储。 - -Swift 编程语言中把这些理论统一用属性来实现。Swift 中的属性没有对应的实例变量,属性的后端存储也无法直接访问。这就避免了不同场景下访问方式的困扰,同时也将属性的定义简化成一个语句。属性的全部信息——包括命名、类型和内存管理特征——都在唯一一个地方(类型定义中)定义。 - - -## 计算属性 - -除存储属性外,类、结构体和枚举可以定义*计算属性*。计算属性不直接存储值,而是提供一个 getter 和一个可选的 setter,来间接获取和设置其他属性或变量的值。 - -```swift -struct Point { - var x = 0.0, y = 0.0 -} -struct Size { - var width = 0.0, height = 0.0 -} -struct Rect { - var origin = Point() - var size = Size() - var center: Point { - get { - let centerX = origin.x + (size.width / 2) - let centerY = origin.y + (size.height / 2) - return Point(x: centerX, y: centerY) - } - set(newCenter) { - origin.x = newCenter.x - (size.width / 2) - origin.y = newCenter.y - (size.height / 2) - } - } -} -var square = Rect(origin: Point(x: 0.0, y: 0.0), - size: Size(width: 10.0, height: 10.0)) -let initialSquareCenter = square.center -square.center = Point(x: 15.0, y: 15.0) -print("square.origin is now at (\(square.origin.x), \(square.origin.y))") -// 输出 "square.origin is now at (10.0, 10.0)” -``` - -这个例子定义了 3 个结构体来描述几何形状: - -- `Point`封装了一个`(x, y)`的坐标 -- `Size`封装了一个`width`和一个`height` -- `Rect`表示一个有原点和尺寸的矩形 - -`Rect`也提供了一个名为`center`的计算属性。一个矩形的中心点可以从原点(`origin`)和尺寸(`size`)算出,所以不需要将它以显式声明的`Point`来保存。`Rect`的计算属性`center`提供了自定义的 getter 和 setter 来获取和设置矩形的中心点,就像它有一个存储属性一样。 - -上述例子中创建了一个名为`square`的`Rect`实例,初始值原点是`(0, 0)`,宽度高度都是`10`。如下图中蓝色正方形所示。 - -`square`的`center`属性可以通过点运算符(`square.center`)来访问,这会调用该属性的 getter 来获取它的值。跟直接返回已经存在的值不同,getter 实际上通过计算然后返回一个新的`Point`来表示`square`的中心点。如代码所示,它正确返回了中心点`(5, 5)`。 - -`center`属性之后被设置了一个新的值`(15, 15)`,表示向右上方移动正方形到如下图橙色正方形所示的位置。设置属性`center`的值会调用它的 setter 来修改属性`origin`的`x`和`y`的值,从而实现移动正方形到新的位置。 - -Computed Properties sample - - -### 便捷 setter 声明 - -如果计算属性的 setter 没有定义表示新值的参数名,则可以使用默认名称`newValue`。下面是使用了便捷 setter 声明的`Rect`结构体代码: - -```swift -struct AlternativeRect { - var origin = Point() - var size = Size() - var center: Point { - get { - let centerX = origin.x + (size.width / 2) - let centerY = origin.y + (size.height / 2) - return Point(x: centerX, y: centerY) - } - set { - origin.x = newValue.x - (size.width / 2) - origin.y = newValue.y - (size.height / 2) - } - } -} -``` - - -### 只读计算属性 - -只有 getter 没有 setter 的计算属性就是*只读计算属性*。只读计算属性总是返回一个值,可以通过点运算符访问,但不能设置新的值。 - -> 注意 -> 必须使用`var`关键字定义计算属性,包括只读计算属性,因为它们的值不是固定的。`let`关键字只用来声明常量属性,表示初始化后再也无法修改的值。 - -只读计算属性的声明可以去掉`get`关键字和花括号: - -```swift -struct Cuboid { - var width = 0.0, height = 0.0, depth = 0.0 - var volume: Double { - return width * height * depth - } -} -let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0) -print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)") -// 输出 "the volume of fourByFiveByTwo is 40.0" -``` - -这个例子定义了一个名为`Cuboid`的结构体,表示三维空间的立方体,包含`width`、`height`和`depth`属性。结构体还有一个名为`volume`的只读计算属性用来返回立方体的体积。为`volume`提供 setter 毫无意义,因为无法确定如何修改`width`、`height`和`depth`三者的值来匹配新的`volume`。然而,`Cuboid`提供一个只读计算属性来让外部用户直接获取体积是很有用的。 - - -## 属性观察器 - -*属性观察器*监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,甚至新值和当前值相同的时候也不例外。 - -可以为除了延迟存储属性之外的其他存储属性添加属性观察器,也可以通过重写属性的方式为继承的属性(包括存储属性和计算属性)添加属性观察器。属性重写请参考[重写](./13_Inheritance.html#overriding)。 - -> 注意 -> 不需要为非重写的计算属性添加属性观察器,因为可以通过它的 setter 直接监控和响应值的变化。 - -可以为属性添加如下的一个或全部观察器: - -- `willSet`在新的值被设置之前调用 -- `didSet`在新的值被设置之后立即调用 - -`willSet`观察器会将新的属性值作为常量参数传入,在`willSet`的实现代码中可以为这个参数指定一个名称,如果不指定则参数仍然可用,这时使用默认名称`newValue`表示。 - -类似地,`didSet`观察器会将旧的属性值作为参数传入,可以为该参数命名或者使用默认参数名`oldValue`。 - -> 注意 -> 父类的属性在子类的构造器中被赋值时,它在父类中的`willSet`和`didSet`观察器会被调用。 -> 有关构造器代理的更多信息,请参考[值类型的构造器代理](./14_Initialization.html#initializer_delegation_for_value_types)和[类的构造器代理规则](./14_Initialization.html#initializer_delegation_for_class_types)。 - -这里是一个`willSet`和`didSet`的实际例子,其中定义了一个名为`StepCounter`的类,用来统计一个人步行时的总步数。这个类可以跟计步器或其他日常锻炼的统计装置的输入数据配合使用。 - -```swift -class StepCounter { - var totalSteps: Int = 0 { - willSet(newTotalSteps) { - print("About to set totalSteps to \(newTotalSteps)") - } - didSet { - if totalSteps > oldValue { - print("Added \(totalSteps - oldValue) steps") - } - } - } -} -let stepCounter = StepCounter() -stepCounter.totalSteps = 200 -// About to set totalSteps to 200 -// Added 200 steps -stepCounter.totalSteps = 360 -// About to set totalSteps to 360 -// Added 160 steps -stepCounter.totalSteps = 896 -// About to set totalSteps to 896 -// Added 536 steps -``` - -`StepCounter`类定义了一个`Int`类型的属性`totalSteps`,它是一个存储属性,包含`willSet`和`didSet`观察器。 - -当`totalSteps`被设置新值的时候,它的`willSet`和`didSet`观察器都会被调用,甚至新值和当前值完全相同时也会被调用。 - -例子中的`willSet`观察器将表示新值的参数自定义为`newTotalSteps`,这个观察器只是简单的将新的值输出。 - -`didSet`观察器在`totalSteps`的值改变后被调用,它把新值和旧值进行对比,如果总步数增加了,就输出一个消息表示增加了多少步。`didSet`没有为旧值提供自定义名称,所以默认值`oldValue`表示旧值的参数名。 - -> 注意 -> 如果在一个属性的`didSet`观察器里为它赋值,这个值会替换之前设置的值。 - - -##全局变量和局部变量 - -计算属性和属性观察器所描述的功能也可以用于*全局变量*和*局部变量*。全局变量是在函数、方法、闭包或任何类型之外定义的变量。局部变量是在函数、方法或闭包内部定义的变量。 - -前面章节提到的全局或局部变量都属于存储型变量,跟存储属性类似,它为特定类型的值提供存储空间,并允许读取和写入。 - -另外,在全局或局部范围都可以定义计算型变量和为存储型变量定义观察器。计算型变量跟计算属性一样,返回一个计算结果而不是存储值,声明格式也完全一样。 - -> 注意 -> 全局的常量或变量都是延迟计算的,跟[延迟存储属性](#lazy_stored_properties)相似,不同的地方在于,全局的常量或变量不需要标记`lazy`修饰符。 -> 局部范围的常量或变量从不延迟计算。 - - -##类型属性 - -实例属性属于一个特定类型的实例,每创建一个实例,实例都拥有属于自己的一套属性值,实例之间的属性相互独立。 - -也可以为类型本身定义属性,无论创建了多少个该类型的实例,这些属性都只有唯一一份。这种属性就是*类型属性*。 - -类型属性用于定义某个类型所有实例共享的数据,比如所有实例都能用的一个常量(就像 C 语言中的静态常量),或者所有实例都能访问的一个变量(就像 C 语言中的静态变量)。 - -存储型类型属性可以是变量或常量,计算型类型属性跟实例的计算型属性一样只能定义成变量属性。 - -> 注意 -> 跟实例的存储型属性不同,必须给存储型类型属性指定默认值,因为类型本身没有构造器,也就无法在初始化过程中使用构造器给类型属性赋值。 -> 存储型类型属性是延迟初始化的,它们只有在第一次被访问的时候才会被初始化。即使它们被多个线程同时访问,系统也保证只会对其进行一次初始化,并且不需要对其使用`lazy`修饰符。 - - -###类型属性语法 - -在 C 或 Objective-C 中,与某个类型关联的静态常量和静态变量,是作为全局(*global*)静态变量定义的。但是在 Swift 中,类型属性是作为类型定义的一部分写在类型最外层的花括号内,因此它的作用范围也就在类型支持的范围内。 - -使用关键字`static`来定义类型属性。在为类定义计算型类型属性时,可以改用关键字`class`来支持子类对父类的实现进行重写。下面的例子演示了存储型和计算型类型属性的语法: - -```swift -struct SomeStructure { - static var storedTypeProperty = "Some value." - static var computedTypeProperty: Int { - return 1 - } -} -enum SomeEnumeration { - static var storedTypeProperty = "Some value." - static var computedTypeProperty: Int { - return 6 - } -} -class SomeClass { - static var storedTypeProperty = "Some value." - static var computedTypeProperty: Int { - return 27 - } - class var overrideableComputedTypeProperty: Int { - return 107 - } -} -``` - -> 注意 -> 例子中的计算型类型属性是只读的,但也可以定义可读可写的计算型类型属性,跟计算型实例属性的语法相同。 - - -###获取和设置类型属性的值 - -跟实例属性一样,类型属性也是通过点运算符来访问。但是,类型属性是通过类型本身来访问,而不是通过实例。比如: - -```swift -print(SomeStructure.storedTypeProperty) -// 输出 "Some value." -SomeStructure.storedTypeProperty = "Another value." -print(SomeStructure.storedTypeProperty) -// 输出 "Another value.” -print(SomeEnumeration.computedTypeProperty) -// 输出 "6" -print(SomeClass.computedTypeProperty) -// 输出 "27" -``` - -下面的例子定义了一个结构体,使用两个存储型类型属性来表示两个声道的音量,每个声道具有`0`到`10`之间的整数音量。 - -下图展示了如何把两个声道结合来模拟立体声的音量。当声道的音量是`0`,没有一个灯会亮;当声道的音量是`10`,所有灯点亮。本图中,左声道的音量是`9`,右声道的音量是`7`: - -Static Properties VUMeter - -上面所描述的声道模型使用`AudioChannel`结构体的实例来表示: - -```swift -struct AudioChannel { - static let thresholdLevel = 10 - static var maxInputLevelForAllChannels = 0 - var currentLevel: Int = 0 { - didSet { - if currentLevel > AudioChannel.thresholdLevel { - // 将当前音量限制在阀值之内 - currentLevel = AudioChannel.thresholdLevel - } - if currentLevel > AudioChannel.maxInputLevelForAllChannels { - // 存储当前音量作为新的最大输入音量 - AudioChannel.maxInputLevelForAllChannels = currentLevel - } - } - } -} -``` - -结构`AudioChannel`定义了 2 个存储型类型属性来实现上述功能。第一个是`thresholdLevel`,表示音量的最大上限阈值,它是一个值为`10`的常量,对所有实例都可见,如果音量高于`10`,则取最大上限值`10`(见后面描述)。 - -第二个类型属性是变量存储型属性`maxInputLevelForAllChannels`,它用来表示所有`AudioChannel`实例的最大音量,初始值是`0`。 - -`AudioChannel`也定义了一个名为`currentLevel`的存储型实例属性,表示当前声道现在的音量,取值为`0`到`10`。 - -属性`currentLevel`包含`didSet`属性观察器来检查每次设置后的属性值,它做如下两个检查: - -- 如果`currentLevel`的新值大于允许的阈值`thresholdLevel`,属性观察器将`currentLevel`的值限定为阈值`thresholdLevel`。 -- 如果修正后的`currentLevel`值大于静态类型属性`maxInputLevelForAllChannels`的值,属性观察器就将新值保存在`maxInputLevelForAllChannels`中。 - -> 注意 -> 在第一个检查过程中,`didSet`属性观察器将`currentLevel`设置成了不同的值,但这不会造成属性观察器被再次调用。 - -可以使用结构体`AudioChannel`创建两个声道`leftChannel`和`rightChannel`,用以表示立体声系统的音量: - -```swift -var leftChannel = AudioChannel() -var rightChannel = AudioChannel() -``` - -如果将左声道的`currentLevel`设置成`7`,类型属性`maxInputLevelForAllChannels`也会更新成`7`: - -```swift -leftChannel.currentLevel = 7 -print(leftChannel.currentLevel) -// 输出 "7" -print(AudioChannel.maxInputLevelForAllChannels) -// 输出 "7" -``` - -如果试图将右声道的`currentLevel`设置成`11`,它会被修正到最大值`10`,同时`maxInputLevelForAllChannels`的值也会更新到`10`: - -```swift -rightChannel.currentLevel = 11 -print(rightChannel.currentLevel) -// 输出 "10" -print(AudioChannel.maxInputLevelForAllChannels) -// 输出 "10" -``` +# 属性 (Properties) +--- + +> 1.0 +> 翻译:[shinyzhu](https://github.com/shinyzhu) +> 校对:[pp-prog](https://github.com/pp-prog) [yangsiy](https://github.com/yangsiy) + + +> 2.0 +> 翻译+校对:[yangsiy](https://github.com/yangsiy) + + +> 2.1 +> 翻译:[buginux](https://github.com/buginux) +> 校对:[shanks](http://codebuild.me),2015-10-29 + +> 2.2 +> +> 翻译:[saitjr](https://github.com/saitjr) + + +本页包含内容: + +- [存储属性(Stored Properties)](#stored_properties) +- [计算属性(Computed Properties)](#computed_properties) +- [属性观察器(Property Observers)](#property_observers) +- [全局变量和局部变量(Global and Local Variables)](#global_and_local_variables) +- [类型属性(Type Properties)](#type_properties) + +*属性*将值跟特定的类、结构或枚举关联。存储属性存储常量或变量作为实例的一部分,而计算属性计算(不是存储)一个值。计算属性可以用于类、结构体和枚举,存储属性只能用于类和结构体。 + +存储属性和计算属性通常与特定类型的实例关联。但是,属性也可以直接作用于类型本身,这种属性称为类型属性。 + +另外,还可以定义属性观察器来监控属性值的变化,以此来触发一个自定义的操作。属性观察器可以添加到自己定义的存储属性上,也可以添加到从父类继承的属性上。 + + +## 存储属性 + +简单来说,一个存储属性就是存储在特定类或结构体的实例里的一个常量或变量。存储属性可以是*变量存储属性*(用关键字`var`定义),也可以是*常量存储属性*(用关键字`let`定义)。 + +可以在定义存储属性的时候指定默认值,请参考[默认构造器](./14_Initialization.html#default_initializers)一节。也可以在构造过程中设置或修改存储属性的值,甚至修改常量存储属性的值,请参考[构造过程中常量属性的修改](./14_Initialization.html#assigning_constant_properties_during_initialization)一节。 + +下面的例子定义了一个名为`FixedLengthRange`的结构体,它描述了一个在创建后无法修改值域宽度的区间: + +```swift +struct FixedLengthRange { + var firstValue: Int + let length: Int +} +var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3) +// 该区间表示整数0,1,2 +rangeOfThreeItems.firstValue = 6 +// 该区间现在表示整数6,7,8 +``` + +`FixedLengthRange`的实例包含一个名为`firstValue`的变量存储属性和一个名为`length`的常量存储属性。在上面的例子中,`length`在创建实例的时候被初始化,因为它是一个常量存储属性,所以之后无法修改它的值。 + + +### 常量结构体的存储属性 + +如果创建了一个结构体的实例并将其赋值给一个常量,则无法修改该实例的任何属性,即使定义了变量存储属性: + +```swift +let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4) +// 该区间表示整数0,1,2,3 +rangeOfFourItems.firstValue = 6 +// 尽管 firstValue 是个变量属性,这里还是会报错 +``` + +因为`rangeOfFourItems`被声明成了常量(用`let`关键字),即使`firstValue`是一个变量属性,也无法再修改它了。 + +这种行为是由于结构体(struct)属于*值类型*。当值类型的实例被声明为常量的时候,它的所有属性也就成了常量。 + +属于*引用类型*的类(class)则不一样。把一个引用类型的实例赋给一个常量后,仍然可以修改该实例的变量属性。 + + +### 延迟存储属性 + +延迟存储属性是指当第一次被调用的时候才会计算其初始值的属性。在属性声明前使用`lazy`来标示一个延迟存储属性。 + +> 注意 +> 必须将延迟存储属性声明成变量(使用`var`关键字),因为属性的初始值可能在实例构造完成之后才会得到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟属性。 + +延迟属性很有用,当属性的值依赖于在实例的构造过程结束后才会知道具体值的外部因素时,或者当获得属性的初始值需要复杂或大量计算时,可以只在需要的时候计算它。 + +下面的例子使用了延迟存储属性来避免复杂类中不必要的初始化。例子中定义了`DataImporter`和`DataManager`两个类,下面是部分代码: + +```swift +class DataImporter { + /* + DataImporter 是一个负责将外部文件中的数据导入的类。 + 这个类的初始化会消耗不少时间。 + */ + var fileName = "data.txt" + // 这里会提供数据导入功能 +} + +class DataManager { + lazy var importer = DataImporter() + var data = [String]() + // 这里会提供数据管理功能 +} + +let manager = DataManager() +manager.data.append("Some data") +manager.data.append("Some more data") +// DataImporter 实例的 importer 属性还没有被创建 +``` + +`DataManager`类包含一个名为`data`的存储属性,初始值是一个空的字符串(`String`)数组。这里没有给出全部代码,只需知道`DataManager`类的目的是管理和提供对这个字符串数组的访问即可。 + +`DataManager`的一个功能是从文件导入数据。该功能由`DataImporter`类提供,`DataImporter`完成初始化需要消耗不少时间:因为它的实例在初始化时可能要打开文件,还要读取文件内容到内存。 + +`DataManager`管理数据时也可能不从文件中导入数据。所以当`DataManager`的实例被创建时,没必要创建一个`DataImporter`的实例,更明智的做法是第一次用到`DataImporter`的时候才去创建它。 + +由于使用了`lazy`,`importer`属性只有在第一次被访问的时候才被创建。比如访问它的属性`fileName`时: + +```swift +print(manager.importer.fileName) +// DataImporter 实例的 importer 属性现在被创建了 +// 输出 "data.txt” +``` + +> 注意 +> 如果一个被标记为`lazy`的属性在没有初始化时就同时被多个线程访问,则无法保证该属性只会被初始化一次。 + + +### 存储属性和实例变量 + +如果您有过 Objective-C 经验,应该知道 Objective-C 为类实例存储值和引用提供两种方法。除了属性之外,还可以使用实例变量作为属性值的后端存储。 + +Swift 编程语言中把这些理论统一用属性来实现。Swift 中的属性没有对应的实例变量,属性的后端存储也无法直接访问。这就避免了不同场景下访问方式的困扰,同时也将属性的定义简化成一个语句。属性的全部信息——包括命名、类型和内存管理特征——都在唯一一个地方(类型定义中)定义。 + + +## 计算属性 + +除存储属性外,类、结构体和枚举可以定义*计算属性*。计算属性不直接存储值,而是提供一个 getter 和一个可选的 setter,来间接获取和设置其他属性或变量的值。 + +```swift +struct Point { + var x = 0.0, y = 0.0 +} +struct Size { + var width = 0.0, height = 0.0 +} +struct Rect { + var origin = Point() + var size = Size() + var center: Point { + get { + let centerX = origin.x + (size.width / 2) + let centerY = origin.y + (size.height / 2) + return Point(x: centerX, y: centerY) + } + set(newCenter) { + origin.x = newCenter.x - (size.width / 2) + origin.y = newCenter.y - (size.height / 2) + } + } +} +var square = Rect(origin: Point(x: 0.0, y: 0.0), + size: Size(width: 10.0, height: 10.0)) +let initialSquareCenter = square.center +square.center = Point(x: 15.0, y: 15.0) +print("square.origin is now at (\(square.origin.x), \(square.origin.y))") +// 输出 "square.origin is now at (10.0, 10.0)” +``` + +这个例子定义了 3 个结构体来描述几何形状: + +- `Point`封装了一个`(x, y)`的坐标 +- `Size`封装了一个`width`和一个`height` +- `Rect`表示一个有原点和尺寸的矩形 + +`Rect`也提供了一个名为`center`的计算属性。一个矩形的中心点可以从原点(`origin`)和尺寸(`size`)算出,所以不需要将它以显式声明的`Point`来保存。`Rect`的计算属性`center`提供了自定义的 getter 和 setter 来获取和设置矩形的中心点,就像它有一个存储属性一样。 + +上述例子中创建了一个名为`square`的`Rect`实例,初始值原点是`(0, 0)`,宽度高度都是`10`。如下图中蓝色正方形所示。 + +`square`的`center`属性可以通过点运算符(`square.center`)来访问,这会调用该属性的 getter 来获取它的值。跟直接返回已经存在的值不同,getter 实际上通过计算然后返回一个新的`Point`来表示`square`的中心点。如代码所示,它正确返回了中心点`(5, 5)`。 + +`center`属性之后被设置了一个新的值`(15, 15)`,表示向右上方移动正方形到如下图橙色正方形所示的位置。设置属性`center`的值会调用它的 setter 来修改属性`origin`的`x`和`y`的值,从而实现移动正方形到新的位置。 + +Computed Properties sample + + +### 便捷 setter 声明 + +如果计算属性的 setter 没有定义表示新值的参数名,则可以使用默认名称`newValue`。下面是使用了便捷 setter 声明的`Rect`结构体代码: + +```swift +struct AlternativeRect { + var origin = Point() + var size = Size() + var center: Point { + get { + let centerX = origin.x + (size.width / 2) + let centerY = origin.y + (size.height / 2) + return Point(x: centerX, y: centerY) + } + set { + origin.x = newValue.x - (size.width / 2) + origin.y = newValue.y - (size.height / 2) + } + } +} +``` + + +### 只读计算属性 + +只有 getter 没有 setter 的计算属性就是*只读计算属性*。只读计算属性总是返回一个值,可以通过点运算符访问,但不能设置新的值。 + +> 注意 +> 必须使用`var`关键字定义计算属性,包括只读计算属性,因为它们的值不是固定的。`let`关键字只用来声明常量属性,表示初始化后再也无法修改的值。 + +只读计算属性的声明可以去掉`get`关键字和花括号: + +```swift +struct Cuboid { + var width = 0.0, height = 0.0, depth = 0.0 + var volume: Double { + return width * height * depth + } +} +let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0) +print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)") +// 输出 "the volume of fourByFiveByTwo is 40.0" +``` + +这个例子定义了一个名为`Cuboid`的结构体,表示三维空间的立方体,包含`width`、`height`和`depth`属性。结构体还有一个名为`volume`的只读计算属性用来返回立方体的体积。为`volume`提供 setter 毫无意义,因为无法确定如何修改`width`、`height`和`depth`三者的值来匹配新的`volume`。然而,`Cuboid`提供一个只读计算属性来让外部用户直接获取体积是很有用的。 + + +## 属性观察器 + +*属性观察器*监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,甚至新值和当前值相同的时候也不例外。 + +可以为除了延迟存储属性之外的其他存储属性添加属性观察器,也可以通过重写属性的方式为继承的属性(包括存储属性和计算属性)添加属性观察器。属性重写请参考[重写](./13_Inheritance.html#overriding)。 + +> 注意 +> 不需要为非重写的计算属性添加属性观察器,因为可以通过它的 setter 直接监控和响应值的变化。 + +可以为属性添加如下的一个或全部观察器: + +- `willSet`在新的值被设置之前调用 +- `didSet`在新的值被设置之后立即调用 + +`willSet`观察器会将新的属性值作为常量参数传入,在`willSet`的实现代码中可以为这个参数指定一个名称,如果不指定则参数仍然可用,这时使用默认名称`newValue`表示。 + +类似地,`didSet`观察器会将旧的属性值作为参数传入,可以为该参数命名或者使用默认参数名`oldValue`。 + +> 注意 +> 父类的属性在子类的构造器中被赋值时,它在父类中的`willSet`和`didSet`观察器会被调用。 +> 有关构造器代理的更多信息,请参考[值类型的构造器代理](./14_Initialization.html#initializer_delegation_for_value_types)和[类的构造器代理规则](./14_Initialization.html#initializer_delegation_for_class_types)。 + +这里是一个`willSet`和`didSet`的实际例子,其中定义了一个名为`StepCounter`的类,用来统计一个人步行时的总步数。这个类可以跟计步器或其他日常锻炼的统计装置的输入数据配合使用。 + +```swift +class StepCounter { + var totalSteps: Int = 0 { + willSet(newTotalSteps) { + print("About to set totalSteps to \(newTotalSteps)") + } + didSet { + if totalSteps > oldValue { + print("Added \(totalSteps - oldValue) steps") + } + } + } +} +let stepCounter = StepCounter() +stepCounter.totalSteps = 200 +// About to set totalSteps to 200 +// Added 200 steps +stepCounter.totalSteps = 360 +// About to set totalSteps to 360 +// Added 160 steps +stepCounter.totalSteps = 896 +// About to set totalSteps to 896 +// Added 536 steps +``` + +`StepCounter`类定义了一个`Int`类型的属性`totalSteps`,它是一个存储属性,包含`willSet`和`didSet`观察器。 + +当`totalSteps`被设置新值的时候,它的`willSet`和`didSet`观察器都会被调用,甚至新值和当前值完全相同时也会被调用。 + +例子中的`willSet`观察器将表示新值的参数自定义为`newTotalSteps`,这个观察器只是简单的将新的值输出。 + +`didSet`观察器在`totalSteps`的值改变后被调用,它把新值和旧值进行对比,如果总步数增加了,就输出一个消息表示增加了多少步。`didSet`没有为旧值提供自定义名称,所以默认值`oldValue`表示旧值的参数名。 + +>注意 +> +>如果将属性通过 in-out 方式传入函数,`willSet` 和 `didSet` 也会调用。这是因为 in-out 参数采用了拷入拷出模式:即在函数内部使用的是参数的 copy,函数结束后,又对参数重新赋值。关于 in-out 参数详细的介绍,请参考[输入输出参数](../chapter3/05_Declarations.html#in-out_parameters) + + +##全局变量和局部变量 + +计算属性和属性观察器所描述的功能也可以用于*全局变量*和*局部变量*。全局变量是在函数、方法、闭包或任何类型之外定义的变量。局部变量是在函数、方法或闭包内部定义的变量。 + +前面章节提到的全局或局部变量都属于存储型变量,跟存储属性类似,它为特定类型的值提供存储空间,并允许读取和写入。 + +另外,在全局或局部范围都可以定义计算型变量和为存储型变量定义观察器。计算型变量跟计算属性一样,返回一个计算结果而不是存储值,声明格式也完全一样。 + +> 注意 +> 全局的常量或变量都是延迟计算的,跟[延迟存储属性](#lazy_stored_properties)相似,不同的地方在于,全局的常量或变量不需要标记`lazy`修饰符。 +> 局部范围的常量或变量从不延迟计算。 + + +##类型属性 + +实例属性属于一个特定类型的实例,每创建一个实例,实例都拥有属于自己的一套属性值,实例之间的属性相互独立。 + +也可以为类型本身定义属性,无论创建了多少个该类型的实例,这些属性都只有唯一一份。这种属性就是*类型属性*。 + +类型属性用于定义某个类型所有实例共享的数据,比如所有实例都能用的一个常量(就像 C 语言中的静态常量),或者所有实例都能访问的一个变量(就像 C 语言中的静态变量)。 + +存储型类型属性可以是变量或常量,计算型类型属性跟实例的计算型属性一样只能定义成变量属性。 + +> 注意 +> 跟实例的存储型属性不同,必须给存储型类型属性指定默认值,因为类型本身没有构造器,也就无法在初始化过程中使用构造器给类型属性赋值。 +> 存储型类型属性是延迟初始化的,它们只有在第一次被访问的时候才会被初始化。即使它们被多个线程同时访问,系统也保证只会对其进行一次初始化,并且不需要对其使用`lazy`修饰符。 + + +###类型属性语法 + +在 C 或 Objective-C 中,与某个类型关联的静态常量和静态变量,是作为全局(*global*)静态变量定义的。但是在 Swift 中,类型属性是作为类型定义的一部分写在类型最外层的花括号内,因此它的作用范围也就在类型支持的范围内。 + +使用关键字`static`来定义类型属性。在为类定义计算型类型属性时,可以改用关键字`class`来支持子类对父类的实现进行重写。下面的例子演示了存储型和计算型类型属性的语法: + +```swift +struct SomeStructure { + static var storedTypeProperty = "Some value." + static var computedTypeProperty: Int { + return 1 + } +} +enum SomeEnumeration { + static var storedTypeProperty = "Some value." + static var computedTypeProperty: Int { + return 6 + } +} +class SomeClass { + static var storedTypeProperty = "Some value." + static var computedTypeProperty: Int { + return 27 + } + class var overrideableComputedTypeProperty: Int { + return 107 + } +} +``` + +> 注意 +> 例子中的计算型类型属性是只读的,但也可以定义可读可写的计算型类型属性,跟计算型实例属性的语法相同。 + + +###获取和设置类型属性的值 + +跟实例属性一样,类型属性也是通过点运算符来访问。但是,类型属性是通过类型本身来访问,而不是通过实例。比如: + +```swift +print(SomeStructure.storedTypeProperty) +// 输出 "Some value." +SomeStructure.storedTypeProperty = "Another value." +print(SomeStructure.storedTypeProperty) +// 输出 "Another value.” +print(SomeEnumeration.computedTypeProperty) +// 输出 "6" +print(SomeClass.computedTypeProperty) +// 输出 "27" +``` + +下面的例子定义了一个结构体,使用两个存储型类型属性来表示两个声道的音量,每个声道具有`0`到`10`之间的整数音量。 + +下图展示了如何把两个声道结合来模拟立体声的音量。当声道的音量是`0`,没有一个灯会亮;当声道的音量是`10`,所有灯点亮。本图中,左声道的音量是`9`,右声道的音量是`7`: + +Static Properties VUMeter + +上面所描述的声道模型使用`AudioChannel`结构体的实例来表示: + +```swift +struct AudioChannel { + static let thresholdLevel = 10 + static var maxInputLevelForAllChannels = 0 + var currentLevel: Int = 0 { + didSet { + if currentLevel > AudioChannel.thresholdLevel { + // 将当前音量限制在阀值之内 + currentLevel = AudioChannel.thresholdLevel + } + if currentLevel > AudioChannel.maxInputLevelForAllChannels { + // 存储当前音量作为新的最大输入音量 + AudioChannel.maxInputLevelForAllChannels = currentLevel + } + } + } +} +``` + +结构`AudioChannel`定义了 2 个存储型类型属性来实现上述功能。第一个是`thresholdLevel`,表示音量的最大上限阈值,它是一个值为`10`的常量,对所有实例都可见,如果音量高于`10`,则取最大上限值`10`(见后面描述)。 + +第二个类型属性是变量存储型属性`maxInputLevelForAllChannels`,它用来表示所有`AudioChannel`实例的最大音量,初始值是`0`。 + +`AudioChannel`也定义了一个名为`currentLevel`的存储型实例属性,表示当前声道现在的音量,取值为`0`到`10`。 + +属性`currentLevel`包含`didSet`属性观察器来检查每次设置后的属性值,它做如下两个检查: + +- 如果`currentLevel`的新值大于允许的阈值`thresholdLevel`,属性观察器将`currentLevel`的值限定为阈值`thresholdLevel`。 +- 如果修正后的`currentLevel`值大于静态类型属性`maxInputLevelForAllChannels`的值,属性观察器就将新值保存在`maxInputLevelForAllChannels`中。 + +> 注意 +> 在第一个检查过程中,`didSet`属性观察器将`currentLevel`设置成了不同的值,但这不会造成属性观察器被再次调用。 + +可以使用结构体`AudioChannel`创建两个声道`leftChannel`和`rightChannel`,用以表示立体声系统的音量: + +```swift +var leftChannel = AudioChannel() +var rightChannel = AudioChannel() +``` + +如果将左声道的`currentLevel`设置成`7`,类型属性`maxInputLevelForAllChannels`也会更新成`7`: + +```swift +leftChannel.currentLevel = 7 +print(leftChannel.currentLevel) +// 输出 "7" +print(AudioChannel.maxInputLevelForAllChannels) +// 输出 "7" +``` + +如果试图将右声道的`currentLevel`设置成`11`,它会被修正到最大值`10`,同时`maxInputLevelForAllChannels`的值也会更新到`10`: + +```swift +rightChannel.currentLevel = 11 +print(rightChannel.currentLevel) +// 输出 "10" +print(AudioChannel.maxInputLevelForAllChannels) +// 输出 "10" +``` \ No newline at end of file From de0d455b036abd3c5fc5054bdbeaf26f6912e444 Mon Sep 17 00:00:00 2001 From: TangJR Date: Sun, 10 Apr 2016 19:22:17 +0800 Subject: [PATCH 18/61] add space --- source/chapter2/10_Properties.md | 89 ++++++++++++++++---------------- 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/source/chapter2/10_Properties.md b/source/chapter2/10_Properties.md index 5bbd9e07..8cf8b558 100755 --- a/source/chapter2/10_Properties.md +++ b/source/chapter2/10_Properties.md @@ -14,6 +14,7 @@ > 翻译:[buginux](https://github.com/buginux) > 校对:[shanks](http://codebuild.me),2015-10-29 + > 2.2 > > 翻译:[saitjr](https://github.com/saitjr) @@ -36,11 +37,11 @@ ## 存储属性 -简单来说,一个存储属性就是存储在特定类或结构体的实例里的一个常量或变量。存储属性可以是*变量存储属性*(用关键字`var`定义),也可以是*常量存储属性*(用关键字`let`定义)。 +简单来说,一个存储属性就是存储在特定类或结构体的实例里的一个常量或变量。存储属性可以是*变量存储属性*(用关键字 `var` 定义),也可以是*常量存储属性*(用关键字 `let` 定义)。 可以在定义存储属性的时候指定默认值,请参考[默认构造器](./14_Initialization.html#default_initializers)一节。也可以在构造过程中设置或修改存储属性的值,甚至修改常量存储属性的值,请参考[构造过程中常量属性的修改](./14_Initialization.html#assigning_constant_properties_during_initialization)一节。 -下面的例子定义了一个名为`FixedLengthRange`的结构体,它描述了一个在创建后无法修改值域宽度的区间: +下面的例子定义了一个名为 `FixedLengthRange` 的结构体,它描述了一个在创建后无法修改值域宽度的区间: ```swift struct FixedLengthRange { @@ -53,7 +54,7 @@ rangeOfThreeItems.firstValue = 6 // 该区间现在表示整数6,7,8 ``` -`FixedLengthRange`的实例包含一个名为`firstValue`的变量存储属性和一个名为`length`的常量存储属性。在上面的例子中,`length`在创建实例的时候被初始化,因为它是一个常量存储属性,所以之后无法修改它的值。 +`FixedLengthRange` 的实例包含一个名为 `firstValue` 的变量存储属性和一个名为 `length` 的常量存储属性。在上面的例子中,`length` 在创建实例的时候被初始化,因为它是一个常量存储属性,所以之后无法修改它的值。 ### 常量结构体的存储属性 @@ -67,7 +68,7 @@ rangeOfFourItems.firstValue = 6 // 尽管 firstValue 是个变量属性,这里还是会报错 ``` -因为`rangeOfFourItems`被声明成了常量(用`let`关键字),即使`firstValue`是一个变量属性,也无法再修改它了。 +因为 `rangeOfFourItems` 被声明成了常量(用 `let` 关键字),即使 `firstValue` 是一个变量属性,也无法再修改它了。 这种行为是由于结构体(struct)属于*值类型*。当值类型的实例被声明为常量的时候,它的所有属性也就成了常量。 @@ -76,14 +77,14 @@ rangeOfFourItems.firstValue = 6 ### 延迟存储属性 -延迟存储属性是指当第一次被调用的时候才会计算其初始值的属性。在属性声明前使用`lazy`来标示一个延迟存储属性。 +延迟存储属性是指当第一次被调用的时候才会计算其初始值的属性。在属性声明前使用 `lazy` 来标示一个延迟存储属性。 > 注意 -> 必须将延迟存储属性声明成变量(使用`var`关键字),因为属性的初始值可能在实例构造完成之后才会得到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟属性。 +> 必须将延迟存储属性声明成变量(使用 `var` 关键字),因为属性的初始值可能在实例构造完成之后才会得到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟属性。 延迟属性很有用,当属性的值依赖于在实例的构造过程结束后才会知道具体值的外部因素时,或者当获得属性的初始值需要复杂或大量计算时,可以只在需要的时候计算它。 -下面的例子使用了延迟存储属性来避免复杂类中不必要的初始化。例子中定义了`DataImporter`和`DataManager`两个类,下面是部分代码: +下面的例子使用了延迟存储属性来避免复杂类中不必要的初始化。例子中定义了 `DataImporter`和`DataManager` 两个类,下面是部分代码: ```swift class DataImporter { @@ -107,13 +108,13 @@ manager.data.append("Some more data") // DataImporter 实例的 importer 属性还没有被创建 ``` -`DataManager`类包含一个名为`data`的存储属性,初始值是一个空的字符串(`String`)数组。这里没有给出全部代码,只需知道`DataManager`类的目的是管理和提供对这个字符串数组的访问即可。 +`DataManager` 类包含一个名为`data`的存储属性,初始值是一个空的字符串(`String`)数组。这里没有给出全部代码,只需知道 `DataManager` 类的目的是管理和提供对这个字符串数组的访问即可。 -`DataManager`的一个功能是从文件导入数据。该功能由`DataImporter`类提供,`DataImporter`完成初始化需要消耗不少时间:因为它的实例在初始化时可能要打开文件,还要读取文件内容到内存。 +`DataManager` 的一个功能是从文件导入数据。该功能由 `DataImporter` 类提供,`DataImporter` 完成初始化需要消耗不少时间:因为它的实例在初始化时可能要打开文件,还要读取文件内容到内存。 -`DataManager`管理数据时也可能不从文件中导入数据。所以当`DataManager`的实例被创建时,没必要创建一个`DataImporter`的实例,更明智的做法是第一次用到`DataImporter`的时候才去创建它。 +`DataManager` 管理数据时也可能不从文件中导入数据。所以当 `DataManager` 的实例被创建时,没必要创建一个 `DataImporter` 的实例,更明智的做法是第一次用到 `DataImporter` 的时候才去创建它。 -由于使用了`lazy`,`importer`属性只有在第一次被访问的时候才被创建。比如访问它的属性`fileName`时: +由于使用了 `lazy` ,`importer` 属性只有在第一次被访问的时候才被创建。比如访问它的属性 `fileName` 时: ```swift print(manager.importer.fileName) @@ -122,7 +123,7 @@ print(manager.importer.fileName) ``` > 注意 -> 如果一个被标记为`lazy`的属性在没有初始化时就同时被多个线程访问,则无法保证该属性只会被初始化一次。 +> 如果一个被标记为 `lazy` 的属性在没有初始化时就同时被多个线程访问,则无法保证该属性只会被初始化一次。 ### 存储属性和实例变量 @@ -172,20 +173,20 @@ print("square.origin is now at (\(square.origin.x), \(square.origin.y))") - `Size`封装了一个`width`和一个`height` - `Rect`表示一个有原点和尺寸的矩形 -`Rect`也提供了一个名为`center`的计算属性。一个矩形的中心点可以从原点(`origin`)和尺寸(`size`)算出,所以不需要将它以显式声明的`Point`来保存。`Rect`的计算属性`center`提供了自定义的 getter 和 setter 来获取和设置矩形的中心点,就像它有一个存储属性一样。 +`Rect`也提供了一个名为`center`的计算属性。一个矩形的中心点可以从原点(`origin`)和尺寸(`size`)算出,所以不需要将它以显式声明的`Point`来保存。`Rect` 的计算属性 `center` 提供了自定义的 getter 和 setter 来获取和设置矩形的中心点,就像它有一个存储属性一样。 -上述例子中创建了一个名为`square`的`Rect`实例,初始值原点是`(0, 0)`,宽度高度都是`10`。如下图中蓝色正方形所示。 +上述例子中创建了一个名为 `square` 的 `Rect` 实例,初始值原点是 `(0, 0)`,宽度高度都是 `10`。如下图中蓝色正方形所示。 -`square`的`center`属性可以通过点运算符(`square.center`)来访问,这会调用该属性的 getter 来获取它的值。跟直接返回已经存在的值不同,getter 实际上通过计算然后返回一个新的`Point`来表示`square`的中心点。如代码所示,它正确返回了中心点`(5, 5)`。 +`square` 的 `center` 属性可以通过点运算符(`square.center`)来访问,这会调用该属性的 getter 来获取它的值。跟直接返回已经存在的值不同,getter 实际上通过计算然后返回一个新的 `Point` 来表示 `square` 的中心点。如代码所示,它正确返回了中心点 `(5, 5)`。 -`center`属性之后被设置了一个新的值`(15, 15)`,表示向右上方移动正方形到如下图橙色正方形所示的位置。设置属性`center`的值会调用它的 setter 来修改属性`origin`的`x`和`y`的值,从而实现移动正方形到新的位置。 +`center` 属性之后被设置了一个新的值 `(15, 15)`,表示向右上方移动正方形到如下图橙色正方形所示的位置。设置属性`center`的值会调用它的 setter 来修改属性 `origin` 的 `x` 和 `y` 的值,从而实现移动正方形到新的位置。 Computed Properties sample ### 便捷 setter 声明 -如果计算属性的 setter 没有定义表示新值的参数名,则可以使用默认名称`newValue`。下面是使用了便捷 setter 声明的`Rect`结构体代码: +如果计算属性的 setter 没有定义表示新值的参数名,则可以使用默认名称 `newValue`。下面是使用了便捷 setter 声明的 `Rect` 结构体代码: ```swift struct AlternativeRect { @@ -211,9 +212,9 @@ struct AlternativeRect { 只有 getter 没有 setter 的计算属性就是*只读计算属性*。只读计算属性总是返回一个值,可以通过点运算符访问,但不能设置新的值。 > 注意 -> 必须使用`var`关键字定义计算属性,包括只读计算属性,因为它们的值不是固定的。`let`关键字只用来声明常量属性,表示初始化后再也无法修改的值。 +> 必须使用 `var` 关键字定义计算属性,包括只读计算属性,因为它们的值不是固定的。`let` 关键字只用来声明常量属性,表示初始化后再也无法修改的值。 -只读计算属性的声明可以去掉`get`关键字和花括号: +只读计算属性的声明可以去掉 `get` 关键字和花括号: ```swift struct Cuboid { @@ -227,7 +228,7 @@ print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)") // 输出 "the volume of fourByFiveByTwo is 40.0" ``` -这个例子定义了一个名为`Cuboid`的结构体,表示三维空间的立方体,包含`width`、`height`和`depth`属性。结构体还有一个名为`volume`的只读计算属性用来返回立方体的体积。为`volume`提供 setter 毫无意义,因为无法确定如何修改`width`、`height`和`depth`三者的值来匹配新的`volume`。然而,`Cuboid`提供一个只读计算属性来让外部用户直接获取体积是很有用的。 +这个例子定义了一个名为 `Cuboid` 的结构体,表示三维空间的立方体,包含 `width`、`height` 和 `depth` 属性。结构体还有一个名为 `volume` 的只读计算属性用来返回立方体的体积。为 `volume` 提供 setter 毫无意义,因为无法确定如何修改 `width`、`height` 和 `depth` 三者的值来匹配新的 `volume`。然而,`Cuboid` 提供一个只读计算属性来让外部用户直接获取体积是很有用的。 ## 属性观察器 @@ -241,18 +242,18 @@ print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)") 可以为属性添加如下的一个或全部观察器: -- `willSet`在新的值被设置之前调用 -- `didSet`在新的值被设置之后立即调用 +- `willSet` 在新的值被设置之前调用 +- `didSet` 在新的值被设置之后立即调用 -`willSet`观察器会将新的属性值作为常量参数传入,在`willSet`的实现代码中可以为这个参数指定一个名称,如果不指定则参数仍然可用,这时使用默认名称`newValue`表示。 +`willSet` 观察器会将新的属性值作为常量参数传入,在 `willSet` 的实现代码中可以为这个参数指定一个名称,如果不指定则参数仍然可用,这时使用默认名称 `newValue` 表示。 -类似地,`didSet`观察器会将旧的属性值作为参数传入,可以为该参数命名或者使用默认参数名`oldValue`。 +类似地,`didSet` 观察器会将旧的属性值作为参数传入,可以为该参数命名或者使用默认参数名 `oldValue`。 > 注意 -> 父类的属性在子类的构造器中被赋值时,它在父类中的`willSet`和`didSet`观察器会被调用。 +> 父类的属性在子类的构造器中被赋值时,它在父类中的 `willSet` 和 `didSet` 观察器会被调用。 > 有关构造器代理的更多信息,请参考[值类型的构造器代理](./14_Initialization.html#initializer_delegation_for_value_types)和[类的构造器代理规则](./14_Initialization.html#initializer_delegation_for_class_types)。 -这里是一个`willSet`和`didSet`的实际例子,其中定义了一个名为`StepCounter`的类,用来统计一个人步行时的总步数。这个类可以跟计步器或其他日常锻炼的统计装置的输入数据配合使用。 +这里是一个 `willSet` 和 `didSet` 的实际例子,其中定义了一个名为 `StepCounter` 的类,用来统计一个人步行时的总步数。这个类可以跟计步器或其他日常锻炼的统计装置的输入数据配合使用。 ```swift class StepCounter { @@ -279,13 +280,13 @@ stepCounter.totalSteps = 896 // Added 536 steps ``` -`StepCounter`类定义了一个`Int`类型的属性`totalSteps`,它是一个存储属性,包含`willSet`和`didSet`观察器。 +`StepCounter` 类定义了一个`Int`类型的属性 `totalSteps`,它是一个存储属性,包含 `willSet` 和 `didSet` 观察器。 -当`totalSteps`被设置新值的时候,它的`willSet`和`didSet`观察器都会被调用,甚至新值和当前值完全相同时也会被调用。 +当 `totalSteps` 被设置新值的时候,它的 `willSet` 和 `didSet` 观察器都会被调用,甚至新值和当前值完全相同时也会被调用。 -例子中的`willSet`观察器将表示新值的参数自定义为`newTotalSteps`,这个观察器只是简单的将新的值输出。 +例子中的 `willSet` 观察器将表示新值的参数自定义为 `newTotalSteps`,这个观察器只是简单的将新的值输出。 -`didSet`观察器在`totalSteps`的值改变后被调用,它把新值和旧值进行对比,如果总步数增加了,就输出一个消息表示增加了多少步。`didSet`没有为旧值提供自定义名称,所以默认值`oldValue`表示旧值的参数名。 +`didSet` 观察器在 `totalSteps` 的值改变后被调用,它把新值和旧值进行对比,如果总步数增加了,就输出一个消息表示增加了多少步。`didSet` 没有为旧值提供自定义名称,所以默认值 `oldValue` 表示旧值的参数名。 >注意 > @@ -324,7 +325,7 @@ stepCounter.totalSteps = 896 在 C 或 Objective-C 中,与某个类型关联的静态常量和静态变量,是作为全局(*global*)静态变量定义的。但是在 Swift 中,类型属性是作为类型定义的一部分写在类型最外层的花括号内,因此它的作用范围也就在类型支持的范围内。 -使用关键字`static`来定义类型属性。在为类定义计算型类型属性时,可以改用关键字`class`来支持子类对父类的实现进行重写。下面的例子演示了存储型和计算型类型属性的语法: +使用关键字 `static` 来定义类型属性。在为类定义计算型类型属性时,可以改用关键字 `class` 来支持子类对父类的实现进行重写。下面的例子演示了存储型和计算型类型属性的语法: ```swift struct SomeStructure { @@ -370,13 +371,13 @@ print(SomeClass.computedTypeProperty) // 输出 "27" ``` -下面的例子定义了一个结构体,使用两个存储型类型属性来表示两个声道的音量,每个声道具有`0`到`10`之间的整数音量。 +下面的例子定义了一个结构体,使用两个存储型类型属性来表示两个声道的音量,每个声道具有 `0` 到 `10` 之间的整数音量。 -下图展示了如何把两个声道结合来模拟立体声的音量。当声道的音量是`0`,没有一个灯会亮;当声道的音量是`10`,所有灯点亮。本图中,左声道的音量是`9`,右声道的音量是`7`: +下图展示了如何把两个声道结合来模拟立体声的音量。当声道的音量是 `0`,没有一个灯会亮;当声道的音量是 `10`,所有灯点亮。本图中,左声道的音量是 `9`,右声道的音量是 `7`: Static Properties VUMeter -上面所描述的声道模型使用`AudioChannel`结构体的实例来表示: +上面所描述的声道模型使用 `AudioChannel` 结构体的实例来表示: ```swift struct AudioChannel { @@ -397,28 +398,28 @@ struct AudioChannel { } ``` -结构`AudioChannel`定义了 2 个存储型类型属性来实现上述功能。第一个是`thresholdLevel`,表示音量的最大上限阈值,它是一个值为`10`的常量,对所有实例都可见,如果音量高于`10`,则取最大上限值`10`(见后面描述)。 +结构 `AudioChannel` 定义了 2 个存储型类型属性来实现上述功能。第一个是 `thresholdLevel`,表示音量的最大上限阈值,它是一个值为 `10` 的常量,对所有实例都可见,如果音量高于 `10`,则取最大上限值 `10`(见后面描述)。 -第二个类型属性是变量存储型属性`maxInputLevelForAllChannels`,它用来表示所有`AudioChannel`实例的最大音量,初始值是`0`。 +第二个类型属性是变量存储型属性 `maxInputLevelForAllChannels`,它用来表示所有 `AudioChannel` 实例的最大音量,初始值是`0`。 -`AudioChannel`也定义了一个名为`currentLevel`的存储型实例属性,表示当前声道现在的音量,取值为`0`到`10`。 +`AudioChannel` 也定义了一个名为 `currentLevel` 的存储型实例属性,表示当前声道现在的音量,取值为 `0` 到 `10`。 -属性`currentLevel`包含`didSet`属性观察器来检查每次设置后的属性值,它做如下两个检查: +属性 `currentLevel` 包含 `didSet` 属性观察器来检查每次设置后的属性值,它做如下两个检查: -- 如果`currentLevel`的新值大于允许的阈值`thresholdLevel`,属性观察器将`currentLevel`的值限定为阈值`thresholdLevel`。 -- 如果修正后的`currentLevel`值大于静态类型属性`maxInputLevelForAllChannels`的值,属性观察器就将新值保存在`maxInputLevelForAllChannels`中。 +- 如果 `currentLevel` 的新值大于允许的阈值 `thresholdLevel`,属性观察器将 `currentLevel` 的值限定为阈值 `thresholdLevel`。 +- 如果修正后的 `currentLevel` 值大于静态类型属性 `maxInputLevelForAllChannels` 的值,属性观察器就将新值保存在 `maxInputLevelForAllChannels` 中。 > 注意 -> 在第一个检查过程中,`didSet`属性观察器将`currentLevel`设置成了不同的值,但这不会造成属性观察器被再次调用。 +> 在第一个检查过程中,`didSet` 属性观察器将 `currentLevel` 设置成了不同的值,但这不会造成属性观察器被再次调用。 -可以使用结构体`AudioChannel`创建两个声道`leftChannel`和`rightChannel`,用以表示立体声系统的音量: +可以使用结构体 `AudioChannel` 创建两个声道 `leftChannel` 和 `rightChannel`,用以表示立体声系统的音量: ```swift var leftChannel = AudioChannel() var rightChannel = AudioChannel() ``` -如果将左声道的`currentLevel`设置成`7`,类型属性`maxInputLevelForAllChannels`也会更新成`7`: +如果将左声道的 `currentLevel` 设置成 `7`,类型属性 `maxInputLevelForAllChannels` 也会更新成 `7`: ```swift leftChannel.currentLevel = 7 @@ -428,7 +429,7 @@ print(AudioChannel.maxInputLevelForAllChannels) // 输出 "7" ``` -如果试图将右声道的`currentLevel`设置成`11`,它会被修正到最大值`10`,同时`maxInputLevelForAllChannels`的值也会更新到`10`: +如果试图将右声道的 `currentLevel` 设置成 `11`,它会被修正到最大值 `10`,同时 `maxInputLevelForAllChannels` 的值也会更新到 `10`: ```swift rightChannel.currentLevel = 11 From b883da0fc296d910d98ecaf9f26d10e05eae8b44 Mon Sep 17 00:00:00 2001 From: TangJR Date: Sun, 10 Apr 2016 20:07:24 +0800 Subject: [PATCH 19/61] proofreading --- source/chapter2/10_Properties.md | 34 +++++++++++++++----------------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/source/chapter2/10_Properties.md b/source/chapter2/10_Properties.md index 8cf8b558..e23df6b8 100755 --- a/source/chapter2/10_Properties.md +++ b/source/chapter2/10_Properties.md @@ -37,11 +37,11 @@ ## 存储属性 -简单来说,一个存储属性就是存储在特定类或结构体的实例里的一个常量或变量。存储属性可以是*变量存储属性*(用关键字 `var` 定义),也可以是*常量存储属性*(用关键字 `let` 定义)。 +简单来说,一个存储属性就是存储在特定类或结构体实例里的一个常量或变量。存储属性可以是*变量存储属性*(用关键字 `var` 定义),也可以是*常量存储属性*(用关键字 `let` 定义)。 可以在定义存储属性的时候指定默认值,请参考[默认构造器](./14_Initialization.html#default_initializers)一节。也可以在构造过程中设置或修改存储属性的值,甚至修改常量存储属性的值,请参考[构造过程中常量属性的修改](./14_Initialization.html#assigning_constant_properties_during_initialization)一节。 -下面的例子定义了一个名为 `FixedLengthRange` 的结构体,它描述了一个在创建后无法修改值域宽度的区间: +下面的例子定义了一个名为 `FixedLengthRange` 的结构体,它描述了一个用于表示整型范围的常量,在创建后就不能进行修改: ```swift struct FixedLengthRange { @@ -59,7 +59,7 @@ rangeOfThreeItems.firstValue = 6 ### 常量结构体的存储属性 -如果创建了一个结构体的实例并将其赋值给一个常量,则无法修改该实例的任何属性,即使定义了变量存储属性: +如果创建了一个结构体的实例并将其赋值给一个常量,则无法修改该实例的任何属性,即使有属性被声明为变量也不行: ```swift let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4) @@ -82,9 +82,9 @@ rangeOfFourItems.firstValue = 6 > 注意 > 必须将延迟存储属性声明成变量(使用 `var` 关键字),因为属性的初始值可能在实例构造完成之后才会得到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟属性。 -延迟属性很有用,当属性的值依赖于在实例的构造过程结束后才会知道具体值的外部因素时,或者当获得属性的初始值需要复杂或大量计算时,可以只在需要的时候计算它。 +延迟属性很有用,当属性的值依赖于在实例的构造过程结束后才会知道影响值的外部因素时,或者当获得属性的初始值需要复杂或大量计算时,可以只在需要的时候计算它。 -下面的例子使用了延迟存储属性来避免复杂类中不必要的初始化。例子中定义了 `DataImporter`和`DataManager` 两个类,下面是部分代码: +下面的例子使用了延迟存储属性来避免复杂类中不必要的初始化。例子中定义了 `DataImporter` 和 `DataManager` 两个类,下面是部分代码: ```swift class DataImporter { @@ -108,7 +108,7 @@ manager.data.append("Some more data") // DataImporter 实例的 importer 属性还没有被创建 ``` -`DataManager` 类包含一个名为`data`的存储属性,初始值是一个空的字符串(`String`)数组。这里没有给出全部代码,只需知道 `DataManager` 类的目的是管理和提供对这个字符串数组的访问即可。 +`DataManager` 类包含一个名为 `data` 的存储属性,初始值是一个空的字符串(`String`)数组。这里没有给出全部代码,只需知道 `DataManager` 类的目的是管理和提供对这个字符串数组的访问即可。 `DataManager` 的一个功能是从文件导入数据。该功能由 `DataImporter` 类提供,`DataImporter` 完成初始化需要消耗不少时间:因为它的实例在初始化时可能要打开文件,还要读取文件内容到内存。 @@ -169,11 +169,11 @@ print("square.origin is now at (\(square.origin.x), \(square.origin.y))") 这个例子定义了 3 个结构体来描述几何形状: -- `Point`封装了一个`(x, y)`的坐标 -- `Size`封装了一个`width`和一个`height` -- `Rect`表示一个有原点和尺寸的矩形 +- `Point` 封装了一个 `(x, y)` 的坐标 +- `Size` 封装了一个 `width` 和一个 `height` +- `Rect` 表示一个有原点和尺寸的矩形 -`Rect`也提供了一个名为`center`的计算属性。一个矩形的中心点可以从原点(`origin`)和尺寸(`size`)算出,所以不需要将它以显式声明的`Point`来保存。`Rect` 的计算属性 `center` 提供了自定义的 getter 和 setter 来获取和设置矩形的中心点,就像它有一个存储属性一样。 +`Rect`也提供了一个名为`center`的计算属性。一个矩形的中心点可以从原点(`origin`)和大小(`size`)算出,所以不需要将它以显式声明的 `Point` 来保存。`Rect` 的计算属性 `center` 提供了自定义的 getter 和 setter 来获取和设置矩形的中心点,就像它有一个存储属性一样。 上述例子中创建了一个名为 `square` 的 `Rect` 实例,初始值原点是 `(0, 0)`,宽度高度都是 `10`。如下图中蓝色正方形所示。 @@ -233,12 +233,10 @@ print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)") ## 属性观察器 -*属性观察器*监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,甚至新值和当前值相同的时候也不例外。 +*属性观察器*监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,即使新值和当前值相同的时候也不例外。 -可以为除了延迟存储属性之外的其他存储属性添加属性观察器,也可以通过重写属性的方式为继承的属性(包括存储属性和计算属性)添加属性观察器。属性重写请参考[重写](./13_Inheritance.html#overriding)。 +可以为除了延迟存储属性之外的其他存储属性添加属性观察器,也可以通过重写属性的方式为继承的属性(包括存储属性和计算属性)添加属性观察器。你不必为非重写的计算属性添加属性观察器,因为可以通过它的 setter 直接监控和响应值的变化。 属性重写请参考[重写](./13_Inheritance.html#overriding)。 -> 注意 -> 不需要为非重写的计算属性添加属性观察器,因为可以通过它的 setter 直接监控和响应值的变化。 可以为属性添加如下的一个或全部观察器: @@ -247,13 +245,13 @@ print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)") `willSet` 观察器会将新的属性值作为常量参数传入,在 `willSet` 的实现代码中可以为这个参数指定一个名称,如果不指定则参数仍然可用,这时使用默认名称 `newValue` 表示。 -类似地,`didSet` 观察器会将旧的属性值作为参数传入,可以为该参数命名或者使用默认参数名 `oldValue`。 +同样,`didSet` 观察器会将旧的属性值作为参数传入,可以为该参数命名或者使用默认参数名 `oldValue`。如果在 `didSet` 方法中再次对该属性赋值,那么新值会覆盖旧的值。 > 注意 -> 父类的属性在子类的构造器中被赋值时,它在父类中的 `willSet` 和 `didSet` 观察器会被调用。 +> 父类的属性在子类的构造器中被赋值时,它在父类中的 `willSet` 和 `didSet` 观察器会被调用,随后才会调用子类的观察器。在父类书初始化方法调用之前,子类给属性赋值时,观察器不会被调用。 > 有关构造器代理的更多信息,请参考[值类型的构造器代理](./14_Initialization.html#initializer_delegation_for_value_types)和[类的构造器代理规则](./14_Initialization.html#initializer_delegation_for_class_types)。 -这里是一个 `willSet` 和 `didSet` 的实际例子,其中定义了一个名为 `StepCounter` 的类,用来统计一个人步行时的总步数。这个类可以跟计步器或其他日常锻炼的统计装置的输入数据配合使用。 +下面是一个 `willSet` 和 `didSet` 实际运用的例子,其中定义了一个名为 `StepCounter` 的类,用来统计一个人步行时的总步数。这个类可以跟计步器或其他日常锻炼的统计装置的输入数据配合使用。 ```swift class StepCounter { @@ -282,7 +280,7 @@ stepCounter.totalSteps = 896 `StepCounter` 类定义了一个`Int`类型的属性 `totalSteps`,它是一个存储属性,包含 `willSet` 和 `didSet` 观察器。 -当 `totalSteps` 被设置新值的时候,它的 `willSet` 和 `didSet` 观察器都会被调用,甚至新值和当前值完全相同时也会被调用。 +当 `totalSteps` 被设置新值的时候,它的 `willSet` 和 `didSet` 观察器都会被调用,即使新值和当前值完全相同时也会被调用。 例子中的 `willSet` 观察器将表示新值的参数自定义为 `newTotalSteps`,这个观察器只是简单的将新的值输出。 From 66c6e1287a2b205c1450710afbe08ee1dc71dc63 Mon Sep 17 00:00:00 2001 From: TangJR Date: Mon, 11 Apr 2016 09:58:22 +0800 Subject: [PATCH 20/61] fix format error --- source/chapter2/10_Properties.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/chapter2/10_Properties.md b/source/chapter2/10_Properties.md index e23df6b8..93ae0b9c 100755 --- a/source/chapter2/10_Properties.md +++ b/source/chapter2/10_Properties.md @@ -16,10 +16,10 @@ > 2.2 -> > 翻译:[saitjr](https://github.com/saitjr) + 本页包含内容: - [存储属性(Stored Properties)](#stored_properties) @@ -173,7 +173,7 @@ print("square.origin is now at (\(square.origin.x), \(square.origin.y))") - `Size` 封装了一个 `width` 和一个 `height` - `Rect` 表示一个有原点和尺寸的矩形 -`Rect`也提供了一个名为`center`的计算属性。一个矩形的中心点可以从原点(`origin`)和大小(`size`)算出,所以不需要将它以显式声明的 `Point` 来保存。`Rect` 的计算属性 `center` 提供了自定义的 getter 和 setter 来获取和设置矩形的中心点,就像它有一个存储属性一样。 +`Rect`也提供了一个名为`center` 的计算属性。一个矩形的中心点可以从原点(`origin`)和大小(`size`)算出,所以不需要将它以显式声明的 `Point` 来保存。`Rect` 的计算属性 `center` 提供了自定义的 getter 和 setter 来获取和设置矩形的中心点,就像它有一个存储属性一样。 上述例子中创建了一个名为 `square` 的 `Rect` 实例,初始值原点是 `(0, 0)`,宽度高度都是 `10`。如下图中蓝色正方形所示。 @@ -278,7 +278,7 @@ stepCounter.totalSteps = 896 // Added 536 steps ``` -`StepCounter` 类定义了一个`Int`类型的属性 `totalSteps`,它是一个存储属性,包含 `willSet` 和 `didSet` 观察器。 +`StepCounter` 类定义了一个 `Int` 类型的属性 `totalSteps`,它是一个存储属性,包含 `willSet` 和 `didSet` 观察器。 当 `totalSteps` 被设置新值的时候,它的 `willSet` 和 `didSet` 观察器都会被调用,即使新值和当前值完全相同时也会被调用。 @@ -316,7 +316,7 @@ stepCounter.totalSteps = 896 > 注意 > 跟实例的存储型属性不同,必须给存储型类型属性指定默认值,因为类型本身没有构造器,也就无法在初始化过程中使用构造器给类型属性赋值。 -> 存储型类型属性是延迟初始化的,它们只有在第一次被访问的时候才会被初始化。即使它们被多个线程同时访问,系统也保证只会对其进行一次初始化,并且不需要对其使用`lazy`修饰符。 +> 存储型类型属性是延迟初始化的,它们只有在第一次被访问的时候才会被初始化。即使它们被多个线程同时访问,系统也保证只会对其进行一次初始化,并且不需要对其使用 `lazy` 修饰符。 ###类型属性语法 From 42f3319e7509fe0e1b9367307c0e233b24978d92 Mon Sep 17 00:00:00 2001 From: shanks Date: Mon, 11 Apr 2016 15:51:03 +0800 Subject: [PATCH 21/61] update 2.2 history log date --- source/chapter1/02_a_swift_tour.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/chapter1/02_a_swift_tour.md b/source/chapter1/02_a_swift_tour.md index 547061f6..f244fe65 100755 --- a/source/chapter1/02_a_swift_tour.md +++ b/source/chapter1/02_a_swift_tour.md @@ -10,7 +10,7 @@ > 翻译+校对:[xtymichael](https://github.com/xtymichael) > 2.2 -> 翻译:[175](https://github.com/Brian175) +> 翻译:[175](https://github.com/Brian175),2016-04-09 本页内容包括: From 8459508d677ddebb5fb2a8d64d1e8d8a33158cb4 Mon Sep 17 00:00:00 2001 From: shanks Date: Mon, 11 Apr 2016 15:52:52 +0800 Subject: [PATCH 22/61] update 2.2 history log date --- source/chapter2/10_Properties.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/chapter2/10_Properties.md b/source/chapter2/10_Properties.md index 93ae0b9c..4ca72288 100755 --- a/source/chapter2/10_Properties.md +++ b/source/chapter2/10_Properties.md @@ -16,7 +16,7 @@ > 2.2 -> 翻译:[saitjr](https://github.com/saitjr) +> 翻译:[saitjr](https://github.com/saitjr),2016-04-11 @@ -435,4 +435,4 @@ print(rightChannel.currentLevel) // 输出 "10" print(AudioChannel.maxInputLevelForAllChannels) // 输出 "10" -``` \ No newline at end of file +``` From f270792d5901361297424022e72a960b9050eab2 Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 15 Apr 2016 19:46:47 +0800 Subject: [PATCH 23/61] =?UTF-8?q?=E6=94=B9=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 类型方法代码写成static func 原文为 class func --- source/chapter2/11_Methods.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/chapter2/11_Methods.md b/source/chapter2/11_Methods.md index 1f775c78..f2574ad3 100755 --- a/source/chapter2/11_Methods.md +++ b/source/chapter2/11_Methods.md @@ -224,7 +224,7 @@ ovenLight.next() ```swift class SomeClass { - static func someTypeMethod() { + class func someTypeMethod() { // type method implementation goes here } } From 88befb0b59d28e3670fe91935c66d722875dcc30 Mon Sep 17 00:00:00 2001 From: numbbbbb Date: Wed, 20 Apr 2016 12:52:01 +0800 Subject: [PATCH 24/61] Update README.md --- source/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/README.md b/source/README.md index 25134dc4..d1cf2e96 100755 --- a/source/README.md +++ b/source/README.md @@ -1,4 +1,4 @@ -> 已同步更新到 Swift 2.1 +> 已同步更新到 Swift 2.2 # 2.0 新的开始 @@ -73,4 +73,4 @@ Swift 2.0 参与者名单(按照章节顺序): - [miaosiqi](https://github.com/miaosiqi) - [Realank](https://github.com/Realank) -最后,感谢[极客学院](http://wiki.jikexueyuan.com)提供的wiki系统,在国内访问起来速度很快,优化后的样式看起来也更舒服。 \ No newline at end of file +最后,感谢[极客学院](http://wiki.jikexueyuan.com)提供的wiki系统,在国内访问起来速度很快,优化后的样式看起来也更舒服。 From e04533d2a80e649ddd2e59a3725252ce2a705ddc Mon Sep 17 00:00:00 2001 From: semper_idem Date: Thu, 21 Apr 2016 13:22:21 +0800 Subject: [PATCH 25/61] Declarations -> 2.2 --- source/chapter3/05_Declarations.md | 2557 ++++++++++++++-------------- 1 file changed, 1260 insertions(+), 1297 deletions(-) diff --git a/source/chapter3/05_Declarations.md b/source/chapter3/05_Declarations.md index f43a0156..f674da30 100755 --- a/source/chapter3/05_Declarations.md +++ b/source/chapter3/05_Declarations.md @@ -1,1297 +1,1260 @@ - -# 声明(Declarations) ------------------ - -> 1.0 -> 翻译:[marsprince](https://github.com/marsprince) [Lenhoon](https://github.com/marsprince)[(微博)](http://www.weibo.com/lenhoon) -> 校对:[numbbbbb](https://github.com/numbbbbb), [stanzhai](https://github.com/stanzhai) - -> 2.0 -> 翻译+校对:[Lenhoon](https://github.com/Lenhoon), -> [BridgeQ](https://github.com/WXGBridgeQ) - -> 2.1 -> 翻译:[mmoaay](https://github.com/mmoaay), [shanks](http://codebuild.me) -> 校对:[shanks](http://codebuild.me) - -本页包含内容: - -- [顶级代码](#top-level_code) -- [代码块](#code_blocks) -- [导入声明](#import_declaration) -- [常量声明](#constant_declaration) -- [变量声明](#variable_declaration) - - [存储型变量和存储型变量属性](#stored_variables_and_stored_variable_properties) - - [计算型变量和计算型属性](#computed_variables_and_computed_properties) - - [存储型变量观察器和属性观察器](#stored_variable_observers_and_property_observers) - - [类型变量属性](#type_variable_properties) -- [类型别名声明](#type_alias_declaration) -- [函数声明](#function_declaration) - - [参数名](#parameter_names) - - [输入输出参数](#in-out_parameters) - - [特殊参数](#special_kinds_of_parameters) - - [特殊方法](#special_kinds_of_methods) - - [柯里化函数](#curried_functions) - - [抛出错误的函数和方法](#throwing_functions_and_methods) - - [重新抛出错误的函数和方法](#rethrowing_functions_and_methods) -- [枚举声明](#enumeration_declaration) - - [包含任意类型用例的枚举](#enumerations_with_cases_of_any_type) - - [递归枚举](#enumerations_with_indirection) - - [包含原始值类型的枚举](#enumerations_with_cases_of_a_raw-value_type) - - [访问枚举用例](#accessing_enumeration_cases) -- [结构体声明](#structure_declaration) -- [类声明](#class_declaration) -- [协议声明](#protocol_declaration) - - [协议属性声明](#protocol_property_declaration) - - [协议方法声明](#protocol_method_declaration) - - [协议构造器声明](#protocol_initializer_declaration) - - [协议下标声明](#protocol_subscript_declaration) - - [协议关联类型声明](#protocol_associated_type_declaration) -- [构造器声明](#initializer_declaration) - - [可失败构造器](#failable_initializers) -- [析构器声明](#deinitializer_declaration) -- [扩展声明](#extension_declaration) -- [下标声明](#subscript_declaration) -- [运算符声明](#operator_declaration) -- [声明修饰符](#declaration_modifiers) - - [访问控制级别](#access_control_levels) - -声明可以在程序里引入新的名字或者构造。举例来说,可以使用声明来引入函数和方法,变量和常量,或者来定义新的命名的枚举,结构,类和协议类型。还可以使用声明来扩展一个已经存在的命名的类型的行为,或者在程序里引入在其它地方声明的符号。 - -在 Swift 中,大多数声明在某种意义上讲也是定义,实现或初始化往往伴随着声明。这意味着,由于协议往往并不提供实现,大多数协议仅仅只是声明而已。为了方便起见,也因为这些区别在 Swift 中不是很重要,“声明”这个术语同时包含了声明和定义。 - -> 声明语法 - -> *声明* → [*导入声明*](#import-declaration) -> *声明* → [*常量声明*](#constant-declaration) -> *声明* → [*变量声明*](#variable-declaration) -> *声明* → [*类型别名声明*](#typealias-declaration) -> *声明* → [*函数声明*](#function-declaration) -> *声明* → [*枚举声明*](#enum-declaration) -> *声明* → [*结构体声明*](#struct-declaration) -> *声明* → [*类声明*](#class-declaration) -> *声明* → [*协议声明*](#protocol-declaration) -> *声明* → [*构造器声明*](#initializer-declaration) -> *声明* → [*析构器声明*](#deinitializer-declaration) -> *声明* → [*扩展声明*](#extension-declaration) -> *声明* → [*下标声明*](#subscript-declaration) -> *声明* → [*运算符声明*](#operator-declaration) - -> *多条声明* → [*声明*](#declaration) [*多条声明*](#declarations)可选 - - -## 顶级代码 - -Swift 的源文件中的顶级代码由零个或多个语句,声明和表达式组成。默认情况下,在一个源文件的顶层声明的变量,常量和其他命名的声明语句可以被同一模块中的每一个源文件中的代码访问。可以使用一个访问级别修饰符来标记声明,从而覆盖默认行为,请参阅 [访问控制级别](#access_control_levels)。 - -> 顶级声明语法 -> *顶级声明* → [*多条语句*](10_Statements.md#statements)可选 - - -## 代码块 - -代码块可以将一些声明和控制结构组织在一起。它有如下的形式: - -```swift -{ - 语句 -} -``` - -代码块中的语句包括声明,表达式和各种其他类型的语句,它们按照在源码中的出现顺序被依次执行。 - -> 代码块语法 - -> *代码块* → **{** [*多条语句*](10_Statements.md#statements)可选 **}** - - -## 导入声明 - -导入声明让你可以使用在其他文件中声明的内容。导入语句的基本形式是导入整个模块,它由 `import` 关键字开始,后面紧跟一个模块名: - -> import `模块` - -可以对导入提供更细致的控制,如指定一个特殊的子模块或者指定一个模块或子模块中的某个声明。提供了这些限制后,在当前作用域中,只有导入的符号是可用的,而不是整个模块。 - -> import `导入类型` `模块`.`符号名` -> import `模块`.`子模块` - - -> 导入声明语法 - -> *导入声明* → [*特性列表*](06_Attributes.md#attributes)可选 **import** [*导入类型*](#import-kind)可选 [*导入路径*](#import-path) - -> *导入类型* → **typealias** | **struct** | **class** | **enum** | **protocol** | **var** | **func** - -> *导入路径* → [*导入路径标识符*](#import-path-identifier) | [*导入路径标识符*](#import-path-identifier) **.** [*导入路径*](#import-path) - -> *导入路径标识符* → [*标识符*](02_Lexical_Structure.md#identifier) | [*运算符*](02_Lexical_Structure.md#operator) - - -## 常量声明 - -常量声明可以在程序中命名一个常量。常量以关键字 `let` 来声明,遵循如下的格式: - -> let `常量名称`: `类型` = `表达式` - -当常量的值被给定后,常量就将常量名称和表达式的值绑定在了一起,不能更改。 - -这意味着,如果常量以类对象来初始化,对象本身的内容是可以改变的,但是常量和该对象之间的结合关系是不能改变的。 - -当一个常量被声明为全局变量,它必须被给定一个初始值。在类或者结构体中声明一个常量时,它被认为是一个常量属性。常量声明不能是计算型属性,因此也没有存取方法。 - -如果常量名称是元组形式,元组中的每一项的名称会和对应的初始化表达式的值绑定。 - -```swift -let (firstNumber, secondNumber) = (10, 42) -``` - -在上例中,`firstNumber` 是一个值为 `10` 的常量,`secnodeName` 是一个值为 `42` 的常量。所有常量都可以独立的使用: - -```swift -print("The first number is \(firstNumber).") -// 打印 “The first number is 10.” -print("The second number is \(secondNumber).") -// 打印 “The second number is 42.” -``` - -当常量名称的类型可以被推断出时,类型标注在常量声明中是一个可选项,正如 [类型推断](03_Types.md#type_inference) 中所描述的。 - -声明一个常量类型属性要使用 `static` 声明修饰符。类型属性在 [类型属性](../chapter2/10_Properties.md#type_properties)中有介绍。 - -如果还想获得更多关于常量的信息或者想在使用中获得帮助,请参阅 [常量和变量](../chapter2/01_The_Basics.md#constants_and_variables) 和 [存储属性](../chapter2/10_Properties.md#stored_properties)。 - - -> 常量声明语法 - -> *常量声明* → [*特性列表*](06_Attributes.md#attributes)可选 [*声明修饰符列表*](#declaration-modifiers)可选 **let** [*模式构造器列表*](pattern-initializer-list) - -> *模式构造器列表* → [*模式构造器*](#pattern-initializer) | [*模式构造器*](#pattern-initializer) **,** [*模式构造器列表*](#pattern-initializer-list) - -> *模式构造器* → [*模式*](07_Patterns.md#pattern) [*构造器*](#initializer)可选 - -> *构造器* → **=** [*表达式*](04_Expressions.md#expression) - - -## 变量声明 - -变量声明可以在程序中声明一个变量,它以关键字 `var` 来声明。 - -变量声明有几种不同的形式,可以声明不同种类的命名值和可变值,如存储型和计算型变量和属性,属性观察器,以及静态变量属性。所使用的声明形式取决于变量声明的适用范围和打算声明的变量类型。 - -> 注意 -> 也可以在协议声明的上下文中声明属性,详情请参阅 [协议属性声明](#protocol_property_declaration)。 - -可以在子类中使用 `override` 声明修饰符来标记继承来的属性的声明,从而重写属性,详情请参阅 [重写](../chapter2/13_Inheritance.md#overriding)。 - - -### 存储型变量和存储型变量属性 - -使用如下形式声明一个存储型变量或存储型变量属性: - -> var `变量名称`: `类型` = `表达式` - -可以在全局范围,函数内部,或者在类和结构体的声明中使用这种形式来声明一个变量。当变量以这种形式在全局范围或者函数内部被声明时,它代表一个存储型变量。当它在类或者结构体中被声明时,它代表一个存储型变量属性。 - -用于初始化的表达式不可以在协议的声明中出现,在其他情况下,该表达式是可选的。如果没有初始化表达式,那么变量声明必须包含类型标注。 - -对于常量的声明,如果变量名称是一个元组,元组中每一项的名称都要和初始化表达式中的相应值绑定。 - -正如名字一样,存储型变量和存储型变量属性的值会存储在内存中。 - - -### 计算型变量和计算型属性 - -使用如下形式声明一个计算型变量或计算型属性: - -```swift -var 变量名称: 类型 { - get { - 语句 - } - set(setter 名称) { - 语句 - } -} -``` - -可以在全局范围,函数内部,以及类,结构体,枚举,扩展声明的上下文中使用这种形式的声明。当变量以这种形式在全局范围或者函数内部被声明时,它代表一个计算型变量。当它在类,结构体,枚举,扩展声明的上下文中被声明时,它代表一个计算型属性。 - -getter 用来读取变量值,setter 用来写入变量值。setter 子句是可选的,getter 子句是必须的。也可以将这些子句都省略,直接返回请求的值,正如在 [只读计算型属性](../chapter2/10_Properties.md#computed_properties) 中描述的那样。但是如果提供了一个 setter 子句,也必须提供一个 getter 子句。 - -圆括号内的 setter 的名称是可选的。如果提供了一个 setter 名称,它就会作为 setter 的参数名称使用。如果不提供 setter 名称,setter 的参数的默认名称为 `newValue`,正如在 [便捷 setter 声明](../chapter2/10_Properties.md#shorthand_setter_declaration) 中描述的那样。 - -与存储型变量和存储型属性不同,计算型变量和计算型属性的值不存储在内存中。 - -要获得更多关于计算型属性的信息和例子,请参阅 [计算型属性](../chapter2/10_Properties.md#computed_properties)。 - - -### 存储型变量观察器和属性观察器 - -可以在声明存储型变量或属性时提供 `willSet` 和 `didSet` 观察器。一个包含观察器的存储型变量或属性以如下形式声明: - -```swift -var 变量名称: 类型 = 表达式 { - willSet(setter 名称) { - 语句 - } - didSet(setter 名称) { - 语句 - } -} -``` - -可以在全局范围,函数内部,或者类,结构体声明的上下文中使用这种形式的声明。当变量以这种形式在全局范围或者函数内部被声明时,观察器代表一个存储型变量观察器。当它在类,结构体声明的上下文中被声明时,观察器代表一个属性观察器。 - -可以为任何存储型属性添加观察器。也可以通过重写父类属性的方式为任何继承的属性(无论是存储型还是计算型的)添加观察器 -,正如 [重写属性观察器](../chapter2/13_Inheritance.md#overriding_property_observers) 中所描述的。 - -用于初始化的表达式在一个类或者结构体的声明中是可选的,但是在其他地方是必须的。如果类型可以从初始化表达式中推断而来,那么这个类型标注是可选的。 - -当变量或属性的值被改变时,`willSet` 和 `didSet` 观察器提供了一种观察方法。观察器不会在变量或属性第一次初始化时被调用,它们仅当值在初始化环境之外被改变时才会被调用。 - -`willSet` 观察器只在变量或属性的值被改变之前调用。新的值作为一个常量传入 `willSet` 观察器,因此不可以在 `willSet` 中改变它。`didSet` 观察器在变量或属性的值被改变后立即调用。和 `willSet` 观察器相反,为了方便获取旧值,旧值会传入 `didSet` 观察器。这意味着,如果在变量或属性的 `didiset` 观察器中设置值,设置的新值会取代刚刚在 `willSet` 观察器中传入的那个值。 - -在 `willSet` 和 `didSet` 中,圆括号以及其中的 setter 名称是可选的。如果提供了一个 setter 名称,它就会作为 `willSet` 和 `didSet` 的参数被使用。如果不提供 setter 名称,`willSet` 观察器的默认参数名为 `newValue`,`didSet` 观察器的默认参数名为 `oldValue`。 - -提供了 `willSet` 时,`didSet` 是可选的。同样的,提供了 `didSet` 时,`willSet` 则是可选的。 - -要获得更多信息以及查看如何使用属性观察器的例子,请参阅 [属性观察器](../chapter2/10_Properties.md#property_observers)。 - - -### 类型变量属性 - -要声明一个类型变量属性,用 `static` 声明修饰符标记该声明。类可以改用 `class` 声明修饰符标记类的类型计算型属性从而允许子类重写超类的实现。类型属性在 [类型属性](../chapter2/10_Properties.md#type_properties) 章节有详细讨论。 - -> 注意 -> 在一个类声明中,使用关键字 `static` 与同时使用 `class` 和 `final` 去标记一个声明的效果相同。 - - -> 变量声明语法 - - -> *变量声明* → [*变量声明头*](#variable-declaration-head) [*模式构造器列表*](#pattern-initializer-list) -> *变量声明* → [*变量声明头*](#variable-declaration-head) [*变量名称*](#variable-name) [*类型标注*](03_Types.md#type-annotation) [*代码块*](#code-block) -> *变量声明* → [*变量声明头*](#variable-declaration-head) [*变量名称*](#variable-name) [*类型标注*](03_Types.md#type-annotation) [*getter-setter代码块*](#getter-setter-block) -> *变量声明* → [*变量声明头*](#variable-declaration-head) [*变量名称*](#variable-name) [*类型标注*](03_Types.md#type-annotation) [*getter-setter关键字代码块*](#getter-setter-keyword-block) -> *变量声明* → [*变量声明头*](#variable-declaration-head) [*变量名称*](#variable-name) [*构造器*](#initializer) [*willSet-didSet代码块*](#willSet-didSet-block) -> *变量声明* → [*变量声明头*](#variable-declaration-head) [*变量名称*](#variable-name) [*类型标注*](03_Types.md#type-annotation) [*构造器*](#initializer)可选 [*willSet-didSet代码块*](#willSet-didSet-block) - - -> *变量声明头* → [*特性列表*](06_Attributes.md#attributes)可选 [*声明修饰符列表*](#declaration-modifiers)可选 **var** - -> *变量名称* → [*标识符*](02_Lexical_Structure.md#identifier) - - -> *getter-setter 代码块* → [*代码块*](#code-block) -> *getter-setter 代码块* → **{** [*getter子句*](#getter-clause) [*setter子句*](#setter-clause)可选 **}** -> *getter-setter 代码块* → **{** [*setter子句*](#setter-clause) [*getter子句*](#getter-clause) **}** - -> *getter 子句* → [*特性列表*](06_Attributes.md#attributes)可选 **get** [*代码块*](#code-block) - -> *setter 子句* → [*特性列表*](06_Attributes.md#attributes)可选 **set** [*setter名称*](#setter-name)可选 [*代码块*](#code-block) - -> *setter 名称* → **(** [*标识符*](02_Lexical_Structure.md#identifier) **)** - - -> *getter-setter 关键字代码块* → **{** [*getter关键字子句*](#getter-keyword-clause) [*setter关键字子句*](#setter-keyword-clause)可选 **}** -> *getter-setter 关键字代码块* → **{** [*setter关键字子句*](#setter-keyword-clause) [*getter关键字子句*](#getter-keyword-clause) **}** - -> *getter 关键字子句* → [*特性列表*](06_Attributes.md#attributes)可选 **get** - -> *setter 关键字子句* → [*特性列表*](06_Attributes.md#attributes)可选 **set** - - -> *willSet-didSet 代码块* → **{** [*willSet子句*](#willSet-clause) [*didSet子句*](#didSet-clause)可选 **}** -> *willSet-didSet 代码块* → **{** [*didSet子句*](#didSet-clause) [*willSet子句*](#willSet-clause)可选 **}** - -> *willSet 子句* → [*特性列表*](06_Attributes.md#attributes)可选 **willSet** [*setter名称*](#setter-name)可选 [*代码块*](#code-block) - -> *didSet 子句* → [*特性列表*](06_Attributes.md#attributes)可选 **didSet** [*setter名称*](#setter-name)可选 [*代码块*](#code-block) - - -## 类型别名声明 - -类型别名声明可以在程序中为一个现存类型声明一个别名。类型别名声明语句使用关键字 `typealias` 声明,遵循如下的形式: - -> `类型别名` = `现存类型` - -当声明一个类型的别名后,可以在程序的任何地方使用别名来代替现存类型。现存类型可以是命名类型或者混合类型。类型别名不产生新的类型,它只是可以使用别名来引用现存类型。 - -另请参阅 [协议关联类型声明](#protocol_associated_type_declaration)。 - - -> 类型别名声明语法 - -> *类型别名声明* → [*类型别名头*](#typealias-head) [*类型别名赋值*](#typealias-assignment) - -> *类型别名头* → [*特性列表*](06_Attributes.md#attributes)可选 [*访问级别修饰符*](#access-level-modifier)可选 **typealias** [*类型别名名称*](#typealias-name) - -> *类型别名名称* → [*标识符*](02_Lexical_Structure.md#identifier) - -> *类型别名赋值* → **=** [*类型*](03_Types.md#type) - - -## 函数声明 - -使用函数声明在程序中引入新的函数或者方法。当一个函数被声明在类,结构体,枚举,或者协议的上下文中,会作为一个方法。函数声明使用关键字 `func`,遵循如下的形式: - -```swift -func 函数名称(参数列表) -> 返回类型 { - 语句 -} -``` - -如果函数返回 `Void` 类型,返回类型可以省略,如下所示: - -```swift -func 函数名称(参数列表) { - 语句 -} -``` - -每个参数的类型都要标明,它们不能被推断出来。虽然函数的参数默认是常量,也可以在参数名前添加 `let` 来强调这一行为。在参数名前面添加 `var` 则会使它们成为变量,作用域内任何对变量的改变只在函数体内有效,或者用 `inout` 使这些改变可以在调用域内生效。更多关于 `inout` 参数的讨论,请参阅 [输入输出参数](#in-out_parameters)。 - -函数可以使用元组类型作为返回类型来返回多个值。 - -函数定义可以出现在另一个函数声明内。这种函数被称作嵌套函数。更多关于嵌套函数的讨论,请参阅 [嵌套函数](../chapter2/06_Functions.md#Nested_Functions)。 - - -### 参数名 - -函数的参数列表由一个或多个函数参数组成,参数间以逗号分隔。函数调用时的参数顺序必须和函数声明时的参数顺序一致。最简单的参数列表有着如下的形式: - -> `参数名称`: `参数类型` - -一个参数有一个内部名称,这个内部名称可以在函数体内被使用。同样也可以作为外部名称,当调用函数时这个外部名称被作为实参的标签来使用。默认情况下,第一个参数的外部名称省略不写,第二个和之后的参数使用它们的内部名称作为它们的外部名称。例如: - -```swift -func f(x: Int, y: Int) -> Int { return x + y } -f(1, y: 2) // 参数 y 有标签,参数 x 则没有 -``` - -可以按照如下两种形式之一,重写参数名称的默认行为: - -> `外部参数名称` `内部参数名称`: `参数类型` -> _ `内部参数名称`: `参数类型` - -在内部参数名称前的名称赋予这个参数一个外部名称,这个名称可以和内部参数的名称不同。外部参数名称在函数被调用时必须被使用。对应的参数在方法或函数被调用时必须有外部名。 - -内部参数名称前的下划线(`_`)使该参数在函数被调用时没有名称。在函数或方法调用时,对应的参数必须没有名字。 - -```swift -func f(x x: Int, withY y: Int, _ z: Int) -> Int { return x + y + z } -f(x: 1, withY: 2, 3) // 参数 x 和 y 是有标签的,参数 z 则没有 -``` - - -### 输入输出参数 - -输入输出参数被传递时遵循如下规则: - -1. 函数调用时,参数的值被拷贝。 -2. 函数体内部,拷贝后的值被修改。 -3. 函数返回后,拷贝后的值被赋值给原参数。 - -这种行为被称为拷入拷出(copy-in copy-out)或值结果调用(call by value result)。例如,当一个计算型属性或者一个具有属性观察器的属性被用作函数的输入输出参数时,其 getter 会在函数调用时被调用,而其 setter 会在函数返回时被调用。 - -作为一种优化手段,当参数值存储在内存中的物理地址时,在函数体内部和外部均会使用同一内存位置。这种优化行为被称为引用调用(call by reference),它满足了拷入拷出模型的所有需求,而消除了复制带来的开销。不要依赖于拷入拷出与引用调用之间的行为差异。 - -不要使用传递给输入输出参数的值,即使原始值在当前作用域中依然可用。当函数返回时,你对原始值所做的更改会被拷贝的值所覆盖。不要依赖于引用调用的优化机制来试图阻止这种覆盖。 - -你不能将同一个值传递给多个输入输出参数,因为多个输入输出参数引发的拷贝与覆盖行为的顺序是不确定的,因此原始值的最终值也将无法确定。例如: - -```swift -var x = 10 -func f(inout a: Int, inout _ b: Int) { - a += 1 - b += 10 -} -f(&x, &x) // 编译报错 error: inout arguments are not allowed to alias each other -``` - -如果嵌套函数在外层函数返回后才调用,嵌套函数对输入输出参数造成的任何改变将不会影响到原始值。例如: - -```swift -func outer(inout a: Int) -> () -> Void { - func inner() { - a += 1 - } - return inner -} - -var x = 10 -let f = outer(&x) -f() -print(x) -// 打印 “10” -``` - -调用嵌套函数 `inner()` 对 `a` 递增后,`x` 的值并未发生改变,因为 `inner()` 在外层函数 `outer()` 返回后才被调用。若要改变 `x` 的值,必须在 `outer()` 返回前调用 `inner()`。 - -关于输入输出参数的详细讨论,请参阅 [输入输出参数](../chapter2/06_Functions.md#in_out_parameters)。 - - -### 特殊参数 - -参数可以被忽略,参数的数量可变,并且还可以提供默认值,使用形式如下: - -> _ : `参数类型` -> `参数名称`: `参数类型`... -> `参数名称`: `参数类型` = `默认参数值` - -以下划线(`_`)命名的参数是被显式忽略的,无法在函数体内使用。 - -一个参数的基础类型名称如果紧跟着三个点(`...`),会被视为可变参数。一个函数至多可以拥有一个可变参数,且必须是最后一个参数。可变参数会作为该参数类型的数组。举例来讲,可变参数 `Int...` 被看作 `[Int]`。关于使用可变参数的例子,请参阅 [可变参数](../chapter2/06_Functions.md#variadic_parameters)。 - -如果在参数类型后面有一个以等号(`=`)连接的表达式,该参数会拥有默认值,即给定表达式的值。当函数被调用时,给定的表达式会被求值。如果参数在函数调用时被省略了,就会使用其默认值。 - -```swift -func f(x: Int = 42) -> Int { return x } -f() // 有效,使用默认值 -f(7) // 有效,提供了值 -f(x: 7) // 无效,该参数没有外部名称 -``` - - -### 特殊方法 - -枚举或结构体的方法如果会修改 `self` 属性,必须以 `mutating` 声明修饰符标记。 - -子类重写超类中的方法必须以 `override` 声明修饰符标记。重写方法时不使用 `override` 修饰符,或者使用了 `override` 修饰符却并没有重写超类方法,都会产生一个编译时错误。 - -枚举或者结构体中的类型方法,要以 `static` 声明修饰符标记,而对于类中的类型方法,除了使用 `static`,也可使用 `class` 声明修饰符标记。 - - -### 柯里化函数 - -可以将一个带有多个参数的函数重写为一个等价的函数,这个重写后的函数只有一个参数并且返回一个函数。这个返回的函数接受下一个参数,并且返回另外一个函数。如此一直持续到再没有剩余的参数,最终返回的函数返回最初的多参函数要返回的原始值。这种重写的函数被称为柯里化函数。例如,可以为 `addTwoInts(a:b:)` 函数重写一个等价的 `addTwoIntsCurried(a:)(b:)` 函数: - -```swift -func addTwoInts(a a: Int, b: Int) -> Int { - return a + b -} - -func addTwoIntsCurried(a a: Int) -> (Int -> Int) { - func addTheOtherInt(b: Int) -> Int { - return a + b - } - return addTheOtherInt -} -``` - -`addTwoInts(a:b:)` 函数接受两个整型值并且返回它们的和。`addTwoIntsCurried(a:)(b:)` 函数接受一个整型值,并且返回另外一个接受第二个整型值的函数,该函数会将其参数和第一个整型值相加。(这个嵌套函数会从外层函数中捕获第一个整型值)。 - -在 Swift 中,可以通过以下语法非常简明地编写一个柯里化函数: - -```swift -func 函数名称(参数)(参数) -> 返回类型 { - 语句 -} -``` - -举例来说,下面的两个声明是等价的: - -```swift -func addTwoIntsCurried(a a: Int)(b: Int) -> Int { - return a + b -} - -func addTwoIntsCurried(a a: Int) -> (Int -> Int) { - func addTheOtherInt(b: Int) -> Int { - return a + b - } - return addTheOtherInt -} -``` - -为了像使用非柯里化函数一样的方式使用 `addTwoIntsCurried(a:)(b:)` 函数,必须用第一个整型参数调用 `addTwoIntsCurried(a:)(b:)`,紧接着用第二个整型参数调用其返回的函数: - -```swift -addTwoInts(a: 4, b: 5) -// 返回值为 9 -addTwoIntsCurried(a: 4)(b: 5) -// 返回值为 9 -``` - -调用一个非柯里化函数时必须一次性提供所有的参数,而使用函数的柯里化形式则可以分多次提供参数,每次调用提供一个(甚至可以在代码中的不同地方)。这被称为偏函数应用。例如,可以为 `addTwoIntsCurried(a:)(b:)` 函数提供整形参数 `1`,然后把返回的函数赋值给常量 `plusOne`: - -```swift -let plusOne = addTwoIntsCurried(a: 1) -// plusOne 是类型为 Int -> Int 的函数 -``` - -因为 `plusOne` 是函数 `addTwoIntsCurried(a:)(b:)` 的首个参数为 `1` 时返回的函数,所以可以调用 `plusOne` 并传入一个整型值使其和 `1` 相加。 - -```swift -plusOne(b: 10) -// 返回值为 11 -``` - - -### 抛出错误的函数和方法 - -可以抛出错误的函数或方法必须使用 `throws` 关键字标记。这类函数和方法被称为抛出函数和抛出方法。它们有着下面的形式: - -```swift -func 函数名称(参数列表) throws -> 返回类型 { - 语句 -} -``` - -抛出函数或抛出方法的调用必须包裹在 `try` 或者 `try!` 表达式中(也就是说,在作用域内使用 `try` 或者 `try!` 运算符)。 - -`throws` 关键字是函数的类型的一部分,非抛出函数是抛出函数的子类型。所以,可以在使用抛出函数的地方使用非抛出函数。对于柯里化函数,`throws` 关键字仅应用于最内层的函数。 - -不能仅基于函数能否抛出错误来进行函数重载。也就是说,可以基于函数的函数类型的参数能否抛出错误来进行函数重载。 - -抛出方法不能重写非抛出方法,而且抛出方法不能满足协议对于非抛出方法的要求。也就是说,非抛出方法可以重写抛出方法,而且非抛出方法可以满足协议对于抛出方法的要求。 - - -### 重新抛出错误的函数和方法 - -函数或方法可以使用 `rethrows` 关键字来声明,从而表明仅当该函数或方法的一个函数类型的参数抛出错误时,该函数或方法才抛出错误。这类函数和方法被称为重抛函数和重抛方法。重新抛出错误的函数或方法必须至少有一个参数的类型为抛出函数。 - -```swift -func functionWithCallback(callback: () throws -> Int) rethrows { - try callback() -} -``` - -抛出方法不能重写重抛方法,而且抛出方法不能满足协议对于重抛方法的要求。也就是说,重抛方法可以重写抛出方法,而且重抛方法可以满足协议对于抛出方法的要求。 - - -> 函数声明语法 - - -> *函数声明* → [*函数头*](#function-head) [*函数名*](#function-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)可选 [*函数签名*](#function-signature) [*函数体*](#function-body)可选 - - -> *函数头* → [*特性列表*](06_Attributes.md#attributes)可选 [*声明修饰符列表*](#declaration-modifiers)可选 **func** - -> *函数名* → [*标识符*](02_Lexical_Structure.md#identifier) | [*运算符*](02_Lexical_Structure.md#operator) - - -> *函数签名* → [*参数子句列表*](#parameter-clauses) **throws**可选 [*函数结果*](#function-result)可选 -> *函数签名* → [*参数子句列表*](#parameter-clauses) **rethrows** [*函数结果*](#function-result)可选 - -> *函数结果* → **->** [*特性列表*](06_Attributes.md#attributes)可选 [*类型*](03_Types.md#type) - -> *函数体* → [*代码块*](#code-block) - - -> *参数子句列表* → [*参数子句*](#parameter-clause) [*参数子句列表*](#parameter-clauses)可选 - -> *参数子句* → **(** **)** | **(** [*参数列表*](#parameter-list) **)** - -> *参数列表* → [*参数*](#parameter) | [*参数*](#parameter) **,** [*参数列表*](#parameter-list) - -> *参数* → **let**可选 [*外部参数名*](#external-parameter-name)可选 [*内部参数名*](#local-parameter-name) [*类型标注*](03_Types.md#type-annotation) [*默认参数子句*](#default-argument-clause)可选 -> *参数* → **var** [*外部参数名*](#external-parameter-name)可选 [*内部参数名*](#local-parameter-name) [*类型标注*](03_Types.md#type-annotation) [*默认参数子句*](#default-argument-clause)可选 -> *参数* → **inout** [*外部参数名*](#external-parameter-name)可选 [*内部参数名*](#local-parameter-name) [*类型标注*](03_Types.md#type-annotation) -> *参数* → [*外部参数名*](#external-parameter-name)可选 [*内部参数名*](#local-parameter-name) [*类型标注*](03_Types.md#type-annotation) **...** - -> *外部参数名* → [*标识符*](02_Lexical_Structure.md#identifier) | **_** - -> *内部参数名* → [*标识符*](02_Lexical_Structure.md#identifier) | **_** - -> *默认参数子句* → **=** [*表达式*](04_Expressions.md#expression) - - -## 枚举声明 - -在程序中使用枚举声明来引入一个枚举类型。 - -枚举声明有两种基本形式,使用关键字 `enum` 来声明。枚举声明体包含零个或多个值,称为枚举用例,还可包含任意数量的声明,包括计算型属性,实例方法,类型方法,构造器,类型别名,甚至其他枚举,结构体,和类。枚举声明不能包含析构器或者协议声明。 - -枚举类型可以采纳任意数量的协议,但是枚举不能从类,结构体和其他枚举继承。 - -不同于类或者结构体,枚举类型并不提供隐式的默认构造器,所有构造器必须显式声明。一个构造器可以委托给枚举中的其他构造器,但是构造过程仅当构造器将一个枚举用例指定给 `self` 后才算完成。 - -和结构体类似但是和类不同,枚举是值类型。枚举实例在被赋值到变量或常量时,或者传递给函数作为参数时会被复制。更多关于值类型的信息,请参阅 [结构体和枚举是值类型](../chapter2/09_Classes_and_Structures.md#structures_and_enumerations_are_value_types)。 - -可以扩展枚举类型,正如在 [扩展声明](#extension_declaration) 中讨论的一样。 - - -### 包含任意类型用例的枚举 - -如下的形式声明了一个包含任意类型枚举用例的枚举变量: - -```swift -enum 枚举名称: 采纳的协议 { - case 枚举用例1 - case 枚举用例2(关联值类型) -} -``` - -这种形式的枚举声明在其他语言中有时被叫做可识别联合。 - -在这种形式中,每个用例块由关键字 `case` 开始,后面紧接一个或多个以逗号分隔的枚举用例。每个用例名必须是独一无二的。每个用例也可以指定它所存储的指定类型的值,这些类型在关联值类型的元组中被指定,紧跟用例名之后。 - -具有关联值的枚举用例可以像函数一样使用,从而通过指定的关联值创建一个枚举实例。和真正的函数一样,你可以获取一个枚举用例的引用,然后在后续代码中调用它。 - -```swift -enum Number { - case Integer(Int) - case Real(Double) -} - -// f 的类型为 (Int) -> Number -let f = Number.Integer - -// 利用 f 把一个整数数组转成 Number 数组 -let evenInts: [Number] = [0, 2, 4, 6].map(f) -``` - -要获得更多关于具有关联值的枚举用例的信息和例子,请参阅 [关联值](../chapter2/08_Enumerations.md#associated_values)。 - - -### 递归枚举 - -枚举类型可以具有递归结构,就是说,枚举用例的关联值类型可以是枚举类型自身。然而,枚举类型的实例具有值语义,这意味着它们在内存中有着固定的位置。为了支持递归,编译器必须插入一个间接层。 - -要让某个枚举用例支持递归,使用 `indirect` 声明修饰符标记该用例。 - -```swift -enum Tree { - case Empty - indirect case Node(value: T, left: Tree, right:Tree) -} -``` - -要让一个枚举类型的所有用例都支持递归,使用 `indirect` 修饰符标记整个枚举类型,当枚举有多个用例且每个用例都需要使用 `indirect` 修饰符标记的时候这将非常便利。 - -被 `indirect` 修饰符标记的枚举用例必须有一个关联值。使用 `indirect` 修饰符标记的枚举类型可以既包含有关联值的用例,同时还可包含没有关联值的用例。但是,它不能再单独使用 `indirect` 修饰符来标记某个用例。 - - -### 包含原始值类型的枚举 - -以下形式声明了一种枚举类型,其中各个枚举用例的类型均为同一种基本类型: - -```swift -enum 枚举名称: 原始值类型, 采纳的协议 { - case 枚举用例1 = 原始值1 - case 枚举用例2 = 原始值2 -} -``` - -在这种形式中,每一个用例块由 `case` 关键字开始,后面紧跟一个或多个以逗号分隔的枚举用例。和第一种形式的枚举用例不同,这种形式的枚举用例包含一个基础值,叫做原始值,各个枚举用例的原始值的类型必须相同。这些原始值的类型通过原始值类型指定,必须表示一个整数,浮点数,字符串,或者字符。原始值类型必须符合 `Equatable` 协议和下列字面量转换协议中的一种:整型字面量需符合 `IntergerLiteralConvertible` 协议,浮点型字面量需符合 `FloatingPointLiteralConvertible` 协议,包含任意数量字符的字符串型字面量需符合 `StringLiteralConvertible` 协议,仅包含一个单一字符的字符串型字面量需符合 `ExtendedGraphemeClusterLiteralConvertible` 协议。每一个用例的名字和原始值必须唯一。 - -如果原始值类型被指定为 `Int`,则不必为用例显式地指定原始值,它们会隐式地被赋值 `0`,`1`,`2` 等。每个未被赋值的 `Int` 类型的用例会被隐式地赋值,其值为上一个用例的原始值加 `1`。 - -```Swift -enum ExampleEnum: Int { - case A, B, C = 5, D -} -``` - -在上面的例子中,`ExampleEnum.A` 的原始值是 `0`,`ExampleEnum.B` 的原始值是 `1`。因为 `ExampleEnum.C` 的原始值被显式地设定为 `5`,因此 `ExampleEnum.D` 的原始值会自动增长为 `6`。 - -如果原始值类型被指定为 `String` 类型,你不用明确地为用例指定原始值,每个没有指定原始值的用例会隐式地将用例名字作为原始值。 - -```swift -enum WeekendDay: String { - case Saturday, Sunday -} -``` - -在上面这个例子中,`WeekendDay.Saturday` 的原始值是 `"Saturday"`,`WeekendDay.Sunday` 的原始值是 `"Sunday"`。 - -枚举用例具有原始值的枚举类型隐式地符合定义在 Swift 标准库中的 `RawRepresentable` 协议。所以,它们拥有一个 `rawValue` 属性和一个可失败构造器 `init?(rawValue: RawValue)`。可以使用 `rawValue` 属性去获取枚举用例的原始值,例如 `ExampleEnum.B.rawValue`。还可以根据原始值来获取一个相对应的枚举用例,只需调用枚举的可失败构造器,例如 `ExampleEnum(rawValue: 5)`,这个可失败构造器返回一个可选类型的用例。要获得更多关于具有原始值的枚举用例的信息和例子,请参阅 [原始值](../chapter2/08_Enumerations.md#raw_values)。 - - -### 访问枚举用例 - -使用点语法(`.`)来引用枚举类型的枚举用例,例如 `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` 块中的枚举用例模式,正如 [枚举用例模式](07_Patterns.md#enumeration_case_pattern) 所述。 - - -> 枚举声明语法 - - -> *枚举声明* → [*特性列表*](06_Attributes.md#attributes)可选 [*访问级别修饰符*](#access-level-modifier)可选 [*联合风格枚举*](#union-style-enum) -> *枚举声明* → [*特性列表*](06_Attributes.md#attributes)可选 [*访问级别修饰符*](#access-level-modifier) 可选 [*原始值风格枚举*](#raw-value-style-enum) - - -> *联合风格枚举* → **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-member) [*多个联合风格枚举成员*](#union-style-enum-members)可选 - -> *联合风格枚举成员* → [*声明*](#declaration) | [*联合风格枚举用例子句*](#union-style-enum-case-clause) - -> *联合风格枚举用例子句* → [*特性列表*](06_Attributes.md#attributes)可选 **indirect**可选 **case** [*联合风格枚举用例列表*](#union-style-enum-case-list) - -> *联合风格枚举用例列表* → [*联合风格枚举用例*](#union-style-enum-case) | [*联合风格枚举用例*](#union-style-enum-case) **,** [*联合风格枚举用例列表*](#union-style-enum-case-list) - -> *联合风格枚举用例* → [*枚举用例名称*](#enum-case-name) [*元组类型*](03_Types.md#tuple-type)可选 - -> *枚举名称* → [*标识符*](02_Lexical_Structure.md#identifier) - -> *枚举用例名称* → [*标识符*](02_Lexical_Structure.md#identifier) - - -> *原始值风格枚举* → **enum** [*枚举名称*](#enum-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)可选 [类型继承子句](03_Types.md#type-inheritance-clause) **{** [*多个原始值风格枚举成员*](#raw-value-style-enum-members) **}** - -> *多个原始值风格枚举成员* → [*原始值风格枚举成员*](#raw-value-style-enum-member) [*多个原始值风格枚举成员*](#raw-value-style-enum-members)可选 - -> *原始值风格枚举成员* → [*声明*](#declaration) | [*原始值风格枚举用例子句*](#raw-value-style-enum-case-clause) - -> *原始值风格枚举用例子句* → [*特性列表*](06_Attributes.md#attributes)可选 **case** [*原始值风格枚举用例列表*](#raw-value-style-enum-case-list) - -> *原始值风格枚举用例列表* → [*原始值风格枚举用例*](#raw-value-style-enum-case) | [*原始值风格枚举用例*](#raw-value-style-enum-case) **,** [*原始值风格枚举用例列表*](#raw-value-style-enum-case-list) - -> *原始值风格枚举用例* → [*枚举用例名称*](#enum-case-name) [*原始值赋值*](#raw-value-assignment)可选 - -> *原始值赋值* → **=** [*原始值字面量*](#raw-value-literal) - -> *原始值字面量* → [数字型字面量](02_Lexical_Structure.md#numeric-literal) | [字符串型字面量](02_Lexical_Structure.md#static-string-literal) | [布尔型字面量](02_Lexical_Structure.md#boolean-literal) - - -## 结构体声明 - -使用结构体声明可以在程序中引入一个结构体类型。结构体声明使用 `struct` 关键字,遵循如下的形式: - -```swift -struct 结构体名称: 采纳的协议 { - 多条声明 -} -``` - -结构体内可包含零个或多个声明。这些声明可以包括存储型和计算型属性,类型属性,实例方法,类型方法,构造器,下标,类型别名,甚至其他结构体,类,和枚举声明。结构体声明不能包含析构器或者协议声明。关于结构体的详细讨论和示例,请参阅 [类和结构体](../chapter2/09_Classes_and_Structures.md)。 - -结构体可以采纳任意数量的协议,但是不能继承自类,枚举或者其他结构体。 - -有三种方法可以创建一个声明过的结构体实例: - -* 调用结构体内声明的构造器,正如 [构造器](../chapter2/14_Initialization.md#initializers) 所述。 - -* 如果没有声明构造器,调用结构体的成员逐一构造器,正如 [结构体类型的成员逐一构造器](../chapter2/14_Initialization.md#memberwise_initializers_for_structure_types) 所述。 - -* 如果没有声明构造器,而且结构体的所有属性都有初始值,调用结构体的默认构造器,正如 [默认构造器](../chapter2/14_Initialization.md#default_initializers) 所述。 - -结构体的构造过程请参阅 [构造过程](../chapter2/14_Initialization.md)。 - -结构体实例的属性可以用点语法(`.`)来访问,正如 [访问属性](../chapter2/09_Classes_and_Structures.md#accessing_properties) 所述。 - -结构体是值类型。结构体的实例在被赋予变量或常量,或传递给函数作为参数时会被复制。关于值类型的更多信息,请参阅 -[结构体和枚举是值类型](../chapter2/09_Classes_and_Structures.md#structures_and_enumerations_are_value_types)。 - -可以使用扩展声明来扩展结构体类型的行为,请参阅 [扩展声明](#extension_declaration)。 - - -> 结构体声明语法 - -> *结构体声明* → [*特性列表*](06_Attributes.md#attributes)可选 [*访问级别修饰符*](#access-level-modifier) 可选 **struct** [*结构体名称*](#struct-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)可选 [类型继承子句](03_Types.md#type-inheritance-clause)可选 [*结构体主体*](#struct-body) - -> *结构体名称* → [*标识符*](02_Lexical_Structure.md#identifier) - -> *结构体主体* → **{** [*多条声明*](#declarations)可选 **}** - - -## 类声明 - -可以在程序中使用类声明来引入一个类。类声明使用关键字 `class`,遵循如下的形式: - -```swift -class 类名: 超类, 采纳的协议 { - 多条声明 -} -``` - -类内可以包含零个或多个声明。这些声明可以包括存储型和计算型属性,实例方法,类型方法,构造器,析构器,下标,类型别名,甚至其他结构体,类,和枚举声明。类声明不能包含协议声明。关于类的详细讨论和示例,请参阅 [类和结构体](../chapter2/09_Classes_and_Structures.md)。 - -一个类只能继承自一个超类,但是可以采纳任意数量的协议。超类紧跟在类名和冒号后面,其后跟着采纳的协议。泛型类可以继承自其它泛型类和非泛型类,但是非泛型类只能继承自其它非泛型类。当在冒号后面写泛型超类的名称时,必须写上泛型类的全名,包括它的泛型形参子句。 - -正如 [构造器声明](#initializer_declaration) 所讨论的,类可以有指定构造器和便利构造器。类的指定构造器必须初始化类中声明的所有属性,并且必须在调用超类构造器之前。 - -类可以重写属性,方法,下标,以及构造器。重写的属性,方法,下标,和指定构造器必须以 `override` 声明修饰符标记。 - -为了要求子类去实现超类的构造器,使用 `required` 声明修饰符标记超类的构造器。子类实现超类构造器时也必须使用 `required` 声明修饰符。 - -虽然超类属性和方法声明可以被当前类继承,但是超类声明的指定构造器却不能。即便如此,如果当前类重写了超类的所有指定构造器,它就继承了超类的便利构造器。Swift 的类并不继承自一个通用基础类。 - -有两种方法来创建已声明的类的实例: - -* 调用类中声明的构造器,请参阅 [构造器](../chapter2/14_Initialization.md#initializers)。 - -* 如果没有声明构造器,而且类的所有属性都被赋予了初始值,调用类的默认构造器,请参阅 [默认构造器](../chapter2/14_Initialization.md#default_initializers)。 - -类实例属性可以用点语法(`.`)来访问,请参阅 [访问属性](../chapter2/09_Classes_and_Structures.md#accessing_properties)。 - -类是引用类型。当被赋予常量或变量,或者传递给函数作为参数时,类的实例会被引用,而不是被复制。关于引用类型的更多信息,请参阅 [结构体和枚举是值类型](../chapter2/09_Classes_and_Structures.md#structures_and_enumerations_are_value_types)。 - -可以使用扩展声明来扩展类的行为,请参阅 [扩展声明](#extension_declaration)。 - - -> 类声明语法 - -> *类声明* → [*特性列表*](06_Attributes.md#attributes)可选 [访问级别修饰符](#access-level-modifier)可选 **class** [*类名*](#class-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)可选 [*类型继承子句*](03_Types.md#type-inheritance-clause)可选 [*类主体*](#class-body) - -> *类名* → [*标识符*](02_Lexical_Structure.md#identifier) - -> *类主体* → **{** [*多条声明*](#declarations)可选 **}** - - -## 协议声明 - -协议声明可以为程序引入一个命名的协议类型。协议声明只能在全局区域使用 `protocol` 关键字来进行声明,并遵循如下形式: - -```swift -protocol 协议名称: 继承的协议 { - 协议成员声明 -} -``` - -协议的主体包含零个或多个协议成员声明,这些成员描述了任何采纳该协议的类型必须满足的一致性要求。一个协议可以声明采纳者必须实现的某些属性、方法、构造器以及下标。协议也可以声明特殊类型的类型别名,叫做关联类型,它可以指定协议的不同声明之间的关系。协议声明不能包括类,结构体,枚举或者其它协议的声明。协议成员声明会在后面进行讨论。 - -协议类型可以继承自任意数量的其它协议。当一个协议类型继承自其它协议的时候,来自其它协议的所有要求会聚合在一起,而且采纳当前协议的类型必须符合所有的这些要求。关于如何使用协议继承的例子,请参阅 [协议继承](../chapter2/22_Protocols.md#protocol_inheritance)。 - -> 注意 -> 也可以使用协议合成类型来集合多个协议的一致性要求,请参阅 [协议合成类型](03_Types.md#protocol_composition_type) 和 [协议合成](../chapter2/22_Protocols.md#protocol_composition)。 - -可以通过类型的扩展声明来采纳协议,从而为之前声明的类型添加协议一致性。在扩展中,必须实现所有采纳协议的要求。如果该类型已经实现了所有的要求,可以让这个扩展声明的主体留空。 - -默认地,符合某个协议的类型必须实现所有在协议中声明的属性、方法和下标。即便如此,可以用 `optional` 声明修饰符标注协议成员声明,以指定它们的实现是可选的。`optional` 修饰符仅仅可以用于使用 `objc` 特性标记过的协议。因此,仅仅类类型可以采用并符合包含可选成员要求的协议。更多关于如何使用 `optional` 声明修饰符的信息,以及如何访问可选协议成员的指导——例如不能确定采纳协议的类型是否实现了它们时——请参阅 [可选协议要求](../chapter2/22_Protocols.md#optional_protocol_requirements) - -为了限制协议只能被类类型采纳,需要使用 `class` 关键字来标记协议,将 `class` 关键在写在冒号后面的继承的协议列表的首位。例如,下面的协议只能被类类型采纳: - -```swift -protocol SomeProtocol: class { - /* 这里是协议成员 */ -} -``` - -任何继承自标记有 `class` 关键字的协议的协议也仅能被类类型采纳。 - -> 注意 -> 如果协议已经用 `objc` 特性标记了,`class` 要求就隐式地应用于该协议,无需显式使用 `class` 关键字。 - -协议类型是命名的类型,因此它们可以像其他命名类型一样使用,正如 [协议作为类型](../chapter2/22_Protocols.md#protocols_as_types) 所讨论的。然而,不能构造一个协议的实例,因为协议实际上不提供它们指定的要求的实现。 - -可以使用协议来声明作为代理的类或者结构体应该实现的方法,正如 [委托(代理)模式](../chapter2/22_Protocols.md#delegation) 中所述。 - - -> 协议声明语法 - - -> *协议声明* → [*特性列表*](06_Attributes.md#attributes)可选 [访问级别修饰符](#access-level-modifier)可选 **protocol** [*协议名称*](#protocol-name) [*类型继承子句*](03_Types.html#type-inheritance-clause)可选 [*协议主体*](#protocol-body) - -> *协议名称* → [*标识符*](02_Lexical_Structure.md#identifier) - -> *协议主体* → **{** [*协议成员声明列表*](#protocol-member-declarations)可选 **}** - - -> *协议成员声明* → [*协议属性声明*](#protocol-property-declaration) -> *协议成员声明* → [*协议方法声明*](#protocol-method-declaration) -> *协议成员声明* → [*协议构造器声明*](#protocol-initializer-declaration) -> *协议成员声明* → [*协议下标声明*](#protocol-subscript-declaration) -> *协议成员声明* → [*协议关联类型声明*](#protocol-associated-type-declaration) - -> *协议成员声明列表* → [*协议成员声明*](#protocol-member-declaration) [*协议成员声明列表*](#protocol-member-declarations)可选 - - -### 协议属性声明 - -协议可以通过在协议声明主体中引入一个协议属性声明,来声明符合的类型必须实现的属性。协议属性声明有一种特殊的变量声明形式: - -> var `属性名`: `类型` { get set } - -同其它协议成员声明一样,这些属性声明仅仅针对符合该协议的类型声明了 getter 和 setter 要求,你不能在协议中直接实现 getter 和 setter。 - -符合类型可以通过多种方式满足 getter 和 setter 要求。如果属性声明包含 `get` 和 `set` 关键字,符合类型就可以用可读写(实现了 getter 和 setter)的存储型变量属性或计算型属性来满足此要求,但是属性不能以常量属性或只读计算型属性实现。如果属性声明仅仅包含 `get` 关键字的话,它可以作为任意类型的属性被实现。关于如何实现协议中的属性要求的例子,请参阅 [属性要求](../chapter2/22_Protocols.md#property_requirements) - -另请参阅 [变量声明](#variable_declaration)。 - - -> 协议属性声明语法 - -> *协议属性声明* → [*变量声明头*](#variable-declaration-head) [*变量名称*](#variable-name) [*类型标注*](03_Types.md#type-annotation) [*getter-setter关键字代码块*](#getter-setter-keyword-block) - - -### 协议方法声明 - -协议可以通过在协议声明主体中引入一个协议方法声明,来声明符合的类型必须实现的方法。协议方法声明和函数方法声明有着相同的形式,但有两项例外:它们不包括函数体,也不能包含默认参数。关于如何实现协议中的方法要求的例子,请参阅 [方法要求](../chapter2/22_Protocols.md#method_requirements)。 - -使用 `static` 声明修饰符可以在协议声明中声明一个静态方法。结构体实现这些方法时使用 `static` 声明修饰符,类在实现这些方法时,除了使用 `static` 声明修饰符,还可以选择使用 `class` 声明修饰符。通过扩展实现时亦是如此。 - -另请参阅 [函数声明](#function_declaration)。 - - -> 协议方法声明语法 - -> *协议方法声明* → [*函数头*](#function-head) [*函数名*](#function-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)可选 [*函数签名*](#function-signature) - - -### 协议构造器声明 - -协议可以通过在协议声明主体中引入一个协议构造器声明,来声明符合的类型必须实现的构造器。协议构造器声明 -除了不包含实现主体外,和构造器声明有着相同的形式。 - -符合类型可以通过实现一个非可失败构造器或者 `init!` 可失败构造器来满足一个非可失败协议构造器的要求,可以通过实现任意类型的构造器来满足一个可失败协议构造器的要求。 - -类在实现一个构造器去满足一个协议的构造器要求时,如果这个类还没有用 `final` 声明修饰符标记,这个构造器必须用 `required` 声明修饰符标记。 - -另请参阅 [构造器声明](#initializer_declaration)。 - - -> 协议构造器声明语法 - -> *协议构造器声明* → [*构造器头*](#initializer-head) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)可选 [*参数子句*](#parameter-clause) **throws**可选 -> *协议构造器声明* → [*构造器头*](#initializer-head) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)可选 [*参数子句*](#parameter-clause) **rethrows** - - -### 协议下标声明 - -协议可以通过在协议声明主体中引入一个协议下标声明,来声明符合的类型必须实现的下标。协议下标声明有一个特殊的下标声明形式: - -> subscript (`参数列表`) -> `返回类型` { get set } - -下标声明只为符合类型声明了 getter 和 setter 要求。如果下标声明包含 `get` 和 `set` 关键字,符合类型也必须实现 getter 和 setter 子句。如果下标声明只包含 `get` 关键字,符合类型必须实现 getter 子句,可以选择是否实现 setter 子句。 - -另请参阅 [下标声明](#subscript_declaration)。 - - -> 协议下标声明语法 - -> *协议下标声明* → [*下标头*](#subscript-head) [*下标结果*](#subscript-result) [*getter-setter关键字代码块*](#getter-setter-keyword-block) - - -### 协议关联类型声明 - -使用关键字 `typealias` 来声明协议关联类型。关联类型为作为协议声明的一部分,为某种类型提供了一个别名。关联类型和泛型参数子句中的类型参数很相似,但是它们和 `Self` 一样,用于协议中。`Self` 指代采纳协议的类型。要获得更多信息和例子,请参阅 [关联类型](../chapter2/23_Generics.md#associated_types)。 - -另请参阅 [类型别名声明](#type_alias_declaration)。 - - -> 协议关联类型声明语法 - -> *协议关联类型声明* → [*类型别名头*](#typealias-head) [*类型继承子句*](03_Types.md#type-inheritance-clause)可选 [*类型别名赋值*](#typealias-assignment)可选 - - -## 构造器声明 - -构造器声明会为程序中的类,结构体或枚举引入构造器。构造器使用关键字 `init` 来声明,有两种基本形式。 - -结构体,枚举,类可以有任意数量的构造器,但是类的构造器具有不同的规则和行为。不同于结构体和枚举,类有两种构造器,即指定构造器和便利构造器,请参阅 [构造过程](../chapter2/14_Initialization.md)。 - -采用如下形式声明结构体和枚举的构造器,以及类的指定构造器: - -```swift -init(参数列表) { - 构造语句 -} -``` - -类的指定构造器直接将类的所有属性初始化。它不能调用类中的其他构造器,如果类有超类,它还必须调用超类的一个指定构造器。如果该类从它的超类继承了属性,必须在调用超类的指定构造器后才能修改这些属性。 - -指定构造器只能在类声明的上下文中声明,不能在扩展声明中声明。 - -结构体和枚举的构造器可以调用其他已声明的构造器,委托其他构造器来进行部分或者全部构造过程。 - -要为类声明一个便利构造器,用 `convenience` 声明修饰符来标记构造器声明: - -```swift -convenience init(参数列表) { - 构造语句 -} -``` - -便利构造器可以将构造过程委托给另一个便利构造器或一个指定构造器。但是,类的构造过程必须以一个将类中所有属性完全初始化的指定构造器的调用作为结束。便利构造器不能调用超类的构造器。 - -可以使用 `required` 声明修饰符,将便利构造器和指定构造器标记为每个子类都必须实现的构造器。这种构造器的子类实现也必须使用 `required` 声明修饰符标记。 - -默认情况下,超类中的构造器不会被子类继承。但是,如果子类的所有存储型属性都有默认值,而且子类自身没有定义任何构造器,它将继承超类的构造器。如果子类重写了超类的所有指定构造器,子类将继承超类的所有便利构造器。 - -和方法,属性和下标一样,需要使用 `override` 声明修饰符标记重写的指定构造器。 - -> 注意 -> 如果使用 `required` 声明修饰符标记一个构造器,在子类中重写这种构造器时,无需使用 `override` 修饰符。 - -就像函数和方法,构造器也可以抛出或者重抛出错误,你可以在构造器参数列表的圆括号之后使用 `throws` 或 `rethrows` 关键字来表明相应的抛出行为。 - -关于在不同类型中声明构造器的例子,请参阅 [构造过程](../chapter2/14_Initialization.md)。 - - -### 可失败构造器 - -可失败构造器可以生成所属类型的可选实例或者隐式解包可选实例,因此,这种构造器通过返回 `nil` 来指明构造过程失败。 - -声明生成可选实例的可失败构造器时,在构造器声明的 `init` 关键字后加追加一个问号(`init?`)。声明生成隐式解包可选实例的可失败构造器时,在构造器声明后追加一个叹号(`init!`)。使用 `init?` 可失败构造器生成结构体的一个可选实例的例子如下。 - -```swift -struct SomeStruct { - let string: String - //生成一个 SomeStruct 的可选实例 - init?(input: String) { - if input.isEmpty { - // 丢弃 self,并返回 nil - return nil - } - string = input - } -} -``` - -调用 `init?` 可失败构造器和调用非可失败构造器的方式相同,不过你需要处理可选类型的返回值。 - -```swift -if let actualInstance = SomeStruct(input: "Hello") { - // 利用 SomeStruct 实例做些事情 -} else { - // SomeStruct 实例的构造过程失败,构造器返回了 nil -} -``` - -结构体或者枚举的可失败构造器可以在构造器实现中的任意位置返回 `nil`。然而,对于类的可失败构造器,仅在类的所有存储属性被初始化之后,并且 `self.init` 或 `super.init` 被调用之后(就是说,构造器委托过程完成后)才能返回 `nil`。 - -可失败构造器可以委托任意种类的构造器。非可失败可以委托其它非可失败构造器或者 `init!` 可失败构造器。非可失败构造器可以委托超类的 `init?` 可失败指定构造器,但是需要使用强制解包,例如 `super.init()!`。 - -构造过程的失败通过构造器委托来传递。具体来说,如果可失败构造器委托的可失败构造器构造过程失败并返回 `nil`,那么该可失败构造器也会构造失败并隐式地返回 `nil`。如果非可失败构造器委托的 `init!` 可失败构造器构造失败并返回了 `nil`,那么会发生运行时错误(如同使用 `!` 操作符去强制解包一个值为 `nil` 的可选值)。 - -子类可以用任意种类的指定构造器重写超类的可失败指定构造器,但是只能用非可失败指定构造器重写超类的非可失败指定构造器。 - -更多关于可失败构造器的信息和例子,请参阅 [可失败构造器](../chapter2/14_Initialization.md#failable_initializers)。 - - -> 构造器声明语法 - -> *构造器声明* → [*构造器头*](#initializer-head) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)可选 [*参数子句*](#parameter-clause) **throws**可选 [*构造器主体*](#initializer-body) -> *构造器声明* → [*构造器头*](#initializer-head) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)可选 [*参数子句*](#parameter-clause) **rethrows**可选 [*构造器主体*](#initializer-body) - -> *构造器头* → [*特性列表*](06_Attributes.md#attributes)可选 [*声明修饰符列表*](#declaration-modifiers)可选 **init** -> *构造器头* → [*特性列表*](06_Attributes.md#attributes)可选 [*声明修饰符列表*](#declaration-modifiers)可选 **init** **?** -> *构造器头* → [*特性列表*](06_Attributes.md#attributes)可选 [*声明修饰符列表*](#declaration-modifiers)可选 **init** **!** - -> *构造器主体* → [*代码块*](#code-block) - - -## 析构器声明 - -析构器声明可以为类声明一个析构器。析构器没有参数,遵循如下格式: - -```swift -deinit { - 语句 -} -``` - -当没有任何强引用引用着类的对象,对象即将被释放时,析构器会被自动调用。析构器只能在类主体的声明中声明,不能在类的扩展声明中声明,并且每个类最多只能有一个析构器。 - -子类会继承超类的析构器,并会在子类对象将要被释放时隐式调用。继承链上的所有析构器全部调用完毕后子类对象才会被释放。 - -析构器不能直接调用。 - -关于如何在类声明中使用析构器的例子,请参阅 [析构过程](../chapter2/15_Deinitialization.md)。 - - -> 析构器声明语法 - -> *析构器声明* → [*特性列表*](06_Attributes.md#attributes)可选 **deinit** [*代码块*](#code-block) - - -## 扩展声明 - -扩展声明可以扩展一个现存的类,结构体,和枚举类型的行为。扩展声明使用关键字 `extension`,遵循如下格式: - -```swift -extension 类型名称: 采纳的协议 { - 声明语句 -} -``` - -扩展声明体可包含零个或多个声明语句。这些声明语句可以包括计算型属性,计算型类型属性,实例方法,类型方法,构造器,下标声明,甚至其他结构体,类,和枚举声明。扩展声明不能包含析构器,协议声明,存储型属性,属性观察器,或其他扩展声明。关于扩展声明的详细讨论,以及各种扩展声明的例子,请参阅 [扩展](../chapter2/21_Extensions.md)。 - -扩展声明可以为现存的类,结构体,枚举添加协议一致性,但是不能为类添加超类,因此,在扩展声明的类型名称的冒号后面仅能指定一个协议列表。 - -现存类型的属性,方法,构造器不能在扩展中被重写。 - -扩展声明可以包含构造器声明。这意味着,如果被扩展的类型在其他模块中定义,构造器声明必须委托另一个在那个模块中声明的构造器,以此确保该类型能被正确地初始化。 - - -> 扩展声明语法 - -> *扩展声明* → [访问级别修饰符](#access-level-modifier)可选 **extension** [*类型标识符*](03_Types.md#type-identifier) [*类型继承子句*](03_Types.md#type-inheritance-clause)可选 [*扩展主体*](#extension-body) - -> *扩展主体* → **{** [*多条声明*](#declarations)可选 **}** - - -## 下标声明 - -下标声明用于为特定类型的对象添加下标支持,通常也用于为访问集合,列表和序列中的元素提供语法便利。下标声明使用关键字 `subscript`,形式如下: - -```swift -subscript (参数列表) -> 返回类型 { - get { - 语句 - } - set(setter 名称) { - 语句 - } -} -``` - -下标声明只能出现在类,结构体,枚举,扩展和协议声明的上下文中。 - -参数列表指定一个或多个用于在相关类型的下标表达式中访问元素的索引(例如,表达式 `object[i]` 中的 `i`)。索引可以是任意类型,但是必须包含类型标注。返回类型指定被访问的元素的类型。 - -和计算型属性一样,下标声明支持对元素的读写操作。getter 用于读取值,setter 用于写入值。setter 子句是可选的,当仅需要一个 getter 子句时,可以将二者都忽略,直接返回请求的值即可。但是,如果提供了 setter 子句,就必须提供 getter 子句。 - -圆括号以及其中的 setter 名称是可选的。如果提供了 setter 名称,它会作为 setter 的参数名称。如果不提供 setter 名称,那么 setter 的参数名称默认是 `value`。setter 名称的类型必须与返回类型相同。 - -可以重载下标,只要参数列表或返回类型与先前不同即可。还可以重写继承自超类的下标,此时必须使用 `override` 声明修饰符声明被重写的下标。 - -同样可以在协议声明的上下文中声明下标,正如 [协议下标声明](#protocol_subscript_declaration) 中所述。 - -更多关于下标的信息和例子,请参阅 [下标](../chapter2/12_Subscripts.md)。 - - -> 下标声明语法 - -> *下标声明* → [*下标头*](#subscript-head) [*下标结果*](#subscript-result) [*代码块*](#code-block) -> *下标声明* → [*下标头*](#subscript-head) [*下标结果*](#subscript-result) [*getter-setter代码块*](#getter-setter-block) -> *下标声明* → [*下标头*](#subscript-head) [*下标结果*](#subscript-result) [*getter-setter关键字代码块*](#getter-setter-keyword-block) - -> *下标头* → [*特性列表*](06_Attributes.md#attributes)可选 [*声明修饰符列表*](#declaration-modifiers)可选 **subscript** [*参数子句*](#parameter-clause) - -> *下标结果* → **->** [*特性列表*](06_Attributes.md#attributes)可选 [*类型*](03_Types.md#type) - - -## 运算符声明 - -运算符声明会向程序中引入中缀、前缀或后缀运算符,使用关键字 `operator` 来声明。 - -可以声明三种不同的缀性:中缀、前缀和后缀。运算符的缀性指定了运算符与其运算对象的相对位置。 - -运算符声明有三种基本形式,每种缀性各一种。运算符的缀性通过在 `operator` 关键字之前添加声明修饰符 `infix`,`prefix` 或 `postfix` 来指定。每种形式中,运算符的名字只能包含 [运算符](02_Lexical_Structure.md#operators) 中定义的运算符字符。 - -下面的形式声明了一个新的中缀运算符: - -```swift -infix operator 运算符名称 { - precedence 优先级 - associativity 结合性 -} -``` - -中缀运算符是二元运算符,置于两个运算对象之间,例如加法运算符(`+`)位于表达式 `1 + 2` 的中间。 - -中缀运算符可以选择指定优先级或结合性,或者两者同时指定。 - -运算符的优先级可以指定在没有括号包围的情况下,运算符与其运算对象如何结合。可以使用上下文相关的关键字 `precedence` 以及紧随其后的优先级数字来指定一个运算符的优先级。优先级可以是 `0` 到 `255` 之间的任何十进制整数。与十进制整数字面量不同的是,它不可以包含任何下划线字符。尽管优先级是一个特定的数字,但它仅用作与另一个运算符的优先级比较大小。也就是说,当两个运算符为同一个运算对象竞争时,例如 `2 + 3 * 5`,优先级更高的运算符将优先参与运算。 - -运算符的结合性可以指定在没有括号包围的情况下,多个优先级相同的运算符将如何组合。可以使用上下文相关的关键字 `associativity` 以及紧随其后的结合性关键字来指定运算符的结合性,结合性关键字也是上下文相关的,包括 `left`,`right`,和 `none`。左结合运算符以从左到右的顺序进行组合。例如,减法运算符(`-`)具有左结合性,因此 `4 - 5 - 6` 以 `(4 - 5) - 6` 的形式组合,其结果为 `-7`。右结合运算符以从右到左的顺序组合,而设置为 `none` 的运算符不进行组合。具有相同优先级的非结合运算符,不可以互相邻接。例如,表达式 `1 < 2 < 3` 是非法的。 - -声明时不指定任何优先级或结合性的中缀运算符,优先级为 `100`,结合性为 `none`。 - -下面的形式声明了一个新的前缀运算符: - -```swift -prefix operator 运算符名称 {} -``` - -出现在运算对象前边的前缀运算符是一元运算符,例如表达式 `++i` 中的前缀递增运算符(`++`)。 - -前缀运算符的声明中不指定优先级,而且前缀运算符是非结合的。 - -下面的形式声明了一个新的后缀运算符: - -```swift -postfix operator 运算符名称 {} -``` - -紧跟在运算对象后边的后缀运算符是一元运算符,例如表达式 `i++` 中的后缀递增运算符(`++`)。 - -和前缀运算符一样,后缀运算符的声明中不指定优先级,而且后缀运算符是非结合的。 - -声明了一个新的运算符以后,需要实现一个跟这个运算符同名的函数来实现这个运算符。如果是实现一个前缀或者后缀运算符,也必须使用相符的 `prefix` 或者 `postfix` 声明修饰符标记函数声明。如果是实现中缀运算符,则不需要使用 `infix` 声明修饰符标记函数声明。关于如何实现一个新的运算符的例子,请参阅 [自定义运算符](../chapter2/25_Advanced_Operators.md#custom_operators)。 - - -> 运算符声明语法 - - -> *运算符声明* → [*前缀运算符声明*](#prefix-operator-declaration) | [*后缀运算符声明*](#postfix-operator-declaration) | [*中缀运算符声明*](#infix-operator-declaration) - - -> *前缀运算符声明* → **prefix** **运算符** [*运算符*](02_Lexical_Structure.md#operator) **{** **}** - -> *后缀运算符声明* → **postfix** **运算符** [*运算符*](02_Lexical_Structure.md#operator) **{** **}** - -> *中缀运算符声明* → **infix** **运算符** [*运算符*](02_Lexical_Structure.md#operator) **{** [*中缀运算符属性*](#infix-operator-attributes)可选 **}** - - -> *中缀运算符属性* → [*优先级子句*](#precedence-clause)可选 [*结和性子句*](#associativity-clause)可选 - -> *优先级子句* → **precedence** [*优先级水平*](#precedence-level) - -> *优先级水平* → 十进制整数 0 到 255,包含 0 和 255 - -> *结和性子句* → **associativity** [*结和性*](#associativity) - -> *结和性* → **left** | **right** | **none** - - -## 声明修饰符 - -声明修饰符都是关键字或上下文相关的关键字,可以修改一个声明的行为或者含义。可以在声明的特性(如果存在)和引入该声明的关键字之间,利用声明修饰符的关键字或上下文相关的关键字指定一个声明修饰符。 - -`dynamic` - -该修饰符用于修饰任何兼容 Objective-C 的类的成员。访问被 `dynamic` 修饰符标记的类成员将总是由 Objective-C 运行时系统进行动态派发,而不会由编译器进行内联或消虚拟化。 - -因为被标记 `dynamic` 修饰符的类成员会由 Objective-C 运行时系统进行动态派发,所以它们会被隐式标记 `objc` 特性。 - -`final` - -该修饰符用于修饰类或类中的属性,方法,以及下标。如果用它修饰一个类,那么这个类不能被继承。如果用它修饰类中的属性,方法或下标,那么它们不能在子类中被重写。 - -`lazy` - -该修饰符用于修饰类或结构体中的存储型变量属性,表示该属性的初始值最多只被计算和存储一次,且发生在它被第一次访问时。关于如何使用 `lazy` 修饰符的例子,请参阅 [惰性存储型属性](../chapter2/10_Properties.md#lazy_stored_properties)。 - -`optional` - -该修饰符用于修饰协议中的属性,方法,以及下标成员,表示符合类型不必实现这些成员要求。 - -只能将 `optional` 修饰符用于被 `objc` 特性标记的协议。这样一来,就只有类类型可以采纳并符合拥有可选成员要求的协议。关于如何使用 `optional` 修饰符,以及如何访问可选协议成员(比如,不确定符合类型是否已经实现了这些可选成员)的信息,请参阅 [可选协议要求](../chapter2/22_Protocols.md#optional_protocol_requirements)。 - -`required` - -该修饰符用于修饰类的指定构造器或便利构造器,表示该类所有的子类都必须实现该构造器。在子类实现该构造器时,必须同样使用 `required` 修饰符修饰该构造器。 - -`weak` - -该修饰符用于修饰变量或存储型变量属性,表示该变量或属性持有其存储的对象的弱引用。这种变量或属性的类型必须是可选的类类型。使用 `weak` 修饰符可避免强引用循环。关于 `weak` 修饰符的更多信息和例子,请参阅 [弱引用](../chapter2/16_Automatic_Reference_Counting.md#resolving_strong_reference_cycles_between_class_instances)。 - - -### 访问控制级别 - -Swift 提供了三个级别的访问控制:`public`,`internal` 和 `private`。可以使用以下任意一种访问级别修饰符来指定声明的访问级别。访问控制在 [访问控制](../chapter2/24_Access_Control.md) 中有详细讨论。 - -`public` - -该修饰符表示声明可被同模块的代码访问,只要其他模块导入了声明所在的模块,也可以进行访问。 - -`internal` - -该修饰符表示声明只能被同模块的代码访问。默认情况下,绝大多数声明会被隐式标记 `internal` 访问级别修饰符。 - -`private` - -该修饰符表示声明只能被所在源文件的代码访问。 - -以上访问级别修饰符都可以选择带上一个参数,该参数由一对圆括号和其中的 `set` 关键字组成(例如,`private(set)`)。使用这种形式的访问级别修饰符来限制某个属性或下标的 setter 的访问级别低于其本身的访问级别,正如 [Getter 和 Setter](../chapter2/24_Access_Control.md#getters_and_setters) 中所讨论的。 - - -> 声明修饰符的语法 - - -> *声明修饰符* → **class** | **convenience**| **dynamic** | **final** | **infix** | **lazy** | **mutating** | **nonmutating** | **optional** | **override** | **postfix** | **prefix** | **required** | **static** | **unowned** | **unowned ( safe )** | **unowned ( unsafe )** | **weak** -> 声明修饰符 → [*访问级别修饰符*](#access-level-modifier) - -> *声明修饰符列表* → [*声明修饰符*](#declaration-modifier) [*声明修饰符列表*](#declaration-modifiers)可选 - - ->访问级别修饰符 → **internal** | **internal ( set )** ->访问级别修饰符 → **private** | **private ( set )** ->访问级别修饰符 → **public** | **public ( set )** + +# 声明(Declarations) +----------------- + +> 1.0 +> 翻译:[marsprince](https://github.com/marsprince) [Lenhoon](https://github.com/marsprince)[(微博)](http://www.weibo.com/lenhoon) +> 校对:[numbbbbb](https://github.com/numbbbbb), [stanzhai](https://github.com/stanzhai) + + + +> 2.0 +> 翻译+校对:[Lenhoon](https://github.com/Lenhoon), +> [BridgeQ](https://github.com/WXGBridgeQ) + + + +> 2.1 +> 翻译:[mmoaay](https://github.com/mmoaay), [shanks](http://codebuild.me) +> 校对:[shanks](http://codebuild.me) + + + +> 2.2 +> 翻译:[星夜暮晨](https://github.com/SemperIdem) +> 校对: + +本页包含内容: + +- [顶级代码](#top-level_code) +- [代码块](#code_blocks) +- [导入声明](#import_declaration) +- [常量声明](#constant_declaration) +- [变量声明](#variable_declaration) + - [存储型变量和存储型变量属性](#stored_variables_and_stored_variable_properties) + - [计算型变量和计算型属性](#computed_variables_and_computed_properties) + - [存储型变量观察器和属性观察器](#stored_variable_observers_and_property_observers) + - [类型变量属性](#type_variable_properties) +- [类型别名声明](#type_alias_declaration) +- [函数声明](#function_declaration) + - [参数名](#parameter_names) + - [输入输出参数](#in-out_parameters) + - [特殊参数](#special_kinds_of_parameters) + - [特殊方法](#special_kinds_of_methods) + - [柯里化函数](#curried_functions) + - [抛出错误的函数和方法](#throwing_functions_and_methods) + - [重新抛出错误的函数和方法](#rethrowing_functions_and_methods) +- [枚举声明](#enumeration_declaration) + - [包含任意类型用例的枚举](#enumerations_with_cases_of_any_type) + - [递归枚举](#enumerations_with_indirection) + - [包含原始值类型的枚举](#enumerations_with_cases_of_a_raw-value_type) + - [访问枚举用例](#accessing_enumeration_cases) +- [结构体声明](#structure_declaration) +- [类声明](#class_declaration) +- [协议声明](#protocol_declaration) + - [协议属性声明](#protocol_property_declaration) + - [协议方法声明](#protocol_method_declaration) + - [协议构造器声明](#protocol_initializer_declaration) + - [协议下标声明](#protocol_subscript_declaration) + - [协议关联类型声明](#protocol_associated_type_declaration) +- [构造器声明](#initializer_declaration) + - [可失败构造器](#failable_initializers) +- [析构器声明](#deinitializer_declaration) +- [扩展声明](#extension_declaration) +- [下标声明](#subscript_declaration) +- [运算符声明](#operator_declaration) +- [声明修饰符](#declaration_modifiers) + - [访问控制级别](#access_control_levels) + +*声明 (declaration)* 用以向程序里引入新的名字或者构造。举例来说,可以使用声明来引入函数和方法,变量和常量,或者来定义新的命名枚举,结构,类和协议类型。还可以使用声明来扩展一个已经存在的命名的类型的行为,或者在程序里引入在其它地方声明的符号。 + +在 Swift 中,大多数声明在某种意义上讲也是定义,实现或初始化往往伴随着声明。这意味着,由于协议往往并不提供实现,大多数协议仅仅只是声明而已。为了方便起见,也因为这些区别在 Swift 中不是很重要,*“声明”*这个术语同时包含了声明和定义。 + +> 声明语法 +> +> *声明* → [*导入声明*](#import-declaration) +> *声明* → [*常量声明*](#constant-declaration) +> *声明* → [*变量声明*](#variable-declaration) +> *声明* → [*类型别名声明*](#typealias-declaration) +> *声明* → [*函数声明*](#function-declaration) +> *声明* → [*枚举声明*](#enum-declaration) +> *声明* → [*结构体声明*](#struct-declaration) +> *声明* → [*类声明*](#class-declaration) +> *声明* → [*协议声明*](#protocol-declaration) +> *声明* → [*构造器声明*](#initializer-declaration) +> *声明* → [*析构器声明*](#deinitializer-declaration) +> *声明* → [*扩展声明*](#extension-declaration) +> *声明* → [*下标声明*](#subscript-declaration) +> *声明* → [*运算符声明*](#operator-declaration) +> +> *多条声明* → [*声明*](#declaration) [*多条声明*](#declarations)可选 + + +## 顶级代码 + +Swift 的源文件中的顶级代码 (top-level code) 由零个或多个语句,声明和表达式组成。默认情况下,在一个源文件的顶层声明的变量,常量和其他命名的声明语句可以被同一模块中的每一个源文件中的代码访问。可以使用一个访问级别修饰符来标记声明,从而覆盖默认行为,请参阅 [访问控制级别](#access_control_levels)。 + +> 顶级声明语法 +> *顶级声明* → [*多条语句*](10_Statements.md#statements)可选 + + +## 代码块 + +*代码块 (code block)* 可以将一些声明和控制结构组织在一起。它有如下的形式: + +```swift +{ + <#语句#> +} +``` + +代码块中的*语句*包括声明,表达式和各种其他类型的语句,它们按照在源码中的出现顺序被依次执行。 + +> 代码块语法 +> +> *代码块* → **{** [*多条语句*](10_Statements.md#statements)可选 **}** + + +## 导入声明 + +*导入声明 (import declaration)* 让你可以使用在其他文件中声明的内容。导入语句的基本形式是导入整个模块,它由 `import` 关键字开始,后面紧跟一个模块名: + +```swift +import <#模块#> +``` + +可以对导入提供更细致的控制,如指定一个特殊的子模块或者指定一个模块或子模块中的某个声明。提供了这些限制后,在当前作用域中,只有导入的符号是可用的,而不是整个模块。 + +```swift +import <#导入类型#> <#模块#>.<#符号名#> +import <#模块#>.<#子模块#> +``` + + +> 导入声明语法 +> +> *导入声明* → [*特性列表*](06_Attributes.md#attributes)可选 **import** [*导入类型*](#import-kind)可选 [*导入路径*](#import-path) +> +> *导入类型* → **typealias** | **struct** | **class** | **enum** | **protocol** | **var** | **func** +> +> *导入路径* → [*导入路径标识符*](#import-path-identifier) | [*导入路径标识符*](#import-path-identifier) **.** [*导入路径*](#import-path) +> +> *导入路径标识符* → [*标识符*](02_Lexical_Structure.md#identifier) | [*运算符*](02_Lexical_Structure.md#operator) + + +## 常量声明 + +*常量声明 (constant declaration)* 可以在程序中命名一个常量。常量以关键字 `let` 来声明,遵循如下的格式: + +```swift +let <#常量名称#>: <#类型#> = <#表达式#> +``` + +常量声明在*常量名称*和初始化*表达式*的值之间,定义了一种不可变的绑定关系;当常量的值被设定之后,它就无法被更改。这意味着,如果常量以类对象来初始化,对象本身的内容是可以改变的,但是常量和该对象之间的结合关系是不能改变的。 + +当一个常量被声明为全局变量,它必须被给定一个初始值。在类或者结构体中声明一个常量时,它被认为是一个*常量属性 (constant property)*。常量声明不能是计算型属性,因此也没有存取方法。 + +如果常量名称是元组形式,元组中的每一项的名称会和对应的初始化表达式的值绑定。 + +```swift +let (firstNumber, secondNumber) = (10, 42) +``` + +在上例中,`firstNumber` 是一个值为 `10` 的常量,`secnodeName` 是一个值为 `42` 的常量。所有常量都可以独立的使用: + +```swift +print("The first number is \(firstNumber).") +// 打印 “The first number is 10.” +print("The second number is \(secondNumber).") +// 打印 “The second number is 42.” +``` + +当常量名称的类型 (`:` *type*) 可以被推断出时,类型标注在常量声明中是一个可选项,正如 [类型推断](03_Types.md#type_inference) 中所描述的。 + +声明一个常量类型属性要使用 `static` 声明修饰符。类型属性在 [类型属性](../chapter2/10_Properties.md#type_properties)中有介绍。 + +如果还想获得更多关于常量的信息或者想在使用中获得帮助,请参阅 [常量和变量](../chapter2/01_The_Basics.md#constants_and_variables) 和 [存储属性](../chapter2/10_Properties.md#stored_properties)。 + + +> 常量声明语法 +> +> *常量声明* → [*特性列表*](06_Attributes.md#attributes)可选 [*声明修饰符列表*](#declaration-modifiers)可选 **let** [*模式构造器列表*](pattern-initializer-list) +> +> *模式构造器列表* → [*模式构造器*](#pattern-initializer) | [*模式构造器*](#pattern-initializer) **,** [*模式构造器列表*](#pattern-initializer-list) +> +> *模式构造器* → [*模式*](07_Patterns.md#pattern) [*构造器*](#initializer)可选 +> +> *构造器* → **=** [*表达式*](04_Expressions.md#expression) + + +## 变量声明 + +*变量声明 (variable declaration)* 可以在程序中声明一个变量,它以关键字 `var` 来声明。 + +变量声明有几种不同的形式,可以声明不同种类的命名值和可变值,如存储型和计算型变量和属性,属性观察器,以及静态变量属性。所使用的声明形式取决于变量声明的适用范围和打算声明的变量类型。 + +> 注意 +> 也可以在协议声明的上下文中声明属性,详情请参阅 [协议属性声明](#protocol_property_declaration)。 + +可以在子类中使用 `override` 声明修饰符来标记继承来的属性的声明,从而重写属性,详情请参阅 [重写](../chapter2/13_Inheritance.md#overriding)。 + + +### 存储型变量和存储型变量属性 + +使用如下形式声明一个存储型变量或存储型变量属性: + +```swift +var <#变量名称#>: <#类型#> = <#表达式#> +``` + +可以在全局范围,函数内部,或者在类和结构体的声明中使用这种形式来声明一个变量。当变量以这种形式在全局范围或者函数内部被声明时,它代表一个存储型变量。当它在类或者结构体中被声明时,它代表一个*存储型变量属性 (stored variable property)*。 + +用于初始化的表达式不可以在协议的声明中出现,在其他情况下,该表达式是可选的。如果没有初始化表达式,那么变量声明必须包含类型标注 (`:` *type*)。 + +对于常量的声明,如果变量名称是一个元组,元组中每一项的名称都要和初始化表达式中的相应值绑定。 + +正如名字一样,存储型变量和存储型变量属性的值会存储在内存中。 + + +### 计算型变量和计算型属性 + +使用如下形式声明一个计算型变量或计算型属性: + +```swift +var <#变量名称#>: <#类型#> { + get { + <#语句#> + } + set(<#setter 名称#>) { + <#语句#> + } +} +``` + +可以在全局范围,函数内部,以及类,结构体,枚举,扩展声明的上下文中使用这种形式的声明。当变量以这种形式在全局范围或者函数内部被声明时,它代表一个计算型变量。当它在类,结构体,枚举,扩展声明的上下文中被声明时,它代表一个*计算型属性 (computed property)*。 + +getter 用来读取变量值,setter 用来写入变量值。setter 子句是可选的,getter 子句是必须的。也可以将这些子句都省略,直接返回请求的值,正如在 [只读计算型属性](../chapter2/10_Properties.md#computed_properties) 中描述的那样。但是如果提供了一个 setter 子句,也必须提供一个 getter 子句。 + +圆括号内的 setter 的名称是可选的。如果提供了一个 setter 名称,它就会作为 setter 的参数名称使用。如果不提供 setter 名称,setter 的参数的默认名称为 `newValue`,正如在 [便捷 setter 声明](../chapter2/10_Properties.md#shorthand_setter_declaration) 中描述的那样。 + +与存储型变量和存储型属性不同,计算型变量和计算型属性的值不存储在内存中。 + +要获得更多关于计算型属性的信息和例子,请参阅 [计算型属性](../chapter2/10_Properties.md#computed_properties)。 + + +### 存储型变量观察器和属性观察器 + +可以在声明存储型变量或属性时提供 `willSet` 和 `didSet` 观察器。一个包含观察器的存储型变量或属性以如下形式声明: + +```swift +var <#变量名称#>: <#类型#> = <#表达式#> { + willSet(<#setter 名称#>) { + <#语句#> + } + didSet(<#setter 名称#>) { + <#语句#> + } +} +``` + +可以在全局范围,函数内部,或者类,结构体声明的上下文中使用这种形式的声明。当变量以这种形式在全局范围或者函数内部被声明时,观察器代表一个存储型变量观察器。当它在类,结构体声明的上下文中被声明时,观察器代表一个属性观察器。 + +可以为任何存储型属性添加观察器。也可以通过重写父类属性的方式为任何继承的属性(无论是存储型还是计算型的)添加观察器 +,正如 [重写属性观察器](../chapter2/13_Inheritance.md#overriding_property_observers) 中所描述的。 + +用于初始化的表达式在一个类或者结构体的声明中是可选的,但是在其他地方是必须的。如果类型可以从初始化表达式中推断而来,那么这个类型标注是可选的。 + +当变量或属性的值被改变时,`willSet` 和 `didSet` 观察器提供了一种观察方法。观察器不会在变量或属性第一次初始化时被调用,它们仅当值在初始化环境之外被改变时才会被调用。 + +`willSet` 观察器只在变量或属性的值被改变之前调用。新的值作为一个常量传入 `willSet` 观察器,因此不可以在 `willSet` 中改变它。`didSet` 观察器在变量或属性的值被改变后立即调用。和 `willSet` 观察器相反,为了方便获取旧值,旧值会传入 `didSet` 观察器。这意味着,如果在变量或属性的 `didiset` 观察器中设置值,设置的新值会取代刚刚在 `willSet` 观察器中传入的那个值。 + +在 `willSet` 和 `didSet` 中,圆括号以及其中的 setter 名称是可选的。如果提供了一个 setter 名称,它就会作为 `willSet` 和 `didSet` 的参数被使用。如果不提供 setter 名称,`willSet` 观察器的默认参数名为 `newValue`,`didSet` 观察器的默认参数名为 `oldValue`。 + +提供了 `willSet` 时,`didSet` 是可选的。同样的,提供了 `didSet` 时,`willSet` 则是可选的。 + +要获得更多信息以及查看如何使用属性观察器的例子,请参阅 [属性观察器](../chapter2/10_Properties.md#property_observers)。 + + +### 类型变量属性 + +要声明一个类型变量属性,用 `static` 声明修饰符标记该声明。类可以改用 `class` 声明修饰符标记类的类型计算型属性从而允许子类重写超类的实现。类型属性在 [类型属性](../chapter2/10_Properties.md#type_properties) 章节有详细讨论。 + +> 注意 +> 在一个类声明中,使用关键字 `static` 与同时使用 `class` 和 `final` 去标记一个声明的效果相同。 + + +> 变量声明语法 + + +> *变量声明* → [*变量声明头*](#variable-declaration-head) [*模式构造器列表*](#pattern-initializer-list) +> *变量声明* → [*变量声明头*](#variable-declaration-head) [*变量名称*](#variable-name) [*类型标注*](03_Types.md#type-annotation) [*代码块*](#code-block) +> *变量声明* → [*变量声明头*](#variable-declaration-head) [*变量名称*](#variable-name) [*类型标注*](03_Types.md#type-annotation) [*getter-setter代码块*](#getter-setter-block) +> *变量声明* → [*变量声明头*](#variable-declaration-head) [*变量名称*](#variable-name) [*类型标注*](03_Types.md#type-annotation) [*getter-setter关键字代码块*](#getter-setter-keyword-block) +> *变量声明* → [*变量声明头*](#variable-declaration-head) [*变量名称*](#variable-name) [*构造器*](#initializer) [*willSet-didSet代码块*](#willSet-didSet-block) +> *变量声明* → [*变量声明头*](#variable-declaration-head) [*变量名称*](#variable-name) [*类型标注*](03_Types.md#type-annotation) [*构造器*](#initializer)可选 [*willSet-didSet代码块*](#willSet-didSet-block) + + +> *变量声明头* → [*特性列表*](06_Attributes.md#attributes)可选 [*声明修饰符列表*](#declaration-modifiers)可选 **var** +> +> *变量名称* → [*标识符*](02_Lexical_Structure.md#identifier) + + +> *getter-setter 代码块* → [*代码块*](#code-block) +> *getter-setter 代码块* → **{** [*getter子句*](#getter-clause) [*setter子句*](#setter-clause)可选 **}** +> *getter-setter 代码块* → **{** [*setter子句*](#setter-clause) [*getter子句*](#getter-clause) **}** +> +> *getter 子句* → [*特性列表*](06_Attributes.md#attributes)可选 **get** [*代码块*](#code-block) +> +> *setter 子句* → [*特性列表*](06_Attributes.md#attributes)可选 **set** [*setter名称*](#setter-name)可选 [*代码块*](#code-block) +> +> *setter 名称* → **(** [*标识符*](02_Lexical_Structure.md#identifier) **)** + + +> *getter-setter 关键字代码块* → **{** [*getter关键字子句*](#getter-keyword-clause) [*setter关键字子句*](#setter-keyword-clause)可选 **}** +> *getter-setter 关键字代码块* → **{** [*setter关键字子句*](#setter-keyword-clause) [*getter关键字子句*](#getter-keyword-clause) **}** +> +> *getter 关键字子句* → [*特性列表*](06_Attributes.md#attributes)可选 **get** +> +> *setter 关键字子句* → [*特性列表*](06_Attributes.md#attributes)可选 **set** + + +> *willSet-didSet 代码块* → **{** [*willSet子句*](#willSet-clause) [*didSet子句*](#didSet-clause)可选 **}** +> *willSet-didSet 代码块* → **{** [*didSet子句*](#didSet-clause) [*willSet子句*](#willSet-clause)可选 **}** +> +> *willSet 子句* → [*特性列表*](06_Attributes.md#attributes)可选 **willSet** [*setter名称*](#setter-name)可选 [*代码块*](#code-block) +> +> *didSet 子句* → [*特性列表*](06_Attributes.md#attributes)可选 **didSet** [*setter名称*](#setter-name)可选 [*代码块*](#code-block) + + +## 类型别名声明 + +*类型别名 (type alias)* 声明可以在程序中为一个现存类型声明一个别名。类型别名声明语句使用关键字 `typealias` 声明,遵循如下的形式: + +```swift +typealias <#类型别名#> = <#现存类型#> +``` + +当声明一个类型的别名后,可以在程序的任何地方使用*别名*来代替*现存类型*。*现存类型*可以是命名类型或者混合类型。类型别名不产生新的类型,它只是可以使用别名来引用现存类型。 + +另请参阅 [协议关联类型声明](#protocol_associated_type_declaration)。 + + +> 类型别名声明语法 +> +> *类型别名声明* → [*类型别名头*](#typealias-head) [*类型别名赋值*](#typealias-assignment) +> +> *类型别名头* → [*特性列表*](06_Attributes.md#attributes)可选 [*访问级别修饰符*](#access-level-modifier)可选 **typealias** [*类型别名名称*](#typealias-name) +> +> *类型别名名称* → [*标识符*](02_Lexical_Structure.md#identifier) +> +> *类型别名赋值* → **=** [*类型*](03_Types.md#type) + + +## 函数声明 + +使用*函数声明 (function declaration)* 在程序中引入新的函数或者方法。当一个函数被声明在类,结构体,枚举,或者协议的上下文中,会作为一个方法。函数声明使用关键字 `func`,遵循如下的形式: + +```swift +func <#函数名称#>(<#参数列表#>) -> <#返回类型#> { + <#语句#> +} +``` + +如果函数返回 `Void` 类型,返回类型可以省略,如下所示: + +```swift +func <#函数名称#>(<#参数列表#>) { + <#语句#> +} +``` + +每个参数的类型都要标明,它们不能被推断出来。虽然函数的参数默认是常量,也可以在参数名前添加 `let` 来强调这一行为。如果您在某个参数名前面加上了 `inout`,那么这个参数就可以在这个函数作用域当中被修改。更多关于 `inout` 参数的讨论,请参阅 [输入输出参数](#in-out_parameters)。 + +函数可以使用元组类型作为返回类型来返回多个值。 + +函数定义可以出现在另一个函数声明内。这种函数被称作*嵌套函数 (nested function)*。更多关于嵌套函数的讨论,请参阅 [嵌套函数](../chapter2/06_Functions.md#Nested_Functions)。 + + +### 参数名 + +函数的参数列表由一个或多个函数参数组成,参数间以逗号分隔。函数调用时的参数顺序必须和函数声明时的参数顺序一致。最简单的参数列表有着如下的形式: + +```swift +<#参数名称#>: <#参数类型#> +``` + +一个参数有一个内部名称,这个内部名称可以在函数体内被使用。同样也可以作为外部名称,当调用函数时这个外部名称被作为实参的标签来使用。默认情况下,第一个参数的外部名称省略不写,第二个和之后的参数使用它们的内部名称作为它们的外部名称。例如: + +```swift +func f(x: Int, y: Int) -> Int { return x + y } +f(1, y: 2) // 参数 y 有标签,参数 x 则没有 +``` + +可以按照如下两种形式之一,重写参数名称的默认行为: + +```swift +<#外部参数名称#> <#内部参数名称#>: <#参数类型#> +_ <#内部参数名称#>: <#参数类型#> +``` + +在内部参数名称前的名称赋予这个参数一个外部名称,这个名称可以和内部参数的名称不同。外部参数名称在函数被调用时必须被使用。对应的参数在方法或函数被调用时必须有外部名。 + +内部参数名称前的下划线(`_`)使该参数在函数被调用时没有名称。在函数或方法调用时,对应的参数必须没有名字。 + +```swift +func f(x x: Int, withY y: Int, _ z: Int) -> Int { return x + y + z } +f(x: 1, withY: 2, 3) // 参数 x 和 y 是有标签的,参数 z 则没有 +``` + + +### 输入输出参数 + +输入输出参数被传递时遵循如下规则: + +1. 函数调用时,参数的值被拷贝。 +2. 函数体内部,拷贝后的值被修改。 +3. 函数返回后,拷贝后的值被赋值给原参数。 + +这种行为被称为*拷入拷出 (copy-in copy-out)* 或*值结果调用 (call by value result)*。例如,当一个计算型属性或者一个具有属性观察器的属性被用作函数的输入输出参数时,其 getter 会在函数调用时被调用,而其 setter 会在函数返回时被调用。 + +作为一种优化手段,当参数值存储在内存中的物理地址时,在函数体内部和外部均会使用同一内存位置。这种优化行为被称为*引用调用 (call by reference)*,它满足了拷入拷出模型的所有需求,而消除了复制带来的开销。不要依赖于拷入拷出与引用调用之间的行为差异。 + +不要使用传递给输入输出参数的值,即使原始值在当前作用域中依然可用。当函数返回时,你对原始值所做的更改会被拷贝的值所覆盖。不要依赖于引用调用的优化机制来试图阻止这种覆盖。 + +你不能将同一个值传递给多个输入输出参数,因为多个输入输出参数引发的拷贝与覆盖行为的顺序是不确定的,因此原始值的最终值也将无法确定。例如: + +```swift +var x = 10 +func f(inout a: Int, inout _ b: Int) { + a += 1 + b += 10 +} +f(&x, &x) // 编译报错 error: inout arguments are not allowed to alias each other +``` + +如果嵌套函数在外层函数返回后才调用,嵌套函数对输入输出参数造成的任何改变将不会影响到原始值。例如: + +```swift +func outer(inout a: Int) -> () -> Void { + func inner() { + a += 1 + } + return inner +} + +var x = 10 +let f = outer(&x) +f() +print(x) +// 打印 “10” +``` + +调用嵌套函数 `inner()` 对 `a` 递增后,`x` 的值并未发生改变,因为 `inner()` 在外层函数 `outer()` 返回后才被调用。若要改变 `x` 的值,必须在 `outer()` 返回前调用 `inner()`。 + +关于输入输出参数的详细讨论,请参阅 [输入输出参数](../chapter2/06_Functions.md#in_out_parameters)。 + + +### 特殊参数 + +参数可以被忽略,参数的数量可变,并且还可以提供默认值,使用形式如下: + +```swift +_ : <#参数类型#> +<#参数名称#>: <#参数类型#>... +<#参数名称#.: <#参数类型#> = <#默认参数值#> +``` + +以下划线(`_`)命名的参数是被显式忽略的,无法在函数体内使用。 + +一个参数的基础类型名称如果紧跟着三个点(`...`),会被视为可变参数。一个函数至多可以拥有一个可变参数,且必须是最后一个参数。可变参数会作为该参数类型的数组。举例来讲,可变参数 `Int...` 被看作 `[Int]`。关于使用可变参数的例子,请参阅 [可变参数](../chapter2/06_Functions.md#variadic_parameters)。 + +如果在参数类型后面有一个以等号(`=`)连接的表达式,该参数会拥有默认值,即给定表达式的值。当函数被调用时,给定的表达式会被求值。如果参数在函数调用时被省略了,就会使用其默认值。 + +```swift +func f(x: Int = 42) -> Int { return x } +f() // 有效,使用默认值 +f(7) // 有效,提供了值 +f(x: 7) // 无效,该参数没有外部名称 +``` + + +### 特殊方法 + +枚举或结构体的方法如果会修改 `self` 属性,必须以 `mutating` 声明修饰符标记。 + +子类重写超类中的方法必须以 `override` 声明修饰符标记。重写方法时不使用 `override` 修饰符,或者使用了 `override` 修饰符却并没有重写超类方法,都会产生一个编译时错误。 + +枚举或者结构体中的类型方法,要以 `static` 声明修饰符标记,而对于类中的类型方法,除了使用 `static`,也可使用 `class` 声明修饰符标记。 + + +### 抛出错误的函数和方法 + +可以抛出错误的函数或方法必须使用 `throws` 关键字标记。这类函数和方法被称为抛出函数和抛出方法。它们有着下面的形式: + +```swift +func <#函数名称#>(<#参数列表#>) throws -> <#返回类型#> { + <#语句#> +} +``` + +抛出函数或抛出方法的调用必须包裹在 `try` 或者 `try!` 表达式中(也就是说,在作用域内使用 `try` 或者 `try!` 运算符)。 + +`throws` 关键字是函数的类型的一部分,非抛出函数是抛出函数的子类型。所以,可以在使用抛出函数的地方使用非抛出函数。对于柯里化函数,`throws` 关键字仅应用于最内层的函数。 + +不能仅基于函数能否抛出错误来进行函数重载。也就是说,可以基于函数的函数类型的参数能否抛出错误来进行函数重载。 + +抛出方法不能重写非抛出方法,而且抛出方法不能满足协议对于非抛出方法的要求。也就是说,非抛出方法可以重写抛出方法,而且非抛出方法可以满足协议对于抛出方法的要求。 + + +### 重新抛出错误的函数和方法 + +函数或方法可以使用 `rethrows` 关键字来声明,从而表明仅当该函数或方法的一个函数类型的参数抛出错误时,该函数或方法才抛出错误。这类函数和方法被称为重抛函数和重抛方法。重新抛出错误的函数或方法必须至少有一个参数的类型为抛出函数。 + +```swift +func functionWithCallback(callback: () throws -> Int) rethrows { + try callback() +} +``` + +重抛函数或者方法不能够从它本身直接抛出任何错误,这意味着它不能够包含 `throw` 语句。它只能够传递作为参数的抛出函数所抛出的错误。例如,在 `do-catch` 代码块中调用抛出函数,以及在 `catch` 闭包中处理另一种抛出的错误错误都是不允许的。 + +抛出方法不能重写重抛方法,而且抛出方法不能满足协议对于重抛方法的要求。也就是说,重抛方法可以重写抛出方法,而且重抛方法可以满足协议对于抛出方法的要求。 + + +> 函数声明语法 + + +> *函数声明* → [*函数头*](#function-head) [*函数名*](#function-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)可选 [*函数签名*](#function-signature) [*函数体*](#function-body)可选 + + +> *函数头* → [*特性列表*](06_Attributes.md#attributes)可选 [*声明修饰符列表*](#declaration-modifiers)可选 **func** +> +> *函数名* → [*标识符*](02_Lexical_Structure.md#identifier) | [*运算符*](02_Lexical_Structure.md#operator) + + +> *函数签名* → [*参数子句列表*](#parameter-clauses) **throws**可选 [*函数结果*](#function-result)可选 +> *函数签名* → [*参数子句列表*](#parameter-clauses) **rethrows** [*函数结果*](#function-result)可选 +> +> *函数结果* → **->** [*特性列表*](06_Attributes.md#attributes)可选 [*类型*](03_Types.md#type) +> +> *函数体* → [*代码块*](#code-block) + + +> *参数子句列表* → [*参数子句*](#parameter-clause) [*参数子句列表*](#parameter-clauses)可选 +> +> *参数子句* → **(** **)** | **(** [*参数列表*](#parameter-list) **)** +> +> *参数列表* → [*参数*](#parameter) | [*参数*](#parameter) **,** [*参数列表*](#parameter-list) +> +> *参数* → **let**可选 [*外部参数名*](#external-parameter-name)可选 [*内部参数名*](#local-parameter-name) [*类型标注*](03_Types.md#type-annotation) [*默认参数子句*](#default-argument-clause)可选 +> *参数* → **inout** [*外部参数名*](#external-parameter-name)可选 [*内部参数名*](#local-parameter-name) [*类型标注*](03_Types.md#type-annotation) +> *参数* → [*外部参数名*](#external-parameter-name)可选 [*内部参数名*](#local-parameter-name) [*类型标注*](03_Types.md#type-annotation) **...** +> +> *外部参数名* → [*标识符*](02_Lexical_Structure.md#identifier) | **_** +> +> *内部参数名* → [*标识符*](02_Lexical_Structure.md#identifier) | **_** +> +> *默认参数子句* → **=** [*表达式*](04_Expressions.md#expression) + + +## 枚举声明 + +在程序中使用*枚举声明 (enumeration declaration)* 来引入一个枚举类型。 + +枚举声明有两种基本形式,使用关键字 `enum` 来声明。枚举声明体包含零个或多个值,称为枚举用例,还可包含任意数量的声明,包括计算型属性,实例方法,类型方法,构造器,类型别名,甚至其他枚举,结构体,和类。枚举声明不能包含析构器或者协议声明。 + +枚举类型可以采纳任意数量的协议,但是枚举不能从类,结构体和其他枚举继承。 + +不同于类或者结构体,枚举类型并不提供隐式的默认构造器,所有构造器必须显式声明。一个构造器可以委托给枚举中的其他构造器,但是构造过程仅当构造器将一个枚举用例指定给 `self` 后才算完成。 + +和结构体类似但是和类不同,枚举是值类型。枚举实例在被赋值到变量或常量时,或者传递给函数作为参数时会被复制。更多关于值类型的信息,请参阅 [结构体和枚举是值类型](../chapter2/09_Classes_and_Structures.md#structures_and_enumerations_are_value_types)。 + +可以扩展枚举类型,正如在 [扩展声明](#extension_declaration) 中讨论的一样。 + + +### 包含任意类型用例的枚举 + +如下的形式声明了一个包含任意类型枚举用例的枚举变量: + +```swift +enum <#枚举名称#>: <#采纳的协议#> { + case <#枚举用例1#> + case <#枚举用例2#>(<#关联值类型#>) +} +``` + +这种形式的枚举声明在其他语言中有时被叫做可识别联合。 + +在这种形式中,每个用例块由关键字 `case` 开始,后面紧接一个或多个以逗号分隔的枚举用例。每个用例名必须是独一无二的。每个用例也可以指定它所存储的指定类型的值,这些类型在关联值类型的元组中被指定,紧跟用例名之后。 + +具有关联值的枚举用例可以像函数一样使用,从而通过指定的关联值创建一个枚举实例。和真正的函数一样,你可以获取一个枚举用例的引用,然后在后续代码中调用它。 + +```swift +enum Number { + case Integer(Int) + case Real(Double) +} + +// f 的类型为 (Int) -> Number +let f = Number.Integer + +// 利用 f 把一个整数数组转成 Number 数组 +let evenInts: [Number] = [0, 2, 4, 6].map(f) +``` + +要获得更多关于具有关联值的枚举用例的信息和例子,请参阅 [关联值](../chapter2/08_Enumerations.md#associated_values)。 + + +### 递归枚举 + +枚举类型可以具有递归结构,就是说,枚举用例的关联值类型可以是枚举类型自身。然而,枚举类型的实例具有值语义,这意味着它们在内存中有着固定的位置。为了支持递归,编译器必须插入一个间接层。 + +要让某个枚举用例支持递归,使用 `indirect` 声明修饰符标记该用例。 + +```swift +enum Tree { + case Empty + indirect case Node(value: T, left: Tree, right:Tree) +} +``` + +要让一个枚举类型的所有用例都支持递归,使用 `indirect` 修饰符标记整个枚举类型,当枚举有多个用例且每个用例都需要使用 `indirect` 修饰符标记的时候这将非常便利。 + +被 `indirect` 修饰符标记的枚举用例必须有一个关联值。使用 `indirect` 修饰符标记的枚举类型可以既包含有关联值的用例,同时还可包含没有关联值的用例。但是,它不能再单独使用 `indirect` 修饰符来标记某个用例。 + + +### 包含原始值类型的枚举 + +以下形式声明了一种枚举类型,其中各个枚举用例的类型均为同一种基本类型: + +```swift +enum <#枚举名称#>: <#原始值类型#>, <#采纳的协议#> { + case <#枚举用例1#> = <#原始值1#> + case <#枚举用例2#> = <#原始值2#> +} +``` + +在这种形式中,每一个用例块由 `case` 关键字开始,后面紧跟一个或多个以逗号分隔的枚举用例。和第一种形式的枚举用例不同,这种形式的枚举用例包含一个基础值,叫做原始值,各个枚举用例的原始值的类型必须相同。这些原始值的类型通过原始值类型指定,必须表示一个整数,浮点数,字符串,或者字符。原始值类型必须符合 `Equatable` 协议和下列字面量转换协议中的一种:整型字面量需符合 `IntergerLiteralConvertible` 协议,浮点型字面量需符合 `FloatingPointLiteralConvertible` 协议,包含任意数量字符的字符串型字面量需符合 `StringLiteralConvertible` 协议,仅包含一个单一字符的字符串型字面量需符合 `ExtendedGraphemeClusterLiteralConvertible` 协议。每一个用例的名字和原始值必须唯一。 + +如果原始值类型被指定为 `Int`,则不必为用例显式地指定原始值,它们会隐式地被赋值 `0`,`1`,`2` 等。每个未被赋值的 `Int` 类型的用例会被隐式地赋值,其值为上一个用例的原始值加 `1`。 + +```Swift +enum ExampleEnum: Int { + case A, B, C = 5, D +} +``` + +在上面的例子中,`ExampleEnum.A` 的原始值是 `0`,`ExampleEnum.B` 的原始值是 `1`。因为 `ExampleEnum.C` 的原始值被显式地设定为 `5`,因此 `ExampleEnum.D` 的原始值会自动增长为 `6`。 + +如果原始值类型被指定为 `String` 类型,你不用明确地为用例指定原始值,每个没有指定原始值的用例会隐式地将用例名字作为原始值。 + +```swift +enum WeekendDay: String { + case Saturday, Sunday +} +``` + +在上面这个例子中,`WeekendDay.Saturday` 的原始值是 `"Saturday"`,`WeekendDay.Sunday` 的原始值是 `"Sunday"`。 + +枚举用例具有原始值的枚举类型隐式地符合定义在 Swift 标准库中的 `RawRepresentable` 协议。所以,它们拥有一个 `rawValue` 属性和一个可失败构造器 `init?(rawValue: RawValue)`。可以使用 `rawValue` 属性去获取枚举用例的原始值,例如 `ExampleEnum.B.rawValue`。还可以根据原始值来获取一个相对应的枚举用例,只需调用枚举的可失败构造器,例如 `ExampleEnum(rawValue: 5)`,这个可失败构造器返回一个可选类型的用例。要获得更多关于具有原始值的枚举用例的信息和例子,请参阅 [原始值](../chapter2/08_Enumerations.md#raw_values)。 + + +### 访问枚举用例 + +使用点语法(`.`)来引用枚举类型的枚举用例,例如 `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` 块中的枚举用例模式,正如 [枚举用例模式](07_Patterns.md#enumeration_case_pattern) 所述。 + + +> 枚举声明语法 + + +> *枚举声明* → [*特性列表*](06_Attributes.md#attributes)可选 [*访问级别修饰符*](#access-level-modifier)可选 [*联合风格枚举*](#union-style-enum) +> *枚举声明* → [*特性列表*](06_Attributes.md#attributes)可选 [*访问级别修饰符*](#access-level-modifier) 可选 [*原始值风格枚举*](#raw-value-style-enum) + + +> *联合风格枚举* → **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-member) [*多个联合风格枚举成员*](#union-style-enum-members)可选 +> +> *联合风格枚举成员* → [*声明*](#declaration) | [*联合风格枚举用例子句*](#union-style-enum-case-clause) +> +> *联合风格枚举用例子句* → [*特性列表*](06_Attributes.md#attributes)可选 **indirect**可选 **case** [*联合风格枚举用例列表*](#union-style-enum-case-list) +> +> *联合风格枚举用例列表* → [*联合风格枚举用例*](#union-style-enum-case) | [*联合风格枚举用例*](#union-style-enum-case) **,** [*联合风格枚举用例列表*](#union-style-enum-case-list) +> +> *联合风格枚举用例* → [*枚举用例名称*](#enum-case-name) [*元组类型*](03_Types.md#tuple-type)可选 +> +> *枚举名称* → [*标识符*](02_Lexical_Structure.md#identifier) +> +> *枚举用例名称* → [*标识符*](02_Lexical_Structure.md#identifier) + + +> *原始值风格枚举* → **enum** [*枚举名称*](#enum-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)可选 [类型继承子句](03_Types.md#type-inheritance-clause) **{** [*多个原始值风格枚举成员*](#raw-value-style-enum-members) **}** +> +> *多个原始值风格枚举成员* → [*原始值风格枚举成员*](#raw-value-style-enum-member) [*多个原始值风格枚举成员*](#raw-value-style-enum-members)可选 +> +> *原始值风格枚举成员* → [*声明*](#declaration) | [*原始值风格枚举用例子句*](#raw-value-style-enum-case-clause) +> +> *原始值风格枚举用例子句* → [*特性列表*](06_Attributes.md#attributes)可选 **case** [*原始值风格枚举用例列表*](#raw-value-style-enum-case-list) +> +> *原始值风格枚举用例列表* → [*原始值风格枚举用例*](#raw-value-style-enum-case) | [*原始值风格枚举用例*](#raw-value-style-enum-case) **,** [*原始值风格枚举用例列表*](#raw-value-style-enum-case-list) +> +> *原始值风格枚举用例* → [*枚举用例名称*](#enum-case-name) [*原始值赋值*](#raw-value-assignment)可选 +> +> *原始值赋值* → **=** [*原始值字面量*](#raw-value-literal) +> +> *原始值字面量* → [数字型字面量](02_Lexical_Structure.md#numeric-literal) | [字符串型字面量](02_Lexical_Structure.md#static-string-literal) | [布尔型字面量](02_Lexical_Structure.md#boolean-literal) + + +## 结构体声明 + +使用*结构体声明 (structure declaration)* 可以在程序中引入一个结构体类型。结构体声明使用 `struct` 关键字,遵循如下的形式: + +```swift +struct <#结构体名称#>: <#采纳的协议#> { + <#多条声明#> +} +``` + +结构体内可包含零个或多个声明。这些声明可以包括存储型和计算型属性,类型属性,实例方法,类型方法,构造器,下标,类型别名,甚至其他结构体,类,和枚举声明。结构体声明不能包含析构器或者协议声明。关于结构体的详细讨论和示例,请参阅 [类和结构体](../chapter2/09_Classes_and_Structures.md)。 + +结构体可以采纳任意数量的协议,但是不能继承自类,枚举或者其他结构体。 + +有三种方法可以创建一个声明过的结构体实例: + +* 调用结构体内声明的构造器,正如 [构造器](../chapter2/14_Initialization.md#initializers) 所述。 + +* 如果没有声明构造器,调用结构体的成员逐一构造器,正如 [结构体类型的成员逐一构造器](../chapter2/14_Initialization.md#memberwise_initializers_for_structure_types) 所述。 + +* 如果没有声明构造器,而且结构体的所有属性都有初始值,调用结构体的默认构造器,正如 [默认构造器](../chapter2/14_Initialization.md#default_initializers) 所述。 + +结构体的构造过程请参阅 [构造过程](../chapter2/14_Initialization.md)。 + +结构体实例的属性可以用点语法(`.`)来访问,正如 [访问属性](../chapter2/09_Classes_and_Structures.md#accessing_properties) 所述。 + +结构体是值类型。结构体的实例在被赋予变量或常量,或传递给函数作为参数时会被复制。关于值类型的更多信息,请参阅 +[结构体和枚举是值类型](../chapter2/09_Classes_and_Structures.md#structures_and_enumerations_are_value_types)。 + +可以使用扩展声明来扩展结构体类型的行为,请参阅 [扩展声明](#extension_declaration)。 + + +> 结构体声明语法 +> +> *结构体声明* → [*特性列表*](06_Attributes.md#attributes)可选 [*访问级别修饰符*](#access-level-modifier) 可选 **struct** [*结构体名称*](#struct-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)可选 [类型继承子句](03_Types.md#type-inheritance-clause)可选 [*结构体主体*](#struct-body) +> +> *结构体名称* → [*标识符*](02_Lexical_Structure.md#identifier) +> +> *结构体主体* → **{** [*多条声明*](#declarations)可选 **}** + + +## 类声明 + +可以在程序中使用*类声明 (class declaration)* 来引入一个类。类声明使用关键字 `class`,遵循如下的形式: + +```swift +class <#类名#>: <#超类#>, <#采纳的协议#> { + <#多条声明#> +} +``` + +类内可以包含零个或多个声明。这些声明可以包括存储型和计算型属性,实例方法,类型方法,构造器,析构器,下标,类型别名,甚至其他结构体,类,和枚举声明。类声明不能包含协议声明。关于类的详细讨论和示例,请参阅 [类和结构体](../chapter2/09_Classes_and_Structures.md)。 + +一个类只能继承自一个超类,但是可以采纳任意数量的协议。超类紧跟在类名和冒号后面,其后跟着采纳的协议。泛型类可以继承自其它泛型类和非泛型类,但是非泛型类只能继承自其它非泛型类。当在冒号后面写泛型超类的名称时,必须写上泛型类的全名,包括它的泛型形参子句。 + +正如 [构造器声明](#initializer_declaration) 所讨论的,类可以有指定构造器和便利构造器。类的指定构造器必须初始化类中声明的所有属性,并且必须在调用超类构造器之前。 + +类可以重写属性,方法,下标,以及构造器。重写的属性,方法,下标,和指定构造器必须以 `override` 声明修饰符标记。 + +为了要求子类去实现超类的构造器,使用 `required` 声明修饰符标记超类的构造器。子类实现超类构造器时也必须使用 `required` 声明修饰符。 + +虽然超类属性和方法声明可以被当前类继承,但是超类声明的指定构造器却不能。即便如此,如果当前类重写了超类的所有指定构造器,它就继承了超类的便利构造器。Swift 的类并不继承自一个通用基础类。 + +有两种方法来创建已声明的类的实例: + +* 调用类中声明的构造器,请参阅 [构造器](../chapter2/14_Initialization.md#initializers)。 + +* 如果没有声明构造器,而且类的所有属性都被赋予了初始值,调用类的默认构造器,请参阅 [默认构造器](../chapter2/14_Initialization.md#default_initializers)。 + +类实例属性可以用点语法(`.`)来访问,请参阅 [访问属性](../chapter2/09_Classes_and_Structures.md#accessing_properties)。 + +类是引用类型。当被赋予常量或变量,或者传递给函数作为参数时,类的实例会被引用,而不是被复制。关于引用类型的更多信息,请参阅 [结构体和枚举是值类型](../chapter2/09_Classes_and_Structures.md#structures_and_enumerations_are_value_types)。 + +可以使用扩展声明来扩展类的行为,请参阅 [扩展声明](#extension_declaration)。 + + +> 类声明语法 +> +> *类声明* → [*特性列表*](06_Attributes.md#attributes)可选 [访问级别修饰符](#access-level-modifier)可选 **class** [*类名*](#class-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)可选 [*类型继承子句*](03_Types.md#type-inheritance-clause)可选 [*类主体*](#class-body) +> +> *类名* → [*标识符*](02_Lexical_Structure.md#identifier) +> +> *类主体* → **{** [*多条声明*](#declarations)可选 **}** + + +## 协议声明 + +*协议声明 (protocol declaration)* 可以为程序引入一个命名的协议类型。协议声明只能在全局区域使用 `protocol` 关键字来进行声明,并遵循如下形式: + +```swift +protocol <#协议名称#>: <#继承的协议#> { + <#协议成员声明#> +} +``` + +协议的主体包含零个或多个协议成员声明,这些成员描述了任何采纳该协议的类型必须满足的一致性要求。一个协议可以声明采纳者必须实现的某些属性、方法、构造器以及下标。协议也可以声明特殊类型的类型别名,叫做关联类型,它可以指定协议的不同声明之间的关系。协议声明不能包括类,结构体,枚举或者其它协议的声明。协议成员声明会在后面进行讨论。 + +协议类型可以继承自任意数量的其它协议。当一个协议类型继承自其它协议的时候,来自其它协议的所有要求会聚合在一起,而且采纳当前协议的类型必须符合所有的这些要求。关于如何使用协议继承的例子,请参阅 [协议继承](../chapter2/22_Protocols.md#protocol_inheritance)。 + +> 注意 +> 也可以使用协议合成类型来集合多个协议的一致性要求,请参阅 [协议合成类型](03_Types.md#protocol_composition_type) 和 [协议合成](../chapter2/22_Protocols.md#protocol_composition)。 + +可以通过类型的扩展声明来采纳协议,从而为之前声明的类型添加协议一致性。在扩展中,必须实现所有采纳协议的要求。如果该类型已经实现了所有的要求,可以让这个扩展声明的主体留空。 + +默认地,符合某个协议的类型必须实现所有在协议中声明的属性、方法和下标。即便如此,可以用 `optional` 声明修饰符标注协议成员声明,以指定它们的实现是可选的。`optional` 修饰符仅仅可以用于使用 `objc` 特性标记过的协议。因此,仅仅类类型可以采用并符合包含可选成员要求的协议。更多关于如何使用 `optional` 声明修饰符的信息,以及如何访问可选协议成员的指导——例如不能确定采纳协议的类型是否实现了它们时——请参阅 [可选协议要求](../chapter2/22_Protocols.md#optional_protocol_requirements) + +为了限制协议只能被类类型采纳,需要使用 `class` 关键字来标记协议,将 `class` 关键在写在冒号后面的继承的协议列表的首位。例如,下面的协议只能被类类型采纳: + +```swift +protocol SomeProtocol: class { + /* 这里是协议成员 */ +} +``` + +任何继承自标记有 `class` 关键字的协议的协议也仅能被类类型采纳。 + +> 注意 +> 如果协议已经用 `objc` 特性标记了,`class` 要求就隐式地应用于该协议,无需显式使用 `class` 关键字。 + +协议类型是命名的类型,因此它们可以像其他命名类型一样使用,正如 [协议作为类型](../chapter2/22_Protocols.md#protocols_as_types) 所讨论的。然而,不能构造一个协议的实例,因为协议实际上不提供它们指定的要求的实现。 + +可以使用协议来声明作为代理的类或者结构体应该实现的方法,正如 [委托(代理)模式](../chapter2/22_Protocols.md#delegation) 中所述。 + + +> 协议声明语法 + + +> *协议声明* → [*特性列表*](06_Attributes.md#attributes)可选 [访问级别修饰符](#access-level-modifier)可选 **protocol** [*协议名称*](#protocol-name) [*类型继承子句*](03_Types.html#type-inheritance-clause)可选 [*协议主体*](#protocol-body) +> +> *协议名称* → [*标识符*](02_Lexical_Structure.md#identifier) +> +> *协议主体* → **{** [*协议成员声明列表*](#protocol-member-declarations)可选 **}** + + +> *协议成员声明* → [*协议属性声明*](#protocol-property-declaration) +> *协议成员声明* → [*协议方法声明*](#protocol-method-declaration) +> *协议成员声明* → [*协议构造器声明*](#protocol-initializer-declaration) +> *协议成员声明* → [*协议下标声明*](#protocol-subscript-declaration) +> *协议成员声明* → [*协议关联类型声明*](#protocol-associated-type-declaration) +> +> *协议成员声明列表* → [*协议成员声明*](#protocol-member-declaration) [*协议成员声明列表*](#protocol-member-declarations)可选 + + +### 协议属性声明 + +协议可以通过在协议声明主体中引入一个协议属性声明,来声明符合的类型必须实现的属性。协议属性声明有一种特殊的变量声明形式: + +```swift +var <#属性名#>: <#类型#> { get set } +``` + +同其它协议成员声明一样,这些属性声明仅仅针对符合该协议的类型声明了 getter 和 setter 要求,你不能在协议中直接实现 getter 和 setter。 + +符合类型可以通过多种方式满足 getter 和 setter 要求。如果属性声明包含 `get` 和 `set` 关键字,符合类型就可以用可读写(实现了 getter 和 setter)的存储型变量属性或计算型属性来满足此要求,但是属性不能以常量属性或只读计算型属性实现。如果属性声明仅仅包含 `get` 关键字的话,它可以作为任意类型的属性被实现。关于如何实现协议中的属性要求的例子,请参阅 [属性要求](../chapter2/22_Protocols.md#property_requirements) + +另请参阅 [变量声明](#variable_declaration)。 + + +> 协议属性声明语法 +> +> *协议属性声明* → [*变量声明头*](#variable-declaration-head) [*变量名称*](#variable-name) [*类型标注*](03_Types.md#type-annotation) [*getter-setter关键字代码块*](#getter-setter-keyword-block) + + +### 协议方法声明 + +协议可以通过在协议声明主体中引入一个协议方法声明,来声明符合的类型必须实现的方法。协议方法声明和函数方法声明有着相同的形式,但有两项例外:它们不包括函数体,也不能包含默认参数。关于如何实现协议中的方法要求的例子,请参阅 [方法要求](../chapter2/22_Protocols.md#method_requirements)。 + +使用 `static` 声明修饰符可以在协议声明中声明一个静态方法。结构体实现这些方法时使用 `static` 声明修饰符,类在实现这些方法时,除了使用 `static` 声明修饰符,还可以选择使用 `class` 声明修饰符。通过扩展实现时亦是如此。 + +另请参阅 [函数声明](#function_declaration)。 + + +> 协议方法声明语法 +> +> *协议方法声明* → [*函数头*](#function-head) [*函数名*](#function-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)可选 [*函数签名*](#function-signature) + + +### 协议构造器声明 + +协议可以通过在协议声明主体中引入一个协议构造器声明,来声明符合的类型必须实现的构造器。协议构造器声明 +除了不包含实现主体外,和构造器声明有着相同的形式。 + +符合类型可以通过实现一个非可失败构造器或者 `init!` 可失败构造器来满足一个非可失败协议构造器的要求,可以通过实现任意类型的构造器来满足一个可失败协议构造器的要求。 + +类在实现一个构造器去满足一个协议的构造器要求时,如果这个类还没有用 `final` 声明修饰符标记,这个构造器必须用 `required` 声明修饰符标记。 + +另请参阅 [构造器声明](#initializer_declaration)。 + + +> 协议构造器声明语法 +> +> *协议构造器声明* → [*构造器头*](#initializer-head) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)可选 [*参数子句*](#parameter-clause) **throws**可选 +> *协议构造器声明* → [*构造器头*](#initializer-head) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)可选 [*参数子句*](#parameter-clause) **rethrows** + + +### 协议下标声明 + +协议可以通过在协议声明主体中引入一个协议下标声明,来声明符合的类型必须实现的下标。协议下标声明有一个特殊的下标声明形式: + +```swift +subscript (<#参数列表#>) -> <#返回类型#> { get set } +``` + +下标声明只为符合类型声明了 getter 和 setter 要求。如果下标声明包含 `get` 和 `set` 关键字,符合类型也必须实现 getter 和 setter 子句。如果下标声明只包含 `get` 关键字,符合类型必须实现 getter 子句,可以选择是否实现 setter 子句。 + +另请参阅 [下标声明](#subscript_declaration)。 + + +> 协议下标声明语法 +> +> *协议下标声明* → [*下标头*](#subscript-head) [*下标结果*](#subscript-result) [*getter-setter关键字代码块*](#getter-setter-keyword-block) + + +### 协议关联类型声明 + +使用关键字 `associatedtype` 来声明协议关联类型。关联类型为作为协议声明的一部分,为某种类型提供了一个别名。关联类型和泛型参数子句中的类型参数很相似,但是它们和 `Self` 一样,用于协议中。`Self` 指代采纳协议的类型。要获得更多信息和例子,请参阅 [关联类型](../chapter2/23_Generics.md#associated_types)。 + +另请参阅 [类型别名声明](#type_alias_declaration)。 + + +> 协议关联类型声明语法 +> +> *协议关联类型声明* → [*类型别名头*](#typealias-head) [*类型继承子句*](03_Types.md#type-inheritance-clause)可选 [*类型别名赋值*](#typealias-assignment)可选 + + +## 构造器声明 + +构造器声明会为程序中的类,结构体或枚举引入构造器。构造器使用关键字 `init` 来声明,有两种基本形式。 + +结构体,枚举,类可以有任意数量的构造器,但是类的构造器具有不同的规则和行为。不同于结构体和枚举,类有两种构造器,即指定构造器和便利构造器,请参阅 [构造过程](../chapter2/14_Initialization.md)。 + +采用如下形式声明结构体和枚举的构造器,以及类的指定构造器: + +```swift +init(<#参数列表#>) { + <#构造语句#> +} +``` + +类的指定构造器直接将类的所有属性初始化。它不能调用类中的其他构造器,如果类有超类,它还必须调用超类的一个指定构造器。如果该类从它的超类继承了属性,必须在调用超类的指定构造器后才能修改这些属性。 + +指定构造器只能在类声明的上下文中声明,不能在扩展声明中声明。 + +结构体和枚举的构造器可以调用其他已声明的构造器,委托其他构造器来进行部分或者全部构造过程。 + +要为类声明一个便利构造器,用 `convenience` 声明修饰符来标记构造器声明: + +```swift +convenience init(<#参数列表#>) { + <#构造语句#> +} +``` + +便利构造器可以将构造过程委托给另一个便利构造器或一个指定构造器。但是,类的构造过程必须以一个将类中所有属性完全初始化的指定构造器的调用作为结束。便利构造器不能调用超类的构造器。 + +可以使用 `required` 声明修饰符,将便利构造器和指定构造器标记为每个子类都必须实现的构造器。这种构造器的子类实现也必须使用 `required` 声明修饰符标记。 + +默认情况下,超类中的构造器不会被子类继承。但是,如果子类的所有存储型属性都有默认值,而且子类自身没有定义任何构造器,它将继承超类的构造器。如果子类重写了超类的所有指定构造器,子类将继承超类的所有便利构造器。 + +和方法,属性和下标一样,需要使用 `override` 声明修饰符标记重写的指定构造器。 + +> 注意 +> 如果使用 `required` 声明修饰符标记一个构造器,在子类中重写这种构造器时,无需使用 `override` 修饰符。 + +就像函数和方法,构造器也可以抛出或者重抛出错误,你可以在构造器参数列表的圆括号之后使用 `throws` 或 `rethrows` 关键字来表明相应的抛出行为。 + +关于在不同类型中声明构造器的例子,请参阅 [构造过程](../chapter2/14_Initialization.md)。 + + +### 可失败构造器 + +可失败构造器可以生成所属类型的可选实例或者隐式解包可选实例,因此,这种构造器通过返回 `nil` 来指明构造过程失败。 + +声明生成可选实例的可失败构造器时,在构造器声明的 `init` 关键字后加追加一个问号(`init?`)。声明生成隐式解包可选实例的可失败构造器时,在构造器声明后追加一个叹号(`init!`)。使用 `init?` 可失败构造器生成结构体的一个可选实例的例子如下。 + +```swift +struct SomeStruct { + let string: String + //生成一个 SomeStruct 的可选实例 + init?(input: String) { + if input.isEmpty { + // 丢弃 self,并返回 nil + return nil + } + string = input + } +} +``` + +调用 `init?` 可失败构造器和调用非可失败构造器的方式相同,不过你需要处理可选类型的返回值。 + +```swift +if let actualInstance = SomeStruct(input: "Hello") { + // 利用 SomeStruct 实例做些事情 +} else { + // SomeStruct 实例的构造过程失败,构造器返回了 nil +} +``` + +结构体或者枚举的可失败构造器可以在构造器实现中的任意位置返回 `nil`。 + +可失败构造器可以委托任意种类的构造器。非可失败可以委托其它非可失败构造器或者 `init!` 可失败构造器。非可失败构造器可以委托超类的 `init?` 可失败指定构造器,但是需要使用强制解包,例如 `super.init()!`。 + +构造过程的失败通过构造器委托来传递。具体来说,如果可失败构造器委托的可失败构造器构造过程失败并返回 `nil`,那么该可失败构造器也会构造失败并隐式地返回 `nil`。如果非可失败构造器委托的 `init!` 可失败构造器构造失败并返回了 `nil`,那么会发生运行时错误(如同使用 `!` 操作符去强制解包一个值为 `nil` 的可选值)。 + +子类可以用任意种类的指定构造器重写超类的可失败指定构造器,但是只能用非可失败指定构造器重写超类的非可失败指定构造器。 + +更多关于可失败构造器的信息和例子,请参阅 [可失败构造器](../chapter2/14_Initialization.md#failable_initializers)。 + + +> 构造器声明语法 +> +> *构造器声明* → [*构造器头*](#initializer-head) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)可选 [*参数子句*](#parameter-clause) **throws**可选 [*构造器主体*](#initializer-body) +> *构造器声明* → [*构造器头*](#initializer-head) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)可选 [*参数子句*](#parameter-clause) **rethrows**可选 [*构造器主体*](#initializer-body) +> +> *构造器头* → [*特性列表*](06_Attributes.md#attributes)可选 [*声明修饰符列表*](#declaration-modifiers)可选 **init** +> *构造器头* → [*特性列表*](06_Attributes.md#attributes)可选 [*声明修饰符列表*](#declaration-modifiers)可选 **init** **?** +> *构造器头* → [*特性列表*](06_Attributes.md#attributes)可选 [*声明修饰符列表*](#declaration-modifiers)可选 **init** **!** +> +> *构造器主体* → [*代码块*](#code-block) + + +## 析构器声明 + +*析构器声明 (deinitializer declaration)* 可以为类声明一个析构器。析构器没有参数,遵循如下格式: + +```swift +deinit { + <#语句#> +} +``` + +当没有任何强引用引用着类的对象,对象即将被释放时,析构器会被自动调用。析构器只能在类主体的声明中声明,不能在类的扩展声明中声明,并且每个类最多只能有一个析构器。 + +子类会继承超类的析构器,并会在子类对象将要被释放时隐式调用。继承链上的所有析构器全部调用完毕后子类对象才会被释放。 + +析构器不能直接调用。 + +关于如何在类声明中使用析构器的例子,请参阅 [析构过程](../chapter2/15_Deinitialization.md)。 + + +> 析构器声明语法 +> +> *析构器声明* → [*特性列表*](06_Attributes.md#attributes)可选 **deinit** [*代码块*](#code-block) + + +## 扩展声明 + +*扩展声明 (extension declaration)* 可以扩展一个现存的类,结构体,和枚举类型的行为。扩展声明使用关键字 `extension`,遵循如下格式: + +```swift +extension <#类型名称#>: <#采纳的协议#> { + <#声明语句#> +} +``` + +扩展声明体可包含零个或多个声明语句。这些声明语句可以包括计算型属性,计算型类型属性,实例方法,类型方法,构造器,下标声明,甚至其他结构体,类,和枚举声明。扩展声明不能包含析构器,协议声明,存储型属性,属性观察器,或其他扩展声明。关于扩展声明的详细讨论,以及各种扩展声明的例子,请参阅 [扩展](../chapter2/21_Extensions.md)。 + +扩展声明可以为现存的类,结构体,枚举添加协议一致性,但是不能为类添加超类,因此,在扩展声明的类型名称的冒号后面仅能指定一个协议列表。 + +现存类型的属性,方法,构造器不能在扩展中被重写。 + +扩展声明可以包含构造器声明。这意味着,如果被扩展的类型在其他模块中定义,构造器声明必须委托另一个在那个模块中声明的构造器,以此确保该类型能被正确地初始化。 + + +> 扩展声明语法 +> +> *扩展声明* → [访问级别修饰符](#access-level-modifier)可选 **extension** [*类型标识符*](03_Types.md#type-identifier) [*类型继承子句*](03_Types.md#type-inheritance-clause)可选 [*扩展主体*](#extension-body) +> +> *扩展主体* → **{** [*多条声明*](#declarations)可选 **}** + + +## 下标声明 + +*下标声明 (subscript declaration)* 用于为特定类型的对象添加下标支持,通常也用于为访问集合,列表和序列中的元素提供语法便利。下标声明使用关键字 `subscript`,形式如下: + +```swift +subscript (<#参数列表#>) -> <#返回类型#> { + get { + <#语句#> + } + set(<#setter 名称#>) { + <#语句#> + } +} +``` + +下标声明只能出现在类,结构体,枚举,扩展和协议声明的上下文中。 + +参数列表指定一个或多个用于在相关类型的下标表达式中访问元素的索引(例如,表达式 `object[i]` 中的 `i`)。索引可以是任意类型,但是必须包含类型标注。返回类型指定被访问的元素的类型。 + +和计算型属性一样,下标声明支持对元素的读写操作。getter 用于读取值,setter 用于写入值。setter 子句是可选的,当仅需要一个 getter 子句时,可以将二者都忽略,直接返回请求的值即可。但是,如果提供了 setter 子句,就必须提供 getter 子句。 + +圆括号以及其中的 setter 名称是可选的。如果提供了 setter 名称,它会作为 setter 的参数名称。如果不提供 setter 名称,那么 setter 的参数名称默认是 `value`。setter 名称的类型必须与返回类型相同。 + +可以重载下标,只要参数列表或返回类型与先前不同即可。还可以重写继承自超类的下标,此时必须使用 `override` 声明修饰符声明被重写的下标。 + +同样可以在协议声明的上下文中声明下标,正如 [协议下标声明](#protocol_subscript_declaration) 中所述。 + +更多关于下标的信息和例子,请参阅 [下标](../chapter2/12_Subscripts.md)。 + + +> 下标声明语法 +> +> *下标声明* → [*下标头*](#subscript-head) [*下标结果*](#subscript-result) [*代码块*](#code-block) +> *下标声明* → [*下标头*](#subscript-head) [*下标结果*](#subscript-result) [*getter-setter代码块*](#getter-setter-block) +> *下标声明* → [*下标头*](#subscript-head) [*下标结果*](#subscript-result) [*getter-setter关键字代码块*](#getter-setter-keyword-block) +> +> *下标头* → [*特性列表*](06_Attributes.md#attributes)可选 [*声明修饰符列表*](#declaration-modifiers)可选 **subscript** [*参数子句*](#parameter-clause) +> +> *下标结果* → **->** [*特性列表*](06_Attributes.md#attributes)可选 [*类型*](03_Types.md#type) + + +## 运算符声明 + +*运算符声明 (operator declaration)* 会向程序中引入中缀、前缀或后缀运算符,使用关键字 `operator` 来声明。 + +可以声明三种不同的缀性:中缀、前缀和后缀。运算符的缀性指定了运算符与其运算对象的相对位置。 + +运算符声明有三种基本形式,每种缀性各一种。运算符的缀性通过在 `operator` 关键字之前添加声明修饰符 `infix`,`prefix` 或 `postfix` 来指定。每种形式中,运算符的名字只能包含 [运算符](02_Lexical_Structure.md#operators) 中定义的运算符字符。 + +下面的形式声明了一个新的中缀运算符: + +```swift +infix operator <#运算符名称#> { + precedence <#优先级#> + associativity <#结合性#> +} +``` + +中缀运算符是二元运算符,置于两个运算对象之间,例如加法运算符(`+`)位于表达式 `1 + 2` 的中间。 + +中缀运算符可以选择指定优先级或结合性,或者两者同时指定。 + +运算符的优先级可以指定在没有括号包围的情况下,运算符与其运算对象如何结合。可以使用上下文相关的关键字 `precedence` 以及紧随其后的优先级数字来指定一个运算符的优先级。优先级可以是 `0` 到 `255` 之间的任何十进制整数。与十进制整数字面量不同的是,它不可以包含任何下划线字符。尽管优先级是一个特定的数字,但它仅用作与另一个运算符的优先级比较大小。也就是说,当两个运算符为同一个运算对象竞争时,例如 `2 + 3 * 5`,优先级更高的运算符将优先参与运算。 + +运算符的结合性可以指定在没有括号包围的情况下,多个优先级相同的运算符将如何组合。可以使用上下文相关的关键字 `associativity` 以及紧随其后的结合性关键字来指定运算符的结合性,结合性关键字也是上下文相关的,包括 `left`,`right`,和 `none`。左结合运算符以从左到右的顺序进行组合。例如,减法运算符(`-`)具有左结合性,因此 `4 - 5 - 6` 以 `(4 - 5) - 6` 的形式组合,其结果为 `-7`。右结合运算符以从右到左的顺序组合,而设置为 `none` 的运算符不进行组合。具有相同优先级的非结合运算符,不可以互相邻接。例如,表达式 `1 < 2 < 3` 是非法的。 + +声明时不指定任何优先级或结合性的中缀运算符,优先级为 `100`,结合性为 `none`。 + +下面的形式声明了一个新的前缀运算符: + +```swift +prefix operator <#运算符名称#> {} +``` + +出现在运算对象前边的前缀运算符是一元运算符,例如表达式 `!a` 中的前缀非运算符(`!`)。 + +前缀运算符的声明中不指定优先级,而且前缀运算符是非结合的。 + +下面的形式声明了一个新的后缀运算符: + +```swift +postfix operator <#运算符名称#> {} +``` + +紧跟在运算对象后边的后缀运算符是一元运算符,例如表达式 `i!` 中的后缀强制解包运算符(`!`)。 + +和前缀运算符一样,后缀运算符的声明中不指定优先级,而且后缀运算符是非结合的。 + +声明了一个新的运算符以后,需要实现一个跟这个运算符同名的函数来实现这个运算符。如果是实现一个前缀或者后缀运算符,也必须使用相符的 `prefix` 或者 `postfix` 声明修饰符标记函数声明。如果是实现中缀运算符,则不需要使用 `infix` 声明修饰符标记函数声明。关于如何实现一个新的运算符的例子,请参阅 [自定义运算符](../chapter2/25_Advanced_Operators.md#custom_operators)。 + + +> 运算符声明语法 + + +> *运算符声明* → [*前缀运算符声明*](#prefix-operator-declaration) | [*后缀运算符声明*](#postfix-operator-declaration) | [*中缀运算符声明*](#infix-operator-declaration) + + +> *前缀运算符声明* → **prefix** **运算符** [*运算符*](02_Lexical_Structure.md#operator) **{** **}** +> +> *后缀运算符声明* → **postfix** **运算符** [*运算符*](02_Lexical_Structure.md#operator) **{** **}** +> +> *中缀运算符声明* → **infix** **运算符** [*运算符*](02_Lexical_Structure.md#operator) **{** [*中缀运算符属性*](#infix-operator-attributes)可选 **}** + + +> *中缀运算符属性* → [*优先级子句*](#precedence-clause)可选 [*结和性子句*](#associativity-clause)可选 +> +> *优先级子句* → **precedence** [*优先级水平*](#precedence-level) +> +> *优先级水平* → 十进制整数 0 到 255,包含 0 和 255 +> +> *结和性子句* → **associativity** [*结和性*](#associativity) +> +> *结和性* → **left** | **right** | **none** + + +## 声明修饰符 + +声明修饰符都是关键字或上下文相关的关键字,可以修改一个声明的行为或者含义。可以在声明的特性(如果存在)和引入该声明的关键字之间,利用声明修饰符的关键字或上下文相关的关键字指定一个声明修饰符。 + +`dynamic` + +该修饰符用于修饰任何兼容 Objective-C 的类的成员。访问被 `dynamic` 修饰符标记的类成员将总是由 Objective-C 运行时系统进行动态派发,而不会由编译器进行内联或消虚拟化。 + +因为被标记 `dynamic` 修饰符的类成员会由 Objective-C 运行时系统进行动态派发,所以它们会被隐式标记 `objc` 特性。 + +`final` + +该修饰符用于修饰类或类中的属性,方法,以及下标。如果用它修饰一个类,那么这个类不能被继承。如果用它修饰类中的属性,方法或下标,那么它们不能在子类中被重写。 + +`lazy` + +该修饰符用于修饰类或结构体中的存储型变量属性,表示该属性的初始值最多只被计算和存储一次,且发生在它被第一次访问时。关于如何使用 `lazy` 修饰符的例子,请参阅 [惰性存储型属性](../chapter2/10_Properties.md#lazy_stored_properties)。 + +`optional` + +该修饰符用于修饰协议中的属性,方法,以及下标成员,表示符合类型不必实现这些成员要求。 + +只能将 `optional` 修饰符用于被 `objc` 特性标记的协议。这样一来,就只有类类型可以采纳并符合拥有可选成员要求的协议。关于如何使用 `optional` 修饰符,以及如何访问可选协议成员(比如,不确定符合类型是否已经实现了这些可选成员)的信息,请参阅 [可选协议要求](../chapter2/22_Protocols.md#optional_protocol_requirements)。 + +`required` + +该修饰符用于修饰类的指定构造器或便利构造器,表示该类所有的子类都必须实现该构造器。在子类实现该构造器时,必须同样使用 `required` 修饰符修饰该构造器。 + +`weak` + +该修饰符用于修饰变量或存储型变量属性,表示该变量或属性持有其存储的对象的弱引用。这种变量或属性的类型必须是可选的类类型。使用 `weak` 修饰符可避免强引用循环。关于 `weak` 修饰符的更多信息和例子,请参阅 [弱引用](../chapter2/16_Automatic_Reference_Counting.md#resolving_strong_reference_cycles_between_class_instances)。 + + +### 访问控制级别 + +Swift 提供了三个级别的访问控制:`public`,`internal` 和 `private`。可以使用以下任意一种访问级别修饰符来指定声明的访问级别。访问控制在 [访问控制](../chapter2/24_Access_Control.md) 中有详细讨论。 + +`public` + +该修饰符表示声明可被同模块的代码访问,只要其他模块导入了声明所在的模块,也可以进行访问。 + +`internal` + +该修饰符表示声明只能被同模块的代码访问。默认情况下,绝大多数声明会被隐式标记 `internal` 访问级别修饰符。 + +`private` + +该修饰符表示声明只能被所在源文件的代码访问。 + +以上访问级别修饰符都可以选择带上一个参数,该参数由一对圆括号和其中的 `set` 关键字组成(例如,`private(set)`)。使用这种形式的访问级别修饰符来限制某个属性或下标的 setter 的访问级别低于其本身的访问级别,正如 [Getter 和 Setter](../chapter2/24_Access_Control.md#getters_and_setters) 中所讨论的。 + + +> 声明修饰符的语法 + + +> *声明修饰符* → **class** | **convenience**| **dynamic** | **final** | **infix** | **lazy** | **mutating** | **nonmutating** | **optional** | **override** | **postfix** | **prefix** | **required** | **static** | **unowned** | **unowned ( safe )** | **unowned ( unsafe )** | **weak** +> 声明修饰符 → [*访问级别修饰符*](#access-level-modifier) +> +> *声明修饰符列表* → [*声明修饰符*](#declaration-modifier) [*声明修饰符列表*](#declaration-modifiers)可选 + + +>访问级别修饰符 → **internal** | **internal ( set )** +>访问级别修饰符 → **private** | **private ( set )** +>访问级别修饰符 → **public** | **public ( set )** From 26c5b2ac9a7bf892bdbca1d7b1c4d5ea6f732c66 Mon Sep 17 00:00:00 2001 From: 949478479 <949478479@qq.com> Date: Wed, 4 May 2016 23:40:54 +0800 Subject: [PATCH 26/61] Update 10_Statements.md --- source/chapter3/10_Statements.md | 104 ++++++++++++++++--------------- 1 file changed, 54 insertions(+), 50 deletions(-) diff --git a/source/chapter3/10_Statements.md b/source/chapter3/10_Statements.md index 36e899b5..f623b6a6 100755 --- a/source/chapter3/10_Statements.md +++ b/source/chapter3/10_Statements.md @@ -11,7 +11,6 @@ > 2.2 > 翻译:[chenmingbiao](https://github.com/chenmingbiao) -> 校对:[]() 本页包含内容: @@ -34,10 +33,10 @@ - [Do 语句](#do_statements) - [编译器控制语句](#compiler_control_statements) - [编译配置语句](#build_config_statements) - - [源代码控制语句](#line_control_statements) + - [行控制语句](#line_control_statements) - [可用性条件](#availability_condition) -在 Swift 中,有三种类型的语句:简单语句、编译器控制语句和控制流语句。简单语句是最常见的,用于构造表达式或者声明。编译器控制语句允许程序改变编译器的行为,包含编译配置语句和线路控制语句。 +在 Swift 中,有三种类型的语句:简单语句、编译器控制语句和控制流语句。简单语句是最常见的,用于构造表达式或者声明。编译器控制语句允许程序改变编译器的行为,包含编译配置语句和行控制语句。 控制流语句则用于控制程序执行的流程,Swift 中有多种类型的控制流语句:循环语句、分支语句和控制转移语句。循环语句用于重复执行代码块;分支语句用于执行满足特定条件的代码块;控制转移语句则用于改变代码的执行顺序。另外,Swift 提供了 `do` 语句,用于构建局部作用域,还用于错误的捕获和处理;还提供了 `defer` 语句,用于退出当前作用域之前执行清理操作。 @@ -83,7 +82,7 @@ for 项 in 集合 { } ``` -`for-in` 语句在循环开始前会调用集合表达式的 `generate()` 方法来获取一个符合 `GeneratorType` 协议的类型的值。接下来循环开始,反复调用该值的 `next()` 方法。如果其返回值不是 `None`,它将会被赋给“项”,然后执行循环体语句,执行完毕后回到循环开始处,继续重复这一过程;否则,既不会赋值也不会执行循环体语句,`for-in` 语句至此执行完毕。 +`for-in` 语句在循环开始前会调用集合表达式的 `generate()` 方法来获取一个实现了 `GeneratorType` 协议的类型的值。接下来循环开始,反复调用该值的 `next()` 方法。如果其返回值不是 `None`,它将会被赋给“项”,然后执行循环体语句,执行完毕后回到循环开始处,继续重复这一过程;否则,既不会赋值也不会执行循环体语句,`for-in` 语句至此执行完毕。 > for-in 语句语法 @@ -274,9 +273,9 @@ default: `switch` 语句会先计算控制表达式的值,然后与每一个 `case` 的模式进行匹配。如果匹配成功,程序将会执行对应的 `case` 中的语句。另外,每一个 `case` 都不能为空,也就是说在每一个 `case` 中必须至少有一条语句。如果你不想在匹配到的 `case` 中执行代码,只需在该 `case` 中写一条 `break` 语句即可。 -可以用作控制表达式的值是十分灵活的。除了标量类型外,如 `Int`、`Character`,你可以使用任何类型的值,包括浮点数、字符串、元组、自定义类型的实例和可选类型,甚至是指定的 `Range` 或枚举类型中的成员值。关于如何在 `switch` 语句中使用这些类型,请参阅 [控制流](../chapter2/05_Control_Flow.md) 一章中的 [Switch](../chapter2/05_Control_Flow.html#switch)。 +可以用作控制表达式的值是十分灵活的。除了标量类型外,如 `Int`、`Character`,你可以使用任何类型的值,包括浮点数、字符串、元组、自定义类型的实例和可选类型。控制表达式的值还可以用来匹配枚举类型中的成员值或是检查该值是否包含在指定的 `Range` 中。关于如何在 `switch` 语句中使用这些类型,请参阅 [控制流](../chapter2/05_Control_Flow.md) 一章中的 [Switch](../chapter2/05_Control_Flow.html#switch)。 -每个 `case` 的模式后面可以有一个 `where` 子句。`where` 子句由 `where` 关键字紧跟一个提供额外测试条件的表达式组成。因此,当且仅当控制表达式匹配一个 `case` 的模式且 `where` 子句的表达式为真时,`case` 中的语句才会被执行。在下面的例子中,控制表达式只会匹配包含两个相等元素的元组,例如 `(1, 1)`: +每个 `case` 的模式后面可以有一个 `where` 子句。`where` 子句由 `where` 关键字紧跟一个提供额外条件的表达式组成。因此,当且仅当控制表达式匹配一个 `case` 的模式且 `where` 子句的表达式为真时,`case` 中的语句才会被执行。在下面的例子中,控制表达式只会匹配包含两个相等元素的元组,例如 `(1, 1)`: ```swift case let (x, y) where x == y: @@ -288,11 +287,11 @@ case let (x, y) where x == y: `switch` 语句中 `case` 的匹配顺序和源代码中的书写顺序保持一致。因此,当多个模式都能匹配控制表达式时,只有第一个匹配的 `case` 中的代码会被执行。 -#### Switch 语句必须是详尽的 +#### Switch 语句不能有遗漏 在 Swift 中,`switch` 语句中控制表达式的每一个可能的值都必须至少有一个 `case` 与之对应。在某些无法面面俱到的情况下(例如,表达式的类型是 `Int`),你可以使用 `default` 分支满足该要求。 -#### 不存在隐式落空 +#### 不存在隐式落入 当匹配到的 `case` 中的代码执行完毕后,`switch` 语句会直接退出,而不会继续执行下一个 `case` 。这就意味着,如果你想执行下一个 `case`,需要显式地在当前 `case` 中使用 `fallthrough` 语句。关于 `fallthrough` 语句的更多信息,请参阅 [Fallthrough 语句](#fallthrough_statements)。 @@ -322,7 +321,7 @@ case let (x, y) where x == y: 你可以在循环语句或 `switch` 语句前面加上标签,它由标签名和紧随其后的冒号(`:`)组成。在 `break` 和 `continue` 后面跟上标签名可以显式地在循环语句或 `switch` 语句中改变相应的控制流。关于这两条语句用法,请参阅 [Break 语句](#break_statement) 和 [Continue 语句](#continue_statement)。 -标签的作用域在该标签所标记的语句内。你可以不使用带标签的语句,但只要使用它,在作用域内需保证标签名唯一。 +标签的作用域在该标签所标记的语句内。可以嵌套使用带标签的语句,但标签名必须唯一。 关于使用带标签的语句的例子,请参阅 [控制流](../chapter2/05_Control_Flow.md) 一章中的 [带标签的语句](../chapter2/05_Control_Flow.md#labeled_statements)。 @@ -350,16 +349,16 @@ case let (x, y) where x == y: ### Break 语句 -`break` 语句用于终止循环语句或 `switch` 语句的执行。使用 `break` 语句时,可以只写 `break` 这个关键词,也可以在 `break` 后面跟上标签名,像下面这样: +`break` 语句用于终止循环语句、`if` 语句或 `switch` 语句的执行。使用 `break` 语句时,可以只写 `break` 这个关键词,也可以在 `break` 后面跟上标签名,像下面这样: > break > break `标签名` -当 `break` 语句后面带标签名时,可用于终止由这个标签标记的循环语句或 `switch` 语句的执行。 +当 `break` 语句后面带标签名时,可用于终止由这个标签标记的循环语句、`if` 语句或 `switch` 语句的执行。 -而只写 `break` 时,则会终止 `switch` 语句或包含 `break` 语句的最内层循环的执行。 +而只写 `break` 时,则会终止 `switch` 语句或 `break` 语句所属的最内层循环语句的执行。不能使用 `break` 语句来终止未使用标签的 `if` 语句。 -在这两种情况下,控制权都会被传递给循环语句或 `switch` 语句后面的第一行语句。 +无论哪种情况,控制权都会被转移给被终止的控制流语句后面的第一行语句。 关于使用 `break` 语句的例子,请参阅 [控制流](../chapter2/05_Control_Flow.md) 一章的 [Break](../chapter2/05_Control_Flow.md#break) 和 [带标签的语句](../chapter2/05_Control_Flow.md#labeled_statements)。 @@ -377,9 +376,9 @@ case let (x, y) where x == y: 当 `continue` 语句后面带标签名时,可用于终止由这个标签标记的循环中当前迭代的执行。 -而当只写 `continue` 时,可用于终止上下文中包含 `continue` 语句的最内层循环中当前迭代的执行。 +而当只写 `continue` 时,可用于终止 `continue` 语句所属的最内层循环中当前迭代的执行。 -在这两种情况下,控制权都会被传递给循环外面的第一行语句。 +在这两种情况下,控制权都会被转移给循环语句的条件语句。 在 `for` 语句中,`continue` 语句执行后,增量表达式还是会被计算,这是因为每次循环体执行完毕后,增量表达式都会被计算。 @@ -394,7 +393,7 @@ case let (x, y) where x == y: `fallthrough` 语句用于在 `switch` 语句中转移控制权。`fallthrough` 语句会把控制权从 `switch` 语句中的一个 `case` 转移到下一个 `case`。这种控制权转移是无条件的,即使下一个 `case` 的模式与 `switch` 语句的控制表达式的值不匹配。 -`fallthrough` 语句可出现在 `switch` 语句中的任意 `case` 中,但不能出现在最后一个 `case` 中。同时,`fallthrough` 语句也不能把控制权转移到使用了可选绑定的 `case`。 +`fallthrough` 语句可出现在 `switch` 语句中的任意 `case` 中,但不能出现在最后一个 `case` 中。同时,`fallthrough` 语句也不能把控制权转移到使用了值绑定的 `case`。 关于在 `switch` 语句中使用 `fallthrough` 语句的例子,请参阅 [控制流](../chapter2/05_Control_Flow.md) 一章的 [控制转移语句](../chapter2/05_Control_Flow.md#control_transfer_statements)。 @@ -405,19 +404,19 @@ case let (x, y) where x == y: ### Return 语句 -`return` 语句用于在函数或方法的实现中将控制权转移给调用者,接着程序将会从调用者的位置继续向下执行。 +`return` 语句用于在函数或方法的实现中将控制权转移到调用函数或方法,接着程序将会从调用位置继续向下执行。 使用 `return` 语句时,可以只写 `return` 这个关键词,也可以在 `return` 后面跟上表达式,像下面这样: > return > return `表达式` -当 `return` 语句后面带表达式时,表达式的值将会返回给调用者。如果表达式的值的类型与函数或者方法声明的返回类型不匹配,Swift 则会在返回表达式的值之前将表达式的值的类型转换为返回类型。 +当 `return` 语句后面带表达式时,表达式的值将会返回给调用函数或方法。如果表达式的值的类型与函数或者方法声明的返回类型不匹配,Swift 则会在返回表达式的值之前将表达式的值的类型转换为返回类型。 > 注意 > 正如 [可失败构造器](05_Declarations.md#failable_initializers) 中所描述的,`return nil` 在可失败构造器中用于表明构造失败。 -而只写 `return` 时,仅仅是将控制权从该函数或方法转移给调用者,而不返回一个值(也就是说,函数或方法的返回类型为 `Void` 或者说 `()`)。 +而只写 `return` 时,仅仅是从该函数或方法中返回,而不返回任何值(也就是说,函数或方法的返回类型为 `Void` 或者说 `()`)。 > return 语句语法 @@ -428,7 +427,7 @@ case let (x, y) where x == y: `throw` 语句出现在抛出函数或者抛出方法体内,或者类型被 `throws` 关键字标记的闭包表达式体内。 -`throw` 语句使程序在当前作用域结束执行,并向外围作用域传播错误。抛出的错误会一直传播,直到被 `do` 语句的 `catch` 子句处理掉。 +`throw` 语句使程序在当前作用域结束执行,并向外围作用域传播错误。抛出的错误会一直传递,直到被 `do` 语句的 `catch` 子句处理掉。 `throw` 语句由 `throw` 关键字紧跟一个表达式组成,如下所示: @@ -455,9 +454,9 @@ defer { } ``` -在 `defer` 语句中的语句无论程序控制如何转移都会执行。这意味着 `defer` 语句可以被使用在以下这些情况,例如关闭文件描述,或者即使抛出了错误也需要执行的一些动作。 +在 `defer` 语句中的语句无论程序控制如何转移都会被执行。在某些情况下,例如,手动管理资源时,比如关闭文件描述符,或者即使抛出了错误也需要执行一些操作时,就可以使用 `defer` 语句。 -如果多个 `defer` 语句出现在同一作用域内,那么它们执行的顺序与出现的顺序相反。给定作用域中的第一个 `defer` 语句,会在最后执行,这意味着最后执行的 `defer` 语句中涉及的资源可以被其他 `defer` 语句清理掉。 +如果多个 `defer` 语句出现在同一作用域内,那么它们执行的顺序与出现的顺序相反。给定作用域中的第一个 `defer` 语句,会在最后执行,这意味着代码中最靠后的 `defer` 语句中引用的资源可以被其他 `defer` 语句清理掉。 ```swift func f() { @@ -480,9 +479,9 @@ f() ## Do 语句 -`do` 语句用于引入一个新的作用域,该作用域中可以含有一个或多个 `catch` 子句,`catch` 子句中定义了一些匹配错误条件的模式。`do` 语句作用域内定义的常量和变量,只能在 `do` 语句作用域内使用。 +`do` 语句用于引入一个新的作用域,该作用域中可以含有一个或多个 `catch` 子句,`catch` 子句中定义了一些匹配错误条件的模式。`do` 语句作用域内定义的常量和变量只能在 `do` 语句作用域内使用。 -Swift 中的 `do` 语句与 C 中限定代码块界限的大括号(`{}`)很相似,并且在程序运行的时候并不会造成系统开销。 +Swift 中的 `do` 语句与 C 中限定代码块界限的大括号(`{}`)很相似,也并不会降低程序运行时的性能。 `do` 语句的形式如下: @@ -497,11 +496,11 @@ do { } ``` -如同 `switch` 语句,编译器会判断 `catch` 子句是否有遗漏。如果 `catch` 子句没有遗漏,则认为错误被处理。否则,错误会自动传播到外围作用域,被一个 `catch` 语句处理掉或者继续向外抛出,抛出函数必须以 `throws` 关键字声明。 +如同 `switch` 语句,编译器会判断 `catch` 子句是否有遗漏。如果 `catch` 子句没有遗漏,则认为错误已被处理。否则,错误会自动传递到外围作用域,被某个 `catch` 子句处理掉或者被用 `throws` 关键字声明的抛出函数继续向外抛出。 -为了确保错误已经被处理,可以让 `catch` 子句使用匹配所有错误的模式,如通配符模式(`_`)。如果一个 `catch` 子句不指定一种具体模式,`catch` 子句会匹配任何错误,并绑定到名为 `error` 的局部变量。有关在 `catch` 子句中使用模式的更多信息,请参阅 [模式](07_Patterns.md)。 +为了确保错误已经被处理,可以让 `catch` 子句使用匹配所有错误的模式,如通配符模式(`_`)。如果一个 `catch` 子句不指定一种具体模式,`catch` 子句会匹配任何错误,并绑定到名为 `error` 的局部常量。有关在 `catch` 子句中使用模式的更多信息,请参阅 [模式](07_Patterns.md)。 -关于如何在 `do` 语句中使用一些 `catch` 子句的例子,请参阅 [处理错误](../chapter2/18_Error_Handling.md#handling_errors)。 +关于如何在 `do` 语句中使用一系列 `catch` 子句的例子,请参阅 [错误处理](../chapter2/18_Error_Handling.md#handling_errors)。 > do 语句语法 @@ -536,18 +535,18 @@ do { 和 `if` 语句的条件不同,编译配置的条件是在编译时进行判断的。只有编译配置在编译时判断为 `true` 的情况下,相应的语句才会被编译和执行。 -编译配置可以是 `true` 和 `false` 的字面量,也可以是使用 `-D` 命令行标志的标识符,或者是下列表格中的任意一个平台测试函数。 +编译配置可以是 `true` 和 `false` 的字面量,也可以是使用 `-D` 命令行标志的标识符,或者是下列表格中的任意一个平台检测函数。 | 函数 | 可用参数 | | --- | --- | -| `os()` | `OSX`, `iOS`, `watchOS`, `tvOS` | +| `os()` | `OSX`, `iOS`, `watchOS`, `tvOS`, `Linux` | | `arch()` | `i386`, `x86_64`, `arm`, `arm64` | -| `swift()` | >= followed by a version number | +| `swift()` | `>=` 后跟版本号 | -`swift()` (语言版本检测函数)的版本号参数主要由主版本号和次版本号组成并且使用点号(`.`)分割开,`>=` 和版本号之间不能有空白符。 +`swift()`(语言版本检测函数)的版本号参数主要由主版本号和次版本号组成并且使用点号(`.`)分隔开,`>=` 和版本号之间不能有空格。 -> 注意 -> `arch(arm)` 编译配置在 ARM 64位设备上不会返回 `true`。如果代码在 32 位的 iOS 模拟器上编译,`arch(i386)` 编译配置返回 `true`。 +> 注意 +> `arch(arm)` 平台检测函数在 ARM 64 位设备上不会返回 `true`。如果代码在 32 位的 iOS 模拟器上编译,`arch(i386)` 平台检测函数会返回 `true`。 你可以使用逻辑操作符 `&&`、`||` 和 `!` 来组合多个编译配置,还可以使用圆括号来进行分组。 @@ -564,9 +563,9 @@ do { ``` > 注意 -> 即使没有被编译,编译配置中的语句仍然会被解析。但是,在编译配置语句中如果包含语言版本检测函数则是例外:仅当 `Swift` 编译器版本和语言版本检测函数中指定的版本号匹配时语句才能被解析。这种设定能确保旧的编译器不会尝试去解析新 `Swift` 版本的语法。 +> 即使没有被编译,编译配置中的语句仍然会被解析。然而,唯一的例外是编译配置语句中包含语言版本检测函数:仅当 `Swift` 编译器版本和语言版本检测函数中指定的版本号匹配时,语句才会被解析。这种设定能确保旧的编译器不会尝试去解析新 Swift 版本的语法。 - + > 编译配置语句语法 @@ -579,7 +578,8 @@ do { > *单个编译配置 else 子句* → **#else** [*语句*](#statements)可选 -> *编译配置* → [*平台测试函数*](#platform-testing-function) +> *编译配置* → [*平台检测函数*](#platform-testing-function) +> *编译配置* → [*语言版本检测函数*](#language-version-testing-function) > *编译配置* → [*标识符*](02_Lexical_Structure.md#identifier) > *编译配置* → [*布尔值字面量*](02_Lexical_Structure.md#boolean-literal) > *编译配置* → **(** [*编译配置*](#build-configuration) **)** @@ -588,33 +588,37 @@ do { > *编译配置* → [*编译配置*](#build-configuration) **||** [*编译配置*](#build-configuration) -> *平台测试函数* → **os** **(** [*操作系统*](#operating-system) **)** -> *平台测试函数* → **arch** **(** [*架构*](#architecture) **)** +> *平台检测函数* → **os** **(** [*操作系统*](#operating-system) **)** +> *平台检测函数* → **arch** **(** [*架构*](#architecture) **)** + +> *语言版本检测函数* → **swift** **(** **>=** [*swift版本*](#swift-version) **)** > *操作系统* → **OSX** | **iOS** | **watchOS** | **tvOS** > *架构* → **i386** | **x86_64** | **arm** | **arm64** + > *swift 版本* → [*十进制数字*](02_Lexical_Structure.md#decimal-digit) ­**.** ­[*十进制数字*](02_Lexical_Structure.md#decimal-digit) -### 线路控制语句 +### 行控制语句 -线路控制语句用来为被编译的源代码指定一个与原始行号和文件名不同的行号和文件名。使用线路控制语句可以改变源代码的位置,以便进行分析和调试。 +行控制语句可以为被编译的源代码指定行号和文件名,从而改变源代码的定位信息,以便进行分析和调试。 -线路控制语句形式如下: +行控制语句形式如下: > \#line `行号` `文件名` -线路控制语句会改变之后的字面量表达式 `__LINE__` 和 `__FILE__` 的值。`行号` 是一个大于 0 的整形字面量,会改变 `__LINE__` 的值。`文件名` 是一个字符串字面量,会改变 `__FILE__` 的值。 +行控制语句会改变该语句之后的代码中的字面量表达式 `#line` 和 `#file` 所表示的值。`行号` 是一个大于 0 的整形字面量,会改变 `#line` 表达式的值。`文件名` 是一个字符串字面量,会改变 `#file` 表达式的值。 -你可以通过 `#line` 语句,即不指定行号和文件名,来将源代码的位置重置回默认的行号和文件名。 +你可以只写 `#line`,而不指定行号和文件名,从而将源代码的定位信息重置回默认的行号和文件名。 -线路控制语句必须独占一行,而且不能是源代码文件的最后一行。 +`#line` 标记具有两种含义,作为行控制语句使用时必须独占一行,而且不能是源代码文件的最后一行。否则,它将作为字面量表达式使用,详情请参阅 [字面量表达式](04_Expressions.md#literal_expression)。 -> 线路控制语句语法 - -> *线路控制语句* → **#line** -> *线路控制语句* → **#line** [*行号*](#line-number) [*文件名*](#file-name) + +> 行控制语句语法 + +> *行控制语句* → **#line** +> *行控制语句* → **#line** [*行号*](#line-number) [*文件名*](#file-name) > *行号* → 大于 0 的十进制整数 @@ -635,11 +639,11 @@ if #available(平台名称 版本, ..., *) { } ``` -使用可用性条件来执行一个代码块时,取决于使用的接口在运行时是否可用。编译器会根据可用性条件提供的信息以及运行时的平台来决定是否执行相应的代码块。 +使用可用性条件来执行一个代码块时,取决于使用的 API 在运行时是否可用,编译器会根据可用性条件提供的信息来决定是否执行相应的代码块。 可用性条件使用一系列逗号分隔的平台名称和版本。使用 `iOS`,`OSX`,以及 `watchOS` 等作为平台名称,并写上相应的版本号。`*` 参数是必须写的,用于处理未来的潜在平台。可用性条件确保了运行时的平台不低于条件中指定的平台版本时才执行代码块。 -与布尔类型的条件不同,不能用逻辑运算符 `&&` 和 `||` 合并可用性条件。 +与布尔类型的条件不同,不能用逻辑运算符 `&&` 和 `||` 组合可用性条件。 > 可用性条件语法 From fa6bc5c329ca3fdf41604c039d79a88758f76ee7 Mon Sep 17 00:00:00 2001 From: 949478479 <949478479@qq.com> Date: Fri, 6 May 2016 10:58:59 +0800 Subject: [PATCH 27/61] Update 04_Expressions.md --- source/chapter3/04_Expressions.md | 167 +++++++++++++++++++++--------- 1 file changed, 118 insertions(+), 49 deletions(-) diff --git a/source/chapter3/04_Expressions.md b/source/chapter3/04_Expressions.md index c4dcd5de..6bacc476 100644 --- a/source/chapter3/04_Expressions.md +++ b/source/chapter3/04_Expressions.md @@ -27,6 +27,7 @@ - [隐式成员表达式](#implicit_member_expression) - [圆括号表达式](#parenthesized_expression) - [通配符表达式](#wildcard_expression) + - [选择器表达式](#selector_expression) - [后缀表达式](#postfix_expressions) - [函数调用表达式](#function_call_expression) - [构造器表达式](#initializer_expression) @@ -37,9 +38,9 @@ - [强制取值表达式](#forced-Value_expression) - [可选链表达式](#optional-chaining_expression) -Swift 中存在四种表达式:前缀表达式,二元表达式,基本表达式和后缀表达式。表达式可以返回一个值,还可以执行某些代码。 +Swift 中存在四种表达式:前缀表达式,二元表达式,基本表达式和后缀表达式。表达式在返回一个值的同时还可以引发副作用。 -前缀表达式和二元表达式就是对某些表达式使用各种运算符。基本表达式是最短小的表达式,它提供了获取值的一种途径。后缀表达式则允许你建立复杂的表达式,例如函数调用和成员访问。每种表达式都在下面有详细论述。 +通过前缀表达式和二元表达式可以对简单表达式使用各种运算符。基本表达式从概念上讲是最短小的表达式,它是一种访问值的方式。后缀表达式则允许你建立复杂的表达式,例如函数调用和成员访问。每种表达式都在下面有详细论述。 > 表达式语法 @@ -50,13 +51,13 @@ Swift 中存在四种表达式:前缀表达式,二元表达式,基本表 ## 前缀表达式 -前缀表达式由可选的前缀运算符和表达式组成。前缀运算符只接收一个参数。 +前缀表达式由可选的前缀运算符和表达式组成。前缀运算符只接收一个参数,表达式则紧随其后。 关于这些运算符的更多信息,请参阅 [基本运算符](../chapter2/02_Basic_Operators.html) 和 [高级运算符](../chapter2/25_Advanced_Operators.html)。 关于 Swift 标准库提供的运算符的更多信息,请参阅 [*Swift Standard Library Operators Reference*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_StandardLibrary_Operators/index.html#//apple_ref/doc/uid/TP40016054)。 -除了标准库运算符,你也可以对某个变量使用 `&` 运算符,从而将其传递给函数的输入输出参数。 更多信息,请参阅 [输入输出参数](../chapter2/06_Functions.html#in_out_parameters)。 +除了标准库运算符,你也可以对某个变量使用 `&` 运算符,从而将其传递给函数的输入输出参数。更多信息,请参阅 [输入输出参数](../chapter2/06_Functions.html#in_out_parameters)。 > 前缀表达式语法 @@ -87,14 +88,14 @@ try 表达式由 `try` 运算符加上紧随其后的可抛出错误的表达式 在二进制运算符左侧的表达式被标记上 `try`、`try?` 或者 `try!` 时,这个运算符对整个二进制表达式都产生作用。也就是说,你可以使用括号来明确运算符的作用范围。 ```swift -sum = try someThrowingFunction() + anotherThrowingFunction() // try 对两个方法调用都产生作用 -sum = try (someThrowingFunction() + anotherThrowingFunction()) // try 对两个方法调用都产生作用 -sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误:try 只对第一个方法调用产生作用 +sum = try someThrowingFunction() + anotherThrowingFunction() // try 对两个函数调用都产生作用 +sum = try (someThrowingFunction() + anotherThrowingFunction()) // try 对两个函数调用都产生作用 +sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误:try 只对第一个函数调用产生作用 ``` `try` 表达式不能出现在二进制运算符的的右侧,除非二进制运算符是赋值运算符或者 `try` 表达式是被圆括号括起来的。 -关于 `try`、`try?` 和 `try!` 的更多信息,以及如何使用的例子,请参阅 [错误处理](../chapter2/18_Error_Handling.html)。 +关于 `try`、`try?` 和 `try!` 的更多信息,以及该如何使用的例子,请参阅 [错误处理](../chapter2/18_Error_Handling.html)。 > try 表达式语法 @@ -112,7 +113,7 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误:try 关于 Swift 标准库提供的运算符的更多信息,请参阅 [*Swift Standard Library Operators Reference*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_StandardLibrary_Operators/index.html#//apple_ref/doc/uid/TP40016054)。 > 注意 -> 在解析时,一个二元表达式将作为一个简单列表表示,然后根据运算符的优先级,再进一步进行组合。例如,`2 + 3 * 5` 首先被看作具有五个元素的列表,即 `2`、`+`、`3`、`*`、`5`,随后根据运算符优先级组合为 `(2 + (3 * 5))`。 +> 在解析时,一个二元表达式将作为一个扁平列表表示,然后根据运算符的优先级,再进一步进行组合。例如,`2 + 3 * 5` 首先被看作具有五个元素的扁平列表,即 `2`、`+`、`3`、`*`、`5`,随后根据运算符优先级组合为 `(2 + (3 * 5))`。 > 二元表达式语法 @@ -130,7 +131,7 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误:try > `表达式` = `值` -右边的值会被赋值给左边的表达式。如果左边表达式是一个元组,那么右边必须是一个具有同样元素个数的元组。嵌套元组也是允许的。 +右边的值会被赋值给左边的表达式。如果左边表达式是一个元组,那么右边必须是一个具有同样元素个数的元组。嵌套元组也是允许的。右边的值中的每一部分都会被赋值给左边的表达式中的相应部分。例如: ```swift (a, _, (b, c)) = ("test", 9.45, (12, 3)) @@ -161,7 +162,7 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误:try ### 类型转换运算符 -有 4 种类型转换运算符:`is`、`as`、`? `和`!`。它们有如下的形式: +有 4 种类型转换运算符:`is`、`as`、`as?` 和 `as!`。它们有如下的形式: > `表达式` is `类型` `表达式` as `类型` @@ -189,9 +190,9 @@ f(x as Any) 桥接可将 Swift 标准库中的类型(例如 `String`)作为一个与之相关的 Foundation 类型(例如 `NSString`)来使用,而不需要新建一个实例。关于桥接的更多信息,请参阅 [*Using Swift with Cocoa and Objective-C*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中的 [Working with Cocoa Data Types](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)。 -`as?` 运算符有条件地执行类型转换,返回目标类型的可选值。在运行时,如果转换成功,返回的可选值将包含转换后的值,否则返回 `nil`。如果在编译时就能确定转换一定会成功或是失败,则会编译出错。 +`as?` 运算符有条件地执行类型转换,返回目标类型的可选值。在运行时,如果转换成功,返回的可选值将包含转换后的值,否则返回 `nil`。如果在编译时就能确定转换一定会成功或是失败,则会导致编译报错。 -`as!` 运算符执行强制类型转换,返回目标类型的非可选值。如果转换失败,则会导致运行时错误。表达式 `x as T` 效果等同于 `(x as? T)!`。 +`as!` 运算符执行强制类型转换,返回目标类型的非可选值。如果转换失败,则会导致运行时错误。表达式 `x as T` 的效果等同于 `(x as? T)!`。 关于类型转换的更多内容和例子,请参阅 [类型转换](../chapter2/19_Type_Casting.html)。 @@ -205,7 +206,7 @@ f(x as Any) ## 基本表达式 -基本表达式是最基本的表达式。 它们可以跟前缀表达式、二元表达式、后缀表达式以及其他基本表达式组合使用。 +基本表达式是最基本的表达式。它们可以单独使用,也可以跟前缀表达式、二元表达式、后缀表达式组合使用。 > 基本表达式语法 @@ -217,6 +218,7 @@ f(x as Any) > *基本表达式* → [*圆括号表达式*](#parenthesized-expression) > *基本表达式* → [*隐式成员表达式*](#implicit-member-expression) > *基本表达式* → [*通配符表达式*](#wildcard-expression) +> *基本表达式* → [*选择器表达式*](#selector-expression) ### 字面量表达式 @@ -225,17 +227,19 @@ f(x as Any) 字面量 | 类型 | 值 :------------- | :---------- | :---------- -`__FILE__` | `String` | 所在的文件名 -`__LINE__` | `Int` | 所在的行数 -`__COLUMN__` | `Int` | 所在的列数 -`__FUNCTION__` | `String` | 所在的声明的名字 +`#file` | `String` | 表达式所在文件的名字 +`#line` | `Int` | 表达式所在的行数 +`#column` | `Int` | 表达式所在的列数 +`#function` | `String` | 表达式所在的函数或方法的名字 -对于 `__FUNCTION__`,在函数中会返回当前函数的名字,在方法中会返回当前方法的名字,在属性的存取器中会返回属性的名字,在特殊的成员如 `init` 或 `subscript` 中会返回这个关键字的名字,在某个文件中会返回当前模块的名字。 +`#line` 除了上述含义外,还有另一种含义。当它出现在单独一行时,它会作为行控制语句使用,请参阅 [行控制语句](10_Statements.md#line_control_statements)。 -`__FUNCTION__` 作为函数或者方法的默认参数值时,该字面量的值取决于函数或方法调用时所处的环境。 +对于 `#function`,在函数中会返回当前函数的名字,在方法中会返回当前方法的名字,在属性的存取器中会返回属性的名字,在特殊的成员如 `init` 或 `subscript` 中会返回这个关键字的名字,在某个文件的顶级作用域中会返回当前模块的名字。 + +`#function` 作为函数或者方法的默认参数值时,该字面量的值取决于函数或方法的调用环境。 ```swift -func logFunctionName(string: String = __FUNCTION__) { +func logFunctionName(string: String = #function) { print(string) } func myFunction() { @@ -248,7 +252,7 @@ myFunction() // 打印 “myFunction()” > [`值 1`, `值 2`, `...`] -数组中的最后一个表达式可以紧跟一个逗号。数组字面量的类型是 `[T]`,这个 `T` 就是数组中元素的类型。如果数组中包含多种类型,`T` 则是跟这些类型最接近的的公共父类型。空数组字面量由一组方括号定义,可用来创建特定类型的空数组。 +数组中的最后一个表达式可以紧跟一个逗号。数组字面量的类型是 `[T]`,这个 `T` 就是数组中元素的类型。如果数组中包含多种类型,`T` 则是跟这些类型最近的的公共父类型。空数组字面量由一组方括号定义,可用来创建特定类型的空数组。 ```swift var emptyArray: [Double] = [] @@ -258,7 +262,7 @@ var emptyArray: [Double] = [] > [`键 1` : `值 1`, `键 2` : `值 2`, `...`] -字典中的最后一个表达式可以紧跟一个逗号。字典字面量的类型是 `[Key : Value]`,`Key` 表示键的类型,`Value` 表示值的类型。如果字典中包含多种类型,那么 `Key` 表示的类型则为所有键最接近的公共父类型,`Value` 也是同样如此。一个空的字典字面量由方括号中加一个冒号组成(`[:]`),从而与空数组字面量区分开,可以使用空字典字面量来创建特定类型的字典。 +字典中的最后一个表达式可以紧跟一个逗号。字典字面量的类型是 `[Key : Value]`,`Key` 表示键的类型,`Value` 表示值的类型。如果字典中包含多种类型,那么 `Key` 表示的类型则为所有键最近的公共父类型,`Value` 与之类似。一个空的字典字面量由方括号中加一个冒号组成(`[:]`),从而与空数组字面量区分开,可以使用空字典字面量来创建特定类型的字典。 ```swift var emptyDictionary: [String : Double] = [:] @@ -353,7 +357,7 @@ struct Point { ### 闭包表达式 -闭包表达式会创建一个闭包,在其他语言中也叫匿名函数。跟函数一样,闭包包含了待执行的代码,不同的是闭包还会捕获所在环境中的常量和变量。它的形式如下: +闭包表达式会创建一个闭包,在其他编程语言中也叫 lambda 表达式或匿名函数。跟函数一样,闭包包含了待执行的代码,不同的是闭包还会捕获附近作用域中的常量和变量。它的形式如下: ```swift { (parameters) -> return type in @@ -365,9 +369,9 @@ struct Point { 闭包还有几种特殊的形式,能让闭包使用起来更加简洁: -- 闭包可以省略它的参数和返回值的类型。如果省略了参数名和参数类型,也要省略 `in` 关键字。如果被省略的类型无法被编译器推断,那么就会导致编译错误。 -- 闭包可以省略参数名,参数会被隐式命名为 `$` 跟上其索引位置,例如 `$0`、`$1`、`$2` 分别表示第一个、第二个、第三个参数,以此类推。 -- 如果闭包中只包含一个表达式,那么该表达式的结果就会自动成为闭包的返回值。表达式结果的类型也会被推断为闭包的返回类型。 +- 闭包可以省略它的参数和返回值的类型。如果省略了参数名和所有的类型,也要省略 `in` 关键字。如果被省略的类型无法被编译器推断,那么就会导致编译错误。 +- 闭包可以省略参数名,参数会被隐式命名为 `$` 加上其索引位置,例如 `$0`、`$1`、`$2` 分别表示第一个、第二个、第三个参数,以此类推。 +- 如果闭包中只包含一个表达式,那么该表达式的结果就会被视为闭包的返回值。表达式结果的类型也会被推断为闭包的返回类型。 下面几个闭包表达式是等价的: @@ -391,11 +395,11 @@ myFunction { $0 + $1 } #### 捕获列表 -默认情况下,闭包会通过强引用捕获所在环境中的常量和变量。你可以通过一个捕获列表来显式指定它的捕获行为。 +默认情况下,闭包会捕获附近作用域中的常量和变量,并使用强引用指向它们。你可以通过一个捕获列表来显式指定它的捕获行为。 -捕获列表在参数列表之前,由中括号括起来,里面是由逗号分隔的一系列表达式。一旦使用了捕获列表,就必须使用 `in` 关键字,即使省略了参数名、参数类型、返回类型。 +捕获列表在参数列表之前,由中括号括起来,里面是由逗号分隔的一系列表达式。一旦使用了捕获列表,就必须使用 `in` 关键字,即使省略了参数名、参数类型和返回类型。 -捕获列表中的条目会在闭包创建时被初始化。每一个条目都会被闭包所在环境中的同名常量或者变量初始化。例如下面的代码示例中,捕获列表包含 `a` 而不包含 `b`,这将导致这两个变量具有不同的行为。 +捕获列表中的项会在闭包创建时被初始化。每一项都会用闭包附近作用域中的同名常量或者变量的值初始化。例如下面的代码示例中,捕获列表包含 `a` 而不包含 `b`,这将导致这两个变量具有不同的行为。 ```swift var a = 0 @@ -412,7 +416,7 @@ closure() 在示例中,变量 `b` 只有一个,然而,变量 `a` 有两个,一个在闭包外,一个在闭包内。闭包内的变量 `a` 会在闭包创建时用闭包外的变量 `a` 的值来初始化,除此之外它们并无其他联系。这意味着在闭包创建后,改变某个 `a` 的值都不会对另一个 `a` 的值造成任何影响。与此相反,闭包内外都是同一个变量 `b`,因此在闭包外改变其值,闭包内的值也会受影响。 -如果闭包捕获的值是引用语义,则又会有所不同。例如,下面示例中,有两个变量 `x`,一个在闭包外,一个在闭包内,由于它们的值是引用语义,虽然这是两个不同的变量,它们却都引用着同一实例。 +如果闭包捕获的值具有引用语义则有所不同。例如下面示例中,总共有两个变量 `x`,一个在闭包外,一个在闭包内,由于它们的值具有引用语义,虽然这是两个不同的变量,它们却都引用着同一实例。 ```swift class SimpleClass { @@ -438,7 +442,7 @@ myFunction { [weak self] in print(self!.title) } // 以弱引用捕获 myFunction { [unowned self] in print(self.title) } // 以无主引用捕获 ``` -在捕获列表中,也可以使用任意表达式来赋值。该表达式会在闭包被创建时进行求值,闭包会按照指定的引用类型来捕获表达式的值。例如: +在捕获列表中,也可以将任意表达式的值绑定到一个常量上。表达式会在闭包被创建时进行求值,闭包会按照指定的引用类型来捕获表达式的值。例如: ```swift // 以弱引用捕获 self.parent 并赋值给 parent @@ -471,7 +475,7 @@ myFunction { [weak parent = self.parent] in print(parent!.title) } ### 隐式成员表达式 -在可以判断出类型的上下文中,隐式成员表达式是访问某个类型的成员(例如某个枚举成员或某个类型方法)的简洁方法,形式如下: +若类型可被推断出来,可以使用隐式成员表达式来访问某个类型的成员(例如某个枚举成员或某个类型方法),其形式如下: > .`成员名称` @@ -489,7 +493,7 @@ x = .AnotherValue ### 圆括号表达式 -圆括号表达式由多个逗号分隔的子表达式组成。每个子表达式前面可以有一个标识符,用冒号隔开,其形式如下: +圆括号表达式由圆括号和其中多个逗号分隔的子表达式组成。每个子表达式前面可以有一个标识符,用冒号隔开。圆括号表达式形式如下: > (`标识符 1` : `表达式 1`, `标识符 2` : `表达式 2`, `...`) @@ -506,7 +510,7 @@ x = .AnotherValue ### 通配符表达式 -通配符表达式用来忽略传递进来的某个参数。例如,下面的代码中,`10` 被传递给 `x`,`20` 被忽略: +通配符表达式可以在赋值过程中显式忽略某个值。例如下面的代码中,`10` 被赋值给 `x`,而 `20` 则被忽略: ```swift (x, _) = (10, 20) @@ -517,10 +521,49 @@ x = .AnotherValue > *通配符表达式* → **_** + +### 选择器表达式 + +可通过选择器表达式来获取引用 Objective-C 方法的选择器。 + +> \#selector(方法名) + +方法名必须是存在于 Objective-C 运行时中的方法的方法名。选择器表达式的返回值是一个 `Selector` 类型的值。例如: + +```swift +class SomeClass: NSObject { + @objc(doSomethingWithInt:) + func doSomething(x: Int) { } +} +let x = SomeClass() +let selector = #selector(x.doSomething(_:)) +``` + +具有相同方法名但类型不同的方法可以使用 `as` 操作符来区分: + +```swift +extension SomeClass { + @objc(doSomethingWithString:) + func doSomething(x: String) { } +} +let anotherSelector = #selector(x.doSomething(_:) as (String) -> Void) +``` + +由于选择器是在编译时创建的,因此编译器可以检查方法是否存在,以及方法是否暴露给了 Objective-C 运行时。 + +> 注意 +> 虽然方法名是个表达式,但是它不会被求值。 + +更多关于如何在 Swift 代码中使用选择器来与 Objective-C API 进行交互的信息,请参阅 [*Using Swift with Cocoa and Objective-C*](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中的 [*Objective-C Selectors*](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html#//apple_ref/doc/uid/TP40014216-CH4-ID59) 部分。 + +> 选择器表达式语法 + +> *选择器表达式* → __#selector__ **(** [*表达式*](#expression) **)** + ## 后缀表达式 -后缀表达式就是在某个表达式的后面加上后缀运算符。严格地讲,每个基本表达式也是一个后缀表达式。 +后缀表达式就是在某个表达式的后面运用后缀运算符或其他后缀语法。从语法构成上来看,基本表达式也是后缀表达式。 关于这些运算符的更多信息,请参阅 [基本运算符](../chapter2/02_Basic_Operators.html) 和 [高级运算符](../chapter2/25_Advanced_Operators.html)。 @@ -582,8 +625,7 @@ myData.someMethod {$0 == 13} > `表达式`.init(`构造器参数`) -你可以在函数调用表达式中使用构造器表达式来初始化某个类型的新实例。也可以使用构造器表达式来代理到超类的构造器。 - +你可以在函数调用表达式中使用构造器表达式来初始化某个类型的新实例。也可以使用构造器表达式来代理给超类构造器。 ```swift class SomeSubClass: SomeSuperClass { @@ -606,7 +648,6 @@ print(oneTwoThree) 如果通过名字来指定某个类型,可以不用构造器表达式而直接使用类型的构造器。在其他情况下,你必须使用构造器表达式。 - ```swift let s1 = SomeType.init(data: 3) // 有效 let s2 = SomeType(data: 1) // 有效 @@ -622,7 +663,7 @@ let s3 = someValue.dynamicType.init(data: 7) // 有效 ### 显式成员表达式 -显式成员表达式允许我们访问命名类型、元组或者模块的成员,形式如下: +显式成员表达式允许我们访问命名类型、元组或者模块的成员,其形式如下: > `表达式`.`成员名` @@ -636,7 +677,7 @@ let c = SomeClass() let y = c.someProperty // 访问成员 ``` -元组的成员会根据表示它们出现顺序的整数来隐式命名,以 0 开始,例如: +元组的成员会隐式地根据表示它们出现顺序的整数来命名,以 0 开始,例如: ```swift var t = (10, 20, 30) @@ -646,6 +687,34 @@ t.0 = t.1 对于模块的成员来说,只能直接访问顶级声明中的成员。 +为了区分只有参数名有所不同的方法或构造器,在圆括号中写出参数名,参数名后紧跟一个冒号,对于没有参数名的参数,使用下划线代替参数名。而对于重载方法,则需使用类型标注进行区分。例如: + +```swift +class SomeClass { + func someMethod(x: Int, y: Int) {} + func someMethod(x: Int, z: Int) {} + func overloadedMethod(x: Int, y: Int) {} + func overloadedMethod(x: Int, y: Bool) {} +} +let instance = SomeClass() + +let a = instance.someMethod // 有歧义 +let b = instance.someMethod(_:y:) // 无歧义 + +let d = instance.overloadedMethod // 有歧义 +let d = instance.overloadedMethod(_:y:) // 有歧义 +let d: (Int, Bool) -> Void = instance.overloadedMethod(_:y:) // 无歧义 +``` + +如果点号(`.`)出现在行首,它会被视为显式成员表达式的一部分,而不是隐式成员表达式的一部分。例如如下代码所展示的被分为多行的链式方法调用: + +```swift +let x = [10, 3, 20, 15, 4] + .sort() + .filter { $0 > 5 } + .map { $0 * 100 } +``` + > 显式成员表达式语法 > *显式成员表达式* → [*后缀表达式*](#postfix-expression) **.** [*十进制数字*](02_Lexical_Structure.md#decimal-digit) @@ -654,14 +723,14 @@ t.0 = t.1 ### 后缀 self 表达式 -后缀 `self` 表达式由某个表达式紧跟 `.self` 组成,形式如下: +后缀 `self` 表达式由某个表达式或类型名紧跟 `.self` 组成,其形式如下: > `表达式`.self > `类型`.self 第一种形式返回表达式的值。例如:`x.self` 返回 `x`。 -第二种形式返回表示对应类型的值。我们可以用它来动态地获取某个实例的类型。例如,`SomeClass.self` 会返回 `SomeClass` 类型本身,你可以将其传递给相应函数或者方法作为参数。 +第二种形式返回相应的类型。我们可以用它来获取某个实例的类型作为一个值来使用。例如,`SomeClass.self` 会返回 `SomeClass` 类型本身,你可以将其传递给相应函数或者方法作为参数。 > 后缀 self 表达式语法 @@ -670,11 +739,11 @@ t.0 = t.1 ### dynamicType 表达式 -`dynamicType` 表达式由某个表达式紧跟 `.dynamicType` 组成,形式如下: +`dynamicType` 表达式由某个表达式紧跟 `.dynamicType` 组成,其形式如下: > `表达式`.dynamicType -上述形式中的表达式不能是类型名。`dynamicType` 表达式会返回某个实例在运行时的类型,具体请看下面的列子: +上述形式中的表达式不能是类型名。`dynamicType` 表达式会返回某个实例在运行时的类型,具体请看下面的例子: ```swift class SomeBaseClass { @@ -705,7 +774,7 @@ someInstance.dynamicType.printClassName() > `表达式`[`索引表达式`] -要获取下标表达式的值,可将索引表达式作为下标表达式的参数,调用表达式类型的下标 getter。下标 setter 的调用方式与之一样。 +要获取下标表达式的值,可将索引表达式作为下标表达式的参数来调用下标 getter。下标 setter 的调用方式与之一样。 关于下标的声明,请参阅 [协议下标声明](05_Declarations.md#protocol_subscript_declaration)。 @@ -745,11 +814,11 @@ someDictionary["a"]![0] = 100 > `表达式`? -后缀 `?` 根据表达式生成可选链表达式,而不会改变表达式的值。 +后缀 `?` 运算符会根据表达式生成可选链表达式而不会改变表达式的值。 -如果某个后缀表达式包含可选链表达式,那么它的执行过程会比较特殊。如果该可选链表达式的值是 `nil`,整个后缀表达式会直接返回 `nil`。如果该可选链表达式的值不是 `nil`,则返回可选链表达式解包后的值,并用于后缀表达式中剩余的表达式。在这两种情况下,整个后缀表达式的值都会是可选类型。 +如果某个后缀表达式包含可选链表达式,那么它的执行过程会比较特殊。如果该可选链表达式的值是 `nil`,整个后缀表达式会直接返回 `nil`。如果该可选链表达式的值不是 `nil`,则返回可选链表达式解包后的值,并将该值用于后缀表达式中剩余的表达式。在这两种情况下,整个后缀表达式的值都会是可选类型。 -如果某个后缀表达式中包含了可选链表达式,那么只有最外层的表达式会返回一个可选类型。例如,在下面的例子中,如果 `c` 不是 `nil`,那么它的值会被解包,然后通过 `.property` 访问它的属性,接着进一步通过 `.performAction()` 调用相应方法。整个 `c?.property.performAction()` 表达式返回一个可选类型的值。 +如果某个后缀表达式中包含了可选链表达式,那么只有最外层的表达式会返回一个可选类型。例如,在下面的例子中,如果 `c` 不是 `nil`,那么它的值会被解包,然后通过 `.property` 访问它的属性,接着进一步通过 `.performAction()` 调用相应方法。整个 `c?.property.performAction()` 表达式返回一个可选类型的值,而不是多重可选类型。 ```swift var c: SomeClass? From 4685efc35d59f0649c054b6f46777b66f4fcbaab Mon Sep 17 00:00:00 2001 From: Brian175 Date: Sun, 8 May 2016 11:31:16 +0800 Subject: [PATCH 28/61] Update 04_Expressions.md Fix some urls, add and correct some contents. --- source/chapter3/04_Expressions.md | 190 +++++++++++++++++++++--------- 1 file changed, 137 insertions(+), 53 deletions(-) diff --git a/source/chapter3/04_Expressions.md b/source/chapter3/04_Expressions.md index c4dcd5de..b4e8c8ed 100644 --- a/source/chapter3/04_Expressions.md +++ b/source/chapter3/04_Expressions.md @@ -11,6 +11,9 @@ > 2.1 > 翻译:[mmoaay](https://github.com/mmoaay) +> 2.2 +> 校对 [175](https://github.com/Brian175) + 本页包含内容: - [前缀表达式](#prefix_expressions) @@ -27,6 +30,7 @@ - [隐式成员表达式](#implicit_member_expression) - [圆括号表达式](#parenthesized_expression) - [通配符表达式](#wildcard_expression) + - [选择器表达式](#selector_expression) - [后缀表达式](#postfix_expressions) - [函数调用表达式](#function_call_expression) - [构造器表达式](#initializer_expression) @@ -37,9 +41,9 @@ - [强制取值表达式](#forced-Value_expression) - [可选链表达式](#optional-chaining_expression) -Swift 中存在四种表达式:前缀表达式,二元表达式,基本表达式和后缀表达式。表达式可以返回一个值,还可以执行某些代码。 +Swift 中存在四种表达式:前缀表达式,二元表达式,基本表达式和后缀表达式。表达式在返回一个值的同时还可以引发副作用。 -前缀表达式和二元表达式就是对某些表达式使用各种运算符。基本表达式是最短小的表达式,它提供了获取值的一种途径。后缀表达式则允许你建立复杂的表达式,例如函数调用和成员访问。每种表达式都在下面有详细论述。 +通过前缀表达式和二元表达式可以对简单表达式使用各种运算符。基本表达式从概念上讲是最简单的一种表达式,它是一种访问值的方式。后缀表达式则允许你建立复杂的表达式,例如函数调用和成员访问。每种表达式都在下面有详细论述。 > 表达式语法 @@ -50,20 +54,20 @@ Swift 中存在四种表达式:前缀表达式,二元表达式,基本表 ## 前缀表达式 -前缀表达式由可选的前缀运算符和表达式组成。前缀运算符只接收一个参数。 +前缀表达式由可选的前缀运算符和表达式组成。前缀运算符只接收一个参数,表达式则紧随其后。 -关于这些运算符的更多信息,请参阅 [基本运算符](../chapter2/02_Basic_Operators.html) 和 [高级运算符](../chapter2/25_Advanced_Operators.html)。 +关于这些运算符的更多信息,请参阅 [基本运算符](../chapter2/02_Basic_Operators.md) 和 [高级运算符](../chapter2/25_Advanced_Operators.md)。 关于 Swift 标准库提供的运算符的更多信息,请参阅 [*Swift Standard Library Operators Reference*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_StandardLibrary_Operators/index.html#//apple_ref/doc/uid/TP40016054)。 -除了标准库运算符,你也可以对某个变量使用 `&` 运算符,从而将其传递给函数的输入输出参数。 更多信息,请参阅 [输入输出参数](../chapter2/06_Functions.html#in_out_parameters)。 +除了标准库运算符,你也可以对某个变量使用 `&` 运算符,从而将其传递给函数的输入输出参数。更多信息,请参阅 [输入输出参数](../chapter2/06_Functions.md#in_out_parameters)。 > 前缀表达式语法 -> *前缀表达式* → [*前缀运算符*](02_Lexical_Structure.html#prefix-operator)可选 [*后缀表达式*](#postfix-expression) +> *前缀表达式* → [*前缀运算符*](02_Lexical_Structure.md#prefix-operator)可选 [*后缀表达式*](#postfix-expression) > *前缀表达式* → [*输入输出表达式*](#in-out-expression) -> *输入输出表达式* → **&** [*标识符*](02_Lexical_Structure.html#identifier) +> *输入输出表达式* → **&** [*标识符*](02_Lexical_Structure.md#identifier) ### try 运算符 @@ -94,7 +98,7 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误:try `try` 表达式不能出现在二进制运算符的的右侧,除非二进制运算符是赋值运算符或者 `try` 表达式是被圆括号括起来的。 -关于 `try`、`try?` 和 `try!` 的更多信息,以及如何使用的例子,请参阅 [错误处理](../chapter2/18_Error_Handling.html)。 +关于 `try`、`try?` 和 `try!` 的更多信息,以及如何使用的例子,请参阅 [错误处理](../chapter2/18_Error_Handling.md)。 > try 表达式语法 @@ -103,20 +107,20 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误:try ## 二元表达式 -二元表达式形式如下: +二元表达式由中缀运算符和左右参数表达式组成。形式如下: > `左侧参数` `二元运算符` `右侧参数` -关于这些运算符的更多信息,请参阅 [基本运算符](../chapter2/02_Basic_Operators.html) 和 [高级运算符](../chapter2/25_Advanced_Operators.html)。 +关于这些运算符的更多信息,请参阅 [基本运算符](../chapter2/02_Basic_Operators.md) 和 [高级运算符](../chapter2/25_Advanced_Operators.md)。 关于 Swift 标准库提供的运算符的更多信息,请参阅 [*Swift Standard Library Operators Reference*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_StandardLibrary_Operators/index.html#//apple_ref/doc/uid/TP40016054)。 > 注意 -> 在解析时,一个二元表达式将作为一个简单列表表示,然后根据运算符的优先级,再进一步进行组合。例如,`2 + 3 * 5` 首先被看作具有五个元素的列表,即 `2`、`+`、`3`、`*`、`5`,随后根据运算符优先级组合为 `(2 + (3 * 5))`。 +> 在解析时,一个二元表达式将作为一个扁平列表表示,然后根据运算符的优先级,再进一步进行组合。例如,`2 + 3 * 5` 首先被看作具有五个元素的列表,即 `2`、`+`、`3`、`*`、`5`,随后根据运算符优先级组合为 `(2 + (3 * 5))`。 > 二元表达式语法 -> *二元表达式* → [*二元运算符*](02_Lexical_Structure.html#binary-operator) [*前缀表达式*](#prefix-expression) +> *二元表达式* → [*二元运算符*](02_Lexical_Structure.md#binary-operator) [*前缀表达式*](#prefix-expression) > *二元表达式* → [*赋值运算符*](#assignment-operator) [*try运算符*](#try-operator)可选 [*前缀表达式*](#prefix-expression) > *二元表达式* → [*条件运算符*](#conditional-operator) [*try运算符*](#try-operator)可选 [*前缀表达式*](#prefix-expression) > *二元表达式* → [*类型转换运算符*](#type-casting-operator) @@ -130,7 +134,7 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误:try > `表达式` = `值` -右边的值会被赋值给左边的表达式。如果左边表达式是一个元组,那么右边必须是一个具有同样元素个数的元组。嵌套元组也是允许的。 +右边的值会被赋值给左边的表达式。如果左边表达式是一个元组,那么右边必须是一个具有同样元素个数的元组。(嵌套元组也是允许的。)右边的值中的每一部分都会被赋值给左边的表达式中的相应部分。例如: ```swift (a, _, (b, c)) = ("test", 9.45, (12, 3)) @@ -152,7 +156,7 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误:try 如果条件为真,那么对第一个表达式进行求值并返回结果。否则,对第二个表达式进行求值并返回结果。未使用的表达式不会进行求值。 -关于使用三元条件运算符的例子,请参阅 [三元条件运算符](../chapter2/02_Basic_Operators.html#ternary_conditional_operator)。 +关于使用三元条件运算符的例子,请参阅 [三元条件运算符](../chapter2/02_Basic_Operators.md#ternary_conditional_operator)。 > 三元条件运算符语法 @@ -161,11 +165,11 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误:try ### 类型转换运算符 -有 4 种类型转换运算符:`is`、`as`、`? `和`!`。它们有如下的形式: +有 4 种类型转换运算符:`is`、`as`、`as? `和`as!`。它们有如下的形式: > `表达式` is `类型` `表达式` as `类型` -`表达式` is? `类型` +`表达式` as? `类型` `表达式` as! `类型` `is` 运算符在运行时检查表达式能否向下转化为指定的类型,如果可以则返回 `ture`,否则返回 `false`。 @@ -187,25 +191,25 @@ f(x as Any) // 打印 “Function for Any” ``` -桥接可将 Swift 标准库中的类型(例如 `String`)作为一个与之相关的 Foundation 类型(例如 `NSString`)来使用,而不需要新建一个实例。关于桥接的更多信息,请参阅 [*Using Swift with Cocoa and Objective-C*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中的 [Working with Cocoa Data Types](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)。 +桥接可将 Swift 标准库中的类型(例如 `String`)作为一个与之相关的 Foundation 类型(例如 `NSString`)来使用,而不需要新建一个实例。关于桥接的更多信息,请参阅 [*Using Swift with Cocoa and Objective-C (Swift2.2)*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中的 [Working with Cocoa Data Types](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)。 `as?` 运算符有条件地执行类型转换,返回目标类型的可选值。在运行时,如果转换成功,返回的可选值将包含转换后的值,否则返回 `nil`。如果在编译时就能确定转换一定会成功或是失败,则会编译出错。 -`as!` 运算符执行强制类型转换,返回目标类型的非可选值。如果转换失败,则会导致运行时错误。表达式 `x as T` 效果等同于 `(x as? T)!`。 +`as!` 运算符执行强制类型转换,返回目标类型的非可选值。如果转换失败,则会导致运行时错误。表达式 `x as! T` 效果等同于 `(x as? T)!`。 -关于类型转换的更多内容和例子,请参阅 [类型转换](../chapter2/19_Type_Casting.html)。 +关于类型转换的更多内容和例子,请参阅 [类型转换](../chapter2/19_Type_Casting.md)。 > 类型转换运算符语法 -> *类型转换运算符* → **is** [*类型*](03_Types.html#type) -> *类型转换运算符* → **as** [*类型*](03_Types.html#type) -> *类型转换运算符* → **is** **?** [*类型*](03_Types.html#type) -> *类型转换运算符* → **as** **!** [*类型*](03_Types.html#type) +> *类型转换运算符* → **is** [*类型*](03_Types.md#type) +> *类型转换运算符* → **as** [*类型*](03_Types.md#type) +> *类型转换运算符* → **as** **?** [*类型*](03_Types.md#type) +> *类型转换运算符* → **as** **!** [*类型*](03_Types.md#type) ## 基本表达式 -基本表达式是最基本的表达式。 它们可以跟前缀表达式、二元表达式、后缀表达式以及其他基本表达式组合使用。 +基本表达式是最基本的表达式。它们可以单独使用,也可以跟前缀表达式、二元表达式、后缀表达式组合使用。 > 基本表达式语法 @@ -225,17 +229,19 @@ f(x as Any) 字面量 | 类型 | 值 :------------- | :---------- | :---------- -`__FILE__` | `String` | 所在的文件名 -`__LINE__` | `Int` | 所在的行数 -`__COLUMN__` | `Int` | 所在的列数 -`__FUNCTION__` | `String` | 所在的声明的名字 +`#file` | `String` | 所在的文件名 +`#line` | `Int` | 所在的行数 +`#column` | `Int` | 所在的列数 +`#function` | `String` | 所在的声明的名字 -对于 `__FUNCTION__`,在函数中会返回当前函数的名字,在方法中会返回当前方法的名字,在属性的存取器中会返回属性的名字,在特殊的成员如 `init` 或 `subscript` 中会返回这个关键字的名字,在某个文件中会返回当前模块的名字。 +`#line`除了上述含义外,还有另一种含义。当它出现在单独一行时,会被理解成行控制语句,请参阅[线路控制语句](../chapter3/10_Statements.md#线路控制语句)。 -`__FUNCTION__` 作为函数或者方法的默认参数值时,该字面量的值取决于函数或方法调用时所处的环境。 +对于 `#function`,在函数中会返回当前函数的名字,在方法中会返回当前方法的名字,在属性的存取器中会返回属性的名字,在特殊的成员如 `init` 或 `subscript` 中会返回这个关键字的名字,在某个文件中会返回当前模块的名字。 + +`#function` 作为函数或者方法的默认参数值时,该字面量的值取决于函数或方法的调用环境。 ```swift -func logFunctionName(string: String = __FUNCTION__) { +func logFunctionName(string: String = #function) { print(string) } func myFunction() { @@ -258,7 +264,7 @@ var emptyArray: [Double] = [] > [`键 1` : `值 1`, `键 2` : `值 2`, `...`] -字典中的最后一个表达式可以紧跟一个逗号。字典字面量的类型是 `[Key : Value]`,`Key` 表示键的类型,`Value` 表示值的类型。如果字典中包含多种类型,那么 `Key` 表示的类型则为所有键最接近的公共父类型,`Value` 也是同样如此。一个空的字典字面量由方括号中加一个冒号组成(`[:]`),从而与空数组字面量区分开,可以使用空字典字面量来创建特定类型的字典。 +字典中的最后一个表达式可以紧跟一个逗号。字典字面量的类型是 `[Key : Value]`,`Key` 表示键的类型,`Value` 表示值的类型。如果字典中包含多种类型,那么 `Key` 表示的类型则为所有键最接近的公共父类型,`Value` 与之相似。一个空的字典字面量由方括号中加一个冒号组成(`[:]`),从而与空数组字面量区分开,可以使用空字典字面量来创建特定类型的字典。 ```swift var emptyDictionary: [String : Double] = [:] @@ -269,7 +275,7 @@ var emptyDictionary: [String : Double] = [:] > *字面量表达式* → [*字面量*](02_Lexical_Structure.md#literal) > *字面量表达式* → [*数组字面量*](#array-literal) | [*字典字面量*](#dictionary-literal) -> *字面量表达式* → **\_\_FILE\_\_** | **\_\_LINE\_\_** | **\_\_COLUMN\_\_** | **\_\_FUNCTION\_\_** +> *字面量表达式* → **#file** | **#line** | **#column** | **#function** > *数组字面量* → **[** [*数组字面量项列表*](#array-literal-items)可选 **]** @@ -322,10 +328,14 @@ struct Point { > self 表达式语法 -> *self 表达式* → **self** -> *self 表达式* → **self** **.** [*标识符*](02_Lexical_Structure.md#identifier) -> *self 表达式* → **self** **[** [*表达式*](#expression) **]** -> *self 表达式* → **self** **.** **init** +> *self 表达式* → **self** | [*self 方法表达式*](#self-method-expression) | [*self 下标表达式*](#self-subscript-expression) | [*self 构造器表达式*](#self-initializer-expression) +> + +> *self 方法表达式* → **self** **.** [*标识符*](02_Lexical_Structure.md#identifier) + +> *self 下标表达式* → **self** **[** [*表达式*](#expression) **]** + +> *self 构造器表达式* → **self** **.** **init** ### 超类表达式 @@ -353,7 +363,7 @@ struct Point { ### 闭包表达式 -闭包表达式会创建一个闭包,在其他语言中也叫匿名函数。跟函数一样,闭包包含了待执行的代码,不同的是闭包还会捕获所在环境中的常量和变量。它的形式如下: +闭包表达式会创建一个闭包,在其他语言中也叫 *lambda* 或匿名函数。跟函数一样,闭包包含了待执行的代码,不同的是闭包还会捕获所在环境中的常量和变量。它的形式如下: ```swift { (parameters) -> return type in @@ -391,11 +401,11 @@ myFunction { $0 + $1 } #### 捕获列表 -默认情况下,闭包会通过强引用捕获所在环境中的常量和变量。你可以通过一个捕获列表来显式指定它的捕获行为。 +默认情况下,闭包会捕获附近作用域中的常量和变量,并使用强引用指向它们。你可以通过一个捕获列表来显式指定它的捕获行为。 捕获列表在参数列表之前,由中括号括起来,里面是由逗号分隔的一系列表达式。一旦使用了捕获列表,就必须使用 `in` 关键字,即使省略了参数名、参数类型、返回类型。 -捕获列表中的条目会在闭包创建时被初始化。每一个条目都会被闭包所在环境中的同名常量或者变量初始化。例如下面的代码示例中,捕获列表包含 `a` 而不包含 `b`,这将导致这两个变量具有不同的行为。 +捕获列表中的项会在闭包创建时被初始化。每一项都会用闭包附近作用域中的同名常量或者变量的值初始化。例如下面的代码示例中,捕获列表包含 `a` 而不包含 `b`,这将导致这两个变量具有不同的行为。 ```swift var a = 0 @@ -412,7 +422,7 @@ closure() 在示例中,变量 `b` 只有一个,然而,变量 `a` 有两个,一个在闭包外,一个在闭包内。闭包内的变量 `a` 会在闭包创建时用闭包外的变量 `a` 的值来初始化,除此之外它们并无其他联系。这意味着在闭包创建后,改变某个 `a` 的值都不会对另一个 `a` 的值造成任何影响。与此相反,闭包内外都是同一个变量 `b`,因此在闭包外改变其值,闭包内的值也会受影响。 -如果闭包捕获的值是引用语义,则又会有所不同。例如,下面示例中,有两个变量 `x`,一个在闭包外,一个在闭包内,由于它们的值是引用语义,虽然这是两个不同的变量,它们却都引用着同一实例。 +如果闭包捕获的值具有引用语义则有所不同。例如,下面示例中,有两个变量 `x`,一个在闭包外,一个在闭包内,由于它们的值是引用语义,虽然这是两个不同的变量,它们却都引用着同一实例。 ```swift class SimpleClass { @@ -438,7 +448,7 @@ myFunction { [weak self] in print(self!.title) } // 以弱引用捕获 myFunction { [unowned self] in print(self.title) } // 以无主引用捕获 ``` -在捕获列表中,也可以使用任意表达式来赋值。该表达式会在闭包被创建时进行求值,闭包会按照指定的引用类型来捕获表达式的值。例如: +在捕获列表中,也可以将任意表达式的值绑定到一个常量上。该表达式会在闭包被创建时进行求值,闭包会按照指定的引用类型来捕获表达式的值。例如: ```swift // 以弱引用捕获 self.parent 并赋值给 parent @@ -471,7 +481,7 @@ myFunction { [weak parent = self.parent] in print(parent!.title) } ### 隐式成员表达式 -在可以判断出类型的上下文中,隐式成员表达式是访问某个类型的成员(例如某个枚举成员或某个类型方法)的简洁方法,形式如下: +若类型可被推断出来,可以使用隐式成员表达式来访问某个类型的成员(例如某个枚举成员或某个类型方法),形式如下: > .`成员名称` @@ -517,12 +527,51 @@ x = .AnotherValue > *通配符表达式* → **_** + +### 选择器表达式 + +可通过选择器表达式来获取引用 Objective-C 方法的选择器。 + +> \#selector(方法名) + +方法名必须是存在于 Objective-C 运行时中的方法的引用。选择器表达式的返回值是一个 `Selector` 类型的实例。例如: + +```swift +class SomeClass: NSObject { + @objc(doSomethingWithInt:) + func doSomething(x: Int) { } +} +let x = SomeClass() +let selector = #selector(x.doSomething(_:)) +``` + +具有相同方法名但类型不同的方法可以使用 `as` 操作符来区分: + +```swift +extension SomeClass { + @objc(doSomethingWithString:) + func doSomething(x: String) { } +} +let anotherSelector = #selector(x.doSomething(_:) as (String) -> Void) +``` + +由于选择器是在编译时创建的,因此编译器可以检查方法是否存在,以及方法是否在运行时暴露给了 Objective-C 。 + +> 注意 +> 虽然方法名是个表达式,但是它不会被求值。 + +更多关于如何在 Swift 代码中使用选择器来与 Objective-C API 进行交互的信息,请参阅 [*Using Swift with Cocoa and Objective-C*](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中的 [*Objective-C Selectors*](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html#//apple_ref/doc/uid/TP40014216-CH4-ID59) 部分。 + +> 选择器表达式语法 + +> *选择器表达式* → __#selector__ **(** [*表达式*](#expression) **)** + ## 后缀表达式 -后缀表达式就是在某个表达式的后面加上后缀运算符。严格地讲,每个基本表达式也是一个后缀表达式。 +后缀表达式就是在某个表达式的后面运用后缀运算符或其他后缀语法。从语法构成上来看,基本表达式也是后缀表达式。 -关于这些运算符的更多信息,请参阅 [基本运算符](../chapter2/02_Basic_Operators.html) 和 [高级运算符](../chapter2/25_Advanced_Operators.html)。 +关于这些运算符的更多信息,请参阅 [基本运算符](../chapter2/02_Basic_Operators.md) 和 [高级运算符](../chapter2/25_Advanced_Operators.md)。 关于 Swift 标准库提供的运算符的更多信息,请参阅 [*Swift Standard Library Operators Reference*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_StandardLibrary_Operators/index.html#//apple_ref/doc/uid/TP40016054)。 @@ -587,7 +636,7 @@ myData.someMethod {$0 == 13} ```swift class SomeSubClass: SomeSuperClass { - init() { + override init() { // 此处为子类构造过程 super.init() } @@ -617,7 +666,8 @@ let s3 = someValue.dynamicType.init(data: 7) // 有效 > 构造器表达式语法 -> *构造器表达式* → [*后缀表达式*](postfix-expression) **.** **init** +> *构造器表达式* → [*后缀表达式*](#postfix-expression) **.** **init** +> *构造器表达式* → [*后缀表达式*](#postfix-expression) **.** **init** **(** [*参数名称*](#argument-names) **)** ### 显式成员表达式 @@ -646,10 +696,44 @@ t.0 = t.1 对于模块的成员来说,只能直接访问顶级声明中的成员。 +为了区分只有参数名有所不同的方法或构造器,在圆括号中写出参数名,参数名后紧跟一个冒号,对于没有参数名的参数,使用下划线代替参数名。而对于重载方法,则需使用类型标注进行区分。例如: + +```swift +class SomeClass { + func someMethod(x: Int, y: Int) {} + func someMethod(x: Int, z: Int) {} + func overloadedMethod(x: Int, y: Int) {} + func overloadedMethod(x: Int, y: Bool) {} +} +let instance = SomeClass() + +let a = instance.someMethod // 有歧义 +let b = instance.someMethod(_:y:) // 无歧义 + +let d = instance.overloadedMethod // 有歧义 +let d = instance.overloadedMethod(_:y:) // 有歧义 +let d: (Int, Bool) -> Void = instance.overloadedMethod(_:y:) // 无歧义 +``` + +如果点号(`.`)出现在行首,它会被视为显式成员表达式的一部分,而不是隐式成员表达式的一部分。例如如下代码所展示的被分为多行的链式方法调用: + +```swift +let x = [10, 3, 20, 15, 4] + .sort() + .filter { $0 > 5 } + .map { $0 * 100 } +``` + > 显式成员表达式语法 > *显式成员表达式* → [*后缀表达式*](#postfix-expression) **.** [*十进制数字*](02_Lexical_Structure.md#decimal-digit) -> *显式成员表达式* → [*后缀表达式*](#postfix-expression) **.** [*标识符*](02_Lexical_Structure.html#identifier) [*泛型实参子句*](08_Generic_Parameters_and_Arguments.md#generic-argument-clause)可选 +> *显式成员表达式* → [*后缀表达式*](#postfix-expression) **.** [*标识符*](02_Lexical_Structure.md#identifier) [*泛型实参子句*](08_Generic_Parameters_and_Arguments.md#generic-argument-clause)可选
+> *显式成员表达式* → [*后缀表达式*](#postfix-expression) **.** [*标识符*](02_Lexical_Structure.md#identifier) **(** [*参数名称*](#argument-names) **)** +> + +> *参数名称* → [*参数名*](#argument-name) [*参数名称*](#argument-names)可选
+ +> *参数名* → [*标识符*](02_Lexical_Structure.md#identifier) **:** ### 后缀 self 表达式 @@ -661,7 +745,7 @@ t.0 = t.1 第一种形式返回表达式的值。例如:`x.self` 返回 `x`。 -第二种形式返回表示对应类型的值。我们可以用它来动态地获取某个实例的类型。例如,`SomeClass.self` 会返回 `SomeClass` 类型本身,你可以将其传递给相应函数或者方法作为参数。 +第二种形式返回相应的类型。我们可以用它来获取某个实例的类型作为一个值来使用。例如,`SomeClass.self` 会返回 `SomeClass` 类型本身,你可以将其传递给相应函数或者方法作为参数。 > 后缀 self 表达式语法 @@ -705,7 +789,7 @@ someInstance.dynamicType.printClassName() > `表达式`[`索引表达式`] -要获取下标表达式的值,可将索引表达式作为下标表达式的参数,调用表达式类型的下标 getter。下标 setter 的调用方式与之一样。 +要获取下标表达式的值,可将索引表达式作为下标表达式的参数来调用下标 getter。下标 setter 的调用方式与之一样。 关于下标的声明,请参阅 [协议下标声明](05_Declarations.md#protocol_subscript_declaration)。 @@ -745,11 +829,11 @@ someDictionary["a"]![0] = 100 > `表达式`? -后缀 `?` 根据表达式生成可选链表达式,而不会改变表达式的值。 +后缀 `?` 运算符会根据表达式生成可选链表达式而不会改变表达式的值。 -如果某个后缀表达式包含可选链表达式,那么它的执行过程会比较特殊。如果该可选链表达式的值是 `nil`,整个后缀表达式会直接返回 `nil`。如果该可选链表达式的值不是 `nil`,则返回可选链表达式解包后的值,并用于后缀表达式中剩余的表达式。在这两种情况下,整个后缀表达式的值都会是可选类型。 +如果某个后缀表达式包含可选链表达式,那么它的执行过程会比较特殊。如果该可选链表达式的值是 `nil`,整个后缀表达式会直接返回 `nil`。如果该可选链表达式的值不是 `nil`,则返回可选链表达式解包后的值,并将该值用于后缀表达式中剩余的表达式。在这两种情况下,整个后缀表达式的值都会是可选类型。 -如果某个后缀表达式中包含了可选链表达式,那么只有最外层的表达式会返回一个可选类型。例如,在下面的例子中,如果 `c` 不是 `nil`,那么它的值会被解包,然后通过 `.property` 访问它的属性,接着进一步通过 `.performAction()` 调用相应方法。整个 `c?.property.performAction()` 表达式返回一个可选类型的值。 +如果某个后缀表达式中包含了可选链表达式,那么只有最外层的表达式会返回一个可选类型。例如,在下面的例子中,如果 `c` 不是 `nil`,那么它的值会被解包,然后通过 `.property` 访问它的属性,接着进一步通过 `.performAction()` 调用相应方法。整个 `c?.property.performAction()` 表达式返回一个可选类型的值,而不是多重可选类型。 ```swift var c: SomeClass? From 1502ff17f3b64b5f6d77c5aee26714a5d130252f Mon Sep 17 00:00:00 2001 From: Brian175 Date: Sun, 8 May 2016 11:35:00 +0800 Subject: [PATCH 29/61] Update 04_Expressions.md --- source/chapter3/04_Expressions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/chapter3/04_Expressions.md b/source/chapter3/04_Expressions.md index b4e8c8ed..eabcd950 100644 --- a/source/chapter3/04_Expressions.md +++ b/source/chapter3/04_Expressions.md @@ -12,7 +12,7 @@ > 翻译:[mmoaay](https://github.com/mmoaay) > 2.2 -> 校对 [175](https://github.com/Brian175) +> 校对:[175](https://github.com/Brian175) 本页包含内容: From 6a9571cb8e87f0172945ead04a67c16347e551fc Mon Sep 17 00:00:00 2001 From: Changkun Date: Mon, 9 May 2016 20:51:13 +0800 Subject: [PATCH 30/61] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BA=86=E8=AF=AD?= =?UTF-8?q?=E6=B3=95=E5=8F=98=E6=9B=B4=E5=8E=86=E5=8F=B2=E5=86=85=E5=AE=B9?= =?UTF-8?q?=20(#605)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 1. 校对了翻译内容,删除了与 Swift 语言本身无关的相关变动 2. 页面内容适配为与 https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/RevisionHistory.html 相关的组织结构 * 1. 更新了 Swift 2.0 的相关语法变动 2. 更新了 Swift 2.1 的相关语法变动 3. 更新了 Swift 2.2 的相关语法变动 --- source/chapter1/03_revision_history.md | 664 +++++++++++-------------- 1 file changed, 285 insertions(+), 379 deletions(-) diff --git a/source/chapter1/03_revision_history.md b/source/chapter1/03_revision_history.md index cb7781c0..00a796fe 100644 --- a/source/chapter1/03_revision_history.md +++ b/source/chapter1/03_revision_history.md @@ -1,37 +1,44 @@ -# Swift 版本历史记录 +# Swift 文档修订历史 --- +> 1.0 > 翻译:[成都老码团队翻译组-Arya](http://weibo.com/littlekok/) > 校对:[成都老码团队翻译组-Oberyn](http://weibo.com/u/5241713117) +[changkun](http://changkun.us/about/) +> +> 1.1 +> 翻译:[成都老码团队翻译组-Arya](http://weibo.com/littlekok/) +> 校对:[成都老码团队翻译组-Oberyn](http://weibo.com/u/5241713117) +[changkun](http://changkun.us/about/) +> +> 1.2 +> 翻译:[成都老码团队翻译组-Arya](http://weibo.com/littlekok/) +> 校对:[成都老码团队翻译组-Oberyn](http://weibo.com/u/5241713117) +[changkun](http://changkun.us/about/) +> +> 2.0 +> 翻译+校对:[changkun](http://changkun.us/about/) +> +> 2.1 +> 翻译+校对:[changkun](http://changkun.us/about/) +> +> 2.2 +> 翻译+校对:[changkun](http://changkun.us/about/) + +本页面根据 [Document Revision History](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/RevisionHistory.html) 进行适配更新。 本页内容包括: -- [XCode6.4 Beta Swift语法文档更新](#xcode6_4_Beta) -- [XCode6.3正式版 Swift语法文档更新](#xcode6_3) -- [XCode6.2正式版 Swift语法文档更新](#xcode6_2) -- [XCode6.2 Beta3 Swift语法文档更新](#xcode6_2_Beta3) -- [XCode6.2 Beta2 Swift语法文档更新](#xcode6_2_Beta2) -- [XCode6.2 Beta1 Swift语法文档更新](#xcode6_2_Beta1) -- [XCode6.1.1正式版 Swift语法文档更新](#xcode6_1_1) -- [XCode6.1 Swift语法文档更新](#xcode6_1) -- [XCode6.1 Beta2 Swift语法文档更新](#xcode6_1_Beta2) -- [XCode6.1 Beta1 Swift语法文档更新](#xcode6_1_Beta1) -- [XCode6 Beta7 Swift语法文档更新](#xcode6_beta7) -- [XCode6 Beta6 Swift语法文档更新](#xcode6_beta6) -- [XCode6 Beta5 Swift语法文档更新](#xcode6_beta5) -- [XCode6 Beta4 Swift语法文档更新](#xcode6_beta4) -- [XCode6 Beta3 Swift语法文档更新](#xcode6_beta3) -- [XCode6 Beta2 Swift语法文档更新](#xcode6_beta2) -- [XCode6 Beta1 Swift语法文档更新](#xcode6_beta1) -- XCode6下载: [老码云盘下载](http://pan.baidu.com/disk/home#from=share_pan_logo&path=%252F%25E8%2580%2581%25E7%25A0%2581%25E4%25BA%2591%25E7%259B%2598-XCode6%252FXCode6-Beta5) +- [Swift 2.2 更新](#swift_2_2) +- [Swift 2.1 更新](#swift_2_1) +- [Swift 2.0 更新](#swift_2_0) +- [Swift 1.2 更新](#swift_1_2) +- [Swift 1.1 更新](#swift_1_1) +- [Swift 1.0 更新](#swift_1_0) -以下部分是针对XCode6每一次Beta版本直至正式版发布,Swift语法部分的更新归类 - - -### XCode6.4 Beta中Swift语法更新 - -***注意:苹果在这个版本发布后没有及时的更新Swift Programming Language文档,以下是[老码团队](http://weibo.com/u/5241713117)通过XCode6.4 Beta Release Note总结的更改说明:*** + +### Swift 2.2 更新 @@ -42,10 +49,55 @@ - + @@ -53,9 +105,195 @@
2015-04-132016-03-21
  • - XCode6.4包含了对于构建和调试基于iOS8.4 App的支持 + 更新至 Swift 2.2。 +
  • +
  • + 增加了编译配置语句一节中关于如何根据 Swift 版本进行条件编译。 +
  • +
  • + 增加了显示成员表达式一节中关于如何区分只有参数名不同的方法和构造器的信息。 +
  • +
  • + 增加了选择器表达式一节中针对 Objective-C 选择器的 #selector 语法。 +
  • +
  • + 更新了关联类型协议关联类型声明,使用 associatedtype 关键词修改了对于关联类型的讨论。 +
  • +
  • + 更新了可失败构造器一节中关于当构造器在实例完全初始化之前返回 nil的相关信息。 +
  • +
  • + 增加了比较运算符一节中关于比较元组的信息。 +
  • +
  • + 增加了关键字和标点符号一节中关于使用关键字作为外部参数名的信息。 +
  • +
  • + 增加了声明特性一节中关于@objc特性的讨论,并指出枚举(Enumeration)和枚举用例(Enumaration Case)。 +
  • +
  • + 增加了操作符一节中对于自定义运算符的讨论包含了.。 +
  • +
  • + 增加了重新抛出错误的函数和方法一节中关于重新抛出错误函数不能直接抛出错误的笔记。 +
  • +
  • + 增加了属性观察器一节中关于当作为 in-out 参数传递属性时,属性观察器的调用行为。 +
  • +
  • + 增加了Swift 初见一节中关于错误处理的内容。 +
  • +
  • + 更新了弱引用一节中的图片用以更清楚的展示重新分配过程。 +
  • +
  • + 删除了 C 语言风格的 for 循环,++ 前缀和后缀运算符,以及-- 前缀和后缀运算符。 +
  • +
  • + 删除了对变量函数参数和柯里化函数的特殊语法的讨论。
+ +### Swift 2.1 更新 + + + + + + + + + + + + + + +
发布日期语法变更记录
2015-10-20 +
+ + +### Swift 2.0 更新 + + + + + + + + + + + + + + +
发布日期语法变更记录
2015-09-16
    +
  • + 更新至 Swift 2.0。 +
  • +
  • + 增加了对于错误处理相关内容,包括 错误处理一章、Do 语句Throw 语句Defer 语句以及try 运算符 的多个小节。 +
  • +
  • + 更新了表示并抛出错误一节,现在所有类型均可遵循 ErrorType 协议。 +
  • +
  • + 增加了将错误转换成可选值一节 try? 关键字的相关信息。 +
  • +
  • + 增加了枚举一章的递归枚举一节和声明一章的任意类型用例的枚举一节中关于递归枚举的内容。 +
  • +
  • + 增加了控制流一章中a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ControlFlow.html#//apple_ref/doc/uid/TP40014097-CH9-ID523">检查 API 可用性一节和语句一章中可用性条件一节中关于 API 可用性检查的内容。 +
  • + +
  • + 增加了控制流一章的早期退出一节和语句一章的guard语句中关于新 guard 语句的内容。 +
  • +
  • + 增加了协议一章中协议扩展一节中关于协议扩展的内容。 +
  • +
  • + 增加了访问控制一章中单元测试 target 的访问级别一节中关于单元测试的访问控制相关的内容。 +
  • +
  • + 增加了模式一章中可选模式一节中的新可选模式。 +
  • +
  • + 更新了 Repeat-While 一节中关于repeat-while循环的信息。 +
  • +
  • + 更新了字符串和字符一章,现在String在 Swift 标准库中不再遵循CollectionType协议。 +
  • +
  • + 增加了打印常量和变量一节中关于新 Swift 标准库中关于 print(_:separator:terminator) 的信息。 +
  • +
  • + 增加了枚举一章中原始值的隐式赋值一节和声明一章的包含原始值类型的枚举一节中关于包含String原始值的枚举用例的行为。 +
  • +
  • + 增加了自闭包一节中关于@autoclosure特性的相关信息,包括它的@autoclosure(escaping)形式。 +
  • +
  • + 更新了声明特性一节中关于@avaliablewarn_unused_result特性的相关内容。 +
  • +
  • + 更新了类型特性一节中关于@convention特性的相关信息。 +
  • +
  • + 增加了可选绑定一节中关于使用where子句进行多可选绑定的内容。 +
  • +
  • + 增加了字符串字面量一节中关于在编译时使用 + 运算符凭借字符串字面量的相关信息。 +
  • +
  • + 增加了元类型一节中关于元类型值的比较和使用它们通过构造器表达式构造实例。 +
  • +
  • + 增加了断言调试一节中关于用户定义断言是被警用的相关内容。 +
  • +
  • + 更新了声明特性一节中,对@NSManaged特性的讨论,现在这个特性可以被应用到一个确定实例方法。 +
  • +
  • + 更新了可变参数一节,现在可变参数可以声明在函数参数列表的任意位置中。 +
  • +
  • + 增加了重写可失败构造器一节中,关于非可失败构造器相当于一个可失败构造器通过父类构造器的结果进行强制拆包的相关内容。 +
  • +
  • + 增加了任意类型用例的枚举一节中关于枚举用例作为函数的内容。 +
  • +
  • + 增加了构造器表达式一节中关于显式引用一个构造器的内容。 +
  • +
  • + 更新了编译控制语句一节中关于编译信息以及行控制语句的相关信息。 +
  • +
  • + 更新了元类型一节中关于如何从元类型值中构造类实例。 +
  • +
  • + 更新了弱引用一节中关于弱引用作为缓存的显存的不足。 +
  • +
  • + 更新了类型特性一节,提到了存储型特性其实是懒加载。 +
  • +
  • + 更新了捕获类型一节,阐明了变量和常量在闭包中如何被捕获。 +
  • +
  • + 更新了声明特性一节用以描述如何在类中使用@objc关键字。 +
  • +
  • + 增加了错误处理一节中关于执行throw语句的性能的讨论。增加了 Do 语句一节中相似的信息。 +
  • +
  • + 更新了类型特性一节中关于类、结构体和枚举的存储型和计算型特性的信息。 +
  • +
  • + 更新了Break 语句一节中关于带标签的 break 语句。 +
  • +
  • + 更新了属性观察器一节,阐明了willSetdidSet观察器的行为。 +
  • +
  • + 增加了访问级一节中关于private作用域访问的相关信息。 +
  • +
  • + 增加了弱引用一节中关于若应用在垃圾回收系统和 ARC 之间的区别。 +
  • +
  • + 更新了字符串字面量中特殊字符一节中对 Unicode 标量更精确的定义。 +
  • +
+
+ + + +### Swift 1.2 更新 -### XCode6.3中Swift语法更新 -***注意:苹果此时发布了统一的版本XCode6.3,其中将以前的XCode6.3 Beta系列版本合并, 而XCode6.3共计发布了4次Beta版本,[老码团队](http://weibo.com/u/5241713117)通过Release Note总结的详细更改说明请参看:[Swift语法更新记录表格](https://docs.baihui.com/sheet/published.do?rid=mxpis6d36a8b7bc254c36ae2a808c64c2361e)*** @@ -68,6 +306,9 @@ - - -
2015-4-8
    +
  • + 更新至 Swift 1.2。 +
  • Swift现在自身提供了一个Set集合类型,更多信息请看集合 @@ -85,7 +326,7 @@ 增加了一个新的指导章节,它是关于字符串索引
  • - 从溢出运算符中移除了溢出除运算符和求余溢出运算符 + 从溢出运算符中移除了溢出除运算符(&/)和求余溢出运算符(&%)。
  • 更新了常量和常量属性在声明和构造时的规则,更多信息,请看常量声明 @@ -111,29 +352,7 @@
  • 更新了运算符章节来明确指明一些例子来说明自定义运算符所支持的特性,如数学运算符,各种符号,Unicode符号块等
  • -
-
- - -### XCode6.2正式版中Swift语法更新 - -***注意:苹果此时发布了统一的版本XCode6.2,其中将以前的XCode6.2 Beta系列版本合并*** - - - - - - - - - - - -
发布日期语法变更记录
2015-02-09
    -
  • +
  • 在函数作用域中的常量声明时可以不被初始化,它必须在第一次使用前被赋值。更多的信息,请看常量声明
  • @@ -157,139 +376,9 @@
- -### XCode6.2 Beta3中Swift语法更新 -***注意:苹果在这个版本发布后没有及时的更新Swift Programming Language文档,以下是[老码团队](http://weibo.com/u/5241713117)通过XCode6.2 Beta3 Release Note总结的更改说明:*** - - - - - - - - - - - - - - -
发布日期语法变更记录
2014-12-19 -
    -
  • - 在对Watch App做消息通知模拟调试时,第一个payload.apns文件将会被默认选择 -
  • -
  • - 在为Watch App使用asset catalog时,38mm和42mm尺寸的图片就会被使用 -
  • -
  • - 在做Watch App开发时,@IBAction属性支持WKInterfaceSwitchWKInterfaceSlider Swift类型了 -
  • -
  • - 现在可以通过Device窗口安装,删除和访问App容器中的数据了。 -
  • -
-
- - -### XCode6.2 Beta2中Swift语法更新 - -***注意:苹果在这个版本发布后没有及时的更新Swift Programming Language文档,以下是[老码团队](http://weibo.com/u/5241713117)通过XCode6.2 Beta2 Release Note总结的更改说明:*** - - - - - - - - - - - - - - -
发布日期语法变更记录
2014-12-10
    -
  • - 现在在Interface Builder中可以针对特定的Device设备自定义Watch应用的Layout布局了 -
  • -
-
- - -### XCode6.2 Beta1中Swift语法更新 - -***注意:苹果在这个版本发布后没有及时的更新Swift Programming Language文档,以下是[老码团队](http://weibo.com/u/5241713117)通过XCode6.2 Beta1 Release Note总结的更改说明:*** - - - - - - - - - - - - - - -
发布日期语法变更记录
2014-11-28
    -
  • - XCode6.2包含了iOS8.2 SDK,该SDK中包含WatchKit用来开发Apple Watch应用。 -
  • -
  • - 在工具集中增加了对WatchKit的支持: - 1)UI设计工具增加了Apple Watch应用的界面组件,通知和小部件。 - 2)增加了调试和性能统计功能 - 3)增加Apple Watch应用的模拟器帮助调试应用功能 -
  • -
  • - 为了使Apple Watch应用能够正常工作,一些具体的参数必须设置: - 1)WatchKit中扩展配置文件Info.plist中的NSExtensionAttributes配置项WKAppBundleIdentifier必须和WatchKit App中的通用配置文件中的属性CFBundleIdentifier项目保持一致。2)WatchKit中的CFBundleIdentifier配置项必须和WKCompanionAppBundleIdentifier中的配置项保持一致 -
  • -
-
- - -### XCode6.1.1中Swift语法更新 - -***注意:苹果在这个版本发布后没有及时的更新Swift Programming Language文档,以下是[老码团队](http://weibo.com/u/5241713117)通过XCode6.1.1 Release Note总结的更改说明:*** - - - - - - - - - - - - - - -
发布日期语法变更记录
2014-12-2
    -
  • - 在SourceKit中一些导致Crash的常见问题被修复,比如名字冲突和遗留废弃数据的问题等。 -
  • -
  • - 把纯正的Swift类对象实例赋值给AnyObject量不会再Crash了。 -
  • -
  • - 在泛型使用场景下,遵循了协议类要求的构造器方法或者类型方法可以直接调用继承类中的方法了。 -
  • -
  • - 修正了InterfaceBuild中如果图片名字含有“/”时,会在OSX10.10上Crash或者无法打开的问题 -
  • -
-
- - -### XCode6.1中Swift语法更新 - -***注意:苹果此时发布了统一的版本XCode6.1,其中将以前的XCode6.0.1和XCode6.1 Beta系列版本合并*** + +### Swift 1.1 更新 @@ -303,112 +392,32 @@ - - -
2014-10-16 -
- - -### XCode6.1 Beta2中Swift语法更新 - -***注意:苹果此时发布了XCode6.0.1版本(也称为XCode6正式版),此版本用于iOS的开发,同时也发布子版本XCode6.1 Beta2,此版本为OSX开发做准备,以下所述的更改仅对XCode6.1 Beta2有效*** - - - - - - - - - - - - - - -
发布日期语法变更记录
2014-09-15
  • - 带有原始值的枚举类型增加了一个rawValue属性替代toRaw()方法,同时使用了一个以rawValue为参数的失败构造器来替代fromRaw()方法。更多的信息,请看原始值(Raw Values)带原始值的枚举类型(Enumerations with Cases of a Raw-Value Type)部分 + 常量和变量的 Any 类型现可以包含函数实例。更新了关于 Any 相关的示例来展示如何在 switch 语句中如何检查并转换到一个函数类型。
  • -
-
- - -### XCode6.1 Beta1中Swift语法更新 - -***注意:苹果此时发布了XCode6 GM版本,此版本用于iOS的开发,同时也发布子版本XCode6.1 Beta1,此版本为OSX开发做准备,以下所述的更改仅对XCode6.1 Beta1有效*** - - - - - - - - - - - -
发布日期语法变更记录
2014-09-09
- -### XCode6 Beta7中Swift语法更新 - -***注意:苹果在这个版本发布后没有及时的更新Swift Programming Language文档,以下是[老码团队](http://weibo.com/u/5241713117)通过XCode Beta7 Release Note总结的更改说明:*** - - - - - - - - - - - - - - -
发布日期语法变更记录
2014-09-03
    -
  • - 实现了内部库的修改和适配,主要包括如下: - 1)大量内部类或者函数遵循Optional类型和协议 - 2)移除大部分函数返回类型隐式解封可选类型的使用 - -
  • -
  • - 对于泛型的类库函数或接口统一从T!更换为T?T,这样使得语法更加严谨,明确了可能返回为空和不为空的情况 -
  • -
  • - 字符类型不能使用+运算法链接,可以以String(C1)+String(2) 的方式实现字符间链接 -
  • -
  • - 重写了Sort函数,解决了栈溢出的问题 -
  • -
-
- - -### XCode6 Beta6中Swift语法更新 + +### Swift 1.0 更新 @@ -421,6 +430,9 @@ - - -
2014-08-18 -
- - -### XCode6 Beta5中Swift语法更新 - - - - - - - - - - - - - - -
发布日期语法变更记录
2014-08-04
  • 可选类型(Optionals) 若有值时,不再隐式的转换为 true,同样,若无值时,也不再隐式的转换为 false, 这是为了避免在判别 optional Bool 的值时产生困惑。 替代的方案是,用==!= 运算符显式地去判断Optinal是否是 nil,以确认其是否包含值。
  • @@ -510,26 +502,6 @@
  • 为章节Curried Functions添加了更多的信息.
  • -
-
- - -#### XCode6 Beta4中Swift语法更新 - - - - - - - - - - - - - - -
发布日期语法变更记录
2014-07-21 -
- - -#### XCode6 Beta3中Swift语法更新 - - - - - - - - - - - - - - -
发布日期语法变更记录
2014-07-7 -
- - -#### XCode6 Beta2中Swift语法更新 - - - - - - - - - - - - - - -
发布日期语法变更记录
2014-07-7
    -
  • - 发布新的文档用以详述Swift - 苹果公司针对iOS和OS X应用的全新开发语言 -
  • -
-
- - -#### XCode6 Beta1中Swift语法更新 - - - - - - - - - - - - From 667587bfca91635f994939689f86b8eda5724de4 Mon Sep 17 00:00:00 2001 From: shanksyang Date: Tue, 10 May 2016 16:17:10 +0800 Subject: [PATCH 31/61] merge --- source/chapter3/04_Expressions.md | 97 ++++++++++++++++++------------- 1 file changed, 55 insertions(+), 42 deletions(-) diff --git a/source/chapter3/04_Expressions.md b/source/chapter3/04_Expressions.md index 6bacc476..83f70043 100644 --- a/source/chapter3/04_Expressions.md +++ b/source/chapter3/04_Expressions.md @@ -11,6 +11,9 @@ > 2.1 > 翻译:[mmoaay](https://github.com/mmoaay) +> 2.2 +> 校对:[175](https://github.com/Brian175) + 本页包含内容: - [前缀表达式](#prefix_expressions) @@ -40,7 +43,7 @@ Swift 中存在四种表达式:前缀表达式,二元表达式,基本表达式和后缀表达式。表达式在返回一个值的同时还可以引发副作用。 -通过前缀表达式和二元表达式可以对简单表达式使用各种运算符。基本表达式从概念上讲是最短小的表达式,它是一种访问值的方式。后缀表达式则允许你建立复杂的表达式,例如函数调用和成员访问。每种表达式都在下面有详细论述。 +通过前缀表达式和二元表达式可以对简单表达式使用各种运算符。基本表达式从概念上讲是最简单的一种表达式,它是一种访问值的方式。后缀表达式则允许你建立复杂的表达式,例如函数调用和成员访问。每种表达式都在下面有详细论述。 > 表达式语法 @@ -53,7 +56,7 @@ Swift 中存在四种表达式:前缀表达式,二元表达式,基本表 前缀表达式由可选的前缀运算符和表达式组成。前缀运算符只接收一个参数,表达式则紧随其后。 -关于这些运算符的更多信息,请参阅 [基本运算符](../chapter2/02_Basic_Operators.html) 和 [高级运算符](../chapter2/25_Advanced_Operators.html)。 +关于这些运算符的更多信息,请参阅 [基本运算符](../chapter2/02_Basic_Operators.md) 和 [高级运算符](../chapter2/25_Advanced_Operators.md)。 关于 Swift 标准库提供的运算符的更多信息,请参阅 [*Swift Standard Library Operators Reference*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_StandardLibrary_Operators/index.html#//apple_ref/doc/uid/TP40016054)。 @@ -61,10 +64,10 @@ Swift 中存在四种表达式:前缀表达式,二元表达式,基本表 > 前缀表达式语法 -> *前缀表达式* → [*前缀运算符*](02_Lexical_Structure.html#prefix-operator)可选 [*后缀表达式*](#postfix-expression) +> *前缀表达式* → [*前缀运算符*](02_Lexical_Structure.md#prefix-operator)可选 [*后缀表达式*](#postfix-expression) > *前缀表达式* → [*输入输出表达式*](#in-out-expression) -> *输入输出表达式* → **&** [*标识符*](02_Lexical_Structure.html#identifier) +> *输入输出表达式* → **&** [*标识符*](02_Lexical_Structure.md#identifier) ### try 运算符 @@ -96,7 +99,6 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误:try `try` 表达式不能出现在二进制运算符的的右侧,除非二进制运算符是赋值运算符或者 `try` 表达式是被圆括号括起来的。 关于 `try`、`try?` 和 `try!` 的更多信息,以及该如何使用的例子,请参阅 [错误处理](../chapter2/18_Error_Handling.html)。 - > try 表达式语法 > *try 运算符* → **try** | **try?** | **try!** @@ -104,20 +106,20 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误:try ## 二元表达式 -二元表达式形式如下: +二元表达式由中缀运算符和左右参数表达式组成。形式如下: > `左侧参数` `二元运算符` `右侧参数` -关于这些运算符的更多信息,请参阅 [基本运算符](../chapter2/02_Basic_Operators.html) 和 [高级运算符](../chapter2/25_Advanced_Operators.html)。 +关于这些运算符的更多信息,请参阅 [基本运算符](../chapter2/02_Basic_Operators.md) 和 [高级运算符](../chapter2/25_Advanced_Operators.md)。 关于 Swift 标准库提供的运算符的更多信息,请参阅 [*Swift Standard Library Operators Reference*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_StandardLibrary_Operators/index.html#//apple_ref/doc/uid/TP40016054)。 > 注意 -> 在解析时,一个二元表达式将作为一个扁平列表表示,然后根据运算符的优先级,再进一步进行组合。例如,`2 + 3 * 5` 首先被看作具有五个元素的扁平列表,即 `2`、`+`、`3`、`*`、`5`,随后根据运算符优先级组合为 `(2 + (3 * 5))`。 +> 在解析时,一个二元表达式将作为一个扁平列表表示,然后根据运算符的优先级,再进一步进行组合。例如,`2 + 3 * 5` 首先被看作具有五个元素的列表,即 `2`、`+`、`3`、`*`、`5`,随后根据运算符优先级组合为 `(2 + (3 * 5))`。 > 二元表达式语法 -> *二元表达式* → [*二元运算符*](02_Lexical_Structure.html#binary-operator) [*前缀表达式*](#prefix-expression) +> *二元表达式* → [*二元运算符*](02_Lexical_Structure.md#binary-operator) [*前缀表达式*](#prefix-expression) > *二元表达式* → [*赋值运算符*](#assignment-operator) [*try运算符*](#try-operator)可选 [*前缀表达式*](#prefix-expression) > *二元表达式* → [*条件运算符*](#conditional-operator) [*try运算符*](#try-operator)可选 [*前缀表达式*](#prefix-expression) > *二元表达式* → [*类型转换运算符*](#type-casting-operator) @@ -131,7 +133,7 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误:try > `表达式` = `值` -右边的值会被赋值给左边的表达式。如果左边表达式是一个元组,那么右边必须是一个具有同样元素个数的元组。嵌套元组也是允许的。右边的值中的每一部分都会被赋值给左边的表达式中的相应部分。例如: +右边的值会被赋值给左边的表达式。如果左边表达式是一个元组,那么右边必须是一个具有同样元素个数的元组。(嵌套元组也是允许的。)右边的值中的每一部分都会被赋值给左边的表达式中的相应部分。例如: ```swift (a, _, (b, c)) = ("test", 9.45, (12, 3)) @@ -153,7 +155,7 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误:try 如果条件为真,那么对第一个表达式进行求值并返回结果。否则,对第二个表达式进行求值并返回结果。未使用的表达式不会进行求值。 -关于使用三元条件运算符的例子,请参阅 [三元条件运算符](../chapter2/02_Basic_Operators.html#ternary_conditional_operator)。 +关于使用三元条件运算符的例子,请参阅 [三元条件运算符](../chapter2/02_Basic_Operators.md#ternary_conditional_operator)。 > 三元条件运算符语法 @@ -162,11 +164,11 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误:try ### 类型转换运算符 -有 4 种类型转换运算符:`is`、`as`、`as?` 和 `as!`。它们有如下的形式: +有 4 种类型转换运算符:`is`、`as`、`as? `和`as!`。它们有如下的形式: > `表达式` is `类型` `表达式` as `类型` -`表达式` is? `类型` +`表达式` as? `类型` `表达式` as! `类型` `is` 运算符在运行时检查表达式能否向下转化为指定的类型,如果可以则返回 `ture`,否则返回 `false`。 @@ -188,20 +190,20 @@ f(x as Any) // 打印 “Function for Any” ``` -桥接可将 Swift 标准库中的类型(例如 `String`)作为一个与之相关的 Foundation 类型(例如 `NSString`)来使用,而不需要新建一个实例。关于桥接的更多信息,请参阅 [*Using Swift with Cocoa and Objective-C*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中的 [Working with Cocoa Data Types](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)。 +桥接可将 Swift 标准库中的类型(例如 `String`)作为一个与之相关的 Foundation 类型(例如 `NSString`)来使用,而不需要新建一个实例。关于桥接的更多信息,请参阅 [*Using Swift with Cocoa and Objective-C (Swift2.2)*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中的 [Working with Cocoa Data Types](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)。 `as?` 运算符有条件地执行类型转换,返回目标类型的可选值。在运行时,如果转换成功,返回的可选值将包含转换后的值,否则返回 `nil`。如果在编译时就能确定转换一定会成功或是失败,则会导致编译报错。 -`as!` 运算符执行强制类型转换,返回目标类型的非可选值。如果转换失败,则会导致运行时错误。表达式 `x as T` 的效果等同于 `(x as? T)!`。 +`as!` 运算符执行强制类型转换,返回目标类型的非可选值。如果转换失败,则会导致运行时错误。表达式 `x as! T` 效果等同于 `(x as? T)!`。 -关于类型转换的更多内容和例子,请参阅 [类型转换](../chapter2/19_Type_Casting.html)。 +关于类型转换的更多内容和例子,请参阅 [类型转换](../chapter2/19_Type_Casting.md)。 > 类型转换运算符语法 -> *类型转换运算符* → **is** [*类型*](03_Types.html#type) -> *类型转换运算符* → **as** [*类型*](03_Types.html#type) -> *类型转换运算符* → **is** **?** [*类型*](03_Types.html#type) -> *类型转换运算符* → **as** **!** [*类型*](03_Types.html#type) +> *类型转换运算符* → **is** [*类型*](03_Types.md#type) +> *类型转换运算符* → **as** [*类型*](03_Types.md#type) +> *类型转换运算符* → **as** **?** [*类型*](03_Types.md#type) +> *类型转换运算符* → **as** **!** [*类型*](03_Types.md#type) ## 基本表达式 @@ -227,14 +229,14 @@ f(x as Any) 字面量 | 类型 | 值 :------------- | :---------- | :---------- -`#file` | `String` | 表达式所在文件的名字 -`#line` | `Int` | 表达式所在的行数 -`#column` | `Int` | 表达式所在的列数 -`#function` | `String` | 表达式所在的函数或方法的名字 +`#file` | `String` | 所在的文件名 +`#line` | `Int` | 所在的行数 +`#column` | `Int` | 所在的列数 +`#function` | `String` | 所在的声明的名字 -`#line` 除了上述含义外,还有另一种含义。当它出现在单独一行时,它会作为行控制语句使用,请参阅 [行控制语句](10_Statements.md#line_control_statements)。 +`#line`除了上述含义外,还有另一种含义。当它出现在单独一行时,会被理解成行控制语句,请参阅[线路控制语句](../chapter3/10_Statements.md#线路控制语句)。 -对于 `#function`,在函数中会返回当前函数的名字,在方法中会返回当前方法的名字,在属性的存取器中会返回属性的名字,在特殊的成员如 `init` 或 `subscript` 中会返回这个关键字的名字,在某个文件的顶级作用域中会返回当前模块的名字。 +对于 `#function`,在函数中会返回当前函数的名字,在方法中会返回当前方法的名字,在属性的存取器中会返回属性的名字,在特殊的成员如 `init` 或 `subscript` 中会返回这个关键字的名字,在某个文件中会返回当前模块的名字。 `#function` 作为函数或者方法的默认参数值时,该字面量的值取决于函数或方法的调用环境。 @@ -262,7 +264,7 @@ var emptyArray: [Double] = [] > [`键 1` : `值 1`, `键 2` : `值 2`, `...`] -字典中的最后一个表达式可以紧跟一个逗号。字典字面量的类型是 `[Key : Value]`,`Key` 表示键的类型,`Value` 表示值的类型。如果字典中包含多种类型,那么 `Key` 表示的类型则为所有键最近的公共父类型,`Value` 与之类似。一个空的字典字面量由方括号中加一个冒号组成(`[:]`),从而与空数组字面量区分开,可以使用空字典字面量来创建特定类型的字典。 +字典中的最后一个表达式可以紧跟一个逗号。字典字面量的类型是 `[Key : Value]`,`Key` 表示键的类型,`Value` 表示值的类型。如果字典中包含多种类型,那么 `Key` 表示的类型则为所有键最接近的公共父类型,`Value` 与之相似。一个空的字典字面量由方括号中加一个冒号组成(`[:]`),从而与空数组字面量区分开,可以使用空字典字面量来创建特定类型的字典。 ```swift var emptyDictionary: [String : Double] = [:] @@ -273,7 +275,7 @@ var emptyDictionary: [String : Double] = [:] > *字面量表达式* → [*字面量*](02_Lexical_Structure.md#literal) > *字面量表达式* → [*数组字面量*](#array-literal) | [*字典字面量*](#dictionary-literal) -> *字面量表达式* → **\_\_FILE\_\_** | **\_\_LINE\_\_** | **\_\_COLUMN\_\_** | **\_\_FUNCTION\_\_** +> *字面量表达式* → **#file** | **#line** | **#column** | **#function** > *数组字面量* → **[** [*数组字面量项列表*](#array-literal-items)可选 **]** @@ -326,10 +328,14 @@ struct Point { > self 表达式语法 -> *self 表达式* → **self** -> *self 表达式* → **self** **.** [*标识符*](02_Lexical_Structure.md#identifier) -> *self 表达式* → **self** **[** [*表达式*](#expression) **]** -> *self 表达式* → **self** **.** **init** +> *self 表达式* → **self** | [*self 方法表达式*](#self-method-expression) | [*self 下标表达式*](#self-subscript-expression) | [*self 构造器表达式*](#self-initializer-expression) +> + +> *self 方法表达式* → **self** **.** [*标识符*](02_Lexical_Structure.md#identifier) + +> *self 下标表达式* → **self** **[** [*表达式*](#expression) **]** + +> *self 构造器表达式* → **self** **.** **init** ### 超类表达式 @@ -357,7 +363,7 @@ struct Point { ### 闭包表达式 -闭包表达式会创建一个闭包,在其他编程语言中也叫 lambda 表达式或匿名函数。跟函数一样,闭包包含了待执行的代码,不同的是闭包还会捕获附近作用域中的常量和变量。它的形式如下: +闭包表达式会创建一个闭包,在其他语言中也叫 *lambda* 或匿名函数。跟函数一样,闭包包含了待执行的代码,不同的是闭包还会捕获所在环境中的常量和变量。它的形式如下: ```swift { (parameters) -> return type in @@ -416,7 +422,7 @@ closure() 在示例中,变量 `b` 只有一个,然而,变量 `a` 有两个,一个在闭包外,一个在闭包内。闭包内的变量 `a` 会在闭包创建时用闭包外的变量 `a` 的值来初始化,除此之外它们并无其他联系。这意味着在闭包创建后,改变某个 `a` 的值都不会对另一个 `a` 的值造成任何影响。与此相反,闭包内外都是同一个变量 `b`,因此在闭包外改变其值,闭包内的值也会受影响。 -如果闭包捕获的值具有引用语义则有所不同。例如下面示例中,总共有两个变量 `x`,一个在闭包外,一个在闭包内,由于它们的值具有引用语义,虽然这是两个不同的变量,它们却都引用着同一实例。 +如果闭包捕获的值具有引用语义则有所不同。例如,下面示例中,有两个变量 `x`,一个在闭包外,一个在闭包内,由于它们的值是引用语义,虽然这是两个不同的变量,它们却都引用着同一实例。 ```swift class SimpleClass { @@ -442,7 +448,7 @@ myFunction { [weak self] in print(self!.title) } // 以弱引用捕获 myFunction { [unowned self] in print(self.title) } // 以无主引用捕获 ``` -在捕获列表中,也可以将任意表达式的值绑定到一个常量上。表达式会在闭包被创建时进行求值,闭包会按照指定的引用类型来捕获表达式的值。例如: +在捕获列表中,也可以将任意表达式的值绑定到一个常量上。该表达式会在闭包被创建时进行求值,闭包会按照指定的引用类型来捕获表达式的值。例如: ```swift // 以弱引用捕获 self.parent 并赋值给 parent @@ -475,7 +481,7 @@ myFunction { [weak parent = self.parent] in print(parent!.title) } ### 隐式成员表达式 -若类型可被推断出来,可以使用隐式成员表达式来访问某个类型的成员(例如某个枚举成员或某个类型方法),其形式如下: +若类型可被推断出来,可以使用隐式成员表达式来访问某个类型的成员(例如某个枚举成员或某个类型方法),形式如下: > .`成员名称` @@ -528,7 +534,7 @@ x = .AnotherValue > \#selector(方法名) -方法名必须是存在于 Objective-C 运行时中的方法的方法名。选择器表达式的返回值是一个 `Selector` 类型的值。例如: +方法名必须是存在于 Objective-C 运行时中的方法的引用。选择器表达式的返回值是一个 `Selector` 类型的实例。例如: ```swift class SomeClass: NSObject { @@ -549,7 +555,7 @@ extension SomeClass { let anotherSelector = #selector(x.doSomething(_:) as (String) -> Void) ``` -由于选择器是在编译时创建的,因此编译器可以检查方法是否存在,以及方法是否暴露给了 Objective-C 运行时。 +由于选择器是在编译时创建的,因此编译器可以检查方法是否存在,以及方法是否在运行时暴露给了 Objective-C 。 > 注意 > 虽然方法名是个表达式,但是它不会被求值。 @@ -565,7 +571,7 @@ let anotherSelector = #selector(x.doSomething(_:) as (String) -> Void) 后缀表达式就是在某个表达式的后面运用后缀运算符或其他后缀语法。从语法构成上来看,基本表达式也是后缀表达式。 -关于这些运算符的更多信息,请参阅 [基本运算符](../chapter2/02_Basic_Operators.html) 和 [高级运算符](../chapter2/25_Advanced_Operators.html)。 +关于这些运算符的更多信息,请参阅 [基本运算符](../chapter2/02_Basic_Operators.md) 和 [高级运算符](../chapter2/25_Advanced_Operators.md)。 关于 Swift 标准库提供的运算符的更多信息,请参阅 [*Swift Standard Library Operators Reference*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_StandardLibrary_Operators/index.html#//apple_ref/doc/uid/TP40016054)。 @@ -629,7 +635,7 @@ myData.someMethod {$0 == 13} ```swift class SomeSubClass: SomeSuperClass { - init() { + override init() { // 此处为子类构造过程 super.init() } @@ -658,7 +664,8 @@ let s3 = someValue.dynamicType.init(data: 7) // 有效 > 构造器表达式语法 -> *构造器表达式* → [*后缀表达式*](postfix-expression) **.** **init** +> *构造器表达式* → [*后缀表达式*](#postfix-expression) **.** **init** +> *构造器表达式* → [*后缀表达式*](#postfix-expression) **.** **init** **(** [*参数名称*](#argument-names) **)** ### 显式成员表达式 @@ -718,7 +725,13 @@ let x = [10, 3, 20, 15, 4] > 显式成员表达式语法 > *显式成员表达式* → [*后缀表达式*](#postfix-expression) **.** [*十进制数字*](02_Lexical_Structure.md#decimal-digit) -> *显式成员表达式* → [*后缀表达式*](#postfix-expression) **.** [*标识符*](02_Lexical_Structure.html#identifier) [*泛型实参子句*](08_Generic_Parameters_and_Arguments.md#generic-argument-clause)可选 +> *显式成员表达式* → [*后缀表达式*](#postfix-expression) **.** [*标识符*](02_Lexical_Structure.md#identifier) [*泛型实参子句*](08_Generic_Parameters_and_Arguments.md#generic-argument-clause)可选
+> *显式成员表达式* → [*后缀表达式*](#postfix-expression) **.** [*标识符*](02_Lexical_Structure.md#identifier) **(** [*参数名称*](#argument-names) **)** +> + +> *参数名称* → [*参数名*](#argument-name) [*参数名称*](#argument-names)可选
+ +> *参数名* → [*标识符*](02_Lexical_Structure.md#identifier) **:** ### 后缀 self 表达式 From 0bdade3c6649d5f208bf2f4448f441ef108bff81 Mon Sep 17 00:00:00 2001 From: Sketchk Date: Wed, 11 May 2016 17:24:25 +0800 Subject: [PATCH 32/61] =?UTF-8?q?=E6=A0=A1=E6=AD=A3:=201.A=20Swift=20Tour?= =?UTF-8?q?=E4=B8=AD=E7=9A=84"=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86"?= =?UTF-8?q?=E4=B8=80=E8=8A=82=E7=9A=84=E4=BB=A3=E7=A0=81=20do=20=E4=B8=8E?= =?UTF-8?q?=20{=20=E4=B8=AD=E9=97=B4=E7=BC=BA=E5=B0=91=E4=BA=86=E7=A9=BA?= =?UTF-8?q?=E6=A0=BC=202.Basic=20Operators=E4=B8=80=E8=8A=82=E4=B8=AD=20"?= =?UTF-8?q?=E5=BC=80=E5=A4=B4"=E7=9A=84"=E6=88=96=E8=AE=A9=20i=20=E5=80=BC?= =?UTF-8?q?=E5=8A=A0=201=20=E7=9A=84=E4=BE=BF=E6=8D=B7=E8=87=AA=E5=A2=9E?= =?UTF-8?q?=E8=BF=90=E7=AE=97=E7=AC=A6=20++i=20=E7=AD=89"=E5=9C=A8?= =?UTF-8?q?=E5=8E=9F=E6=96=87=E4=B8=AD=E5=B7=B2=E7=BB=8F=E5=88=A0=E9=99=A4?= =?UTF-8?q?=203.Basic=20Operators=E4=B8=80=E8=8A=82=E4=B8=AD=20"=E6=9C=AF?= =?UTF-8?q?=E8=AF=AD"=E7=9A=84"=E5=90=8E=E7=BD=AE=E8=BF=90=E7=AE=97?= =?UTF-8?q?=E7=AC=A6=E9=9C=80=E7=B4=A7=E8=B7=9F=E5=9C=A8=E6=93=8D=E4=BD=9C?= =?UTF-8?q?=E5=AF=B9=E8=B1=A1=E4=B9=8B=E5=90=8E=EF=BC=88=E5=A6=82=20i++?= =?UTF-8?q?=EF=BC=89"=E6=94=B9=E4=B8=BA=E4=BA=86=EF=BC=88=E5=A6=82=20c!?= =?UTF-8?q?=EF=BC=89=204.Basic=20Operators=E4=B8=80=E8=8A=82=E4=B8=AD=20"?= =?UTF-8?q?=E8=87=AA=E5=A2=9E=E5=92=8C=E8=87=AA=E5=87=8F=E8=BF=90=E7=AE=97?= =?UTF-8?q?"=E7=9A=84=E5=86=85=E5=AE=B9=E5=B7=B2=E8=A2=AB=E5=88=A0?= =?UTF-8?q?=E9=99=A4=205.Strings=20and=20Characters=E4=B8=80=E8=8A=82?= =?UTF-8?q?=E4=B8=AD"=E5=89=8D=E7=BC=80/=E5=90=8E=E7=BC=80=E7=9B=B8?= =?UTF-8?q?=E7=AD=89"=E7=9A=84"++"=E5=8F=B7=E5=B7=B2=E7=BB=8F=E8=A2=AB?= =?UTF-8?q?=E6=9B=BF=E6=8D=A2=E4=B8=BA"+=3D1"=206.Collection=20Types?= =?UTF-8?q?=E4=B8=80=E8=8A=82=E4=B8=AD=E7=9A=84"=E9=80=9A=E8=BF=87?= =?UTF-8?q?=E4=B8=A4=E4=B8=AA=E6=95=B0=E7=BB=84=E7=9B=B8=E5=8A=A0=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E4=B8=80=E4=B8=AA=E6=95=B0=E7=BB=84"=E7=9A=84?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=B8=8E=E5=8E=9F=E6=96=87=E4=B8=8D=E7=AC=A6?= =?UTF-8?q?,=E4=B8=94=E5=BD=93=E5=89=8D=E4=BB=A3=E7=A0=81=E5=9C=A8?= =?UTF-8?q?=E8=BF=90=E8=A1=8C=E6=97=B6=E4=BC=9A=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/chapter1/02_a_swift_tour.md | 2 +- source/chapter2/02_Basic_Operators.md | 36 ++------------------ source/chapter2/03_Strings_and_Characters.md | 6 ++-- source/chapter2/04_Collection_Types.md | 2 +- 4 files changed, 7 insertions(+), 39 deletions(-) diff --git a/source/chapter1/02_a_swift_tour.md b/source/chapter1/02_a_swift_tour.md index f244fe65..f2b5c441 100755 --- a/source/chapter1/02_a_swift_tour.md +++ b/source/chapter1/02_a_swift_tour.md @@ -713,7 +713,7 @@ func sendToPrinter(printerName: String) throws -> String { 有多种方式可以用来进行错误处理。一种方式是使用`do-catch`。在`do`代码块中,使用`try`来标记可以抛出错误的代码。在`catch`代码块中,除非你另外命名,否则错误会自动命名为`error`。 ```swift -do{ +do { let printerResponse = try sendToPrinter("Bi Sheng") print(printerResponse) } catch { diff --git a/source/chapter2/02_Basic_Operators.md b/source/chapter2/02_Basic_Operators.md index d034f68b..70984fb6 100755 --- a/source/chapter2/02_Basic_Operators.md +++ b/source/chapter2/02_Basic_Operators.md @@ -26,7 +26,7 @@ - [区间运算符](#range_operators) - [逻辑运算符](#logical_operators) -运算符是检查、改变、合并值的特殊符号或短语。例如,加号(`+`)将两个数相加(如 `let i = 1 + 2`)。更复杂的运算例子包括逻辑与运算符 `&&`(如 `if enteredDoorCode && passedRetinaScan`),或让 i 值加 1 的便捷自增运算符 `++i` 等。 +运算符是检查、改变、合并值的特殊符号或短语。例如,加号(`+`)将两个数相加(如 `let i = 1 + 2`)。更复杂的运算例子包括逻辑与运算符 `&&`(如 `if enteredDoorCode && passedRetinaScan`)。 Swift 支持大部分标准 C 语言的运算符,且改进许多特性来减少常规编码错误。如:赋值符(`=`)不返回值,以防止把想要判断相等运算符(`==`)的地方写成赋值符导致的错误。算术运算符(`+`,`-`,`*`,`/`,`%`等)会检测并不允许值溢出,以此来避免保存变量时由于变量大于或小于其类型所能承载的范围时导致的异常结果。当然允许你使用 Swift 的溢出运算符来实现溢出。详情参见[溢出运算符](../chapter2/25_Advanced_Operators.html#overflow_operators)。 @@ -39,7 +39,7 @@ Swift 支持大部分标准 C 语言的运算符,且改进许多特性来减 运算符分为一元、二元和三元运算符。 -- 一元运算符对单一操作对象操作(如 `-a`)。一元运算符分前置运算符和后置运算符,前置运算符需紧跟在操作对象之前(如 `!b`),后置运算符需紧跟在操作对象之后(如 `i++`)。 +- 一元运算符对单一操作对象操作(如 `-a`)。一元运算符分前置运算符和后置运算符,前置运算符需紧跟在操作对象之前(如 `!b`),后置运算符需紧跟在操作对象之后(如 `c!`)。 - 二元运算符操作两个操作对象(如 `2 + 3`),是中置的,因为它们出现在两个操作对象之间。 - 三元运算符操作三个操作对象,和 C 语言一样,Swift 只有一个三元运算符,就是三目运算符(`a ? b : c`)。 @@ -154,38 +154,6 @@ Swift 中所有数值类型都支持了基本的四则算术运算: ![Art/remainderFloat_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/remainderFloat_2x.png "Art/remainderFloat_2x.png") -### 自增和自减运算 - -和 C 语言一样,Swift 也提供了对变量本身加 1 或减 1 的自增(`++`)和自减(`--`)的缩略算符。其操作对象可以是整形和浮点型。 - -```swift -var i = 0 -++i // 现在 i = 1 -``` - -每调用一次 `++i`,`i` 的值就会加 1。实际上,`++i` 是 `i = i + 1` 的简写,而 `--i` 是 `i = i - 1` 的简写。 - -`++` 和 `--` 既可以用作前置运算又可以用作后置运算。`++i`、`i++` 都是有效的自增的写法;相类似对应自减的写法则是 `--i` 和 `i--`。 - -我们需要注意的是这些运算符即可修改了 `i` 的值也可以返回 `i` 的值。如果你只想修改 `i` 的值,那你就可以忽略这个返回值。但如果你想使用返回值,你就需要留意前置和后置操作的返回值是不同的,它们遵循以下原则: - -- 当 `++` 前置的时候,先自増再返回。 -- 当 `++` 后置的时候,先返回再自增。 - -例如: - -```swift -var a = 0 -let b = ++a // a 和 b 现在都是 1 -let c = a++ // a 现在 2, 但 c 是 a 自增前的值 1 -``` - -上述例子,`let b = ++a` 先把 `a` 加 1 了再返回 `a` 的值。所以 `a` 和 `b` 都是新值 `1`。 - -而 `let c = a++`,是先返回了 `a` 的值,然后 `a` 才加 1。所以 `c` 得到了 `a` 的旧值 1,而 `a` 加 1 后变成 2。 - -除非你需要使用 `i++` 的特性,不然推荐你使用 `++i` 和 `--i`,因为先修改后返回这样的行为更符合我们的逻辑。 - ### 一元负号运算符 diff --git a/source/chapter2/03_Strings_and_Characters.md b/source/chapter2/03_Strings_and_Characters.md index 20704884..ae58c599 100755 --- a/source/chapter2/03_Strings_and_Characters.md +++ b/source/chapter2/03_Strings_and_Characters.md @@ -472,7 +472,7 @@ let romeoAndJuliet = [ var act1SceneCount = 0 for scene in romeoAndJuliet { if scene.hasPrefix("Act 1 ") { - ++act1SceneCount + act1SceneCount += 1 } } print("There are \(act1SceneCount) scenes in Act 1") @@ -486,9 +486,9 @@ var mansionCount = 0 var cellCount = 0 for scene in romeoAndJuliet { if scene.hasSuffix("Capulet's mansion") { - ++mansionCount + mansionCount += 1 } else if scene.hasSuffix("Friar Lawrence's cell") { - ++cellCount + cellCount += 1 } } print("\(mansionCount) mansion scenes; \(cellCount) cell scenes") diff --git a/source/chapter2/04_Collection_Types.md b/source/chapter2/04_Collection_Types.md index 249e19da..8f862b2d 100755 --- a/source/chapter2/04_Collection_Types.md +++ b/source/chapter2/04_Collection_Types.md @@ -87,7 +87,7 @@ var threeDoubles = [Double](count: 3, repeatedValue:0.0) 我们可以使用加法操作符(`+`)来组合两种已存在的相同类型数组。新数组的数据类型会被从两个数组的数据类型中推断出来: ```swift -var anotherThreeDoubles = Array(count: 3, repeatedValue: 2.5) +var anotherThreeDoubles = [Double](count: 3, repeatedValue: 2.5) // anotherThreeDoubles 被推断为 [Double],等价于 [2.5, 2.5, 2.5] var sixDoubles = threeDoubles + anotherThreeDoubles From fc6f33567dafc61bef711d2ee34101e3f1e6045f Mon Sep 17 00:00:00 2001 From: zhungxd Date: Thu, 12 May 2016 15:36:35 +0800 Subject: [PATCH 33/61] =?UTF-8?q?update=2013=5FInheritance.md.=20=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3=E6=A0=87=E9=A2=98=E9=94=99=E8=AF=AF=20(#608)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/chapter2/13_Inheritance.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/chapter2/13_Inheritance.md b/source/chapter2/13_Inheritance.md index 17c8f717..61b53052 100755 --- a/source/chapter2/13_Inheritance.md +++ b/source/chapter2/13_Inheritance.md @@ -10,7 +10,7 @@ 本页包含内容: -- [定义一个基类(Base class)](#defining_a_base_class) +- [定义一个基类(Defining a Base Class)](#defining_a_base_class) - [子类生成(Subclassing)](#subclassing) - [重写(Overriding)](#overriding) - [防止重写(Preventing Overrides)](#preventing_overrides) @@ -22,7 +22,7 @@ 可以为类中继承来的属性添加属性观察器(property observers),这样一来,当属性值改变时,类就会被通知到。可以为任何属性添加属性观察器,无论它原本被定义为存储型属性(stored property)还是计算型属性(computed property)。 -## 定义一个基类(Base class) +## 定义一个基类(Defining a Base Class) 不继承于其它类的类,称之为*基类(base class)*。 From b9d3f5ead63a2a7a0d6f7c70a88e3511c6657280 Mon Sep 17 00:00:00 2001 From: zhungxd Date: Thu, 12 May 2016 17:29:56 +0800 Subject: [PATCH 34/61] Fix img not found. Update img URL. --- source/chapter2/14_Initialization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/chapter2/14_Initialization.md b/source/chapter2/14_Initialization.md index 04712d26..7f6924f0 100755 --- a/source/chapter2/14_Initialization.md +++ b/source/chapter2/14_Initialization.md @@ -993,7 +993,7 @@ class SomeClass { 下面例子中定义了一个结构体`Checkerboard`,它构建了西洋跳棋游戏的棋盘: -![西洋跳棋棋盘](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/checkersBoard_2x.png) +![西洋跳棋棋盘](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/chessBoard_2x.png) 西洋跳棋游戏在一副黑白格交替的`10x10`的棋盘中进行。为了呈现这副游戏棋盘,`Checkerboard`结构体定义了一个属性`boardColors`,它是一个包含`100`个`Bool`值的数组。在数组中,值为`true`的元素表示一个黑格,值为`false`的元素表示一个白格。数组中第一个元素代表棋盘上左上角的格子,最后一个元素代表棋盘上右下角的格子。 From 4976c25347b5ab3c74b9e92725a0572bfac22d84 Mon Sep 17 00:00:00 2001 From: Sketchk Date: Thu, 12 May 2016 20:50:37 +0800 Subject: [PATCH 35/61] =?UTF-8?q?=E6=A0=A1=E6=AD=A3+=E7=BF=BB=E8=AF=91=20F?= =?UTF-8?q?rom=20SketchK=20=E4=B8=BB=E8=A6=81=E5=86=85=E5=AE=B9=201=20`?= =?UTF-8?q?=E6=8E=A7=E5=88=B6=E6=B5=81`=E7=AB=A0=E8=8A=82=E4=B8=AD?= =?UTF-8?q?=E7=9A=84`while=E5=BE=AA=E7=8E=AF`=E4=B8=80=E8=8A=82=E4=B8=AD?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=E6=9B=B4=E6=96=B0(=E5=BA=9F?= =?UTF-8?q?=E5=BC=83=E4=BA=86++)=202=20`=E6=8E=A7=E5=88=B6=E6=B5=81`?= =?UTF-8?q?=E7=AB=A0=E8=8A=82=E4=B8=AD=E7=9A=84`repeat-while=E5=BE=AA?= =?UTF-8?q?=E7=8E=AF`=E4=B8=80=E8=8A=82=E4=B8=AD=E7=9A=84=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E6=9B=B4=E6=96=B0(=E5=BA=9F=E5=BC=83=E4=BA=86++)=203?= =?UTF-8?q?=20`=E6=8E=A7=E5=88=B6=E6=B5=81`=E7=AB=A0=E8=8A=82=E4=B8=AD?= =?UTF-8?q?=E7=9A=84`if`=E4=BB=A3=E7=A0=81=E6=9C=89=E8=AF=AF(=E5=8E=9F?= =?UTF-8?q?=E6=96=87=E4=B8=AD=E4=B8=8E=E5=BD=93=E5=89=8D=E7=BF=BB=E8=AF=91?= =?UTF-8?q?=E4=B8=AD=E6=95=B0=E5=80=BC=E4=B8=8D=E7=9B=B8=E7=AC=A6)=204=20`?= =?UTF-8?q?=E5=87=BD=E6=95=B0`=E7=AB=A0=E8=8A=82=E4=B8=AD`=E5=B8=B8?= =?UTF-8?q?=E9=87=8F=E4=B8=8E=E5=8F=98=E9=87=8F=E5=8F=82=E6=95=B0`?= =?UTF-8?q?=E4=B8=80=E8=8A=82=E5=B7=B2=E7=BB=8F=E8=A2=AB=E5=BA=9F=E5=BC=83?= =?UTF-8?q?,=E8=BF=9B=E8=A1=8C=E4=BA=86=E5=88=A0=E9=99=A4=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=205=20`=E5=87=BD=E6=95=B0`=E7=AB=A0=E8=8A=82=E4=B8=AD?= =?UTF-8?q?`=E8=BE=93=E5=85=A5=E4=B8=8E=E8=BE=93=E5=87=BA=E5=8F=82?= =?UTF-8?q?=E6=95=B0`=E4=B8=80=E8=8A=82=E5=86=85=E5=AE=B9=E8=BF=9B?= =?UTF-8?q?=E8=A1=8C=E6=9B=B4=E6=96=B0(=E5=8E=9F=E6=96=87=E4=B8=8E?= =?UTF-8?q?=E5=BD=93=E5=89=8D=E7=BF=BB=E8=AF=91=E4=B8=AD=E7=9A=84=E5=86=85?= =?UTF-8?q?=E5=AE=B9=E4=B8=8D=E7=9B=B8=E7=AC=A6)=206=20`=E9=97=AD=E5=8C=85?= =?UTF-8?q?`=E7=AB=A0=E8=8A=82=E4=B8=AD`=E5=8D=95=E8=A1=A8=E8=BE=BE?= =?UTF-8?q?=E5=BC=8F=E9=97=AD=E5=8C=85=E9=9A=90=E5=BC=8F=E8=BF=94=E5=9B=9E?= =?UTF-8?q?`=E4=B8=80=E8=8A=82=E5=86=85=E5=AE=B9=E7=BF=BB=E8=AF=91?= =?UTF-8?q?=E6=9C=89=E8=AF=AF(=E5=8E=9F=E6=96=87=E4=B8=8E=E5=BD=93?= =?UTF-8?q?=E5=89=8D=E7=BF=BB=E8=AF=91=E4=B8=AD=E7=9A=84=E5=86=85=E5=AE=B9?= =?UTF-8?q?=E4=B8=8D=E7=9B=B8=E7=AC=A6)=207=20`=E9=97=AD=E5=8C=85`?= =?UTF-8?q?=E7=AB=A0=E8=8A=82=E4=B8=AD`=E8=BF=90=E7=AE=97=E7=AC=A6?= =?UTF-8?q?=E5=87=BD=E6=95=B0`=E4=B8=80=E8=8A=82=E5=86=85=E5=AE=B9?= =?UTF-8?q?=E7=BF=BB=E8=AF=91=E6=9C=89=E8=AF=AF(=E5=8E=9F=E6=96=87?= =?UTF-8?q?=E4=B8=8E=E5=BD=93=E5=89=8D=E7=BF=BB=E8=AF=91=E4=B8=AD=E7=9A=84?= =?UTF-8?q?=E5=86=85=E5=AE=B9=E4=B8=8D=E7=9B=B8=E7=AC=A6)=208=20`=E9=97=AD?= =?UTF-8?q?=E5=8C=85`=E7=AB=A0=E8=8A=82=E4=B8=AD`=E5=B0=BE=E9=9A=8F?= =?UTF-8?q?=E9=97=AD=E5=8C=85`=E4=B8=80=E8=8A=82=E5=86=85=E5=AE=B9?= =?UTF-8?q?=E6=9B=B4=E6=96=B0(=E5=BA=9F=E5=BC=83=E4=BA=86var=E4=BF=AE?= =?UTF-8?q?=E9=A5=B0=E7=AC=A6,=E5=8E=9F=E6=96=87=E4=B8=8E=E5=BD=93?= =?UTF-8?q?=E5=89=8D=E7=BF=BB=E8=AF=91=E4=B8=AD=E5=86=85=E5=AE=B9=E4=B8=8D?= =?UTF-8?q?=E7=AC=A6,)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/chapter1/02_a_swift_tour.md | 2 +- source/chapter2/02_Basic_Operators.md | 2 +- source/chapter2/03_Strings_and_Characters.md | 5 +- source/chapter2/04_Collection_Types.md | 6 ++- source/chapter2/05_Control_Flow.md | 10 ++-- source/chapter2/06_Functions.md | 50 +++----------------- source/chapter2/07_Closures.md | 8 +++- 7 files changed, 30 insertions(+), 53 deletions(-) diff --git a/source/chapter1/02_a_swift_tour.md b/source/chapter1/02_a_swift_tour.md index f2b5c441..b646e3d7 100755 --- a/source/chapter1/02_a_swift_tour.md +++ b/source/chapter1/02_a_swift_tour.md @@ -10,7 +10,7 @@ > 翻译+校对:[xtymichael](https://github.com/xtymichael) > 2.2 -> 翻译:[175](https://github.com/Brian175),2016-04-09 +> 翻译:[175](https://github.com/Brian175),2016-04-09 校对:[SketchK](https://github.com/SketchK),2016-05-11 本页内容包括: diff --git a/source/chapter2/02_Basic_Operators.md b/source/chapter2/02_Basic_Operators.md index 70984fb6..cac08640 100755 --- a/source/chapter2/02_Basic_Operators.md +++ b/source/chapter2/02_Basic_Operators.md @@ -12,7 +12,7 @@ > 校对:[shanks](http://codebuild.me) > 2.2 -> 翻译+校对:[Cee](https://github.com/Cee) +> 翻译+校对:[Cee](https://github.com/Cee) 校对:[SketchK](https://github.com/SketchK),2016-05-11 本页包含内容: diff --git a/source/chapter2/03_Strings_and_Characters.md b/source/chapter2/03_Strings_and_Characters.md index ae58c599..e86dc29f 100755 --- a/source/chapter2/03_Strings_and_Characters.md +++ b/source/chapter2/03_Strings_and_Characters.md @@ -10,7 +10,10 @@ > 2.1 > 翻译:[DianQK](https://github.com/DianQK) -> 校对:[shanks](http://codebuild.me), [Realank](https://github.com/Realank) +> 校对:[shanks](http://codebuild.me), [Realank](https://github.com/Realank), + +> 2.2 +> 校对:[SketchK](https://github.com/SketchK) 2016-05-11 本页包含内容: diff --git a/source/chapter2/04_Collection_Types.md b/source/chapter2/04_Collection_Types.md index 8f862b2d..dd2d0567 100755 --- a/source/chapter2/04_Collection_Types.md +++ b/source/chapter2/04_Collection_Types.md @@ -9,7 +9,11 @@ > 翻译+校对:[JackAlan](https://github.com/AlanMelody) > 2.1 -> 校对:[shanks](http://codebuild.me) +> 校对:[shanks](http://codebuild.me) + +> 2.2 +> 校对:[SketchK](https://github.com/SketchK) 2016-05-11 + 本页包含内容: diff --git a/source/chapter2/05_Control_Flow.md b/source/chapter2/05_Control_Flow.md index 012d6920..c3e2e3d3 100755 --- a/source/chapter2/05_Control_Flow.md +++ b/source/chapter2/05_Control_Flow.md @@ -14,7 +14,7 @@ > 2.2 > 翻译:[LinusLing](https://github.com/linusling) -> 校对:[]() +> 校对:[SketchK](https://github.com/SketchK) 2016-05-12 本页包含内容: @@ -150,7 +150,8 @@ var square = 0 var diceRoll = 0 while square < finalSquare { // 掷骰子 - if ++diceRoll == 7 { diceRoll = 1 } + diceRoll += 1 + if diceRoll == 7 { diceRoll = 1 } // 根据点数移动 square += diceRoll if square < board.count { @@ -209,7 +210,8 @@ repeat { // 顺着梯子爬上去或者顺着蛇滑下去 square += board[square] // 掷骰子 - if ++diceRoll == 7 { diceRoll = 1 } + diceRoll += 1 + if diceRoll == 7 { diceRoll = 1 } // 根据点数移动 square += diceRoll } while square < finalSquare @@ -275,7 +277,7 @@ if temperatureInFahrenheit <= 32 { 实际上,最后的`else`语句是可选的: ```swift -temperatureInFahrenheit = 90 +temperatureInFahrenheit = 72 if temperatureInFahrenheit <= 32 { print("It's very cold. Consider wearing a scarf.") } else if temperatureInFahrenheit >= 86 { diff --git a/source/chapter2/06_Functions.md b/source/chapter2/06_Functions.md index cb081456..a63abec2 100755 --- a/source/chapter2/06_Functions.md +++ b/source/chapter2/06_Functions.md @@ -12,6 +12,9 @@ > 翻译:[DianQK](https://github.com/DianQK) > 定稿:[shanks](http://codebuild.me) +> 2.2 +> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-12 + 本页包含内容: - [函数定义与调用(Defining and Calling Functions)](#Defining_and_Calling_Functions) @@ -330,57 +333,18 @@ arithmeticMean(3, 8.25, 18.75) 如果函数有一个或多个带默认值的参数,而且还有一个可变参数,那么把可变参数放在参数表的最后。 - -### 常量参数和变量参数(Constant and Variable Parameters) - -函数参数默认是常量。试图在函数体中更改参数值将会导致编译错误。这意味着你不能错误地更改参数值。 - -但是,有时候,如果函数中有传入参数的变量值副本将是很有用的。你可以通过指定一个或多个参数为变量参数,从而避免自己在函数中定义新的变量。变量参数不是常量,你可以在函数中把它当做新的可修改副本来使用。 - -通过在参数名前加关键字 `var` 来定义变量参数: - -```swift -func alignRight(var string: String, totalLength: Int, pad: Character) -> String { - let amountToPad = totalLength - string.characters.count - if amountToPad < 1 { - return string - } - let padString = String(pad) - for _ in 1...amountToPad { - string = padString + string - } - return string -} -let originalString = "hello" -let paddedString = alignRight(originalString, totalLength: 10, pad: "-") -// paddedString is equal to "-----hello" -// originalString is still equal to "hello" -``` - -这个例子中定义了一个叫做 `alignRight(_:totalLength:pad:)` 的新函数,用来将输入的字符串对齐到更长的输出字符串的右边缘。左侧空余的地方用指定的填充字符填充。这个例子中,字符串`"hello"`被转换成了`"-----hello"`。 - -`alignRight(_:totalLength:pad:)` 函数将输入参数 `string` 定义为变量参数。这意味着 `string` 现在可以作为一个局部变量,被传入的字符串值初始化,并且可以在函数体中进行操作。 - -函数首先计算出有多少字符需要被添加到`string`的左边,从而将其在整个字符串中右对齐。这个值存储在一个称为`amountToPad`的本地常量。如果不需要填充(也就是说,如果`amountToPad`小于1),该函数简单地返回没有任何填充的输入值`string`。 - -否则,该函数用`pad`字符创建一个叫做`padString`的临时`String`常量,并将`amountToPad`个 `padString`添加到现有字符串的左边。(一个`String`值不能被添加到一个`Character`值上,所以`padString`常量用于确保`+`操作符两侧都是`String`值)。 - -> 注意 -> 对变量参数所进行的修改在函数调用结束后便消失了,并且对于函数体外是不可见的。变量参数仅仅存在于函数调用的生命周期中。 - - ### 输入输出参数(In-Out Parameters) -变量参数,正如上面所述,仅仅能在函数体内被更改。如果你想要一个函数可以修改参数的值,并且想要在这些修改在函数调用结束后仍然存在,那么就应该把这个参数定义为输入输出参数(In-Out Parameters)。 +函数参数默认是常量。试图在函数体中更改参数值将会导致编译错误。这意味着你不能错误地更改参数值。如果你想要一个函数可以修改参数的值,并且想要在这些修改在函数调用结束后仍然存在,那么就应该把这个参数定义为输入输出参数(In-Out Parameters)。 -定义一个输入输出参数时,在参数定义前加 `inout` 关键字。一个输入输出参数有传入函数的值,这个值被函数修改,然后被传出函数,替换原来的值。想获取更多的关于输入输出参数的细节和相关的编译器优化,请查看[输入输出参数](../chapter3/05_Declarations.html#function_declaration)一节。 - +定义一个输入输出参数时,在参数定义前加 inout 关键字。一个输入输出参数有传入函数的值,这个值被函数修改,然后被传出函数,替换原来的值。想获取更多的关于输入输出参数的细节和相关的编译器优化,请查看[输入输出参数](../chapter3/05_Declarations.html#function_declaration)一节。 + 你只能传递变量给输入输出参数。你不能传入常量或者字面量(literal value),因为这些量是不能被修改的。当传入的参数作为输入输出参数时,需要在参数名前加`&`符,表示这个值可以被函数修改。 > 注意 -> 输入输出参数不能有默认值,而且可变参数不能用 `inout` 标记。如果你用 `inout` 标记一个参数,这个参数不能被 `var` 或者 `let` 标记。 +> 输入输出参数不能有默认值,而且可变参数不能用 `inout` 标记。 下面是例子,`swapTwoInts(_:_:)` 函数,有两个分别叫做 `a` 和 `b` 的输入输出参数: diff --git a/source/chapter2/07_Closures.md b/source/chapter2/07_Closures.md index ed304d76..b3c73c1e 100755 --- a/source/chapter2/07_Closures.md +++ b/source/chapter2/07_Closures.md @@ -11,6 +11,9 @@ > 2.1 > 翻译:[100mango](https://github.com/100mango), [magicdict](https://github.com/magicdict) > 校对:[shanks](http://codebuild.me) +> +> 2.2 +> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-12 本页包含内容: @@ -213,7 +216,8 @@ let numbers = [16, 58, 510] ```swift let strings = numbers.map { - (var number) -> String in + (number) -> String in + var number = number var output = "" while number > 0 { output = digitNames[number % 10]! + output @@ -227,7 +231,7 @@ let strings = numbers.map { `map(_:)`为数组中每一个元素调用了闭包表达式。您不需要指定闭包的输入参数`number`的类型,因为可以通过要映射的数组类型进行推断。 -在该例中,闭包`number`参数被声明为一个变量参数(变量的具体描述请参看[常量参数和变量参数](./06_Functions.html#constant_and_variable_parameters)),因此可以在闭包函数体内对其进行修改,而不用再定义一个新的局部变量并将`number`的值赋值给它。闭包表达式指定了返回类型为`String`,以表明存储映射值的新数组类型为`String`。 +在该例中,局部变量`number`的值由闭包中的`number`参数获得,因此可以在闭包函数体内对其进行修改,(闭包或者函数的参数总是固定的),闭包表达式指定了返回类型为`String`,以表明存储映射值的新数组类型为`String`。 闭包表达式在每次被调用的时候创建了一个叫做`output`的字符串并返回。其使用求余运算符(`number % 10`)计算最后一位数字并利用`digitNames`字典获取所映射的字符串。 From 019cb42f132260ce3284b12b8e2bd407ab408b40 Mon Sep 17 00:00:00 2001 From: Sketchk Date: Thu, 12 May 2016 20:53:27 +0800 Subject: [PATCH 36/61] =?UTF-8?q?=E6=A0=A1=E6=AD=A3+=E7=BF=BB=E8=AF=91=20F?= =?UTF-8?q?rom=20SketchK=20=E4=B8=BB=E8=A6=81=E5=86=85=E5=AE=B9=206=20`?= =?UTF-8?q?=E9=97=AD=E5=8C=85`=E7=AB=A0=E8=8A=82=E4=B8=AD`=E5=8D=95?= =?UTF-8?q?=E8=A1=A8=E8=BE=BE=E5=BC=8F=E9=97=AD=E5=8C=85=E9=9A=90=E5=BC=8F?= =?UTF-8?q?=E8=BF=94=E5=9B=9E`=E4=B8=80=E8=8A=82=E5=86=85=E5=AE=B9?= =?UTF-8?q?=E7=BF=BB=E8=AF=91=E6=9C=89=E8=AF=AF(=E5=8E=9F=E6=96=87?= =?UTF-8?q?=E4=B8=8E=E5=BD=93=E5=89=8D=E7=BF=BB=E8=AF=91=E4=B8=AD=E7=9A=84?= =?UTF-8?q?=E5=86=85=E5=AE=B9=E4=B8=8D=E7=9B=B8=E7=AC=A6)=207=20`=E9=97=AD?= =?UTF-8?q?=E5=8C=85`=E7=AB=A0=E8=8A=82=E4=B8=AD`=E8=BF=90=E7=AE=97?= =?UTF-8?q?=E7=AC=A6=E5=87=BD=E6=95=B0`=E4=B8=80=E8=8A=82=E5=86=85?= =?UTF-8?q?=E5=AE=B9=E7=BF=BB=E8=AF=91=E6=9C=89=E8=AF=AF(=E5=8E=9F?= =?UTF-8?q?=E6=96=87=E4=B8=8E=E5=BD=93=E5=89=8D=E7=BF=BB=E8=AF=91=E4=B8=AD?= =?UTF-8?q?=E7=9A=84=E5=86=85=E5=AE=B9=E4=B8=8D=E7=9B=B8=E7=AC=A6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/chapter2/07_Closures.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/chapter2/07_Closures.md b/source/chapter2/07_Closures.md index b3c73c1e..90480b7c 100755 --- a/source/chapter2/07_Closures.md +++ b/source/chapter2/07_Closures.md @@ -137,7 +137,7 @@ reversed = names.sort( { s1, s2 in return s1 > s2 } ) reversed = names.sort( { s1, s2 in s1 > s2 } ) ``` -在这个例子中,`sort(_:)`方法的第二个参数函数类型明确了闭包必须返回一个`Bool`类型值。因为闭包函数体只包含了一个单一表达式(`s1 > s2`),该表达式返回`Bool`类型值,因此这里没有歧义,`return`关键字可以省略。 +在这个例子中,`sort(_:)`方法的参数类型明确了闭包必须返回一个`Bool`类型值。因为闭包函数体只包含了一个单一表达式(`s1 > s2`),该表达式返回`Bool`类型值,因此这里没有歧义,`return`关键字可以省略。 ### 参数名称缩写(Shorthand Argument Names) @@ -155,7 +155,7 @@ reversed = names.sort( { $0 > $1 } ) ### 运算符函数(Operator Functions) -实际上还有一种更简短的方式来撰写上面例子中的闭包表达式。Swift 的`String`类型定义了关于大于号(`>`)的字符串实现,其作为一个函数接受两个`String`类型的参数并返回`Bool`类型的值。而这正好与`sort(_:)`方法的第二个参数需要的函数类型相符合。因此,您可以简单地传递一个大于号,Swift 可以自动推断出您想使用大于号的字符串函数实现: +实际上还有一种更简短的方式来撰写上面例子中的闭包表达式。Swift 的`String`类型定义了关于大于号(`>`)的字符串实现,其作为一个函数接受两个`String`类型的参数并返回`Bool`类型的值。而这正好与`sort(_:)`方法的参数需要的函数类型相符合。因此,您可以简单地传递一个大于号,Swift 可以自动推断出您想使用大于号的字符串函数实现: ```swift reversed = names.sort(>) From dadae616fab5f9f6b2bccf76d3eb4fcc6c54c43b Mon Sep 17 00:00:00 2001 From: Jiayuan Zhang Date: Fri, 13 May 2016 16:44:32 +0800 Subject: [PATCH 37/61] fix(source/chapter2/10_Properties.md): typo --- source/chapter2/10_Properties.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/chapter2/10_Properties.md b/source/chapter2/10_Properties.md index 4ca72288..60c4104b 100755 --- a/source/chapter2/10_Properties.md +++ b/source/chapter2/10_Properties.md @@ -248,7 +248,7 @@ print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)") 同样,`didSet` 观察器会将旧的属性值作为参数传入,可以为该参数命名或者使用默认参数名 `oldValue`。如果在 `didSet` 方法中再次对该属性赋值,那么新值会覆盖旧的值。 > 注意 -> 父类的属性在子类的构造器中被赋值时,它在父类中的 `willSet` 和 `didSet` 观察器会被调用,随后才会调用子类的观察器。在父类书初始化方法调用之前,子类给属性赋值时,观察器不会被调用。 +> 父类的属性在子类的构造器中被赋值时,它在父类中的 `willSet` 和 `didSet` 观察器会被调用,随后才会调用子类的观察器。在父类初始化方法调用之前,子类给属性赋值时,观察器不会被调用。 > 有关构造器代理的更多信息,请参考[值类型的构造器代理](./14_Initialization.html#initializer_delegation_for_value_types)和[类的构造器代理规则](./14_Initialization.html#initializer_delegation_for_class_types)。 下面是一个 `willSet` 和 `didSet` 实际运用的例子,其中定义了一个名为 `StepCounter` 的类,用来统计一个人步行时的总步数。这个类可以跟计步器或其他日常锻炼的统计装置的输入数据配合使用。 From cdb3267dff59f81ad65820d3971483a917a39ef0 Mon Sep 17 00:00:00 2001 From: Sketchk Date: Fri, 13 May 2016 20:00:31 +0800 Subject: [PATCH 38/61] =?UTF-8?q?=E6=A0=A1=E6=AD=A3+=E7=BF=BB=E8=AF=91=20F?= =?UTF-8?q?rom=20SketchK=20=E4=B8=BB=E8=A6=81=E5=86=85=E5=AE=B9=201=20`?= =?UTF-8?q?=E6=9E=9A=E4=B8=BE`=E7=AB=A0=E8=8A=82=E4=B8=AD=E7=9A=84`?= =?UTF-8?q?=E9=80=92=E5=BD=92=E6=9E=9A=E4=B8=BE`=E4=B8=80=E8=8A=82?= =?UTF-8?q?=E4=B8=AD=E6=96=87=E7=AB=A0=E5=86=85=E5=AE=B9=E6=9B=B4=E6=96=B0?= =?UTF-8?q?(=E5=8E=9F=E6=96=87=E4=B8=8E=E5=BD=93=E5=89=8D=E7=BF=BB?= =?UTF-8?q?=E8=AF=91=E5=86=85=E5=AE=B9=E4=B8=8D=E7=9B=B8=E7=AC=A6)=202=20`?= =?UTF-8?q?=E5=B1=9E=E6=80=A7`=E7=AB=A0=E8=8A=82=E4=B8=AD=E7=9A=84`?= =?UTF-8?q?=E5=AD=98=E5=82=A8=E5=B1=9E=E6=80=A7`=E4=B8=80=E8=8A=82?= =?UTF-8?q?=E4=B8=AD=E7=9A=84=E6=96=87=E5=AD=97=E5=86=85=E5=AE=B9=E7=BA=A0?= =?UTF-8?q?=E6=AD=A3(=E5=8E=9F=E6=96=87=E4=B8=8E=E5=BD=93=E5=89=8D?= =?UTF-8?q?=E7=BF=BB=E8=AF=91=E5=86=85=E5=AE=B9=E4=B8=8D=E7=9B=B8=E7=AC=A6?= =?UTF-8?q?)=203=20`=E6=96=B9=E6=B3=95`=E7=AB=A0=E8=8A=82=E4=B8=AD?= =?UTF-8?q?=E7=9A=84`=E5=AE=9E=E4=BE=8B=E6=96=B9=E6=B3=95`=E7=9A=84?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=9C=89=E8=AF=AF(++=E7=AC=A6=E5=8F=B7?= =?UTF-8?q?=E5=BA=9F=E5=BC=83)=204=20`=E6=96=B9=E6=B3=95`=E7=AB=A0?= =?UTF-8?q?=E8=8A=82=E4=B8=AD`self=E5=B1=9E=E6=80=A7`=E7=9A=84=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E6=9C=89=E8=AF=AF=E4=BD=9C(++=E7=AC=A6=E5=8F=B7?= =?UTF-8?q?=E5=BA=9F=E5=BC=83)=205=20`=E7=BB=A7=E6=89=BF`=E7=AB=A0?= =?UTF-8?q?=E8=8A=82=E4=B8=AD`=E9=98=B2=E6=AD=A2=E9=87=8D=E5=86=99`?= =?UTF-8?q?=E4=B8=80=E8=8A=82=E4=B8=AD=E7=9A=84=E6=96=87=E5=AD=97=E5=86=85?= =?UTF-8?q?=E5=AE=B9=E7=BA=A0=E6=AD=A3(=E5=8E=9F=E6=96=87=E4=B8=8E?= =?UTF-8?q?=E5=BD=93=E5=89=8D=E7=BF=BB=E8=AF=91=E4=B8=AD=E7=9A=84=E5=86=85?= =?UTF-8?q?=E5=AE=B9=E4=B8=8D=E7=9B=B8=E7=AC=A6)=206=20=20=E6=A0=A1?= =?UTF-8?q?=E6=AD=A3=E4=BA=86"=E7=B1=BB=E5=92=8C=E7=BB=93=E6=9E=84?= =?UTF-8?q?=E4=BD=93","=E4=B8=8B=E6=A0=87"=E4=B8=A4=E7=AB=A0=E7=9A=84?= =?UTF-8?q?=E5=86=85=E5=AE=B9,=E6=B2=A1=E6=9C=89=E5=8F=91=E7=8E=B0?= =?UTF-8?q?=E6=98=8E=E6=98=BE=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/chapter2/08_Enumerations.md | 26 +++++++++++--------- source/chapter2/09_Classes_and_Structures.md | 3 +++ source/chapter2/10_Properties.md | 4 +-- source/chapter2/11_Methods.md | 9 ++++--- source/chapter2/12_Subscripts.md | 5 +++- source/chapter2/13_Inheritance.md | 7 ++++-- 6 files changed, 34 insertions(+), 20 deletions(-) diff --git a/source/chapter2/08_Enumerations.md b/source/chapter2/08_Enumerations.md index 5a4c40f5..7943563e 100755 --- a/source/chapter2/08_Enumerations.md +++ b/source/chapter2/08_Enumerations.md @@ -10,7 +10,10 @@ > 2.1 > 翻译:[Channe](https://github.com/Channe) -> 校对:[shanks](http://codebuild.me) +> 校对:[shanks](http://codebuild.me), + +> 2.2 +> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-13 本页内容包含: @@ -293,11 +296,8 @@ if let somePlanet = Planet(rawValue: positionToFind) { ## 递归枚举(Recursive Enumerations) -当各种可能的情况可以被穷举时,非常适合使用枚举进行数据建模,例如可以用枚举来表示用于简单整数运算的操作符。这些操作符让你可以将简单的算术表达式,例如整数`5`,结合为更为复杂的表达式,例如`5 + 4`。 -算术表达式的一个重要特性是,表达式可以嵌套使用。例如,表达式`(5 + 4) * 2`,乘号右边是一个数字,左边则是另一个表达式。因为数据是嵌套的,因而用来存储数据的枚举类型也需要支持这种嵌套——这意味着枚举类型需要支持递归。 - -*递归枚举(recursive enumeration)*是一种枚举类型,它有一个或多个枚举成员使用该枚举类型的实例作为关联值。使用递归枚举时,编译器会插入一个间接层。你可以在枚举成员前加上`indirect`来表示该成员可递归。 +*递归枚举(recursive enumeration)*是一种枚举类型,它有一个或多个枚举成员使用该枚举类型的实例作为关联值。使用递归枚举时,编译器会插入一个间接层。你可以在枚举成员前加上`indirect`来表示该成员可递归。 例如,下面的例子中,枚举类型存储了简单的算术表达式: @@ -319,7 +319,14 @@ indirect enum ArithmeticExpression { } ``` -上面定义的枚举类型可以存储三种算术表达式:纯数字、两个表达式相加、两个表达式相乘。枚举成员`Addition`和`Multiplication`的关联值也是算术表达式——这些关联值使得嵌套表达式成为可能。 +上面定义的枚举类型可以存储三种算术表达式:纯数字、两个表达式相加、两个表达式相乘。枚举成员`Addition`和`Multiplication`的关联值也是算术表达式——这些关联值使得嵌套表达式成为可能。例如,表达式`(5 + 4) * 2`,乘号右边是一个数字,左边则是另一个表达式。因为数据是嵌套的,因而用来存储数据的枚举类型也需要支持这种嵌套——这意味着枚举类型需要支持递归。下面的代码展示了使用`ArithmeticExpression `这个递归枚举创建表达式`(5 + 4) * 2` + +```swift +let five = ArithmeticExpression.Number(5) +let four = ArithmeticExpression.Number(4) +let sum = ArithmeticExpression.Addition(five, four) +let product = ArithmeticExpression.Multiplication(sum, ArithmeticExpression.Number(2)) +``` 要操作具有递归性质的数据结构,使用递归函数是一种直截了当的方式。例如,下面是一个对算术表达式求值的函数: @@ -334,12 +341,7 @@ func evaluate(expression: ArithmeticExpression) -> Int { return evaluate(left) * evaluate(right) } } - -// 计算 (5 + 4) * 2 -let five = ArithmeticExpression.Number(5) -let four = ArithmeticExpression.Number(4) -let sum = ArithmeticExpression.Addition(five, four) -let product = ArithmeticExpression.Multiplication(sum, ArithmeticExpression.Number(2)) + print(evaluate(product)) // 输出 "18" ``` diff --git a/source/chapter2/09_Classes_and_Structures.md b/source/chapter2/09_Classes_and_Structures.md index 563ab2ca..f1dca69e 100755 --- a/source/chapter2/09_Classes_and_Structures.md +++ b/source/chapter2/09_Classes_and_Structures.md @@ -10,6 +10,9 @@ > 2.1 > 校对:[shanks](http://codebuild.me),2015-10-29 +> 2.2 +> 校对:[SketchK](https://github.com/SketchK) 2016-05-13 + 本页包含内容: - [类和结构体对比](#comparing_classes_and_structures) diff --git a/source/chapter2/10_Properties.md b/source/chapter2/10_Properties.md index 60c4104b..ee844243 100755 --- a/source/chapter2/10_Properties.md +++ b/source/chapter2/10_Properties.md @@ -16,7 +16,7 @@ > 2.2 -> 翻译:[saitjr](https://github.com/saitjr),2016-04-11 +> 翻译:[saitjr](https://github.com/saitjr),2016-04-11,[SketchK](https://github.com/SketchK) 2016-05-13 @@ -41,7 +41,7 @@ 可以在定义存储属性的时候指定默认值,请参考[默认构造器](./14_Initialization.html#default_initializers)一节。也可以在构造过程中设置或修改存储属性的值,甚至修改常量存储属性的值,请参考[构造过程中常量属性的修改](./14_Initialization.html#assigning_constant_properties_during_initialization)一节。 -下面的例子定义了一个名为 `FixedLengthRange` 的结构体,它描述了一个用于表示整型范围的常量,在创建后就不能进行修改: +下面的例子定义了一个名为 `FixedLengthRange` 的结构体,该结构体用于描述整数的范围,且这个范围值在被创建后不能被修改. ```swift struct FixedLengthRange { diff --git a/source/chapter2/11_Methods.md b/source/chapter2/11_Methods.md index f2574ad3..437d533c 100755 --- a/source/chapter2/11_Methods.md +++ b/source/chapter2/11_Methods.md @@ -9,7 +9,10 @@ > 翻译+校对:[DianQK](https://github.com/DianQK) > 2.1 -> 翻译:[DianQK](https://github.com/DianQK),[Realank](https://github.com/Realank) 校对:[shanks](http://codebuild.me),2016-01-18 +> 翻译:[DianQK](https://github.com/DianQK),[Realank](https://github.com/Realank) 校对:[shanks](http://codebuild.me),2016-01-18 +> +> 2.2 +> 校对:[SketchK](https://github.com/SketchK) 2016-05-13 本页包含内容: @@ -33,7 +36,7 @@ class Counter { var count = 0 func increment() { - ++count + count += 1 } func incrementBy(amount: Int) { count += amount @@ -112,7 +115,7 @@ counter.incrementBy(5, numberOfTimes: 3) ```swift func increment() { - self.count++ + self.count += 1 } ``` diff --git a/source/chapter2/12_Subscripts.md b/source/chapter2/12_Subscripts.md index 8b2f89f2..8c48dfa6 100755 --- a/source/chapter2/12_Subscripts.md +++ b/source/chapter2/12_Subscripts.md @@ -9,7 +9,10 @@ > 翻译+校对:[shanks](http://codebuild.me) > 2.1 -> 翻译+校对:[shanks](http://codebuild.me),[Realank](https://github.com/Realank) +> 翻译+校对:[shanks](http://codebuild.me),[Realank](https://github.com/Realank) + +> 2.2 +> 校对:[SketchK](https://github.com/SketchK) 2016-05-13 本页包含内容: diff --git a/source/chapter2/13_Inheritance.md b/source/chapter2/13_Inheritance.md index 61b53052..64028c3d 100755 --- a/source/chapter2/13_Inheritance.md +++ b/source/chapter2/13_Inheritance.md @@ -6,7 +6,10 @@ > 校对:[menlongsheng](https://github.com/menlongsheng) > 2.0,2.1 -> 翻译+校对:[shanks](http://codebuild.me) +> 翻译+校对:[shanks](http://codebuild.me) +> +> 2.2 +> 校对:[SketchK](https://github.com/SketchK) 2016-05-13 本页包含内容: @@ -233,6 +236,6 @@ print("AutomaticCar: \(automatic.description)") 你可以通过把方法,属性或下标标记为*`final`*来防止它们被重写,只需要在声明关键字前加上`final`修饰符即可(例如:`final var`,`final func`,`final class func`,以及`final subscript`)。 -如果你重写了`final`方法,属性或下标,在编译时会报错。在类扩展中的方法,属性或下标也可以在扩展的定义里标记为 final 的。 +如果你重写了带有`final`标记的方法,属性或下标,在编译时会报错。在类扩展中的方法,属性或下标也可以在扩展的定义里标记为 final 的。 你可以通过在关键字`class`前添加`final`修饰符(`final class`)来将整个类标记为 final 的。这样的类是不可被继承的,试图继承这样的类会导致编译报错。 From 7cb0005c653050c5241d3c0e2d823d45ff534c57 Mon Sep 17 00:00:00 2001 From: Sketchk Date: Sat, 14 May 2016 21:52:50 +0800 Subject: [PATCH 39/61] =?UTF-8?q?=E6=A0=A1=E6=AD=A3+=E7=BF=BB=E8=AF=91=20F?= =?UTF-8?q?rom=20SketchK=20=E4=B8=BB=E8=A6=81=E5=86=85=E5=AE=B9=201=20`?= =?UTF-8?q?=E6=9E=84=E9=80=A0=E8=BF=87=E7=A8=8B`=E7=AB=A0=E8=8A=82?= =?UTF-8?q?=E4=B8=AD=E7=9A=84`=E5=80=BC=E7=B1=BB=E5=9E=8B=E7=9A=84?= =?UTF-8?q?=E6=9E=84=E9=80=A0=E5=99=A8=E4=BB=A3=E7=90=86`=E4=B8=80?= =?UTF-8?q?=E8=8A=82=E4=B8=AD=E6=96=87=E5=AD=97=E5=86=85=E5=AE=B9=E7=BA=A0?= =?UTF-8?q?=E6=AD=A3(=E4=B9=8B=E5=89=8D=E7=9A=84=E7=BF=BB=E8=AF=91?= =?UTF-8?q?=E5=AD=98=E5=9C=A8=E6=AD=A7=E4=B9=89)=202=20`=E6=9E=84=E9=80=A0?= =?UTF-8?q?=E8=BF=87=E7=A8=8B`=E7=AB=A0=E8=8A=82=E4=B8=AD=E7=9A=84`?= =?UTF-8?q?=E6=9E=84=E9=80=A0=E5=99=A8=E7=9A=84=E7=BB=A7=E6=89=BF=E5=92=8C?= =?UTF-8?q?=E9=87=8D=E5=86=99`=E4=B8=80=E8=8A=82=E4=B8=AD=E7=9A=84?= =?UTF-8?q?=E6=96=87=E5=AD=97=E5=86=85=E5=AE=B9=E7=BA=A0=E6=AD=A3(?= =?UTF-8?q?=E4=B9=8B=E5=89=8D=E7=BF=BB=E8=AF=91=E5=AD=98=E5=9C=A8=E6=AD=A7?= =?UTF-8?q?=E4=B9=89)=203=20`=E6=9E=84=E9=80=A0=E8=BF=87=E7=A8=8B`?= =?UTF-8?q?=E7=AB=A0=E8=8A=82=E4=B8=AD=E7=9A=84`=E5=8F=AF=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=E6=9E=84=E9=80=A0=E5=99=A8init!`=E4=B8=80=E8=8A=82?= =?UTF-8?q?=E4=B8=AD=E7=9A=84=E4=BB=A3=E7=A0=81=E6=9C=89=E8=AF=AF(?= =?UTF-8?q?=E5=A4=9A=E4=BA=86()=E7=AC=A6=E5=8F=B7)=204=20`=E6=9E=84?= =?UTF-8?q?=E9=80=A0=E8=BF=87=E7=A8=8B`=E7=AB=A0=E8=8A=82=E4=B8=AD?= =?UTF-8?q?=E7=9A=84`=E9=80=9A=E8=BF=87=E9=97=AD=E5=8C=85=E6=88=96?= =?UTF-8?q?=E5=87=BD=E6=95=B0=E8=AE=BE=E7=BD=AE=E5=B1=9E=E6=80=A7=E7=9A=84?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E5=80=BC`=E4=B8=80=E8=8A=82=E4=B8=AD?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=E6=9C=AA=E6=9B=B4=E6=96=B0(?= =?UTF-8?q?=E4=B8=8E=E5=8E=9F=E6=96=87=E4=BB=A3=E7=A0=81=E4=B8=8D=E7=AC=A6?= =?UTF-8?q?)=205=20`=E6=9E=90=E6=9E=84=E8=BF=87=E7=A8=8B`=E7=AB=A0?= =?UTF-8?q?=E8=8A=82=E4=B8=AD`=E6=9E=90=E6=9E=84=E5=99=A8=E5=AE=9E?= =?UTF-8?q?=E8=B7=B5`=E4=B8=80=E8=8A=82=E4=B8=AD=E7=9A=84=E6=96=87?= =?UTF-8?q?=E5=AD=97=E5=86=85=E5=AE=B9=E7=BA=A0=E6=AD=A3(=E4=B9=8B?= =?UTF-8?q?=E5=89=8D=E7=BF=BB=E8=AF=91=E5=AD=98=E5=9C=A8=E6=AD=A7=E4=B9=89?= =?UTF-8?q?)=206=20`=E6=9E=90=E6=9E=84=E8=BF=87=E7=A8=8B`=E7=AB=A0?= =?UTF-8?q?=E8=8A=82=E4=B8=AD`=E6=9E=90=E6=9E=84=E5=99=A8=E5=AE=9E?= =?UTF-8?q?=E8=B7=B5`=E4=B8=80=E8=8A=82=E4=B8=AD=E7=9A=84=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E6=9C=AA=E6=9B=B4=E6=96=B0(=E4=B8=8E=E5=8E=9F?= =?UTF-8?q?=E6=96=87=E4=BB=A3=E7=A0=81=E4=B8=8D=E7=AC=A6)=207=20=E6=A0=A1?= =?UTF-8?q?=E6=AD=A3=E4=BA=86"=E8=87=AA=E5=8A=A8=E5=BC=95=E7=94=A8?= =?UTF-8?q?=E8=AE=A1=E6=95=B0"=E4=B8=80=E7=AB=A0=E7=9A=84=E5=86=85?= =?UTF-8?q?=E5=AE=B9,=E6=B2=A1=E6=9C=89=E5=8F=91=E7=8E=B0=E6=98=8E?= =?UTF-8?q?=E6=98=BE=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/chapter2/01_The_Basics.md | 5 ++++- source/chapter2/14_Initialization.md | 18 +++++++++--------- source/chapter2/15_Deinitialization.md | 11 +++++++---- .../16_Automatic_Reference_Counting.md | 5 ++++- 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/source/chapter2/01_The_Basics.md b/source/chapter2/01_The_Basics.md index ac36c77c..a444e3ae 100755 --- a/source/chapter2/01_The_Basics.md +++ b/source/chapter2/01_The_Basics.md @@ -10,7 +10,10 @@ > 2.1 > 翻译:[Prayer](https://github.com/futantan) -> 校对:[shanks](http://codebuild.me),[overtrue](https://github.com/overtrue) +> 校对:[shanks](http://codebuild.me),[overtrue](https://github.com/overtrue) +> +> 2.2 +> 校对:[SketchK](https://github.com/SketchK) 2016-05-11 本页包含内容: diff --git a/source/chapter2/14_Initialization.md b/source/chapter2/14_Initialization.md index 7f6924f0..37d3fb76 100755 --- a/source/chapter2/14_Initialization.md +++ b/source/chapter2/14_Initialization.md @@ -14,7 +14,7 @@ > 2.2 > 翻译:[pmst](https://github.com/colourful987) -> 校对:[]() +> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-14 本页包含内容: @@ -281,9 +281,9 @@ let twoByTwo = Size(width: 2.0, height: 2.0) 构造器代理的实现规则和形式在值类型和类类型中有所不同。值类型(结构体和枚举类型)不支持继承,所以构造器代理的过程相对简单,因为它们只能代理给自己的其它构造器。类则不同,它可以继承自其它类(请参考[继承](./13_Inheritance.html)),这意味着类有责任保证其所有继承的存储型属性在构造时也能正确的初始化。这些责任将在后续章节[类的继承和构造过程](#class_inheritance_and_initialization)中介绍。 -对于值类型,你可以使用`self.init`在自定义的构造器中引用类型中的其它构造器。并且你只能在构造器内部调用`self.init`。 +对于值类型,你可以使用`self.init`在自定义的构造器中引用相同类型中的其它构造器。并且你只能在构造器内部调用`self.init`。 -如果你为某个值类型定义了一个自定义的构造器,你将无法访问到默认构造器(如果是结构体,还将无法访问逐一成员构造器)。这个限制可以防止你为值类型定义了一个进行额外必要设置的复杂构造器之后,别人还是错误地使用了一个自动生成的构造器。 +如果你为某个值类型定义了一个自定义的构造器,你将无法访问到默认构造器(如果是结构体,还将无法访问逐一成员构造器)。这种限制可以防止你为值类型增加了一个额外的且十分复杂的构造器之后,仍然有人错误的使用自动生成的构造器 > 注意 假如你希望默认构造器、逐一成员构造器以及你自己的自定义构造器都能用来创建实例,可以将自定义的构造器写到扩展(`extension`)中,而不是写在值类型的原始定义中。想查看更多内容,请查看[扩展](./21_Extensions.html)章节。 @@ -490,7 +490,7 @@ Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造 ### 构造器的继承和重写 -跟 Objective-C 中的子类不同,Swift 中的子类默认情况下不会继承父类的构造器。Swift 的这种机制可以防止一个父类的简单构造器被一个更专业的子类继承,并被错误地用来创建子类的实例。 +跟 Objective-C 中的子类不同,Swift 中的子类默认情况下不会继承父类的构造器。Swift 的这种机制可以防止一个父类的简单构造器被一个更精细的子类继承,并被错误地用来创建子类的实例。 > 注意 父类的构造器仅会在安全和适当的情况下被继承。具体内容请参考后续章节[构造器的自动继承](#automatic_initializer_inheritance)。 @@ -937,7 +937,7 @@ class UntitledDocument: Document { ### 可失败构造器 init! -通常来说我们通过在`init`关键字后添加问号的方式(`init?`)来定义一个可失败构造器,但你也可以通过在`init`后面添加惊叹号的方式来定义一个可失败构造器(`(init!)`),该可失败构造器将会构建一个对应类型的隐式解包可选类型的对象。 +通常来说我们通过在`init`关键字后添加问号的方式(`init?`)来定义一个可失败构造器,但你也可以通过在`init`后面添加惊叹号的方式来定义一个可失败构造器(`init!`),该可失败构造器将会构建一个对应类型的隐式解包可选类型的对象。 你可以在`init?`中代理到`init!`,反之亦然。你也可以用`init?`重写`init!`,反之亦然。你还可以用`init`代理到`init!`,不过,一旦`init!`构造失败,则会触发一个断言。 @@ -1004,8 +1004,8 @@ struct Checkerboard { let boardColors: [Bool] = { var temporaryBoard = [Bool]() var isBlack = false - for i in 1...10 { - for j in 1...10 { + for i in 1...8 { + for j in 1...8 { temporaryBoard.append(isBlack) isBlack = !isBlack } @@ -1014,7 +1014,7 @@ struct Checkerboard { return temporaryBoard }() func squareIsBlackAtRow(row: Int, column: Int) -> Bool { - return boardColors[(row * 10) + column] + return boardColors[(row * 8) + column] } } ``` @@ -1025,6 +1025,6 @@ struct Checkerboard { let board = Checkerboard() print(board.squareIsBlackAtRow(0, column: 1)) // 打印 "true" -print(board.squareIsBlackAtRow(9, column: 9)) +print(board.squareIsBlackAtRow(7, column: 7)) // 打印 "false" ``` diff --git a/source/chapter2/15_Deinitialization.md b/source/chapter2/15_Deinitialization.md index 31e191a9..f55b48aa 100755 --- a/source/chapter2/15_Deinitialization.md +++ b/source/chapter2/15_Deinitialization.md @@ -10,6 +10,9 @@ > 2.1 > 校对:[shanks](http://codebuild.me),2015-10-31 +> +> 2.2 +> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-14 本页包含内容: @@ -38,13 +41,13 @@ deinit { ##析构器实践 -这是一个析构器实践的例子。这个例子描述了一个简单的游戏,这里定义了两种新类型,分别是`Bank`和`Player`。`Bank`类管理一种虚拟硬币,确保流通的硬币数量永远不可能超过 10,000。在游戏中有且只能有一个`Bank`存在,因此`Bank`用类来实现,并使用静态属性和静态方法来存储和管理其当前状态。 +这是一个析构器实践的例子。这个例子描述了一个简单的游戏,这里定义了两种新类型,分别是`Bank`和`Player`。`Bank`类管理一种虚拟硬币,确保流通的硬币数量永远不可能超过 10,000。在游戏中有且只能有一个`Bank`存在,因此`Bank`用类来实现,并使用类型属性和类型方法来存储和管理其当前状态。 ```swift class Bank { static var coinsInBank = 10_000 - static func vendCoins(var numberOfCoinsToVend: Int) -> Int { - numberOfCoinsToVend = min(numberOfCoinsToVend, coinsInBank) + static func vendCoins(numberOfCoinsRequested: Int) -> Int { + let numberOfCoinsToVend = min(numberOfCoinsRequested, coinsInBank) coinsInBank -= numberOfCoinsToVend return numberOfCoinsToVend } @@ -56,7 +59,7 @@ class Bank { `Bank`使用`coinsInBank`属性来跟踪它当前拥有的硬币数量。`Bank`还提供了两个方法,`vendCoins(_:)`和`receiveCoins(_:)`,分别用来处理硬币的分发和收集。 -`vendCoins(_:)`方法在`Bank`对象分发硬币之前检查是否有足够的硬币。如果硬币不足,`Bank`对象会返回一个比请求时小的数字(如果`Bank`对象中没有硬币了就返回`0`)。`vendCoins`方法声明`numberOfCoinsToVend`为一个变量参数,这样就可以在方法体内部修改分发的硬币数量,而不需要定义一个新的变量。`vendCoins`方法返回一个整型值,表示提供的硬币的实际数量。 +`vendCoins(_:)`方法在`Bank`对象分发硬币之前检查是否有足够的硬币。如果硬币不足,`Bank`对象会返回一个比请求时小的数字(如果`Bank`对象中没有硬币了就返回`0`)。`vendCoins`方法返回一个整型值,表示提供的硬币的实际数量。 `receiveCoins(_:)`方法只是将`Bank`对象接收到的硬币数目加回硬币存储中。 diff --git a/source/chapter2/16_Automatic_Reference_Counting.md b/source/chapter2/16_Automatic_Reference_Counting.md index ca61cb18..319ec3a3 100755 --- a/source/chapter2/16_Automatic_Reference_Counting.md +++ b/source/chapter2/16_Automatic_Reference_Counting.md @@ -10,7 +10,10 @@ > 2.1 > 翻译:[Channe](https://github.com/Channe) -> 校对:[shanks](http://codebuild.me),[Realank](https://github.com/Realank) ,2016-01-23 +> 校对:[shanks](http://codebuild.me),[Realank](https://github.com/Realank) ,2016-01-23 +> +> 2.2 +> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-14 本页包含内容: From ad5f1f66b449ee9f6ae323f156209202f15dbb3c Mon Sep 17 00:00:00 2001 From: Sketchk Date: Sun, 15 May 2016 23:01:57 +0800 Subject: [PATCH 40/61] =?UTF-8?q?=E6=A0=A1=E6=AD=A3+=E7=BF=BB=E8=AF=91=20F?= =?UTF-8?q?rom=20SketchK=20=E4=B8=BB=E8=A6=81=E5=86=85=E5=AE=B9=201=20`?= =?UTF-8?q?=E5=8F=AF=E9=80=89=E9=93=BE`=E7=AB=A0=E8=8A=82=E4=B8=AD?= =?UTF-8?q?=E7=9A=84`=E4=BD=BF=E7=94=A8=E5=8F=AF=E9=80=89=E9=93=BE?= =?UTF-8?q?=E5=BC=8F=E8=B0=83=E7=94=A8=E4=BB=A3=E6=9B=BF=E5=BC=BA=E5=88=B6?= =?UTF-8?q?=E5=B1=95=E5=BC=80`=E4=B8=80=E8=8A=82=E4=B8=AD=E6=96=87?= =?UTF-8?q?=E5=AD=97=E5=86=85=E5=AE=B9=E7=BA=A0=E6=AD=A3(=E4=B9=8B?= =?UTF-8?q?=E5=89=8D=E7=9A=84=E7=BF=BB=E8=AF=91=E5=AD=98=E5=9C=A8=E6=AD=A7?= =?UTF-8?q?=E4=B9=89)=202=20`=E5=8F=AF=E9=80=89=E9=93=BE`=E7=AB=A0?= =?UTF-8?q?=E8=8A=82=E4=B8=AD=E7=9A=84`=E8=AE=BF=E9=97=AE=E5=8F=AF?= =?UTF-8?q?=E9=80=89=E7=B1=BB=E5=9E=8B=E7=9A=84=E4=B8=8B=E6=A0=87`?= =?UTF-8?q?=E4=B8=80=E8=8A=82=E4=B8=AD=E7=9A=84=E4=BB=A3=E7=A0=81=E6=9C=89?= =?UTF-8?q?=E8=AF=AF(++=E5=8F=B7=E5=BA=9F=E5=BC=83)=203=20`=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E5=A4=84=E7=90=86`=E7=AB=A0=E8=8A=82=E4=B8=AD?= =?UTF-8?q?=E7=9A=84`=E7=94=A8=20throwing=20=E5=87=BD=E6=95=B0=E4=BC=A0?= =?UTF-8?q?=E9=80=92=E9=94=99=E8=AF=AF`=E4=B8=80=E8=8A=82=E4=B8=AD?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=E6=9C=89=E8=AF=AF(=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E4=B8=8E=E5=8E=9F=E6=96=87=E4=B8=8D=E7=AC=A6)=204=20`?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86`=E7=AB=A0=E8=8A=82?= =?UTF-8?q?=E4=B8=AD=E7=9A=84`=E7=94=A8=20throwing=20=E5=87=BD=E6=95=B0?= =?UTF-8?q?=E4=BC=A0=E9=80=92=E9=94=99=E8=AF=AF`=E4=B8=80=E8=8A=82?= =?UTF-8?q?=E4=B8=AD=E6=BC=8F=E7=BF=BB=E8=AF=91=E4=BA=86=E9=83=A8=E5=88=86?= =?UTF-8?q?=E5=86=85=E5=AE=B9=205=20`=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86`?= =?UTF-8?q?=E7=AB=A0=E8=8A=82=E4=B8=AD=E7=9A=84`=E8=A1=A8=E7=A4=BA?= =?UTF-8?q?=E5=B9=B6=E6=8A=9B=E5=87=BA`=E4=B8=80=E8=8A=82=E4=B8=AD?= =?UTF-8?q?=E7=9A=84=E6=96=87=E5=AD=97=E6=9C=89=E8=AF=AF=206=20`=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E5=A4=84=E7=90=86`=E7=AB=A0=E8=8A=82=E4=B8=AD?= =?UTF-8?q?=E7=9A=84`=E5=B0=86=E9=94=99=E8=AF=AF=E8=BD=AC=E6=8D=A2?= =?UTF-8?q?=E6=88=90=E5=8F=AF=E9=80=89=E5=80=BC`=E4=B8=80=E8=8A=82?= =?UTF-8?q?=E4=B8=AD=E6=96=87=E5=AD=97=E5=86=85=E5=AE=B9=E7=BA=A0=E6=AD=A3?= =?UTF-8?q?(=E4=B9=8B=E5=89=8D=E7=9A=84=E7=BF=BB=E8=AF=91=E5=AD=98?= =?UTF-8?q?=E5=9C=A8=E6=AD=A7=E4=B9=89)=207=20`=E9=94=99=E8=AF=AF=E5=A4=84?= =?UTF-8?q?=E7=90=86`=E7=AB=A0=E8=8A=82=E4=B8=AD=E7=9A=84`=E7=A6=81?= =?UTF-8?q?=E7=94=A8=E9=94=99=E8=AF=AF=E4=BC=A0=E9=80=92`=E4=B8=80?= =?UTF-8?q?=E8=8A=82=E4=B8=AD=E6=96=87=E5=AD=97=E5=86=85=E5=AE=B9=E7=BA=A0?= =?UTF-8?q?=E6=AD=A3(=E4=B9=8B=E5=89=8D=E7=9A=84=E7=BF=BB=E8=AF=91?= =?UTF-8?q?=E5=AD=98=E5=9C=A8=E6=AD=A7=E4=B9=89)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/chapter2/17_Optional_Chaining.md | 13 +++++---- source/chapter2/18_Error_Handling.md | 39 ++++++++++++++++++------- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/source/chapter2/17_Optional_Chaining.md b/source/chapter2/17_Optional_Chaining.md index 3cb7ce4b..3c5d2ac8 100755 --- a/source/chapter2/17_Optional_Chaining.md +++ b/source/chapter2/17_Optional_Chaining.md @@ -10,7 +10,10 @@ > 翻译+校对:[lyojo](https://github.com/lyojo) > 2.1 -> 校对:[shanks](http://codebuild.me),2015-10-31 +> 校对:[shanks](http://codebuild.me),2015-10-31 +> +> 2.2 +> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-15 本页包含内容: @@ -50,9 +53,9 @@ class Residence { } ``` -`Residence`有一个`Int`类型的属性`numberOfRooms`,其默认值为`1`。`Person`具有一个可选的`residence`属性,其类型为`Residence?`。 - -如果创建一个新的`Person`实例,因为它的`residence`属性是可选的,`john`属性将初始化为`nil`: +`Residence`有一个`Int`类型的属性`numberOfRooms`,其默认值为`1`。`Person`具有一个可选的`residence`属性,其类型为`Residence?`。 + +假如你创建了一个新的`Person`实例,它的`residence`属性由于是是可选型而将初始化为`nil`,在下面的代码中,`john`有一个值为`nil`的`residence`属性: ```swift let john = Person() @@ -314,7 +317,7 @@ if let firstRoomName = john.residence?[0].name { ```swift var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]] testScores["Dave"]?[0] = 91 -testScores["Bev"]?[0]++ +testScores["Bev"]?[0] += 1 testScores["Brian"]?[0] = 72 // "Dave" 数组现在是 [91, 82, 84],"Bev" 数组现在是 [80, 94, 81] ``` diff --git a/source/chapter2/18_Error_Handling.md b/source/chapter2/18_Error_Handling.md index 0fa9ff1c..a501ca6f 100755 --- a/source/chapter2/18_Error_Handling.md +++ b/source/chapter2/18_Error_Handling.md @@ -3,7 +3,10 @@ > 2.1 > 翻译+校对:[lyojo](https://github.com/lyojo) [ray16897188](https://github.com/ray16897188) 2015-10-23 -> 校对:[shanks](http://codebuild.me) 2015-10-24 +> 校对:[shanks](http://codebuild.me) 2015-10-24 +> +> 2.2 +> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-15 本页包含内容: @@ -18,7 +21,7 @@ 举个例子,假如有个从磁盘上的某个文件读取数据并进行处理的任务,该任务会有多种可能失败的情况,包括指定路径下文件并不存在,文件不具有可读权限,或者文件编码格式不兼容。区分这些不同的失败情况可以让程序解决并处理某些错误,然后把它解决不了的错误报告给用户。 > 注意 -Swift 中的错误处理涉及到错误处理模式,这会用到 Cocoa 和 Objective-C 中的`NSError`。关于这个类的更多信息请参见 [Using Swift with Cocoa and Objective-C (Swift 2.1)](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中的[错误处理](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html#//apple_ref/doc/uid/TP40014216-CH7-ID10)。 +Swift 中的错误处理涉及到错误处理模式,这会用到 Cocoa 和 Objective-C 中的`NSError`。关于这个类的更多信息请参见 [Using Swift with Cocoa and Objective-C (Swift 2.2)](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中的[错误处理](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html#//apple_ref/doc/uid/TP40014216-CH7-ID10)。 ##表示并抛出错误 @@ -35,7 +38,7 @@ enum VendingMachineError: ErrorType { } ``` -抛出一个错误可以让你表明有意外情况发生,导致正常的执行流程无法继续执行。抛出错误使用`throws`关键字。例如,下面的代码抛出一个错误,提示贩卖机还需要`5`个硬币: +抛出一个错误可以让你表明有意外情况发生,导致正常的执行流程无法继续执行。抛出错误使用`throw`关键字。例如,下面的代码抛出一个错误,提示贩卖机还需要`5`个硬币: ```swift throw VendingMachineError.InsufficientFunds(coinsNeeded: 5) @@ -68,7 +71,7 @@ func cannotThrowErrors() -> String > 注意 只有 throwing 函数可以传递错误。任何在某个非 throwing 函数内部抛出的错误只能在函数内部处理。 -下面的例子中,`VendingMechine`类有一个`vend(itemNamed:)`方法,如果请求的物品不存在、缺货或者花费超过了投入金额,该方法就会抛出一个相应的`VendingMachineError`: +下面的例子中,`VendingMechine`类有一个`vend(itemNamed:)`方法,如果请求的物品不存在、缺货或者投入金额小于物品价格,该方法就会抛出一个相应的`VendingMachineError`: ```swift struct Item { @@ -88,7 +91,7 @@ class VendingMachine { } func vend(itemNamed name: String) throws { - guard var item = inventory[name] else { + guard let item = inventory[name] else { throw VendingMachineError.InvalidSelection } @@ -101,8 +104,11 @@ class VendingMachine { } coinsDeposited -= item.price - --item.count - inventory[name] = item + + var newItem = item + newItem.count -= 1 + inventory[name] = newItem + dispenseSnack(name) } } @@ -124,7 +130,20 @@ func buyFavoriteSnack(person: String, vendingMachine: VendingMachine) throws { } ``` -上例中,`buyFavoriteSnack(_:vendingMachine:)`函数会查找某人最喜欢的零食,并通过调用`vend(itemNamed:)`方法来尝试为他们购买。因为`vend(itemNamed:)`方法能抛出错误,所以在调用的它时候在它前面加了`try`关键字。 +上例中,`buyFavoriteSnack(_:vendingMachine:)`函数会查找某人最喜欢的零食,并通过调用`vend(itemNamed:)`方法来尝试为他们购买。因为`vend(itemNamed:)`方法能抛出错误,所以在调用的它时候在它前面加了`try`关键字。 + +throwing构造器能像throwing函数一样传递错误.例如下面代码中的`PurchasedSnack`构造器在构造过程中调用了throwing函数,并且通过传递到它的调用者来处理这些错误。 + +```swift +struct PurchasedSnack { + let name: String + init(name: String, vendingMachine: VendingMachine) throws { + try vendingMachine.vend(itemNamed: name) + self.name = name + } +} +``` + ###用 Do-Catch 处理错误 @@ -166,7 +185,7 @@ do { ###将错误转换成可选值 -可以使用`try?`通过将错误转换成一个可选值来处理错误。如果在评估`try?`表达式时一个错误被抛出,那么表达式的值就是`nil`。例如下面代码中的`x`和`y`具有相同的值: +可以使用`try?`通过将错误转换成一个可选值来处理错误。如果在评估`try?`表达式时一个错误被抛出,那么表达式的值就是`nil`。例如,在下面的代码中,`x`和`y`有着相同的数值和等价的含义: ```swift func someThrowingFunction() throws -> Int { @@ -197,7 +216,7 @@ func fetchData() -> Data? { ### 禁用错误传递 -有时你知道某个 throwing 函数实际上在运行时是不会抛出错误的,在这种情况下,你可以在表达式前面写`try!`来禁用错误传递,这会把调用包装在一个断言不会有错误抛出的运行时断言中。如果实际上抛出了错误,你会得到一个运行时错误。 +有时你知道某个 throwing 函数实际上在运行时是不会抛出错误的,在这种情况下,你可以在表达式前面写`try!`来禁用错误传递,这会把调用包装在一个不会有错误抛出的运行时断言中。如果真的抛出了错误,你会得到一个运行时错误。 例如,下面的代码使用了`loadImage(_:)`函数,该函数从给定的路径加载图片资源,如果图片无法载入则抛出一个错误。在这种情况下,因为图片是和应用绑定的,运行时不会有错误抛出,所以适合禁用错误传递: From f5a6be2ca35ecba36ba1a9067d64e14267587b7b Mon Sep 17 00:00:00 2001 From: Sketchk Date: Mon, 16 May 2016 21:54:23 +0800 Subject: [PATCH 41/61] =?UTF-8?q?=E6=A0=A1=E6=AD=A3+=E7=BF=BB=E8=AF=91=20F?= =?UTF-8?q?rom=20SketchK=20=E4=B8=BB=E8=A6=81=E5=86=85=E5=AE=B9=201=20`?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E8=BD=AC=E6=8D=A2`=E7=AB=A0=E8=8A=82?= =?UTF-8?q?=E4=B8=AD=E7=9A=84`=E6=A3=80=E6=9F=A5=E7=B1=BB=E5=9E=8B`?= =?UTF-8?q?=E4=B8=80=E8=8A=82=E4=B8=AD=E4=BB=A3=E7=A0=81=E6=9B=B4=E6=96=B0?= =?UTF-8?q?(++=E5=8F=B7=E5=BA=9F=E5=BC=83)=202=20`=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E8=BD=AC=E6=8D=A2`=E7=AB=A0=E8=8A=82=E4=B8=AD=E7=9A=84`AnyObjc?= =?UTF-8?q?ect`=E4=B8=80=E8=8A=82=E4=B8=AD=E6=96=87=E5=AD=97=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E5=8F=8A=E9=94=99=E8=AF=AF=E7=BA=A0=E6=AD=A3=203=20`?= =?UTF-8?q?=E5=B5=8C=E5=A5=97=E7=B1=BB=E5=9E=8B`=E7=AB=A0=E8=8A=82?= =?UTF-8?q?=E4=B8=AD=E7=9A=84=E6=AD=A3=E6=96=87=E4=B8=80=E8=8A=82=E4=B8=AD?= =?UTF-8?q?=E6=96=87=E5=AD=97=E6=9C=89=E6=AD=A7=E4=B9=89=204=20`=E6=89=A9?= =?UTF-8?q?=E5=B1=95`=E7=AB=A0=E8=8A=82=E4=B8=AD=E7=9A=84`=E6=9E=84?= =?UTF-8?q?=E9=80=A0=E5=99=A8`=E4=B8=80=E8=8A=82=E4=B8=AD=E7=9A=84?= =?UTF-8?q?=E6=96=87=E5=AD=97=E6=9C=89=E8=AF=AF(=E7=BF=BB=E8=AF=91?= =?UTF-8?q?=E5=86=85=E5=AE=B9=E4=B8=8E=E5=8E=9F=E6=96=87=E4=B8=8D=E7=AC=A6?= =?UTF-8?q?)=205=20`=E6=89=A9=E5=B1=95`=E7=AB=A0=E8=8A=82=E4=B8=AD?= =?UTF-8?q?=E7=9A=84`=E4=B8=8B=E6=A0=87`=E4=B8=80=E8=8A=82=E4=B8=AD?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=E6=9B=B4=E6=96=B0=206=20`=E5=8D=8F?= =?UTF-8?q?=E8=AE=AE`=E7=AB=A0=E8=8A=82=E4=B8=AD=E7=9A=84`=E5=A7=94?= =?UTF-8?q?=E6=89=98=E6=A8=A1=E5=BC=8F`=E4=B8=80=E8=8A=82=E4=B8=AD?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E6=96=87=E5=AD=97=E8=A1=A8=E8=BF=B0(Control?= =?UTF-8?q?=20Flow=E7=BF=BB=E8=AF=91=E4=B8=BA=E6=8E=A7=E5=88=B6=E6=B5=81)?= =?UTF-8?q?=207=20`=E5=8D=8F=E8=AE=AE`=E7=AB=A0=E8=8A=82=E4=B8=AD=E7=9A=84?= =?UTF-8?q?`=E5=A7=94=E6=89=98=E6=A8=A1=E5=BC=8F`=E4=B8=80=E8=8A=82?= =?UTF-8?q?=E4=B8=AD=E4=BB=A3=E7=A0=81=E6=9B=B4=E6=96=B0=208=20`=E5=8D=8F?= =?UTF-8?q?=E8=AE=AE`=E7=AB=A0=E8=8A=82=E4=B8=AD=E7=9A=84`=E5=8F=AF?= =?UTF-8?q?=E9=80=89=E5=8D=8F=E8=AE=AE=E8=A6=81=E6=B1=82`=E4=B8=80?= =?UTF-8?q?=E8=8A=82=E4=B8=AD=E6=96=87=E5=AD=97=E6=9C=89=E8=AF=AF=209=20?= =?UTF-8?q?=20=E6=A0=A1=E6=AD=A3`=E6=B3=9B=E5=9E=8B`=E4=B8=80=E7=AB=A0?= =?UTF-8?q?=E7=9A=84=E5=86=85=E5=AE=B9,=E6=B2=A1=E6=9C=89=E5=8F=91?= =?UTF-8?q?=E7=8E=B0=E6=98=8E=E6=98=BE=E9=94=99=E8=AF=AF=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/chapter2/19_Type_Casting.md | 13 +++++++------ source/chapter2/20_Nested_Types.md | 7 +++++-- source/chapter2/21_Extensions.md | 10 ++++++---- source/chapter2/22_Protocols.md | 11 +++++++---- source/chapter2/23_Generics.md | 2 +- 5 files changed, 26 insertions(+), 17 deletions(-) diff --git a/source/chapter2/19_Type_Casting.md b/source/chapter2/19_Type_Casting.md index 4f663ddf..8eef424b 100644 --- a/source/chapter2/19_Type_Casting.md +++ b/source/chapter2/19_Type_Casting.md @@ -10,6 +10,9 @@ > 2.1 > 校对:[shanks](http://codebuild.me),2015-11-01 +> +> 2.2 +> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-16 本页包含内容: @@ -89,9 +92,9 @@ var songCount = 0 for item in library { if item is Movie { - ++movieCount + movieCount += 1 } else if item is Song { - ++songCount + songCount += 1 } } @@ -164,12 +167,10 @@ Swift 为不确定类型提供了两种特殊的类型别名: ### `AnyObject` 类型 -当在工作中使用 Cocoa APIs 时,我们经常会接收到一个 `[AnyObject]` 类型的数组,或者说“一个任意类型对象的数组”。这是因为 Objective-C 没有明确的类型化数组。但是,你常常可以从 API 提供的信息来确定数组中对象的类型。 +当我们使用 Cocoa APIs 时,我们会接收到一个 `[AnyObject]` 类型的数组,或者说“一个任意类型对象的数组”。Objective-C现在支持明确的数组类型,但早期版本的Objective-C并没有这个功能。不管怎样,你都可以确信API提供的信息能够正确的表明数组中的元素类型。 -> 译者注 -> 这段文档似乎没有及时更新,从 Xcode 7 和 Swift 2.0 开始,由于 Objective-C 引入了轻量泛型,集合类型已经可以类型化了,在 Swift 中使用 Cocoa API 也越来越少遇到 `AnyObject` 类型了。详情请参阅 [Lightweight Generics](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html#//apple_ref/doc/uid/TP40014216-CH4-ID173) 和 [Collection Classes](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6-ID69)。 -在这些情况下,你可以使用强制形式的类型转换(`as`)来下转数组中的每一项到比 `AnyObject` 更明确的类型,不需要可选解包(optional unwrapping)。 +在这些情况下,你可以使用强制形式的类型转换(`as!`)来下转数组中的每一项到比 `AnyObject` 更明确的类型,不需要可选解包(optional unwrapping)。 下面的示例定义了一个 `[AnyObject]` 类型的数组并填入三个 `Movie` 类型的实例: diff --git a/source/chapter2/20_Nested_Types.md b/source/chapter2/20_Nested_Types.md index 1e01fa25..a4578df9 100755 --- a/source/chapter2/20_Nested_Types.md +++ b/source/chapter2/20_Nested_Types.md @@ -9,14 +9,17 @@ > 翻译+校对:[SergioChan](https://github.com/SergioChan) > 2.1 -> 校对:[shanks](http://codebuild.me),2015-11-01 +> 校对:[shanks](http://codebuild.me),2015-11-01 +> +> 2.2 +> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-16 本页包含内容: - [嵌套类型实践](#nested_types_in_action) - [引用嵌套类型](#referring_to_nested_types) -枚举常被用于为特定类或结构体实现某些功能。类似的,也能够在某个复杂的类型中,方便地定义工具类或结构体来使用。为了实现这种功能,Swift 允许你定义嵌套类型,可以在支持的类型中定义嵌套的枚举、类和结构体。 +枚举常被用于为特定类或结构体实现某些功能。类似的,枚举可以方便的定义工具类或结构体,从而为某个复杂的类型所使用。为了实现这种功能,Swift 允许你定义嵌套类型,可以在支持的类型中定义嵌套的枚举、类和结构体。 要在一个类型中嵌套另一个类型,将嵌套类型的定义写在其外部类型的`{}`内,而且可以根据需要定义多级嵌套。 diff --git a/source/chapter2/21_Extensions.md b/source/chapter2/21_Extensions.md index f05c7f95..66433c9a 100644 --- a/source/chapter2/21_Extensions.md +++ b/source/chapter2/21_Extensions.md @@ -10,6 +10,9 @@ > 2.1 > 校对:[shanks](http://codebuild.me) +> +> 2.2 +> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-16 本页包含内容: @@ -106,7 +109,7 @@ print("A marathon is \(aMarathon) meters long") 扩展能为类添加新的便利构造器,但是它们不能为类添加新的指定构造器或析构器。指定构造器和析构器必须总是由原始的类实现来提供。 > 注意 -如果你使用扩展为一个值类型添加构造器,且该值类型的原始实现中未定义任何定制的构造器时,你可以在扩展中的构造器里调用逐一成员构造器。如果该值类型为所有存储型属性提供了默认值,你还可以在扩展中的构造器里调用默认构造器。 +如果你使用扩展为一个值类型添加构造器,同时该值类型的原始实现中未定义任何定制的构造器且所有存储属性提供了默认值,那么我们就可以在扩展中的构造器里调用默认构造器和逐一成员构造器。 正如在[值类型的构造器代理](./14_Initialization.html#initializer_delegation_for_value_types)中描述的,如果你把定制的构造器写在值类型的原始实现中,上述规则将不再适用。 下面的例子定义了一个用于描述几何矩形的结构体 `Rect`。这个例子同时定义了两个辅助结构体 `Size` 和 `Point`,它们都把 `0.0` 作为所有属性的默认值: @@ -223,11 +226,10 @@ someInt.square() ```swift extension Int { - subscript(var digitIndex: Int) -> Int { + subscript(digitIndex: Int) -> Int { var decimalBase = 1 - while digitIndex > 0 { + for _ in 0.. 2.1 > 翻译:[小铁匠Linus](https://github.com/kevin833752) > 校对:[shanks](http://codebuild.me),2015-11-01 +> +> 2.2 +> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-16 本页包含内容: @@ -348,7 +351,7 @@ protocol DiceGameDelegate { `DiceGame` 协议可以被任意涉及骰子的游戏采纳。`DiceGameDelegate` 协议可以被任意类型采纳,用来追踪 `DiceGame` 的游戏过程。 -如下所示,`SnakesAndLadders` 是 [Control Flow](./05_Control_Flow.html) 章节引入的蛇梯棋游戏的新版本。新版本使用 `Dice` 实例作为骰子,并且实现了 `DiceGame` 和 `DiceGameDelegate` 协议,后者用来记录游戏的过程: +如下所示,`SnakesAndLadders` 是 [控制流](./05_Control_Flow.html) 章节引入的蛇梯棋游戏的新版本。新版本使用 `Dice` 实例作为骰子,并且实现了 `DiceGame` 和 `DiceGameDelegate` 协议,后者用来记录游戏的过程: ```swift class SnakesAndLadders: DiceGame { @@ -383,7 +386,7 @@ class SnakesAndLadders: DiceGame { } ``` -关于这个蛇梯棋游戏的详细描述请参阅 [Control Flow](./05_Control_Flow.html) 章节中的 [Break](./05_Control_Flow.html#break) 部分。 +关于这个蛇梯棋游戏的详细描述请参阅 [控制流](./05_Control_Flow.html) 章节中的 [Break](./05_Control_Flow.html#break) 部分。 这个版本的游戏封装到了 `SnakesAndLadders` 类中,该类采纳了 `DiceGame` 协议,并且提供了相应的可读的 `dice` 属性和 `play()` 方法。( `dice` 属性在构造之后就不再改变,且协议只要求 `dice` 为可读的,因此将 `dice` 声明为常量属性。) @@ -408,7 +411,7 @@ class DiceGameTracker: DiceGameDelegate { print("The game is using a \(game.dice.sides)-sided dice") } func game(game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) { - ++numberOfTurns + numberOfTurns += 1 print("Rolled a \(diceRoll)") } func gameDidEnd(game: DiceGame) { @@ -726,7 +729,7 @@ for object in objects { > 注意 > 可选的协议要求只能用在标记 `@objc` 特性的协议中。 -> 该特性表示协议将暴露给 Objective-C 代码,详情参见[`Using Swift with Cocoa and Objective-C(Swift 2.1)`](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216)。即使你不打算和 Objective-C 有什么交互,如果你想要指定可选的协议要求,那么还是要为协议加上 `@obj` 特性。 +> 该特性表示协议将暴露给 Objective-C 代码,详情参见[`Using Swift with Cocoa and Objective-C(Swift 2.2)`](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216)。即使你不打算和 Objective-C 有什么交互,如果你想要指定可选的协议要求,那么还是要为协议加上 `@objc` 特性。 > 还需要注意的是,标记 `@objc` 特性的协议只能被继承自 Objective-C 类的类或者 `@objc` 类采纳,其他类以及结构体和枚举均不能采纳这种协议。 下面的例子定义了一个名为 `Counter` 的用于整数计数的类,它使用外部的数据源来提供每次的增量。数据源由 `CounterDataSource` 协议定义,包含两个可选要求: diff --git a/source/chapter2/23_Generics.md b/source/chapter2/23_Generics.md index 3fde82a8..c0da5ab5 100644 --- a/source/chapter2/23_Generics.md +++ b/source/chapter2/23_Generics.md @@ -12,7 +12,7 @@ > 2.1 > 校对:[shanks](http://codebuild.me),2015-11-01 -> 2.2:翻译+校对:[Lanford](https://github.com/LanfordCai),2016-04-08 +> 2.2:翻译+校对:[Lanford](https://github.com/LanfordCai),2016-04-08 [SketchK](https://github.com/SketchK) 2016-05-16 本页包含内容: From 988c3e036827675bce2b1e7950850f70e09f7bd5 Mon Sep 17 00:00:00 2001 From: Sketchk Date: Tue, 17 May 2016 16:31:33 +0800 Subject: [PATCH 42/61] =?UTF-8?q?=E6=A0=A1=E6=AD=A3+=E7=BF=BB=E8=AF=91=20F?= =?UTF-8?q?rom=20SketchK=20=E4=B8=BB=E8=A6=81=E5=86=85=E5=AE=B9=201=20`?= =?UTF-8?q?=E8=AE=BF=E9=97=AE=E6=8E=A7=E5=88=B6`=E7=AB=A0=E8=8A=82?= =?UTF-8?q?=E4=B8=AD=E7=9A=84`=E6=A3=80=E6=9F=A5=E7=B1=BB=E5=9E=8B`?= =?UTF-8?q?=E4=B8=80=E8=8A=82=E4=B8=AD=E4=BB=A3=E7=A0=81=E6=9B=B4=E6=96=B0?= =?UTF-8?q?(++=E5=8F=B7=E5=BA=9F=E5=BC=83)=202=20`=E9=AB=98=E7=BA=A7?= =?UTF-8?q?=E8=BF=90=E7=AE=97=E7=AC=A6`=E7=AB=A0=E8=8A=82=E4=B8=AD?= =?UTF-8?q?=E7=9A=84`AnyObjcect`=E4=B8=80=E8=8A=82=E4=B8=AD=E6=96=87?= =?UTF-8?q?=E5=AD=97=E6=9B=B4=E6=96=B0=E5=8F=8A=E9=94=99=E8=AF=AF=E7=BA=A0?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/chapter2/24_Access_Control.md | 7 +++++-- source/chapter2/25_Advanced_Operators.md | 24 +++++------------------- 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/source/chapter2/24_Access_Control.md b/source/chapter2/24_Access_Control.md index 0316d34b..243599ca 100644 --- a/source/chapter2/24_Access_Control.md +++ b/source/chapter2/24_Access_Control.md @@ -11,6 +11,9 @@ > 2.1 > 翻译:[Prayer](https://github.com/futantan) > 校对:[shanks](http://codebuild.me),2015-11-01 +> +> 2.2 +> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-17 本页内容包括: @@ -276,7 +279,7 @@ struct TrackedString { private(set) var numberOfEdits = 0 var value: String = "" { didSet { - numberOfEdits++ + numberOfEdits += 1 } } } @@ -306,7 +309,7 @@ public struct TrackedString { public private(set) var numberOfEdits = 0 public var value: String = "" { didSet { - numberOfEdits++ + numberOfEdits += 1 } } public init() {} diff --git a/source/chapter2/25_Advanced_Operators.md b/source/chapter2/25_Advanced_Operators.md index 82b68b5f..ee0757f6 100644 --- a/source/chapter2/25_Advanced_Operators.md +++ b/source/chapter2/25_Advanced_Operators.md @@ -10,6 +10,9 @@ > 2.1 > 校对:[shanks](http://codebuild.me),2015-11-01 +> +> 2.2 +> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-17 本页内容包括: @@ -181,7 +184,7 @@ let blueComponent = pink & 0x0000FF // blueComponent 是 0x99,即 153 其次,使用二进制补码可以使负数的按位左移和右移运算得到跟正数同样的效果,即每向左移一位就将自身的数值乘以 2,每向右一位就将自身的数值除以 2。要达到此目的,对有符号整数的右移有一个额外的规则: -* 当对正整数进行按位右移运算时,遵循与无符号整数相同的规则,但是对于移位产生的空白位使用符号位进行填充,而不是用 `0`。 +* 当对整数进行按位右移运算时,遵循与无符号整数相同的规则,但是对于移位产生的空白位使用符号位进行填充,而不是用 `0`。 ![Art/bitshiftSigned_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitshiftSigned_2x.png "Art/bitshiftSigned_2x.png") @@ -342,7 +345,7 @@ let combinedVector = vector + anotherVector ### 前缀和后缀运算符 -上个例子演示了一个双目中缀运算符的自定义实现。类与结构体也能提供标准单目运算符的实现。单目运算符只运算一个值。当运算符出现在值之前时,它就是前缀的(例如 `-a`),而当它出现在值之后时,它就是后缀的(例如 `i++`)。 +上个例子演示了一个双目中缀运算符的自定义实现。类与结构体也能提供标准单目运算符的实现。单目运算符只运算一个值。当运算符出现在值之前时,它就是前缀的(例如 `-a`),而当它出现在值之后时,它就是后缀的(例如 `b!`)。 要实现前缀或者后缀运算符,需要在声明运算符函数的时候在 `func` 关键字之前指定 `prefix` 或者 `postfix` 修饰符: @@ -383,23 +386,6 @@ original += vectorToAdd // original 的值现在为 (4.0, 6.0) ``` -还可以将赋值与 `prefix` 或 `postfix` 修饰符结合起来,下面的代码为 `Vector2D` 实例实现了前缀自增运算符: - -```swift -prefix func ++ (inout vector: Vector2D) -> Vector2D { - vector += Vector2D(x: 1.0, y: 1.0) - return vector -} -``` - -这个前缀自增运算符使用了前面定义的加法赋值运算。它对 `Vector2D` 的 `x` 和 `y` 属性都进行了加 `1` 运算,再将结果返回: - -```swift -var toIncrement = Vector2D(x: 3.0, y: 4.0) -let afterIncrement = ++toIncrement -// toIncrement 的值现在为 (4.0, 5.0) -// afterIncrement 的值同样为 (4.0, 5.0) -``` > 注意 > 不能对默认的赋值运算符(`=`)进行重载。只有组合赋值运算符可以被重载。同样地,也无法对三目条件运算符 (`a ? b : c`) 进行重载。 From 63de595251d571c61059909d7a41d9f2a860cde4 Mon Sep 17 00:00:00 2001 From: 949478479 <949478479@qq.com> Date: Fri, 20 May 2016 17:33:23 +0800 Subject: [PATCH 43/61] Update 05_Declarations.md (#619) --- source/chapter3/05_Declarations.md | 317 ++++++++++++++--------------- 1 file changed, 152 insertions(+), 165 deletions(-) diff --git a/source/chapter3/05_Declarations.md b/source/chapter3/05_Declarations.md index f674da30..3e8f602a 100755 --- a/source/chapter3/05_Declarations.md +++ b/source/chapter3/05_Declarations.md @@ -6,23 +6,16 @@ > 翻译:[marsprince](https://github.com/marsprince) [Lenhoon](https://github.com/marsprince)[(微博)](http://www.weibo.com/lenhoon) > 校对:[numbbbbb](https://github.com/numbbbbb), [stanzhai](https://github.com/stanzhai) - - > 2.0 > 翻译+校对:[Lenhoon](https://github.com/Lenhoon), > [BridgeQ](https://github.com/WXGBridgeQ) - - > 2.1 > 翻译:[mmoaay](https://github.com/mmoaay), [shanks](http://codebuild.me) > 校对:[shanks](http://codebuild.me) - - > 2.2 > 翻译:[星夜暮晨](https://github.com/SemperIdem) -> 校对: 本页包含内容: @@ -33,7 +26,7 @@ - [变量声明](#variable_declaration) - [存储型变量和存储型变量属性](#stored_variables_and_stored_variable_properties) - [计算型变量和计算型属性](#computed_variables_and_computed_properties) - - [存储型变量观察器和属性观察器](#stored_variable_observers_and_property_observers) + - [存储型变量和属性的观察器](#stored_variable_observers_and_property_observers) - [类型变量属性](#type_variable_properties) - [类型别名声明](#type_alias_declaration) - [函数声明](#function_declaration) @@ -41,13 +34,12 @@ - [输入输出参数](#in-out_parameters) - [特殊参数](#special_kinds_of_parameters) - [特殊方法](#special_kinds_of_methods) - - [柯里化函数](#curried_functions) - [抛出错误的函数和方法](#throwing_functions_and_methods) - - [重新抛出错误的函数和方法](#rethrowing_functions_and_methods) + - [重抛错误的函数和方法](#rethrowing_functions_and_methods) - [枚举声明](#enumeration_declaration) - - [包含任意类型用例的枚举](#enumerations_with_cases_of_any_type) + - [任意类型的枚举用例](#enumerations_with_cases_of_any_type) - [递归枚举](#enumerations_with_indirection) - - [包含原始值类型的枚举](#enumerations_with_cases_of_a_raw-value_type) + - [拥有原始值的枚举用例](#enumerations_with_cases_of_a_raw-value_type) - [访问枚举用例](#accessing_enumeration_cases) - [结构体声明](#structure_declaration) - [类声明](#class_declaration) @@ -66,9 +58,9 @@ - [声明修饰符](#declaration_modifiers) - [访问控制级别](#access_control_levels) -*声明 (declaration)* 用以向程序里引入新的名字或者构造。举例来说,可以使用声明来引入函数和方法,变量和常量,或者来定义新的命名枚举,结构,类和协议类型。还可以使用声明来扩展一个已经存在的命名的类型的行为,或者在程序里引入在其它地方声明的符号。 +*声明 (declaration)* 用以向程序里引入新的名字或者结构。举例来说,可以使用声明来引入函数和方法,变量和常量,或者定义新的具有命名的枚举、结构、类和协议类型。还可以使用声明来扩展一个既有的具有命名的类型的行为,或者在程序里引入在其它地方声明的符号。 -在 Swift 中,大多数声明在某种意义上讲也是定义,实现或初始化往往伴随着声明。这意味着,由于协议往往并不提供实现,大多数协议仅仅只是声明而已。为了方便起见,也因为这些区别在 Swift 中不是很重要,*“声明”*这个术语同时包含了声明和定义。 +在 Swift 中,大多数声明在某种意义上讲也是定义,因为声明往往伴随着实现或初始化。由于协议并不提供实现,大多数协议成员仅仅只是声明而已。为了方便起见,也是因为这些区别在 Swift 中并不是很重要,“声明”这个术语同时包含了声明和定义两种含义。 > 声明语法 > @@ -92,7 +84,7 @@ ## 顶级代码 -Swift 的源文件中的顶级代码 (top-level code) 由零个或多个语句,声明和表达式组成。默认情况下,在一个源文件的顶层声明的变量,常量和其他命名的声明语句可以被同一模块中的每一个源文件中的代码访问。可以使用一个访问级别修饰符来标记声明,从而覆盖默认行为,请参阅 [访问控制级别](#access_control_levels)。 +Swift 的源文件中的顶级代码 (top-level code) 由零个或多个语句、声明和表达式组成。默认情况下,在一个源文件的顶层声明的变量,常量和其他具有命名的声明可以被同模块中的每一个源文件中的代码访问。可以使用一个访问级别修饰符来标记声明来覆盖这种默认行为,请参阅 [访问控制级别](#access_control_levels)。 > 顶级声明语法 > *顶级声明* → [*多条语句*](10_Statements.md#statements)可选 @@ -104,11 +96,10 @@ Swift 的源文件中的顶级代码 (top-level code) 由零个或多个语句 ```swift { - <#语句#> + 语句 } ``` - -代码块中的*语句*包括声明,表达式和各种其他类型的语句,它们按照在源码中的出现顺序被依次执行。 +代码块中的“语句”包括声明、表达式和各种其他类型的语句,它们按照在源码中的出现顺序被依次执行。 > 代码块语法 > @@ -117,17 +108,17 @@ Swift 的源文件中的顶级代码 (top-level code) 由零个或多个语句 ## 导入声明 -*导入声明 (import declaration)* 让你可以使用在其他文件中声明的内容。导入语句的基本形式是导入整个模块,它由 `import` 关键字开始,后面紧跟一个模块名: +*导入声明 (import declaration)* 让你可以使用在其他文件中声明的内容。导入语句的基本形式是导入整个模块,它由 `import` 关键字和紧随其后的模块名组成: ```swift -import <#模块#> +import 模块 ``` -可以对导入提供更细致的控制,如指定一个特殊的子模块或者指定一个模块或子模块中的某个声明。提供了这些限制后,在当前作用域中,只有导入的符号是可用的,而不是整个模块。 +可以对导入操作提供更细致的控制,如指定一个特殊的子模块或者指定一个模块或子模块中的某个声明。提供了这些限制后,在当前作用域中,只有被导入的符号是可用的,而不是整个模块中的所有声明。 ```swift -import <#导入类型#> <#模块#>.<#符号名#> -import <#模块#>.<#子模块#> +import 导入类型 模块.符号名 +import 模块.子模块 ``` @@ -144,23 +135,23 @@ import <#模块#>.<#子模块#> ## 常量声明 -*常量声明 (constant declaration)* 可以在程序中命名一个常量。常量以关键字 `let` 来声明,遵循如下的格式: +*常量声明 (constant declaration)* 可以在程序中引入一个具有命名的常量。常量以关键字 `let` 来声明,遵循如下格式: ```swift -let <#常量名称#>: <#类型#> = <#表达式#> +let 常量名称: 类型 = 表达式 ``` -常量声明在*常量名称*和初始化*表达式*的值之间,定义了一种不可变的绑定关系;当常量的值被设定之后,它就无法被更改。这意味着,如果常量以类对象来初始化,对象本身的内容是可以改变的,但是常量和该对象之间的结合关系是不能改变的。 +常量声明在“常量名称”和用于初始化的“表达式”的值之间定义了一种不可变的绑定关系;当常量的值被设定之后,它就无法被更改。这意味着,如果常量以类对象来初始化,对象本身的内容是可以改变的,但是常量和该对象之间的绑定关系是不能改变的。 -当一个常量被声明为全局变量,它必须被给定一个初始值。在类或者结构体中声明一个常量时,它被认为是一个*常量属性 (constant property)*。常量声明不能是计算型属性,因此也没有存取方法。 +当一个常量被声明为全局常量时,它必须拥有一个初始值。在类或者结构中声明一个常量时,它将作为*常量属性 (constant property)*。常量声明不能是计算型属性,因此也没有存取方法。 -如果常量名称是元组形式,元组中的每一项的名称会和对应的初始化表达式的值绑定。 +如果常量名称是元组形式,元组中每一项的名称都会和初始化表达式中对应的值进行绑定。 ```swift let (firstNumber, secondNumber) = (10, 42) ``` -在上例中,`firstNumber` 是一个值为 `10` 的常量,`secnodeName` 是一个值为 `42` 的常量。所有常量都可以独立的使用: +在上例中,`firstNumber` 是一个值为 `10` 的常量,`secnodeName` 是一个值为 `42` 的常量。所有常量都可以独立地使用: ```swift print("The first number is \(firstNumber).") @@ -169,7 +160,7 @@ print("The second number is \(secondNumber).") // 打印 “The second number is 42.” ``` -当常量名称的类型 (`:` *type*) 可以被推断出时,类型标注在常量声明中是一个可选项,正如 [类型推断](03_Types.md#type_inference) 中所描述的。 +当常量名称的类型 (`:` 类型) 可以被推断出时,类型标注在常量声明中是可选的,正如 [类型推断](03_Types.md#type_inference) 中所描述的。 声明一个常量类型属性要使用 `static` 声明修饰符。类型属性在 [类型属性](../chapter2/10_Properties.md#type_properties)中有介绍。 @@ -189,14 +180,14 @@ print("The second number is \(secondNumber).") ## 变量声明 -*变量声明 (variable declaration)* 可以在程序中声明一个变量,它以关键字 `var` 来声明。 +*变量声明 (variable declaration)* 可以在程序中引入一个具有命名的变量,它以关键字 `var` 来声明。 变量声明有几种不同的形式,可以声明不同种类的命名值和可变值,如存储型和计算型变量和属性,属性观察器,以及静态变量属性。所使用的声明形式取决于变量声明的适用范围和打算声明的变量类型。 > 注意 -> 也可以在协议声明的上下文中声明属性,详情请参阅 [协议属性声明](#protocol_property_declaration)。 +> 也可以在协议声明中声明属性,详情请参阅 [协议属性声明](#protocol_property_declaration)。 -可以在子类中使用 `override` 声明修饰符来标记继承来的属性的声明,从而重写属性,详情请参阅 [重写](../chapter2/13_Inheritance.md#overriding)。 +可以在子类中重写继承来的变量属性,使用 `override` 声明修饰符标记属性的声明即可,详情请参阅 [重写](../chapter2/13_Inheritance.md#overriding)。 ### 存储型变量和存储型变量属性 @@ -204,16 +195,16 @@ print("The second number is \(secondNumber).") 使用如下形式声明一个存储型变量或存储型变量属性: ```swift -var <#变量名称#>: <#类型#> = <#表达式#> +var 变量名称: 类型 = 表达式 ``` -可以在全局范围,函数内部,或者在类和结构体的声明中使用这种形式来声明一个变量。当变量以这种形式在全局范围或者函数内部被声明时,它代表一个存储型变量。当它在类或者结构体中被声明时,它代表一个*存储型变量属性 (stored variable property)*。 +可以在全局范围,函数内部,或者在类和结构的声明中使用这种形式来声明一个变量。当变量以这种形式在全局范围或者函数内部被声明时,它代表一个存储型变量。当它在类或者结构中被声明时,它代表一个*存储型变量属性 (stored variable property)*。 用于初始化的表达式不可以在协议的声明中出现,在其他情况下,该表达式是可选的。如果没有初始化表达式,那么变量声明必须包含类型标注 (`:` *type*)。 -对于常量的声明,如果变量名称是一个元组,元组中每一项的名称都要和初始化表达式中的相应值绑定。 +如同常量声明,如果变量名称是元组形式,元组中每一项的名称都会和初始化表达式中对应的值进行绑定。 -正如名字一样,存储型变量和存储型变量属性的值会存储在内存中。 +正如名字所示,存储型变量和存储型变量属性的值会存储在内存中。 ### 计算型变量和计算型属性 @@ -221,50 +212,50 @@ var <#变量名称#>: <#类型#> = <#表达式#> 使用如下形式声明一个计算型变量或计算型属性: ```swift -var <#变量名称#>: <#类型#> { +var 变量名称: 类型 { get { - <#语句#> + 语句 } - set(<#setter 名称#>) { - <#语句#> + set(setter 名称) { + 语句 } } ``` -可以在全局范围,函数内部,以及类,结构体,枚举,扩展声明的上下文中使用这种形式的声明。当变量以这种形式在全局范围或者函数内部被声明时,它代表一个计算型变量。当它在类,结构体,枚举,扩展声明的上下文中被声明时,它代表一个*计算型属性 (computed property)*。 +可以在全局范围、函数内部,以及类、结构、枚举、扩展的声明中使用这种形式的声明。当变量以这种形式在全局范围或者函数内部被声明时,它表示一个计算型变量。当它在类、结构、枚举、扩展声明的上下文中被声明时,它表示一个*计算型属性 (computed property)*。 -getter 用来读取变量值,setter 用来写入变量值。setter 子句是可选的,getter 子句是必须的。也可以将这些子句都省略,直接返回请求的值,正如在 [只读计算型属性](../chapter2/10_Properties.md#computed_properties) 中描述的那样。但是如果提供了一个 setter 子句,也必须提供一个 getter 子句。 +getter 用来读取变量值,setter 用来写入变量值。setter 子句是可选的,getter 子句是必须的。不过也可以将这些子句都省略,直接返回请求的值,正如在 [只读计算型属性](../chapter2/10_Properties.md#computed_properties) 中描述的那样。但是如果提供了一个 setter 子句,就必须也提供一个 getter 子句。 -圆括号内的 setter 的名称是可选的。如果提供了一个 setter 名称,它就会作为 setter 的参数名称使用。如果不提供 setter 名称,setter 的参数的默认名称为 `newValue`,正如在 [便捷 setter 声明](../chapter2/10_Properties.md#shorthand_setter_declaration) 中描述的那样。 +setter 的圆括号以及 setter 名称是可选的。如果提供了 setter 名称,它就会作为 setter 的参数名称使用。如果不提供 setter 名称,setter 的参数的默认名称为 `newValue`,正如在 [便捷 setter 声明](../chapter2/10_Properties.md#shorthand_setter_declaration) 中描述的那样。 与存储型变量和存储型属性不同,计算型变量和计算型属性的值不存储在内存中。 要获得更多关于计算型属性的信息和例子,请参阅 [计算型属性](../chapter2/10_Properties.md#computed_properties)。 -### 存储型变量观察器和属性观察器 +### 存储型变量和属性的观察器 可以在声明存储型变量或属性时提供 `willSet` 和 `didSet` 观察器。一个包含观察器的存储型变量或属性以如下形式声明: ```swift -var <#变量名称#>: <#类型#> = <#表达式#> { - willSet(<#setter 名称#>) { - <#语句#> +var 变量名称: 类型 = 表达式 { + willSet(setter 名称) { + 语句 } - didSet(<#setter 名称#>) { - <#语句#> + didSet(setter 名称) { + 语句 } } ``` -可以在全局范围,函数内部,或者类,结构体声明的上下文中使用这种形式的声明。当变量以这种形式在全局范围或者函数内部被声明时,观察器代表一个存储型变量观察器。当它在类,结构体声明的上下文中被声明时,观察器代表一个属性观察器。 +可以在全局范围、函数内部,或者类、结构的声明中使用这种形式的声明。当变量以这种形式在全局范围或者函数内部被声明时,观察器表示一个存储型变量观察器。当它在类和结构的声明中被声明时,观察器表示一个属性观察器。 可以为任何存储型属性添加观察器。也可以通过重写父类属性的方式为任何继承的属性(无论是存储型还是计算型的)添加观察器 ,正如 [重写属性观察器](../chapter2/13_Inheritance.md#overriding_property_observers) 中所描述的。 -用于初始化的表达式在一个类或者结构体的声明中是可选的,但是在其他地方是必须的。如果类型可以从初始化表达式中推断而来,那么这个类型标注是可选的。 +用于初始化的表达式在类或者结构的声明中是可选的,但是在其他声明中则是必须的。如果可以从初始化表达式中推断出类型信息,那么可以不提供类型标注。 -当变量或属性的值被改变时,`willSet` 和 `didSet` 观察器提供了一种观察方法。观察器不会在变量或属性第一次初始化时被调用,它们仅当值在初始化环境之外被改变时才会被调用。 +当变量或属性的值被改变时,`willSet` 和 `didSet` 观察器提供了一种观察方法。观察器会在变量的值被改变时调用,但不会在初始化时被调用。 `willSet` 观察器只在变量或属性的值被改变之前调用。新的值作为一个常量传入 `willSet` 观察器,因此不可以在 `willSet` 中改变它。`didSet` 观察器在变量或属性的值被改变后立即调用。和 `willSet` 观察器相反,为了方便获取旧值,旧值会传入 `didSet` 观察器。这意味着,如果在变量或属性的 `didiset` 观察器中设置值,设置的新值会取代刚刚在 `willSet` 观察器中传入的那个值。 @@ -328,13 +319,13 @@ var <#变量名称#>: <#类型#> = <#表达式#> { ## 类型别名声明 -*类型别名 (type alias)* 声明可以在程序中为一个现存类型声明一个别名。类型别名声明语句使用关键字 `typealias` 声明,遵循如下的形式: +*类型别名 (type alias)* 声明可以在程序中为一个既有类型声明一个别名。类型别名声明语句使用关键字 `typealias` 声明,遵循如下的形式: ```swift -typealias <#类型别名#> = <#现存类型#> +typealias 类型别名 = 现存类型 ``` -当声明一个类型的别名后,可以在程序的任何地方使用*别名*来代替*现存类型*。*现存类型*可以是命名类型或者混合类型。类型别名不产生新的类型,它只是可以使用别名来引用现存类型。 +当声明一个类型的别名后,可以在程序的任何地方使用“别名”来代替现有类型。现有类型可以是具有命名的类型或者混合类型。类型别名不产生新的类型,它只是使用别名来引用现有类型。 另请参阅 [协议关联类型声明](#protocol_associated_type_declaration)。 @@ -352,23 +343,23 @@ typealias <#类型别名#> = <#现存类型#> ## 函数声明 -使用*函数声明 (function declaration)* 在程序中引入新的函数或者方法。当一个函数被声明在类,结构体,枚举,或者协议的上下文中,会作为一个方法。函数声明使用关键字 `func`,遵循如下的形式: +使用*函数声明 (function declaration)* 在程序中引入新的函数或者方法。在类、结构体、枚举,或者协议中声明的函数会作为方法。函数声明使用关键字 `func`,遵循如下的形式: ```swift -func <#函数名称#>(<#参数列表#>) -> <#返回类型#> { - <#语句#> +func 函数名称(参数列表) -> 返回类型 { + 语句 } ``` 如果函数返回 `Void` 类型,返回类型可以省略,如下所示: ```swift -func <#函数名称#>(<#参数列表#>) { - <#语句#> +func 函数名称(参数列表) { + 语句 } ``` -每个参数的类型都要标明,它们不能被推断出来。虽然函数的参数默认是常量,也可以在参数名前添加 `let` 来强调这一行为。如果您在某个参数名前面加上了 `inout`,那么这个参数就可以在这个函数作用域当中被修改。更多关于 `inout` 参数的讨论,请参阅 [输入输出参数](#in-out_parameters)。 +每个参数的类型都要标明,因为它们不能被推断出来。虽然函数的参数默认是常量,也可以在参数名前添加 `let` 来强调这一行为。如果您在某个参数名前面加上了 `inout`,那么这个参数就可以在这个函数作用域当中被修改。更多关于 `inout` 参数的讨论,请参阅 [输入输出参数](#in-out_parameters)。 函数可以使用元组类型作为返回类型来返回多个值。 @@ -379,11 +370,9 @@ func <#函数名称#>(<#参数列表#>) { 函数的参数列表由一个或多个函数参数组成,参数间以逗号分隔。函数调用时的参数顺序必须和函数声明时的参数顺序一致。最简单的参数列表有着如下的形式: -```swift -<#参数名称#>: <#参数类型#> -``` +`参数名称`: `参数类型` -一个参数有一个内部名称,这个内部名称可以在函数体内被使用。同样也可以作为外部名称,当调用函数时这个外部名称被作为实参的标签来使用。默认情况下,第一个参数的外部名称省略不写,第二个和之后的参数使用它们的内部名称作为它们的外部名称。例如: +一个参数有一个内部名称,这个内部名称可以在函数体内被使用。还有一个外部名称,当调用函数时这个外部名称被作为实参的标签来使用。默认情况下,第一个参数的外部名称会被省略,第二个和之后的参数使用它们的内部名称作为它们的外部名称。例如: ```swift func f(x: Int, y: Int) -> Int { return x + y } @@ -392,14 +381,12 @@ f(1, y: 2) // 参数 y 有标签,参数 x 则没有 可以按照如下两种形式之一,重写参数名称的默认行为: -```swift -<#外部参数名称#> <#内部参数名称#>: <#参数类型#> -_ <#内部参数名称#>: <#参数类型#> -``` +`外部参数名称` `内部参数名称`: `参数类型` +_ `内部参数名称`: `参数类型` -在内部参数名称前的名称赋予这个参数一个外部名称,这个名称可以和内部参数的名称不同。外部参数名称在函数被调用时必须被使用。对应的参数在方法或函数被调用时必须有外部名。 +在内部参数名称前的名称会作为这个参数的外部名称,这个名称可以和内部参数的名称不同。外部参数名称在函数被调用时必须被使用,即对应的参数在方法或函数被调用时必须有外部名。 -内部参数名称前的下划线(`_`)使该参数在函数被调用时没有名称。在函数或方法调用时,对应的参数必须没有名字。 +内部参数名称前的下划线(`_`)可使该参数在函数被调用时没有名称。在函数或方法调用时,对应的参数必须没有名字。 ```swift func f(x x: Int, withY y: Int, _ z: Int) -> Int { return x + y + z } @@ -417,11 +404,11 @@ f(x: 1, withY: 2, 3) // 参数 x 和 y 是有标签的,参数 z 则没有 这种行为被称为*拷入拷出 (copy-in copy-out)* 或*值结果调用 (call by value result)*。例如,当一个计算型属性或者一个具有属性观察器的属性被用作函数的输入输出参数时,其 getter 会在函数调用时被调用,而其 setter 会在函数返回时被调用。 -作为一种优化手段,当参数值存储在内存中的物理地址时,在函数体内部和外部均会使用同一内存位置。这种优化行为被称为*引用调用 (call by reference)*,它满足了拷入拷出模型的所有需求,而消除了复制带来的开销。不要依赖于拷入拷出与引用调用之间的行为差异。 +作为一种优化手段,当参数值存储在内存中的物理地址时,在函数体内部和外部均会使用同一内存位置。这种优化行为被称为*引用调用 (call by reference)*,它满足了拷入拷出模式的所有要求,且消除了复制带来的开销。在代码中,要规范使用拷入拷出模式,不要依赖于引用调用。 -不要使用传递给输入输出参数的值,即使原始值在当前作用域中依然可用。当函数返回时,你对原始值所做的更改会被拷贝的值所覆盖。不要依赖于引用调用的优化机制来试图阻止这种覆盖。 +不要使用传递给输入输出参数的值,即使原始值在当前作用域中依然可用。当函数返回时,你对原始值所做的更改会被拷贝的值所覆盖。不要依赖于引用调用的优化机制来试图避免这种覆盖。 -你不能将同一个值传递给多个输入输出参数,因为多个输入输出参数引发的拷贝与覆盖行为的顺序是不确定的,因此原始值的最终值也将无法确定。例如: +不能将同一个值传递给多个输入输出参数,因为这种情况下的拷贝与覆盖行为的顺序是不确定的,因此原始值的最终值也将无法确定。例如: ```swift var x = 10 @@ -456,17 +443,17 @@ print(x) ### 特殊参数 -参数可以被忽略,参数的数量可变,并且还可以提供默认值,使用形式如下: +参数可以被忽略,数量可以不固定,还可以为其提供默认值,使用形式如下: ```swift -_ : <#参数类型#> -<#参数名称#>: <#参数类型#>... -<#参数名称#.: <#参数类型#> = <#默认参数值#> +_ : 参数类型 +参数名称: 参数类型... +参数名称: 参数类型 = 默认参数值 ``` -以下划线(`_`)命名的参数是被显式忽略的,无法在函数体内使用。 +以下划线(`_`)命名的参数会被显式忽略,无法在函数体内使用。 -一个参数的基础类型名称如果紧跟着三个点(`...`),会被视为可变参数。一个函数至多可以拥有一个可变参数,且必须是最后一个参数。可变参数会作为该参数类型的数组。举例来讲,可变参数 `Int...` 被看作 `[Int]`。关于使用可变参数的例子,请参阅 [可变参数](../chapter2/06_Functions.md#variadic_parameters)。 +一个参数的基本类型名称如果紧跟着三个点(`...`),会被视为可变参数。一个函数至多可以拥有一个可变参数,且必须是最后一个参数。可变参数会作为包含该参数类型元素的数组处理。举例来讲,可变参数 `Int...` 会作为 `[Int]` 来处理。关于使用可变参数的例子,请参阅 [可变参数](../chapter2/06_Functions.md#variadic_parameters)。 如果在参数类型后面有一个以等号(`=`)连接的表达式,该参数会拥有默认值,即给定表达式的值。当函数被调用时,给定的表达式会被求值。如果参数在函数调用时被省略了,就会使用其默认值。 @@ -480,11 +467,11 @@ f(x: 7) // 无效,该参数没有外部名称 ### 特殊方法 -枚举或结构体的方法如果会修改 `self` 属性,必须以 `mutating` 声明修饰符标记。 +枚举或结构体的方法如果会修改 `self`,则必须以 `mutating` 声明修饰符标记。 -子类重写超类中的方法必须以 `override` 声明修饰符标记。重写方法时不使用 `override` 修饰符,或者使用了 `override` 修饰符却并没有重写超类方法,都会产生一个编译时错误。 +子类重写超类中的方法必须以 `override` 声明修饰符标记。重写方法时不使用 `override` 修饰符,或者被 `override` 修饰符修饰的方法并未对超类方法构成重写,都会导致编译错误。 -枚举或者结构体中的类型方法,要以 `static` 声明修饰符标记,而对于类中的类型方法,除了使用 `static`,也可使用 `class` 声明修饰符标记。 +枚举或者结构体中的类型方法,要以 `static` 声明修饰符标记,而对于类中的类型方法,除了使用 `static`,还可使用 `class` 声明修饰符标记。 ### 抛出错误的函数和方法 @@ -492,21 +479,21 @@ f(x: 7) // 无效,该参数没有外部名称 可以抛出错误的函数或方法必须使用 `throws` 关键字标记。这类函数和方法被称为抛出函数和抛出方法。它们有着下面的形式: ```swift -func <#函数名称#>(<#参数列表#>) throws -> <#返回类型#> { - <#语句#> +func 函数名称(参数列表) throws -> 返回类型 { + 语句 } ``` 抛出函数或抛出方法的调用必须包裹在 `try` 或者 `try!` 表达式中(也就是说,在作用域内使用 `try` 或者 `try!` 运算符)。 -`throws` 关键字是函数的类型的一部分,非抛出函数是抛出函数的子类型。所以,可以在使用抛出函数的地方使用非抛出函数。对于柯里化函数,`throws` 关键字仅应用于最内层的函数。 +`throws` 关键字是函数的类型的一部分,非抛出函数是抛出函数的子类型。所以,可以在使用抛出函数的地方使用非抛出函数。 不能仅基于函数能否抛出错误来进行函数重载。也就是说,可以基于函数的函数类型的参数能否抛出错误来进行函数重载。 抛出方法不能重写非抛出方法,而且抛出方法不能满足协议对于非抛出方法的要求。也就是说,非抛出方法可以重写抛出方法,而且非抛出方法可以满足协议对于抛出方法的要求。 -### 重新抛出错误的函数和方法 +### 重抛错误的函数和方法 函数或方法可以使用 `rethrows` 关键字来声明,从而表明仅当该函数或方法的一个函数类型的参数抛出错误时,该函数或方法才抛出错误。这类函数和方法被称为重抛函数和重抛方法。重新抛出错误的函数或方法必须至少有一个参数的类型为抛出函数。 @@ -516,7 +503,7 @@ func functionWithCallback(callback: () throws -> Int) rethrows { } ``` -重抛函数或者方法不能够从它本身直接抛出任何错误,这意味着它不能够包含 `throw` 语句。它只能够传递作为参数的抛出函数所抛出的错误。例如,在 `do-catch` 代码块中调用抛出函数,以及在 `catch` 闭包中处理另一种抛出的错误错误都是不允许的。 +重抛函数或者方法不能够从自身直接抛出任何错误,这意味着它不能够包含 `throw` 语句。它只能够传递作为参数的抛出函数所抛出的错误。例如,在 `do-catch` 代码块中调用抛出函数,并在 `catch` 子句中抛出其它错误都是不允许的。 抛出方法不能重写重抛方法,而且抛出方法不能满足协议对于重抛方法的要求。也就是说,重抛方法可以重写抛出方法,而且重抛方法可以满足协议对于抛出方法的要求。 @@ -561,33 +548,33 @@ func functionWithCallback(callback: () throws -> Int) rethrows { 在程序中使用*枚举声明 (enumeration declaration)* 来引入一个枚举类型。 -枚举声明有两种基本形式,使用关键字 `enum` 来声明。枚举声明体包含零个或多个值,称为枚举用例,还可包含任意数量的声明,包括计算型属性,实例方法,类型方法,构造器,类型别名,甚至其他枚举,结构体,和类。枚举声明不能包含析构器或者协议声明。 +枚举声明有两种基本形式,使用关键字 `enum` 来声明。枚举声明体包含零个或多个值,称为枚举用例,还可包含任意数量的声明,包括计算型属性、实例方法、类型方法、构造器、类型别名,甚至其他枚举、结构体和类。枚举声明不能包含析构器或者协议声明。 -枚举类型可以采纳任意数量的协议,但是枚举不能从类,结构体和其他枚举继承。 +枚举类型可以采纳任意数量的协议,但是枚举不能从类、结构体和其他枚举继承。 -不同于类或者结构体,枚举类型并不提供隐式的默认构造器,所有构造器必须显式声明。一个构造器可以委托给枚举中的其他构造器,但是构造过程仅当构造器将一个枚举用例指定给 `self` 后才算完成。 +不同于类或者结构体,枚举类型并不隐式提供默认构造器,所有构造器必须显式声明。一个构造器可以委托给枚举中的其他构造器,但是构造过程仅当构造器将一个枚举用例赋值给 `self` 后才算完成。 和结构体类似但是和类不同,枚举是值类型。枚举实例在被赋值到变量或常量时,或者传递给函数作为参数时会被复制。更多关于值类型的信息,请参阅 [结构体和枚举是值类型](../chapter2/09_Classes_and_Structures.md#structures_and_enumerations_are_value_types)。 可以扩展枚举类型,正如在 [扩展声明](#extension_declaration) 中讨论的一样。 -### 包含任意类型用例的枚举 +### 任意类型的枚举用例 如下的形式声明了一个包含任意类型枚举用例的枚举变量: ```swift -enum <#枚举名称#>: <#采纳的协议#> { - case <#枚举用例1#> - case <#枚举用例2#>(<#关联值类型#>) -} +enum 枚举名称: 采纳的协议 { + case 枚举用例1 + case 枚举用例2(关联值类型) +} ``` 这种形式的枚举声明在其他语言中有时被叫做可识别联合。 在这种形式中,每个用例块由关键字 `case` 开始,后面紧接一个或多个以逗号分隔的枚举用例。每个用例名必须是独一无二的。每个用例也可以指定它所存储的指定类型的值,这些类型在关联值类型的元组中被指定,紧跟用例名之后。 -具有关联值的枚举用例可以像函数一样使用,从而通过指定的关联值创建一个枚举实例。和真正的函数一样,你可以获取一个枚举用例的引用,然后在后续代码中调用它。 +具有关联值的枚举用例可以像函数一样使用,通过指定的关联值创建枚举实例。和真正的函数一样,你可以获取枚举用例的引用,然后在后续代码中调用它。 ```swift enum Number { @@ -605,9 +592,9 @@ let evenInts: [Number] = [0, 2, 4, 6].map(f) 要获得更多关于具有关联值的枚举用例的信息和例子,请参阅 [关联值](../chapter2/08_Enumerations.md#associated_values)。 -### 递归枚举 +#### 递归枚举 -枚举类型可以具有递归结构,就是说,枚举用例的关联值类型可以是枚举类型自身。然而,枚举类型的实例具有值语义,这意味着它们在内存中有着固定的位置。为了支持递归,编译器必须插入一个间接层。 +枚举类型可以具有递归结构,就是说,枚举用例的关联值类型可以是枚举类型自身。然而,枚举类型的实例具有值语义,这意味着它们在内存中有固定布局。为了支持递归,编译器必须插入一个间接层。 要让某个枚举用例支持递归,使用 `indirect` 声明修饰符标记该用例。 @@ -623,20 +610,20 @@ enum Tree { 被 `indirect` 修饰符标记的枚举用例必须有一个关联值。使用 `indirect` 修饰符标记的枚举类型可以既包含有关联值的用例,同时还可包含没有关联值的用例。但是,它不能再单独使用 `indirect` 修饰符来标记某个用例。 -### 包含原始值类型的枚举 +### 拥有原始值的枚举用例 以下形式声明了一种枚举类型,其中各个枚举用例的类型均为同一种基本类型: ```swift -enum <#枚举名称#>: <#原始值类型#>, <#采纳的协议#> { - case <#枚举用例1#> = <#原始值1#> - case <#枚举用例2#> = <#原始值2#> +enum 枚举名称: 原始值类型, 采纳的协议 { + case 枚举用例1 = 原始值1 + case 枚举用例2 = 原始值2 } ``` -在这种形式中,每一个用例块由 `case` 关键字开始,后面紧跟一个或多个以逗号分隔的枚举用例。和第一种形式的枚举用例不同,这种形式的枚举用例包含一个基础值,叫做原始值,各个枚举用例的原始值的类型必须相同。这些原始值的类型通过原始值类型指定,必须表示一个整数,浮点数,字符串,或者字符。原始值类型必须符合 `Equatable` 协议和下列字面量转换协议中的一种:整型字面量需符合 `IntergerLiteralConvertible` 协议,浮点型字面量需符合 `FloatingPointLiteralConvertible` 协议,包含任意数量字符的字符串型字面量需符合 `StringLiteralConvertible` 协议,仅包含一个单一字符的字符串型字面量需符合 `ExtendedGraphemeClusterLiteralConvertible` 协议。每一个用例的名字和原始值必须唯一。 +在这种形式中,每一个用例块由 `case` 关键字开始,后面紧跟一个或多个以逗号分隔的枚举用例。和第一种形式的枚举用例不同,这种形式的枚举用例包含一个基础值,叫做原始值,各个枚举用例的原始值的类型必须相同。这些原始值的类型通过原始值类型指定,必须表示一个整数、浮点数、字符串或者字符。原始值类型必须符合 `Equatable` 协议和下列字面量转换协议中的一种:整型字面量需符合 `IntergerLiteralConvertible` 协议,浮点型字面量需符合 `FloatingPointLiteralConvertible` 协议,包含任意数量字符的字符串型字面量需符合 `StringLiteralConvertible` 协议,仅包含一个单一字符的字符串型字面量需符合 `ExtendedGraphemeClusterLiteralConvertible` 协议。每一个用例的名字和原始值必须唯一。 -如果原始值类型被指定为 `Int`,则不必为用例显式地指定原始值,它们会隐式地被赋值 `0`,`1`,`2` 等。每个未被赋值的 `Int` 类型的用例会被隐式地赋值,其值为上一个用例的原始值加 `1`。 +如果原始值类型被指定为 `Int`,则不必为用例显式地指定原始值,它们会隐式地被赋值 `0`、`1`、`2` 等。每个未被赋值的 `Int` 类型的用例会被隐式地赋值,其值为上一个用例的原始值加 `1`。 ```Swift enum ExampleEnum: Int { @@ -656,7 +643,7 @@ enum WeekendDay: String { 在上面这个例子中,`WeekendDay.Saturday` 的原始值是 `"Saturday"`,`WeekendDay.Sunday` 的原始值是 `"Sunday"`。 -枚举用例具有原始值的枚举类型隐式地符合定义在 Swift 标准库中的 `RawRepresentable` 协议。所以,它们拥有一个 `rawValue` 属性和一个可失败构造器 `init?(rawValue: RawValue)`。可以使用 `rawValue` 属性去获取枚举用例的原始值,例如 `ExampleEnum.B.rawValue`。还可以根据原始值来获取一个相对应的枚举用例,只需调用枚举的可失败构造器,例如 `ExampleEnum(rawValue: 5)`,这个可失败构造器返回一个可选类型的用例。要获得更多关于具有原始值的枚举用例的信息和例子,请参阅 [原始值](../chapter2/08_Enumerations.md#raw_values)。 +枚举用例具有原始值的枚举类型隐式地符合定义在 Swift 标准库中的 `RawRepresentable` 协议。所以,它们拥有一个 `rawValue` 属性和一个可失败构造器 `init?(rawValue: RawValue)`。可以使用 `rawValue` 属性去获取枚举用例的原始值,例如 `ExampleEnum.B.rawValue`。还可以根据原始值来创建一个相对应的枚举用例,只需调用枚举的可失败构造器,例如 `ExampleEnum(rawValue: 5)`,这个可失败构造器返回一个可选类型的用例。要获得更多关于具有原始值的枚举用例的信息和例子,请参阅 [原始值](../chapter2/08_Enumerations.md#raw_values)。 ### 访问枚举用例 @@ -712,16 +699,16 @@ enum WeekendDay: String { 使用*结构体声明 (structure declaration)* 可以在程序中引入一个结构体类型。结构体声明使用 `struct` 关键字,遵循如下的形式: ```swift -struct <#结构体名称#>: <#采纳的协议#> { - <#多条声明#> +struct 结构体名称: 采纳的协议 { + 多条声明 } ``` -结构体内可包含零个或多个声明。这些声明可以包括存储型和计算型属性,类型属性,实例方法,类型方法,构造器,下标,类型别名,甚至其他结构体,类,和枚举声明。结构体声明不能包含析构器或者协议声明。关于结构体的详细讨论和示例,请参阅 [类和结构体](../chapter2/09_Classes_and_Structures.md)。 +结构体内可包含零个或多个声明。这些声明可以包括存储型和计算型属性、类型属性、实例方法、类型方法、构造器、下标、类型别名,甚至其他结构体、类、和枚举声明。结构体声明不能包含析构器或者协议声明。关于结构体的详细讨论和示例,请参阅 [类和结构体](../chapter2/09_Classes_and_Structures.md)。 -结构体可以采纳任意数量的协议,但是不能继承自类,枚举或者其他结构体。 +结构体可以采纳任意数量的协议,但是不能继承自类、枚举或者其他结构体。 -有三种方法可以创建一个声明过的结构体实例: +有三种方法可以创建一个已声明的结构体实例: * 调用结构体内声明的构造器,正如 [构造器](../chapter2/14_Initialization.md#initializers) 所述。 @@ -753,22 +740,22 @@ struct <#结构体名称#>: <#采纳的协议#> { 可以在程序中使用*类声明 (class declaration)* 来引入一个类。类声明使用关键字 `class`,遵循如下的形式: ```swift -class <#类名#>: <#超类#>, <#采纳的协议#> { - <#多条声明#> +class 类名: 超类, 采纳的协议 { + 多条声明 } ``` -类内可以包含零个或多个声明。这些声明可以包括存储型和计算型属性,实例方法,类型方法,构造器,析构器,下标,类型别名,甚至其他结构体,类,和枚举声明。类声明不能包含协议声明。关于类的详细讨论和示例,请参阅 [类和结构体](../chapter2/09_Classes_and_Structures.md)。 +类内可以包含零个或多个声明。这些声明可以包括存储型和计算型属性、实例方法、类型方法、构造器、唯一的析构器、下标、类型别名,甚至其他结构体、类和枚举声明。类声明不能包含协议声明。关于类的详细讨论和示例,请参阅 [类和结构体](../chapter2/09_Classes_and_Structures.md)。 一个类只能继承自一个超类,但是可以采纳任意数量的协议。超类紧跟在类名和冒号后面,其后跟着采纳的协议。泛型类可以继承自其它泛型类和非泛型类,但是非泛型类只能继承自其它非泛型类。当在冒号后面写泛型超类的名称时,必须写上泛型类的全名,包括它的泛型形参子句。 正如 [构造器声明](#initializer_declaration) 所讨论的,类可以有指定构造器和便利构造器。类的指定构造器必须初始化类中声明的所有属性,并且必须在调用超类构造器之前。 -类可以重写属性,方法,下标,以及构造器。重写的属性,方法,下标,和指定构造器必须以 `override` 声明修饰符标记。 +类可以重写属性、方法、下标以及构造器。重写的属性、方法、下标和指定构造器必须以 `override` 声明修饰符标记。 为了要求子类去实现超类的构造器,使用 `required` 声明修饰符标记超类的构造器。子类实现超类构造器时也必须使用 `required` 声明修饰符。 -虽然超类属性和方法声明可以被当前类继承,但是超类声明的指定构造器却不能。即便如此,如果当前类重写了超类的所有指定构造器,它就继承了超类的便利构造器。Swift 的类并不继承自一个通用基础类。 +虽然超类属性和方法声明可以被当前类继承,但是超类声明的指定构造器却不能。即便如此,如果当前类重写了超类的所有指定构造器,它就会继承超类的所有便利构造器。Swift 的类并不继承自一个通用基础类。 有两种方法来创建已声明的类的实例: @@ -797,17 +784,17 @@ class <#类名#>: <#超类#>, <#采纳的协议#> { *协议声明 (protocol declaration)* 可以为程序引入一个命名的协议类型。协议声明只能在全局区域使用 `protocol` 关键字来进行声明,并遵循如下形式: ```swift -protocol <#协议名称#>: <#继承的协议#> { - <#协议成员声明#> +protocol 协议名称: 继承的协议 { + 协议成员声明 } ``` -协议的主体包含零个或多个协议成员声明,这些成员描述了任何采纳该协议的类型必须满足的一致性要求。一个协议可以声明采纳者必须实现的某些属性、方法、构造器以及下标。协议也可以声明特殊类型的类型别名,叫做关联类型,它可以指定协议的不同声明之间的关系。协议声明不能包括类,结构体,枚举或者其它协议的声明。协议成员声明会在后面进行讨论。 +协议的主体包含零个或多个协议成员声明,这些成员描述了任何采纳该协议的类型必须满足的一致性要求。一个协议可以声明采纳者必须实现的某些属性、方法、构造器以及下标。协议也可以声明各种各样的类型别名,叫做关联类型,它可以指定协议的不同声明之间的关系。协议声明不能包括类、结构体、枚举或者其它协议的声明。协议成员声明会在后面进行讨论。 协议类型可以继承自任意数量的其它协议。当一个协议类型继承自其它协议的时候,来自其它协议的所有要求会聚合在一起,而且采纳当前协议的类型必须符合所有的这些要求。关于如何使用协议继承的例子,请参阅 [协议继承](../chapter2/22_Protocols.md#protocol_inheritance)。 > 注意 -> 也可以使用协议合成类型来集合多个协议的一致性要求,请参阅 [协议合成类型](03_Types.md#protocol_composition_type) 和 [协议合成](../chapter2/22_Protocols.md#protocol_composition)。 +> 也可以使用协议合成类型来聚合多个协议的一致性要求,请参阅 [协议合成类型](03_Types.md#protocol_composition_type) 和 [协议合成](../chapter2/22_Protocols.md#protocol_composition)。 可以通过类型的扩展声明来采纳协议,从而为之前声明的类型添加协议一致性。在扩展中,必须实现所有采纳协议的要求。如果该类型已经实现了所有的要求,可以让这个扩展声明的主体留空。 @@ -855,12 +842,12 @@ protocol SomeProtocol: class { 协议可以通过在协议声明主体中引入一个协议属性声明,来声明符合的类型必须实现的属性。协议属性声明有一种特殊的变量声明形式: ```swift -var <#属性名#>: <#类型#> { get set } +var 属性名: 类型 { get set } ``` 同其它协议成员声明一样,这些属性声明仅仅针对符合该协议的类型声明了 getter 和 setter 要求,你不能在协议中直接实现 getter 和 setter。 -符合类型可以通过多种方式满足 getter 和 setter 要求。如果属性声明包含 `get` 和 `set` 关键字,符合类型就可以用可读写(实现了 getter 和 setter)的存储型变量属性或计算型属性来满足此要求,但是属性不能以常量属性或只读计算型属性实现。如果属性声明仅仅包含 `get` 关键字的话,它可以作为任意类型的属性被实现。关于如何实现协议中的属性要求的例子,请参阅 [属性要求](../chapter2/22_Protocols.md#property_requirements) +符合类型可以通过多种方式满足 getter 和 setter 要求。如果属性声明包含 `get` 和 `set` 关键字,符合类型就可以用存储型变量属性或可读可写的计算型属性来满足此要求,但是属性不能以常量属性或只读计算型属性实现。如果属性声明仅仅包含 `get` 关键字的话,它可以作为任意类型的属性被实现。关于如何实现协议中的属性要求的例子,请参阅 [属性要求](../chapter2/22_Protocols.md#property_requirements) 另请参阅 [变量声明](#variable_declaration)。 @@ -874,7 +861,7 @@ var <#属性名#>: <#类型#> { get set } 协议可以通过在协议声明主体中引入一个协议方法声明,来声明符合的类型必须实现的方法。协议方法声明和函数方法声明有着相同的形式,但有两项例外:它们不包括函数体,也不能包含默认参数。关于如何实现协议中的方法要求的例子,请参阅 [方法要求](../chapter2/22_Protocols.md#method_requirements)。 -使用 `static` 声明修饰符可以在协议声明中声明一个静态方法。结构体实现这些方法时使用 `static` 声明修饰符,类在实现这些方法时,除了使用 `static` 声明修饰符,还可以选择使用 `class` 声明修饰符。通过扩展实现时亦是如此。 +使用 `static` 声明修饰符可以在协议声明中声明一个类型方法。结构体实现这些方法时使用 `static` 声明修饰符,类在实现这些方法时,除了使用 `static` 声明修饰符,还可以选择使用 `class` 声明修饰符。通过扩展实现时亦是如此。 另请参阅 [函数声明](#function_declaration)。 @@ -907,7 +894,7 @@ var <#属性名#>: <#类型#> { get set } 协议可以通过在协议声明主体中引入一个协议下标声明,来声明符合的类型必须实现的下标。协议下标声明有一个特殊的下标声明形式: ```swift -subscript (<#参数列表#>) -> <#返回类型#> { get set } +subscript (参数列表) -> 返回类型 { get set } ``` 下标声明只为符合类型声明了 getter 和 setter 要求。如果下标声明包含 `get` 和 `set` 关键字,符合类型也必须实现 getter 和 setter 子句。如果下标声明只包含 `get` 关键字,符合类型必须实现 getter 子句,可以选择是否实现 setter 子句。 @@ -934,29 +921,29 @@ subscript (<#参数列表#>) -> <#返回类型#> { get set } ## 构造器声明 -构造器声明会为程序中的类,结构体或枚举引入构造器。构造器使用关键字 `init` 来声明,有两种基本形式。 +构造器声明会为程序中的类、结构体或枚举引入构造器。构造器使用关键字 `init` 来声明,有两种基本形式。 -结构体,枚举,类可以有任意数量的构造器,但是类的构造器具有不同的规则和行为。不同于结构体和枚举,类有两种构造器,即指定构造器和便利构造器,请参阅 [构造过程](../chapter2/14_Initialization.md)。 +结构体、枚举、类可以有任意数量的构造器,但是类的构造器具有不同的规则和行为。不同于结构体和枚举,类有两种构造器,即指定构造器和便利构造器,请参阅 [构造过程](../chapter2/14_Initialization.md)。 采用如下形式声明结构体和枚举的构造器,以及类的指定构造器: ```swift -init(<#参数列表#>) { - <#构造语句#> +init(参数列表) { + 构造语句 } ``` -类的指定构造器直接将类的所有属性初始化。它不能调用类中的其他构造器,如果类有超类,它还必须调用超类的一个指定构造器。如果该类从它的超类继承了属性,必须在调用超类的指定构造器后才能修改这些属性。 +类的指定构造器直接将类的所有属性初始化。它不能调用类中的其他构造器,如果类有超类,则必须调用超类的一个指定构造器。如果该类从它的超类继承了属性,必须在调用超类的指定构造器后才能修改这些属性。 -指定构造器只能在类声明的上下文中声明,不能在扩展声明中声明。 +指定构造器只能在类声明中声明,不能在扩展声明中声明。 -结构体和枚举的构造器可以调用其他已声明的构造器,委托其他构造器来进行部分或者全部构造过程。 +结构体和枚举的构造器可以调用其他已声明的构造器,从而委托其他构造器来进行部分或者全部构造过程。 要为类声明一个便利构造器,用 `convenience` 声明修饰符来标记构造器声明: ```swift -convenience init(<#参数列表#>) { - <#构造语句#> +convenience init(参数列表) { + 构造语句 } ``` @@ -966,12 +953,12 @@ convenience init(<#参数列表#>) { 默认情况下,超类中的构造器不会被子类继承。但是,如果子类的所有存储型属性都有默认值,而且子类自身没有定义任何构造器,它将继承超类的构造器。如果子类重写了超类的所有指定构造器,子类将继承超类的所有便利构造器。 -和方法,属性和下标一样,需要使用 `override` 声明修饰符标记重写的指定构造器。 +和方法、属性和下标一样,需要使用 `override` 声明修饰符标记重写的指定构造器。 > 注意 > 如果使用 `required` 声明修饰符标记一个构造器,在子类中重写这种构造器时,无需使用 `override` 修饰符。 -就像函数和方法,构造器也可以抛出或者重抛出错误,你可以在构造器参数列表的圆括号之后使用 `throws` 或 `rethrows` 关键字来表明相应的抛出行为。 +就像函数和方法,构造器也可以抛出或者重抛错误,你可以在构造器参数列表的圆括号之后使用 `throws` 或 `rethrows` 关键字来表明相应的抛出行为。 关于在不同类型中声明构造器的例子,请参阅 [构造过程](../chapter2/14_Initialization.md)。 @@ -1006,11 +993,11 @@ if let actualInstance = SomeStruct(input: "Hello") { } ``` -结构体或者枚举的可失败构造器可以在构造器实现中的任意位置返回 `nil`。 +可失败构造器可以在构造器实现中的任意位置返回 `nil`。 可失败构造器可以委托任意种类的构造器。非可失败可以委托其它非可失败构造器或者 `init!` 可失败构造器。非可失败构造器可以委托超类的 `init?` 可失败指定构造器,但是需要使用强制解包,例如 `super.init()!`。 -构造过程的失败通过构造器委托来传递。具体来说,如果可失败构造器委托的可失败构造器构造过程失败并返回 `nil`,那么该可失败构造器也会构造失败并隐式地返回 `nil`。如果非可失败构造器委托的 `init!` 可失败构造器构造失败并返回了 `nil`,那么会发生运行时错误(如同使用 `!` 操作符去强制解包一个值为 `nil` 的可选值)。 +构造过程失败通过构造器委托来传递。具体来说,如果可失败构造器委托的可失败构造器构造过程失败并返回 `nil`,那么该可失败构造器也会构造失败并隐式地返回 `nil`。如果非可失败构造器委托的 `init!` 可失败构造器构造失败并返回了 `nil`,那么会发生运行时错误(如同使用 `!` 操作符去强制解包一个值为 `nil` 的可选值)。 子类可以用任意种类的指定构造器重写超类的可失败指定构造器,但是只能用非可失败指定构造器重写超类的非可失败指定构造器。 @@ -1035,7 +1022,7 @@ if let actualInstance = SomeStruct(input: "Hello") { ```swift deinit { - <#语句#> + 语句 } ``` @@ -1055,21 +1042,21 @@ deinit { ## 扩展声明 -*扩展声明 (extension declaration)* 可以扩展一个现存的类,结构体,和枚举类型的行为。扩展声明使用关键字 `extension`,遵循如下格式: +*扩展声明 (extension declaration)* 可以扩展一个现存的类、结构体和枚举类型的行为。扩展声明使用关键字 `extension`,遵循如下格式: ```swift -extension <#类型名称#>: <#采纳的协议#> { - <#声明语句#> +extension 类型名称: 采纳的协议 { + 声明语句 } ``` -扩展声明体可包含零个或多个声明语句。这些声明语句可以包括计算型属性,计算型类型属性,实例方法,类型方法,构造器,下标声明,甚至其他结构体,类,和枚举声明。扩展声明不能包含析构器,协议声明,存储型属性,属性观察器,或其他扩展声明。关于扩展声明的详细讨论,以及各种扩展声明的例子,请参阅 [扩展](../chapter2/21_Extensions.md)。 +扩展声明体可包含零个或多个声明语句。这些声明语句可以包括计算型属性、计算型类型属性、实例方法、类型方法、构造器、下标声明,甚至其他结构体、类和枚举声明。扩展声明不能包含析构器、协议声明、存储型属性、属性观察器或其他扩展声明。关于扩展声明的详细讨论,以及各种扩展声明的例子,请参阅 [扩展](../chapter2/21_Extensions.md)。 -扩展声明可以为现存的类,结构体,枚举添加协议一致性,但是不能为类添加超类,因此,在扩展声明的类型名称的冒号后面仅能指定一个协议列表。 +扩展声明可以为现存的类、结构体、枚举添加协议一致性,但是不能为类添加超类,因此在扩展声明的类型名称的冒号后面仅能指定一个协议列表。 -现存类型的属性,方法,构造器不能在扩展中被重写。 +现存类型的属性、方法、构造器不能在扩展中被重写。 -扩展声明可以包含构造器声明。这意味着,如果被扩展的类型在其他模块中定义,构造器声明必须委托另一个在那个模块中声明的构造器,以此确保该类型能被正确地初始化。 +扩展声明可以包含构造器声明。这意味着,如果被扩展的类型在其他模块中定义,构造器声明必须委托另一个在那个模块中声明的构造器,以确保该类型能被正确地初始化。 > 扩展声明语法 @@ -1081,30 +1068,30 @@ extension <#类型名称#>: <#采纳的协议#> { ## 下标声明 -*下标声明 (subscript declaration)* 用于为特定类型的对象添加下标支持,通常也用于为访问集合,列表和序列中的元素提供语法便利。下标声明使用关键字 `subscript`,形式如下: +*下标声明 (subscript declaration)* 用于为特定类型的对象添加下标支持,通常也用于为访问集合、列表和序列中的元素提供语法便利。下标声明使用关键字 `subscript`,形式如下: ```swift -subscript (<#参数列表#>) -> <#返回类型#> { +subscript (参数列表) -> 返回类型 { get { - <#语句#> + 语句 } - set(<#setter 名称#>) { - <#语句#> + set(setter 名称) { + 语句 } } ``` -下标声明只能出现在类,结构体,枚举,扩展和协议声明的上下文中。 +下标声明只能出现在类、结构体、枚举、扩展和协议的声明中。 -参数列表指定一个或多个用于在相关类型的下标表达式中访问元素的索引(例如,表达式 `object[i]` 中的 `i`)。索引可以是任意类型,但是必须包含类型标注。返回类型指定被访问的元素的类型。 +参数列表指定一个或多个用于在相关类型的下标表达式中访问元素的索引(例如,表达式 `object[i]` 中的 `i`)。索引可以是任意类型,但是必须包含类型标注。返回类型指定了被访问的元素的类型。 和计算型属性一样,下标声明支持对元素的读写操作。getter 用于读取值,setter 用于写入值。setter 子句是可选的,当仅需要一个 getter 子句时,可以将二者都忽略,直接返回请求的值即可。但是,如果提供了 setter 子句,就必须提供 getter 子句。 圆括号以及其中的 setter 名称是可选的。如果提供了 setter 名称,它会作为 setter 的参数名称。如果不提供 setter 名称,那么 setter 的参数名称默认是 `value`。setter 名称的类型必须与返回类型相同。 -可以重载下标,只要参数列表或返回类型与先前不同即可。还可以重写继承自超类的下标,此时必须使用 `override` 声明修饰符声明被重写的下标。 +可以重载下标,只要参数列表或返回类型不同即可。还可以重写继承自超类的下标,此时必须使用 `override` 声明修饰符声明被重写的下标。 -同样可以在协议声明的上下文中声明下标,正如 [协议下标声明](#protocol_subscript_declaration) 中所述。 +同样可以在协议声明中声明下标,正如 [协议下标声明](#protocol_subscript_declaration) 中所述。 更多关于下标的信息和例子,请参阅 [下标](../chapter2/12_Subscripts.md)。 @@ -1131,9 +1118,9 @@ subscript (<#参数列表#>) -> <#返回类型#> { 下面的形式声明了一个新的中缀运算符: ```swift -infix operator <#运算符名称#> { - precedence <#优先级#> - associativity <#结合性#> +infix operator 运算符名称 { + precedence 优先级 + associativity 结合性 } ``` @@ -1143,14 +1130,14 @@ infix operator <#运算符名称#> { 运算符的优先级可以指定在没有括号包围的情况下,运算符与其运算对象如何结合。可以使用上下文相关的关键字 `precedence` 以及紧随其后的优先级数字来指定一个运算符的优先级。优先级可以是 `0` 到 `255` 之间的任何十进制整数。与十进制整数字面量不同的是,它不可以包含任何下划线字符。尽管优先级是一个特定的数字,但它仅用作与另一个运算符的优先级比较大小。也就是说,当两个运算符为同一个运算对象竞争时,例如 `2 + 3 * 5`,优先级更高的运算符将优先参与运算。 -运算符的结合性可以指定在没有括号包围的情况下,多个优先级相同的运算符将如何组合。可以使用上下文相关的关键字 `associativity` 以及紧随其后的结合性关键字来指定运算符的结合性,结合性关键字也是上下文相关的,包括 `left`,`right`,和 `none`。左结合运算符以从左到右的顺序进行组合。例如,减法运算符(`-`)具有左结合性,因此 `4 - 5 - 6` 以 `(4 - 5) - 6` 的形式组合,其结果为 `-7`。右结合运算符以从右到左的顺序组合,而设置为 `none` 的运算符不进行组合。具有相同优先级的非结合运算符,不可以互相邻接。例如,表达式 `1 < 2 < 3` 是非法的。 +运算符的结合性可以指定在没有括号包围的情况下,多个优先级相同的运算符将如何组合。可以使用上下文相关的关键字 `associativity` 以及紧随其后的结合性关键字来指定运算符的结合性,结合性关键字也是上下文相关的,包括 `left`、`right` 和 `none`。左结合运算符以从左到右的顺序进行组合。例如,减法运算符(`-`)具有左结合性,因此 `4 - 5 - 6` 以 `(4 - 5) - 6` 的形式组合,其结果为 `-7`。右结合运算符以从右到左的顺序组合,而设置为 `none` 的运算符不进行组合。具有相同优先级的非结合运算符,不可以互相邻接。例如,表达式 `1 < 2 < 3` 是非法的。 声明时不指定任何优先级或结合性的中缀运算符,优先级为 `100`,结合性为 `none`。 下面的形式声明了一个新的前缀运算符: ```swift -prefix operator <#运算符名称#> {} +prefix operator 运算符名称 {} ``` 出现在运算对象前边的前缀运算符是一元运算符,例如表达式 `!a` 中的前缀非运算符(`!`)。 @@ -1160,10 +1147,10 @@ prefix operator <#运算符名称#> {} 下面的形式声明了一个新的后缀运算符: ```swift -postfix operator <#运算符名称#> {} +postfix operator 运算符名称 {} ``` -紧跟在运算对象后边的后缀运算符是一元运算符,例如表达式 `i!` 中的后缀强制解包运算符(`!`)。 +紧跟在运算对象后边的后缀运算符是一元运算符,例如表达式 `a!` 中的后缀强制解包运算符(`!`)。 和前缀运算符一样,后缀运算符的声明中不指定优先级,而且后缀运算符是非结合的。 @@ -1206,7 +1193,7 @@ postfix operator <#运算符名称#> {} `final` -该修饰符用于修饰类或类中的属性,方法,以及下标。如果用它修饰一个类,那么这个类不能被继承。如果用它修饰类中的属性,方法或下标,那么它们不能在子类中被重写。 +该修饰符用于修饰类或类中的属性、方法以及下标。如果用它修饰一个类,那么这个类不能被继承。如果用它修饰类中的属性、方法或下标,那么它们不能在子类中被重写。 `lazy` @@ -1214,7 +1201,7 @@ postfix operator <#运算符名称#> {} `optional` -该修饰符用于修饰协议中的属性,方法,以及下标成员,表示符合类型不必实现这些成员要求。 +该修饰符用于修饰协议中的属性、方法以及下标成员,表示符合类型可以不实现这些成员要求。 只能将 `optional` 修饰符用于被 `objc` 特性标记的协议。这样一来,就只有类类型可以采纳并符合拥有可选成员要求的协议。关于如何使用 `optional` 修饰符,以及如何访问可选协议成员(比如,不确定符合类型是否已经实现了这些可选成员)的信息,请参阅 [可选协议要求](../chapter2/22_Protocols.md#optional_protocol_requirements)。 @@ -1229,7 +1216,7 @@ postfix operator <#运算符名称#> {} ### 访问控制级别 -Swift 提供了三个级别的访问控制:`public`,`internal` 和 `private`。可以使用以下任意一种访问级别修饰符来指定声明的访问级别。访问控制在 [访问控制](../chapter2/24_Access_Control.md) 中有详细讨论。 +Swift 提供了三个级别的访问控制:`public`、`internal` 和 `private`。可以使用以下任意一种访问级别修饰符来指定声明的访问级别。访问控制在 [访问控制](../chapter2/24_Access_Control.md) 中有详细讨论。 `public` From 443976cbbf9a977a985a40b6559b0f3afef9e4cf Mon Sep 17 00:00:00 2001 From: acevest Date: Wed, 25 May 2016 20:31:55 +0800 Subject: [PATCH 44/61] fix typo (#622) --- source/chapter2/01_The_Basics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/chapter2/01_The_Basics.md b/source/chapter2/01_The_Basics.md index a444e3ae..05fe9ba0 100755 --- a/source/chapter2/01_The_Basics.md +++ b/source/chapter2/01_The_Basics.md @@ -762,7 +762,7 @@ do { 如果你的代码在调试环境下触发了一个断言,比如你在 Xcode 中构建并运行一个应用,你可以清楚地看到不合法的状态发生在哪里并检查断言被触发时你的应用的状态。此外,断言允许你附加一条调试信息。 -你可以使用全局`assert(_:_file:line:)`函数来写一个断言。向这个函数传入一个结果为`true`或者`false`的表达式以及一条信息,当表达式的结果为`false`的时候这条信息会被显示: +你可以使用全局`assert(_:_:file:line:)`函数来写一个断言。向这个函数传入一个结果为`true`或者`false`的表达式以及一条信息,当表达式的结果为`false`的时候这条信息会被显示: ```swift let age = -3 From b39b8a522244287ef05cf1f54aa9c94f4f8a3a6b Mon Sep 17 00:00:00 2001 From: Hong Duan Date: Tue, 2 Aug 2016 10:34:52 +0800 Subject: [PATCH 45/61] Update 02_a_swift_tour.md --- source/chapter1/02_a_swift_tour.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/chapter1/02_a_swift_tour.md b/source/chapter1/02_a_swift_tour.md index b646e3d7..dd72ebc6 100755 --- a/source/chapter1/02_a_swift_tour.md +++ b/source/chapter1/02_a_swift_tour.md @@ -197,7 +197,7 @@ print(largest) ``` > 练习: -> 添加另一个变量来记录现在和之前最大数字的类型。 +> 添加另一个变量来记录最大数字的种类(kind),同时仍然记录这个最大数字的值。 使用`while`来重复运行一段代码直到不满足条件。循环条件也可以在结尾,保证能至少循环一次。 From 82d9cd01562a0fecc90c3d4afec3faf0d9017599 Mon Sep 17 00:00:00 2001 From: Hong Duan Date: Tue, 2 Aug 2016 16:11:27 +0800 Subject: [PATCH 46/61] Update 02_a_swift_tour.md --- source/chapter1/02_a_swift_tour.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/chapter1/02_a_swift_tour.md b/source/chapter1/02_a_swift_tour.md index dd72ebc6..0d5151d9 100755 --- a/source/chapter1/02_a_swift_tour.md +++ b/source/chapter1/02_a_swift_tour.md @@ -547,7 +547,7 @@ if let convertedRank = Rank(rawValue: 3) { } ``` -枚举的成员值是实际值,并不是原始值的另一种表达方法。实际上,以防原始值没有意义,你不需要设置。 +枚举的成员值是实际值,并不是原始值的另一种表达方法。实际上,如果没有比较有意义的原始值,你就不需要提供原始值。 ```swift enum Suit { From 990352db8ee3b328d1479ad3dfbffc371e2f34c6 Mon Sep 17 00:00:00 2001 From: Hong Duan Date: Wed, 3 Aug 2016 14:12:33 +0800 Subject: [PATCH 47/61] Update 02_a_swift_tour.md --- source/chapter1/02_a_swift_tour.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/chapter1/02_a_swift_tour.md b/source/chapter1/02_a_swift_tour.md index 0d5151d9..8e78f23b 100755 --- a/source/chapter1/02_a_swift_tour.md +++ b/source/chapter1/02_a_swift_tour.md @@ -615,7 +615,7 @@ case let .Failure(message): > 练习: > 给`ServerResponse`和`switch`添加第三种情况。 -注意如何从`ServerResponse`中提取日升和日落时间并用得到的值用来和`switch`的情况作比较。 +注意日升和日落时间是如何从`ServerResponse`中提取到并与`switch`的`case`相匹配的。 ## 协议和扩展 From 2baad57b6599dfa57e0f7a71f30cb8e63fda402e Mon Sep 17 00:00:00 2001 From: Hong Duan Date: Fri, 5 Aug 2016 11:16:21 +0800 Subject: [PATCH 48/61] Update 01_The_Basics.md --- source/chapter2/01_The_Basics.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source/chapter2/01_The_Basics.md b/source/chapter2/01_The_Basics.md index 05fe9ba0..bd48d27f 100755 --- a/source/chapter2/01_The_Basics.md +++ b/source/chapter2/01_The_Basics.md @@ -10,9 +10,9 @@ > 2.1 > 翻译:[Prayer](https://github.com/futantan) -> 校对:[shanks](http://codebuild.me),[overtrue](https://github.com/overtrue) -> -> 2.2 +> 校对:[shanks](http://codebuild.me),[overtrue](https://github.com/overtrue) +> +> 2.2 > 校对:[SketchK](https://github.com/SketchK) 2016-05-11 本页包含内容: @@ -747,7 +747,7 @@ do { 在此例中,`makeASandwich()`(做一个三明治)函数会抛出一个错误消息如果没有干净的盘子或者某个原料缺失。因为`makeASandwich()`抛出错误,函数调用被包裹在`try`表达式中。将函数包裹在一个`do`语句中,任何被抛出的错误会被传播到提供的`catch`从句中。 -如果没有错误被抛出, `eatASandwich()`函数会被调用。如果一个匹配`Error.OutOfCleanDishes`的错误被抛出,`washDishes`函数会被调用。如果一个匹配`Error.MissingIngredients`的错误被抛出,`buyGroceries(_:)`函数会随着被`catch`所捕捉到的关联值`[String]`被调用。 +如果没有错误被抛出, `eatASandwich()`函数会被调用。如果一个匹配`Error.OutOfCleanDishes`的错误被抛出,`washDishes`函数会被调用。如果一个匹配`Error.MissingIngredients`的错误被抛出,`buyGroceries(_:)`函数会被调用,并且使用`catch`所捕捉到的关联值`[String]`作为参数。 抛出,捕捉,以及传播错误会在[错误处理](./18_Error_Handling.html)章节详细说明。 @@ -793,5 +793,5 @@ assert(age >= 0) > 注意: 断言可能导致你的应用终止运行,所以你应当仔细设计你的代码来让非法条件不会出现。然而,在你的应用发布之前,有时候非法条件可能出现,这时使用断言可以快速发现问题。 - - + + From 1d46a3ec7663a34db57dd46002c2edc91d193a35 Mon Sep 17 00:00:00 2001 From: Hong Duan Date: Fri, 5 Aug 2016 14:47:26 +0800 Subject: [PATCH 49/61] Update 04_Collection_Types.md --- source/chapter2/04_Collection_Types.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source/chapter2/04_Collection_Types.md b/source/chapter2/04_Collection_Types.md index dd2d0567..68875522 100755 --- a/source/chapter2/04_Collection_Types.md +++ b/source/chapter2/04_Collection_Types.md @@ -9,11 +9,11 @@ > 翻译+校对:[JackAlan](https://github.com/AlanMelody) > 2.1 -> 校对:[shanks](http://codebuild.me) - -> 2.2 -> 校对:[SketchK](https://github.com/SketchK) 2016-05-11 - +> 校对:[shanks](http://codebuild.me) + +> 2.2 +> 校对:[SketchK](https://github.com/SketchK) 2016-05-11 + 本页包含内容: @@ -410,7 +410,7 @@ for genre in favoriteGenres { 更多关于`for-in`循环的信息,参见[For 循环](./05_Control_Flow.html#for_loops)。 -Swift 的`Set`类型没有确定的顺序,为了按照特定顺序来遍历一个`Set`中的值可以使用`sort()`方法,它将根据提供的序列返回一个有序集合. +Swift 的`Set`类型没有确定的顺序,为了按照特定顺序来遍历一个`Set`中的值可以使用`sort()`方法,它将返回一个有序数组,这个数组的元素排列顺序由操作符'<'对元素进行比较的结果来确定. ```swift for genre in favoriteGenres.sort() { From 083aa8b78b68346d126296436b40e9755e0fcaaa Mon Sep 17 00:00:00 2001 From: Hong Duan Date: Fri, 5 Aug 2016 16:02:56 +0800 Subject: [PATCH 50/61] Update 05_Control_Flow.md --- source/chapter2/05_Control_Flow.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/chapter2/05_Control_Flow.md b/source/chapter2/05_Control_Flow.md index c3e2e3d3..cd874fac 100755 --- a/source/chapter2/05_Control_Flow.md +++ b/source/chapter2/05_Control_Flow.md @@ -162,7 +162,7 @@ while square < finalSquare { print("Game over!") ``` -本例中使用了最简单的方法来模拟掷骰子。 `diceRoll`的值并不是一个随机数,而是以`0`为初始值,之后每一次`while`循环,`diceRoll`的值使用前置自增操作符(`++i`)来自增 1 ,然后检测是否超出了最大值。任何时候如果`diceRoll`的值等于 7 时,就超过了骰子的最大值,会被重置为`1`。所以`diceRoll`的取值顺序会一直是`1`。因此,`diceRoll` 所有的值只可能是 `1` ,`2`,`3`,`4`,`5`,`6`,`1`,`2` 等。 +本例中使用了最简单的方法来模拟掷骰子。 `diceRoll`的值并不是一个随机数,而是以`0`为初始值,之后每一次`while`循环,`diceRoll`的值增加 1 ,然后检测是否超出了最大值。任何时候如果`diceRoll`的值等于 7 时,就超过了骰子的最大值,会被重置为`1`。所以`diceRoll`的取值顺序会一直是`1`。因此,`diceRoll` 所有的值只可能是 `1` ,`2`,`3`,`4`,`5`,`6`,`1`,`2` 等。 掷完骰子后,玩家向前移动`diceRoll`个方格,如果玩家移动超过了第 25 个方格,这个时候游戏结束,相应地,代码会在`square`增加`board[square]`的值向前或向后移动(遇到了梯子或者蛇)之前,检测`square`的值是否小于`board`的`count`属性。 @@ -708,7 +708,7 @@ greet(["name": "Jane", "location": "Cupertino"]) 如果条件不被满足,在`else`分支上的代码就会被执行。这个分支必须转移控制以退出`guard`语句出现的代码段。它可以用控制转移语句如`return`,`break`,`continue`或者`throw`做这件事,或者调用一个不返回的方法或函数,例如`fatalError()`。 -相比于可以实现同样功能的`if`语句,按需使用`guard`语句会提升我们代码的可靠性。它可以使你的代码连贯的被执行而不需要将它包在`else`块中,它可以使你处理违反要求的代码接近要求。 +相比于可以实现同样功能的`if`语句,按需使用`guard`语句会提升我们代码的可靠性。它可以使你的代码连贯的被执行而不需要将它包在`else`块中,它可以使你用于处理违反某个条件的代码紧邻于对该条件进行判断的地方。 ## 检测 API 可用性 From 52151afb7f4a3f3ae9015b7521a0c1a833f1b98b Mon Sep 17 00:00:00 2001 From: Hong Duan Date: Mon, 8 Aug 2016 11:36:48 +0800 Subject: [PATCH 51/61] Update 07_Closures.md (#634) * Update 07_Closures.md * Update 09_Classes_and_Structures.md --- source/chapter2/07_Closures.md | 2 +- source/chapter2/09_Classes_and_Structures.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/chapter2/07_Closures.md b/source/chapter2/07_Closures.md index 90480b7c..3a5ebf9b 100755 --- a/source/chapter2/07_Closures.md +++ b/source/chapter2/07_Closures.md @@ -231,7 +231,7 @@ let strings = numbers.map { `map(_:)`为数组中每一个元素调用了闭包表达式。您不需要指定闭包的输入参数`number`的类型,因为可以通过要映射的数组类型进行推断。 -在该例中,局部变量`number`的值由闭包中的`number`参数获得,因此可以在闭包函数体内对其进行修改,(闭包或者函数的参数总是固定的),闭包表达式指定了返回类型为`String`,以表明存储映射值的新数组类型为`String`。 +在该例中,局部变量`number`的值由闭包中的`number`参数获得,因此可以在闭包函数体内对其进行修改,(闭包或者函数的参数总是常量),闭包表达式指定了返回类型为`String`,以表明存储映射值的新数组类型为`String`。 闭包表达式在每次被调用的时候创建了一个叫做`output`的字符串并返回。其使用求余运算符(`number % 10`)计算最后一位数字并利用`digitNames`字典获取所映射的字符串。 diff --git a/source/chapter2/09_Classes_and_Structures.md b/source/chapter2/09_Classes_and_Structures.md index f1dca69e..ac454f4d 100755 --- a/source/chapter2/09_Classes_and_Structures.md +++ b/source/chapter2/09_Classes_and_Structures.md @@ -36,7 +36,7 @@ Swift 中类和结构体有很多共同点。共同处在于: * 定义属性用于存储值 * 定义方法用于提供功能 -* 定义附属脚本用于访问值 +* 定义下标操作使得可以通过下标语法来访问实例所包含的值 * 定义构造器用于生成初始化值 * 通过扩展以增加默认实现的功能 * 实现协议以提供某种标准功能 From 20b60b4224a9b1f4cdb33d04db948793f7e0df50 Mon Sep 17 00:00:00 2001 From: Hong Duan Date: Wed, 10 Aug 2016 13:23:37 +0800 Subject: [PATCH 52/61] Patch (#635) * Update 14_Initialization.md * Update 21_Extensions.md * Update 22_Protocols.md --- source/chapter2/14_Initialization.md | 4 ++-- source/chapter2/21_Extensions.md | 2 +- source/chapter2/22_Protocols.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/source/chapter2/14_Initialization.md b/source/chapter2/14_Initialization.md index 37d3fb76..16f73f68 100755 --- a/source/chapter2/14_Initialization.md +++ b/source/chapter2/14_Initialization.md @@ -610,7 +610,7 @@ let mysteryMeat = Food() // mysteryMeat 的名字是 [Unnamed] ``` -类层级中的第二个类是`Food`的子类`RecipeIngredient`。`RecipeIngredient`类构建了食谱中的一味调味剂。它引入了`Int`类型的属性`quantity`(以及从`Food`继承过来的`name`属性),并且定义了两个构造器来创建`RecipeIngredient`实例: +类层级中的第二个类是`Food`的子类`RecipeIngredient`。`RecipeIngredient`类用来表示食谱中的一项原料。它引入了`Int`类型的属性`quantity`(以及从`Food`继承过来的`name`属性),并且定义了两个构造器来创建`RecipeIngredient`实例: ```swift class RecipeIngredient: Food { @@ -647,7 +647,7 @@ let oneBacon = RecipeIngredient(name: "Bacon") let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6) ``` -类层级中第三个也是最后一个类是`RecipeIngredient`的子类,叫做`ShoppingListItem`。这个类构建了购物单中出现的某一种调味料。 +类层级中第三个也是最后一个类是`RecipeIngredient`的子类,叫做`ShoppingListItem`。这个类构建了购物单中出现的某一种食谱原料。 购物单中的每一项总是从未购买状态开始的。为了呈现这一事实,`ShoppingListItem`引入了一个布尔类型的属性`purchased`,它的默认值是`false`。`ShoppingListItem`还添加了一个计算型属性`description`,它提供了关于`ShoppingListItem`实例的一些文字描述: diff --git a/source/chapter2/21_Extensions.md b/source/chapter2/21_Extensions.md index 66433c9a..1ed93023 100644 --- a/source/chapter2/21_Extensions.md +++ b/source/chapter2/21_Extensions.md @@ -297,7 +297,7 @@ func printIntegerKinds(numbers: [Int]) { print("") } printIntegerKinds([3, 19, -27, 0, -6, 0, 7]) -// 打印 “+ + - 0 - 0 +” +// 打印 “+ + - 0 - 0 + ” ``` 函数 `printIntegerKinds(_:)` 接受一个 `Int` 数组,然后对该数组进行迭代。在每次迭代过程中,对当前整数的计算型属性 `kind` 的值进行评估,并打印出适当的描述。 diff --git a/source/chapter2/22_Protocols.md b/source/chapter2/22_Protocols.md index 6fefd5ba..ad3a04ee 100644 --- a/source/chapter2/22_Protocols.md +++ b/source/chapter2/22_Protocols.md @@ -798,7 +798,7 @@ for _ in 1...4 { // 12 ``` -上述代码新建了一个 `Counter` 实例,并将它的数据源设置为一个 `TreeSource` 的实例,然后调用 `increment()` 方法四次。和预期一样,每次调用都会将 `count` 的值增加 `3`. +上述代码新建了一个 `Counter` 实例,并将它的数据源设置为一个 `ThreeSource` 的实例,然后调用 `increment()` 方法四次。和预期一样,每次调用都会将 `count` 的值增加 `3`. 下面是一个更为复杂的数据源 `TowardsZeroSource`,它将使得最后的值变为 `0`: From 9d4403eada0a7737555e3afdfe1cdccb5f19e817 Mon Sep 17 00:00:00 2001 From: Hong Duan Date: Wed, 10 Aug 2016 15:12:24 +0800 Subject: [PATCH 53/61] Update 07_Patterns.md --- source/chapter3/07_Patterns.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/chapter3/07_Patterns.md b/source/chapter3/07_Patterns.md index 1ec2e5a3..058c338e 100755 --- a/source/chapter3/07_Patterns.md +++ b/source/chapter3/07_Patterns.md @@ -233,7 +233,7 @@ case ("0", "0"): default: print("The point is at (\(point.0), \(point.1)).") } -// 打印 “(1, 2) is near the origin.” +// 打印 “The point is at (1, 2).” ``` > 表达式模式语法 From f928b61d09cd044474fd9ea9be41722738a633d8 Mon Sep 17 00:00:00 2001 From: Wang Yang Date: Tue, 30 Aug 2016 15:58:53 +0800 Subject: [PATCH 54/61] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=AF=AD=E8=A8=80?= =?UTF-8?q?=E5=8F=82=E8=80=83-=E8=AF=AD=E5=8F=A5-guard=E8=AF=AD=E5=8F=A5?= =?UTF-8?q?=E7=9A=84=E7=BF=BB=E8=AF=91=20(#637)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/chapter3/10_Statements.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/source/chapter3/10_Statements.md b/source/chapter3/10_Statements.md index f623b6a6..0b7fd6a8 100755 --- a/source/chapter3/10_Statements.md +++ b/source/chapter3/10_Statements.md @@ -233,8 +233,8 @@ guard 条件 else { 语句 } ``` - -`guard` 语句中条件的结果必须符合 `BooleanType` 协议,而且条件语句可以使用可选绑定,请参阅 [可选绑定](../chapter2/01_The_Basics.md#optional_binding)。 + +`guard` 语句中条件的结果必须符合 `BooleanType` 协议。另外,条件也可以是一条可选绑定,请参阅 [可选绑定](../chapter2/01_The_Basics.html#optional_binding)。 在 `guard` 语句中进行可选绑定的常量或者变量,其可用范围从声明开始直到作用域结束。 @@ -249,7 +249,7 @@ guard 条件 else { > guard 语句语法 -> *guard 语句* → **guard** [*条件子句*](#condition-clause) **else** [*代码块*](05_Declarations.md#code-block) +> *guard 语句* → **guard** [*条件子句*](#condition-clause) **else** [*代码块*](05_Declarations.html#code-block) ### Switch 语句 @@ -663,3 +663,5 @@ if #available(平台名称 版本, ..., *) { > *平台版本* → [十进制数字](02_Lexical_Structure.md#decimal-digits) > *平台版本* → [十进制数字](02_Lexical_Structure.md#decimal-digits) **.** [十进制数字](02_Lexical_Structure.md#decimal-digits) > *平台版本* → [十进制数字](02_Lexical_Structure.md#decimal-digits) **.** [十进制数字](02_Lexical_Structure.md#decimal-digits) **.** [十进制数字](02_Lexical_Structure.md#decimal-digits) + + From 0e160b98c56b44d38cee45d63c716620a7272eaa Mon Sep 17 00:00:00 2001 From: chenmingjia <564575471@qq.com> Date: Sun, 11 Sep 2016 17:18:21 +0800 Subject: [PATCH 55/61] [Translate] Translate Generic Parameters and Arguments in Swift 3.0. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Swift 3.0 Generic Parameters and Arguments 已经补充完毕,主要是 Generic Where Clauses 部分的更新 --- .../08_Generic_Parameters_and_Arguments.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/source/chapter3/08_Generic_Parameters_and_Arguments.md b/source/chapter3/08_Generic_Parameters_and_Arguments.md index 40070f67..84e92707 100755 --- a/source/chapter3/08_Generic_Parameters_and_Arguments.md +++ b/source/chapter3/08_Generic_Parameters_and_Arguments.md @@ -8,6 +8,9 @@ > 2.0 > 翻译+校对:[wardenNScaiyi](https:github.com/wardenNScaiyi) +> 3.0 +> 翻译+校对:[chenmingjia](https:github.com/chenmingjia) + 本页包含内容: - [泛型形参子句](#generic_parameter) @@ -21,10 +24,9 @@ ## 泛型形参子句 -泛型形参子句指定泛型类型或函数的类型形参,以及这些参数相关的约束和要求。泛型形参子句用尖括号(`<>`)包住,并且有以下两种形式: +泛型形参子句指定泛型类型或函数的类型形参,以及这些参数相关的约束和要求。泛型形参子句用尖括号(`<>`)包住,形式如下: > <`泛型形参列表`> -> <`泛型形参列表` where `类型要求`> 泛型形参列表中泛型形参用逗号分开,其中每一个采用以下形式: @@ -36,7 +38,7 @@ ```swift -func simpleMax(x: T, _ y: T) -> T { +func simpleMax(_ x: T, _ y: T) -> T { if x < y { return y } @@ -54,18 +56,19 @@ simpleMax(3.14159, 2.71828) // T 被推断为 Double 类型 ### Where 子句 -要想对类型形参及其关联类型指定额外要求,可以在泛型形参列表之后添加 `where` 子句。`where` 子句由关键字 `where` 及其后的用逗号分隔的一个或多个要求组成。 +要想对类型形参及其关联类型指定额外要求,可以在函数体或者类型的大括号之前添加 `where` 子句。`where` 子句由关键字 `where` 及其后的用逗号分隔的一个或多个要求组成。 -`where` 子句中的要求用于指明该类型形参继承自某个类或符合某个协议或协议组合。尽管 `where` 子句提供了语法糖使其有助于表达类型形参上的简单约束(如 `T: Comparable` 等同于 `T where T: Comparable`,等等),但是依然可以用来对类型形参及其关联类型提供更复杂的约束。如,`` 表示泛型类型 `T` 继承自类 `C` 且符合协议 `P`。 +> `where` : `类型要求` -如上所述,可以强制约束类型形参的关联类型符合某个协议。例如 `` 表示 `S` 符合 `SequenceType` 协议,而且 `S` 的关联类型 `S.Generator.Element` 符合 `Eauatable` 协议。这种约束确保了序列中的每个元素都是符合 `Equatable` 协议的。 +`where` 子句中的要求用于指明该类型形参继承自某个类或符合某个协议或协议组合。尽管 `where` 子句提供了语法糖使其有助于表达类型形参上的简单约束(如 `` 等同于 ` where T: Comparable`,等等),但是依然可以用来对类型形参及其关联类型提供更复杂的约束,例如你可以强制形参的关联类型遵守协议,如,` where S.Iterator.Element: Equatable` 表示泛型类型 `S` 遵守`Sequence`协议并且关联类型`S.Iterator.Element`遵守`Equatable`协议,这个约束确保队列的每一个元素都是符合 `Equatable` 协议的。 -也可以用操作符 `==` 来指定两个类型必须相同。例如,泛型形参子句 `` 表示 `S1` 和 `S2` 必须都符合 `SequenceType` 协议,而且两个序列中的元素类型必须相同。 +也可以用操作符 `==` 来指定两个类型必须相同。例如,泛型形参子句 ` where S1.Iterator.Element == S2.Iterator.Element` 表示 `S1` 和 `S2` 必须都符合 `SequenceType` 协议,而且两个序列中的元素类型必须相同。 当然,替代类型形参的类型实参必须满足所有的约束和要求。 泛型函数或构造器可以重载,但在泛型形参子句中的类型形参必须有不同的约束或要求,抑或二者皆不同。当调用重载的泛型函数或构造器时,编译器会根据这些约束来决定调用哪个重载函数或构造器。 +更多关于泛型where从句的信息和关于泛型函数声明的例子,可以看一看 [泛型where子句](https://github.com/numbbbbb/the-swift-programming-language-in-chinese/blob/gh-pages/source/chapter2/23_Generics.md#where_clauses) > 泛型形参子句语法 @@ -99,7 +102,7 @@ simpleMax(3.14159, 2.71828) // T 被推断为 Double 类型 > <`泛型实参列表`> -泛型实参列表中类型实参用逗号分开。类型实参是实际具体类型的名字,用来替代泛型类型的泛型形参子句中的相应的类型形参。从而得到泛型类型的一个特化版本。例如,Swift 标准库中的泛型字典类型定义如下: +泛型实参列表中类型实参用逗号分开。类型实参是实际具体类型的名字,用来替代泛型类型的泛型形参子句中的相应的类型形参。从而得到泛型类型的一个特化版本。例如,Swift 标准库中的泛型字典类型的的简化定义如下: ```swift struct Dictionary: CollectionType, DictionaryLiteralConvertible { From d46f2c231465fd5eac024fc6d1ab45c3f5a194b7 Mon Sep 17 00:00:00 2001 From: chenmingjia <564575471@qq.com> Date: Sun, 11 Sep 2016 17:44:31 +0800 Subject: [PATCH 56/61] [Translate] Translate Expressions in Swift 3.0. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Swift 3.0 Expressions 已经补充完毕,主要是 Dynamic Type Expression和Selector Expression 部分的更新 --- source/chapter3/04_Expressions.md | 48 ++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/source/chapter3/04_Expressions.md b/source/chapter3/04_Expressions.md index 83f70043..26258390 100644 --- a/source/chapter3/04_Expressions.md +++ b/source/chapter3/04_Expressions.md @@ -14,6 +14,9 @@ > 2.2 > 校对:[175](https://github.com/Brian175) +> 3.0 +> 翻译+校对:[chenmingjia](https://github.com/chenmingjia) + 本页包含内容: - [前缀表达式](#prefix_expressions) @@ -530,41 +533,52 @@ x = .AnotherValue ### 选择器表达式 -可通过选择器表达式来获取引用 Objective-C 方法的选择器。 +选择器表达式可以让你通过选择器来引用在Objective-C中方法(method)和属性(property)的setter和getter方法。 > \#selector(方法名) +\#selector(getter: 属性名) +\#selector(setter: 属性名) -方法名必须是存在于 Objective-C 运行时中的方法的引用。选择器表达式的返回值是一个 `Selector` 类型的实例。例如: +方法名和属性名必须是存在于 Objective-C 运行时中的方法和属性的引用。选择器表达式的返回值是一个 Selector 类型的实例。例如: ```swift class SomeClass: NSObject { + let property: String @objc(doSomethingWithInt:) - func doSomething(x: Int) { } + func doSomething(_ x: Int) { } + + init(property: String) { + self.property = property + } } -let x = SomeClass() -let selector = #selector(x.doSomething(_:)) +let selectorForMethod = #selector(SomeClass.doSomething(_:)) +let selectorForPropertyGetter = #selector(getter: SomeClass.property) ``` +当为属性的getter创建选择器时,属性名可以是变量属性或者常量属性的引用。但是当为属性的setter创建选择器时,属性名只可以是对变量属性的引用。 -具有相同方法名但类型不同的方法可以使用 `as` 操作符来区分: +方法名称可以包含圆括号来进行分组,并使用as 操作符来区分具有相同方法名但类型不同的方法, 例如: ```swift extension SomeClass { @objc(doSomethingWithString:) - func doSomething(x: String) { } + func doSomething(_ x: String) { } } -let anotherSelector = #selector(x.doSomething(_:) as (String) -> Void) +let anotherSelector = #selector(SomeClass.doSomething(_:) as (SomeClass) -> (String) -> Void) ``` -由于选择器是在编译时创建的,因此编译器可以检查方法是否存在,以及方法是否在运行时暴露给了 Objective-C 。 +由于选择器是在编译时创建的,因此编译器可以检查方法或者属性是否存在,以及是否在运行时暴露给了 Objective-C 。 > 注意 -> 虽然方法名是个表达式,但是它不会被求值。 +> 虽然方法名或者属性名是个表达式,但是它不会被求值。 -更多关于如何在 Swift 代码中使用选择器来与 Objective-C API 进行交互的信息,请参阅 [*Using Swift with Cocoa and Objective-C*](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中的 [*Objective-C Selectors*](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html#//apple_ref/doc/uid/TP40014216-CH4-ID59) 部分。 +更多关于如何在 Swift 代码中使用选择器来与 Objective-C API 进行交互的信息,请参阅 [Using Swift with Cocoa and Objective-C (Swift 3)](https://developer.apple.com/library/prerelease/content/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中[Objective-C Selectors](https://developer.apple.com/library/prerelease/content/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html#//apple_ref/doc/uid/TP40014216-CH4-ID59)部分。 > 选择器表达式语法 -> *选择器表达式* → __#selector__ **(** [*表达式*](#expression) **)** +> *选择器表达式* → __#selector__ **(** [*表达式*](#expression) **)** +> *选择器表达式* → __#selector__ **(** [*getter:表达式*](#expression) **)** +> *选择器表达式* → __#selector__ **(** [*setter:表达式*](#expression) **)** + ## 后缀表达式 @@ -752,11 +766,11 @@ let x = [10, 3, 20, 15, 4] ### dynamicType 表达式 -`dynamicType` 表达式由某个表达式紧跟 `.dynamicType` 组成,其形式如下: +dynamicType 表达式由类似[函数调用表达式(Function Call Expression)](#function-call-expression)的特殊语法表达式组成,形式如下: -> `表达式`.dynamicType +> type(of:`表达式`) -上述形式中的表达式不能是类型名。`dynamicType` 表达式会返回某个实例在运行时的类型,具体请看下面的例子: +上述形式中的表达式不能是类型名。type(of:) 表达式会返回某个实例在运行时的类型,具体请看下面的例子: ```swift class SomeBaseClass { @@ -772,13 +786,13 @@ class SomeSubClass: SomeBaseClass { let someInstance: SomeBaseClass = SomeSubClass() // someInstance 在编译时的静态类型为 SomeBaseClass, // 在运行时的动态类型为 SomeSubClass -someInstance.dynamicType.printClassName() +type(of: someInstance).printClassName() // 打印 “SomeSubClass” ``` > 动态类型表达式语法 -> *动态类型表达式* → [*后缀表达式*](#postfix-expression) **.** **dynamicType** +> *动态类型表达式* → type(of:表达式) **.** **dynamicType** ### 下标表达式 From 49cdfb73472b517079600e9053f5b34a3add8f23 Mon Sep 17 00:00:00 2001 From: chenmingbiao Date: Mon, 12 Sep 2016 18:11:08 +0800 Subject: [PATCH 57/61] =?UTF-8?q?=E6=A0=A1=E5=AF=B9=20Strings=20and=20Char?= =?UTF-8?q?acters=20=E7=AB=A0=E8=8A=82=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/chapter2/03_Strings_and_Characters.md | 71 +++++++++++--------- 1 file changed, 38 insertions(+), 33 deletions(-) diff --git a/source/chapter2/03_Strings_and_Characters.md b/source/chapter2/03_Strings_and_Characters.md index e86dc29f..4e2e63c4 100755 --- a/source/chapter2/03_Strings_and_Characters.md +++ b/source/chapter2/03_Strings_and_Characters.md @@ -13,7 +13,10 @@ > 校对:[shanks](http://codebuild.me), [Realank](https://github.com/Realank), > 2.2 -> 校对:[SketchK](https://github.com/SketchK) 2016-05-11 +> 校对:[SketchK](https://github.com/SketchK) + +> 3.0 +> 校对:[CMB](https://github.com/chenmingbiao) 本页包含内容: @@ -30,11 +33,12 @@ - [比较字符串](#comparing_strings) - [字符串的 Unicode 表示形式](#unicode_representations_of_strings) - `String`是例如"hello, world","albatross"这样的有序的`Character`(字符)类型的值的集合。通过`String`类型来表示。 -一个`String`的内容可以用许多方式读取,它包括一个`Character`值的集合。 +一个`String`的内容可以用许多方式读取,它包括一个`Character`值的集合。 + 创建和操作字符串的语法与 C 语言中字符串操作相似,轻量并且易读。 -字符串连接操作只需要简单地通过`+`符号将两个字符串相连即可。与 Swift 中其他值一样,能否更改字符串的值,取决于其被定义为常量还是变量。你也可以在字符串内插过程中使用字符串插入常量、变量、字面量表达成更长的字符串,这样可以很容易的创建自定义的字符串值,进行展示、存储以及打印。 +字符串连接操作只需要简单地通过`+`符号将两个字符串相连即可。与 Swift 中其他值一样,能否更改字符串的值,取决于其被定义为常量还是变量。你也可以在字符串内插过程中使用字符串插入常量、变量、字面量表达成更长的字符串,这样可以很容易的创建自定义的字符串值,进行展示、存储以及打印。 + 尽管语法简易,但`String`类型是一种快速、现代化的字符串实现。 每一个字符串都是由编码无关的 Unicode 字符组成,并支持访问字符的多种 Unicode 表示形式(representations)。 @@ -43,7 +47,6 @@ > 更多关于在 Foundation 和 Cocoa 中使用`String`的信息请查看 *[Using Swift with Cocoa and Objective-C (Swift 2.1)](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216)*。 - ## 字符串字面量(String Literals) @@ -80,6 +83,7 @@ if emptyString.isEmpty { // 打印输出:"Nothing to see here" ``` + ## 字符串可变性 (String Mutability) @@ -98,6 +102,7 @@ constantString += " and another Highlander" > 注意: 在 Objective-C 和 Cocoa 中,您需要通过选择两个不同的类(`NSString`和`NSMutableString`)来指定字符串是否可以被修改。 + ## 字符串是值类型(Strings Are Value Types) @@ -112,6 +117,7 @@ Swift 默认字符串拷贝的方式保证了在函数/方法中传递的是字 在实际编译时,Swift 编译器会优化字符串的使用,使实际的复制只发生在绝对必要的情况下,这意味着您将字符串作为值类型的同时可以获得极高的性能。 + ## 使用字符(Working with Characters) @@ -144,6 +150,7 @@ print(catString) // 打印输出:"Cat!🐱" ``` + ## 连接字符串和字符 (Concatenating Strings and Characters) @@ -206,6 +213,7 @@ Unicode 是一个国际标准,用于文本的编码和表示。 它使您可以用标准格式表示来自任意语言几乎所有的字符,并能够对文本文件或网页这样的外部资源中的字符进行读写操作。 Swift 的`String`和`Character`类型是完全兼容 Unicode 标准的。 + ### Unicode 标量(Unicode Scalars) @@ -326,31 +334,31 @@ print("the number of characters in \(word) is \(word.characters.count)") 使用`startIndex`属性可以获取一个`String`的第一个`Character`的索引。使用`endIndex`属性可以获取最后一个`Character`的后一个位置的索引。因此,`endIndex`属性不能作为一个字符串的有效下标。如果`String`是空串,`startIndex`和`endIndex`是相等的。 -通过调用`String.Index`的`predecessor()`方法,可以立即得到前面一个索引,调用`successor()`方法可以立即得到后面一个索引。任何一个`String`的索引都可以通过锁链作用的这些方法来获取另一个索引,也可以调用`advancedBy(_:)`方法来获取。但如果尝试获取出界的字符串索引,就会抛出一个运行时错误。 +通过调用 `String` 的 `index(before:)` 或 `index(after:)` 方法,可以立即得到前面或后面的一个索引。您还可以通过调用 `index(_:offsetBy:)` 方法来获取对应偏移量的索引,这种方式可以避免多次调用 `index(before:)` 或 `index(after:)` 方法。 -你可以使用下标语法来访问`String`特定索引的`Character`。 +你可以使用下标语法来访问 `String` 特定索引的 `Character`。 ```swift let greeting = "Guten Tag!" greeting[greeting.startIndex] // G -greeting[greeting.endIndex.predecessor()] +greeting[greeting.index(before: greeting.endIndex)] // ! -greeting[greeting.startIndex.successor()] +greeting[greeting.index(after: greeting.startIndex)] // u -let index = greeting.startIndex.advancedBy(7) +let index = greeting.index(greeting.startIndex, offsetBy: 7) greeting[index] // a ``` -试图获取越界索引对应的`Character`,将引发一个运行时错误。 +试图获取越界索引对应的 `Character`,将引发一个运行时错误。 ```swift greeting[greeting.endIndex] // error -greeting.endIndex.successor() // error +greeting.index(after: endIndex) // error ``` -使用`characters`属性的`indices`属性会创建一个包含全部索引的范围(`Range`),用来在一个字符串中访问单个字符。 +使用 `characters` 属性的 `indices` 属性会创建一个包含全部索引的范围(`Range`),用来在一个字符串中访问单个字符。 ```swift for index in greeting.characters.indices { @@ -359,39 +367,37 @@ for index in greeting.characters.indices { // 打印输出 "G u t e n T a g ! " ``` +> 注意: +> 您可以使用 `startIndex` 和 `endIndex` 属性或者 `index(before:)` 、`index(after:)` 和 `index(_:offsetBy:)` 方法在任意一个确认的 `Collection`协议类型里面,如上文所示是使用在 `String` 中,您也可以使用在 `Array`,`Dictionary` 和 `Set`。 + ### 插入和删除 (Inserting and Removing) -调用`insert(_:atIndex:)`方法可以在一个字符串的指定索引插入一个字符。 +调用 `insert(_:atIndex:)` 方法可以在一个字符串的指定索引插入一个字符,调用 `insert(contentsOf:at:)` 方法可以在一个字符串的指定索引插入一个段字符串。 ```swift var welcome = "hello" -welcome.insert("!", atIndex: welcome.endIndex) -// welcome now 现在等于 "hello!" +welcome.insert("!", at: welcome.endIndex) +// welcome 变量现在等于 "hello!" + +welcome.insert(contentsOf:" there".characters, at: welcome.index(before: welcome.endIndex)) +// welcome 变量现在等于 "hello there!" ``` -调用`insertContentsOf(_:at:)`方法可以在一个字符串的指定索引插入一个字符串。 +调用 `remove(at:)` 方法可以在一个字符串的指定索引删除一个字符,调用 `removeSubrange(_:)` 方法可以在一个字符串的指定索引删除一个子字符串。 ```swift -welcome.insertContentsOf(" there".characters, at: welcome.endIndex.predecessor()) -// welcome 现在等于 "hello there!" -``` - -调用`removeAtIndex(_:)`方法可以在一个字符串的指定索引删除一个字符。 - -```swift -welcome.removeAtIndex(welcome.endIndex.predecessor()) +welcome.remove(at: welcome.index(before: welcome.endIndex)) // welcome 现在等于 "hello there" -``` - -调用`removeRange(_:)`方法可以在一个字符串的指定索引删除一个子字符串。 - -```swift -let range = welcome.endIndex.advancedBy(-6).. 注意: +> 您可以使用 `insert(_:at:)`、`insert(contentsOf:at:)`、`remove(at:)` 和 `removeSubrange(_:)` 方法在任意一个确认的 `RangeReplaceableCollection` 协议类型里面,如上文所示是使用在 `String` 中,您也可以使用在 `Array`,`Dictionary` 和 `Set`。 + ## 比较字符串 (Comparing Strings) @@ -523,7 +529,6 @@ Swift 提供了几种不同的方式来访问字符串的 Unicode 表示形式 let dogString = "Dog‼🐶" ``` - ### UTF-8 表示 From 3aa5844ff8b4558072bf38aaabd9847a68975f91 Mon Sep 17 00:00:00 2001 From: chenmingbiao Date: Mon, 12 Sep 2016 18:15:38 +0800 Subject: [PATCH 58/61] fix --- source/chapter2/03_Strings_and_Characters.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/chapter2/03_Strings_and_Characters.md b/source/chapter2/03_Strings_and_Characters.md index 4e2e63c4..d13f4a99 100755 --- a/source/chapter2/03_Strings_and_Characters.md +++ b/source/chapter2/03_Strings_and_Characters.md @@ -368,7 +368,7 @@ for index in greeting.characters.indices { ``` > 注意: -> 您可以使用 `startIndex` 和 `endIndex` 属性或者 `index(before:)` 、`index(after:)` 和 `index(_:offsetBy:)` 方法在任意一个确认的 `Collection`协议类型里面,如上文所示是使用在 `String` 中,您也可以使用在 `Array`,`Dictionary` 和 `Set`。 +> 您可以使用 `startIndex` 和 `endIndex` 属性或者 `index(before:)` 、`index(after:)` 和 `index(_:offsetBy:)` 方法在任意一个确认的并遵循 `Collection` 协议的类型里面,如上文所示是使用在 `String` 中,您也可以使用在 `Array`、`Dictionary` 和 `Set`中。 ### 插入和删除 (Inserting and Removing) @@ -396,7 +396,7 @@ welcome.removeSubrange(range) ``` > 注意: -> 您可以使用 `insert(_:at:)`、`insert(contentsOf:at:)`、`remove(at:)` 和 `removeSubrange(_:)` 方法在任意一个确认的 `RangeReplaceableCollection` 协议类型里面,如上文所示是使用在 `String` 中,您也可以使用在 `Array`,`Dictionary` 和 `Set`。 +> 您可以使用 `insert(_:at:)`、`insert(contentsOf:at:)`、`remove(at:)` 和 `removeSubrange(_:)` 方法在任意一个确认的并遵循 `RangeReplaceableCollection` 协议的类型里面,如上文所示是使用在 `String` 中,您也可以使用在 `Array`、`Dictionary` 和 `Set` 中。 From 4edf9501b4fb3b2eb47f98970f53f857570e6f01 Mon Sep 17 00:00:00 2001 From: chenmingjia <564575471@qq.com> Date: Mon, 12 Sep 2016 19:46:19 +0800 Subject: [PATCH 59/61] [Translate] Translate Generic in Swift 3.0. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Swift 3.0 Generic 已经补充完毕,主要是 Generic Where Clauses 部分的更新 --- source/chapter2/23_Generics.md | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/source/chapter2/23_Generics.md b/source/chapter2/23_Generics.md index c0da5ab5..3956a19b 100644 --- a/source/chapter2/23_Generics.md +++ b/source/chapter2/23_Generics.md @@ -14,6 +14,8 @@ > 2.2:翻译+校对:[Lanford](https://github.com/LanfordCai),2016-04-08 [SketchK](https://github.com/SketchK) 2016-05-16 +> 3.0:翻译+校对:[chenmingjia](https://github.com/chenmingjia),2016-09-12 + 本页包含内容: - [泛型所解决的问题](#the_problem_that_generics_solve) @@ -457,31 +459,30 @@ extension Array: Container {} [类型约束](#type_constraints)让你能够为泛型函数或泛型类型的类型参数定义一些强制要求。 -为关联类型定义约束也是非常有用的。你可以在参数列表中通过 `where` 子句为关联类型定义约束。你能通过 `where` 子句要求一个关联类型遵从某个特定的协议,以及某个特定的类型参数和关联类型必须类型相同。你可以通过将 `where` 关键字紧跟在类型参数列表后面来定义 `where` 子句,`where` 子句后跟一个或者多个针对关联类型的约束,以及一个或多个类型参数和关联类型间的相等关系。 +为关联类型定义约束也是非常有用的。你可以在参数列表中通过 `where` 子句为关联类型定义约束。你能通过 `where` 子句要求一个关联类型遵从某个特定的协议,以及某个特定的类型参数和关联类型必须类型相同。你可以通过将 `where` 关键字紧跟在类型参数列表后面来定义 `where` 子句,`where` 子句后跟一个或者多个针对关联类型的约束,以及一个或多个类型参数和关联类型间的相等关系。你可以在函数体或者类型的大括号之前添加 where 子句。 下面的例子定义了一个名为 `allItemsMatch` 的泛型函数,用来检查两个 `Container` 实例是否包含相同顺序的相同元素。如果所有的元素能够匹配,那么返回 `true`,否则返回 `false`。 被检查的两个 `Container` 可以不是相同类型的容器(虽然它们可以相同),但它们必须拥有相同类型的元素。这个要求通过一个类型约束以及一个 `where` 子句来表示: ```swift -func allItemsMatch< - C1: Container, C2: Container - where C1.ItemType == C2.ItemType, C1.ItemType: Equatable> - (someContainer: C1, _ anotherContainer: C2) -> Bool { - - // 检查两个容器含有相同数量的元素 +func allItemsMatch + (_ someContainer: C1, _ anotherContainer: C2) -> Bool + where C1.ItemType == C2.ItemType, C1.ItemType: Equatable { + + // Check that both containers contain the same number of items. if someContainer.count != anotherContainer.count { return false } - - // 检查每一对元素是否相等 + + // Check each pair of items to see if they are equivalent. for i in 0.. Date: Mon, 12 Sep 2016 19:51:30 +0800 Subject: [PATCH 60/61] [Translate] Translate Generic in Swift 3.0. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Swift 3.0 Generic 已经补充完毕,主要是 Generic Where Clauses 部分的更新 --- source/chapter2/23_Generics.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/chapter2/23_Generics.md b/source/chapter2/23_Generics.md index 3956a19b..03633c0b 100644 --- a/source/chapter2/23_Generics.md +++ b/source/chapter2/23_Generics.md @@ -470,19 +470,19 @@ func allItemsMatch (_ someContainer: C1, _ anotherContainer: C2) -> Bool where C1.ItemType == C2.ItemType, C1.ItemType: Equatable { - // Check that both containers contain the same number of items. + // 检查两个容器含有相同数量的元素 if someContainer.count != anotherContainer.count { return false } - // Check each pair of items to see if they are equivalent. + // 检查每一对元素是否相等 for i in 0.. Date: Mon, 12 Sep 2016 20:22:22 +0800 Subject: [PATCH 61/61] [Translate] Swift 3.0 Functions(fixed) --- source/chapter2/06_Functions.md | 389 ++++++++++++++++---------------- 1 file changed, 197 insertions(+), 192 deletions(-) diff --git a/source/chapter2/06_Functions.md b/source/chapter2/06_Functions.md index a63abec2..81d3e15e 100755 --- a/source/chapter2/06_Functions.md +++ b/source/chapter2/06_Functions.md @@ -15,48 +15,57 @@ > 2.2 > 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-12 -本页包含内容: +> 3.0 +> 翻译: [crayygy](https://github.com/crayygy) 2016-09-12 +本页包含内容: - [函数定义与调用(Defining and Calling Functions)](#Defining_and_Calling_Functions) - [函数参数与返回值(Function Parameters and Return Values)](#Function_Parameters_and_Return_Values) -- [函数参数名称(Function Parameter Names)](#Function_Parameter_Names) +- [函数参数标签和参数名称 (Function Argument Labels and Parameter Names) ](#Function_Argument_Labels_and_Parameter_Names) - [函数类型(Function Types)](#Function_Types) - [嵌套函数(Nested Functions)](#Nested_Functions) -*函数*是用来完成特定任务的独立的代码块。你给一个函数起一个合适的名字,用来标识函数做什么,并且当函数需要执行的时候,这个名字会被用于“调用”函数。 -Swift 统一的函数语法足够灵活,可以用来表示任何函数,包括从最简单的没有参数名字的 C 风格函数,到复杂的带局部和外部参数名的 Objective-C 风格函数。参数可以提供默认值,以简化函数调用。参数也可以既当做传入参数,也当做传出参数,也就是说,一旦函数执行结束,传入的参数值可以被修改。 +`函数` 是一段完成特定任务的独立代码片段。你可以通过给函数命名来标识某个函数的功能,这个名字可以被用来在需要的时候"调用"这个函数来完成它的任务。 -在 Swift 中,每个函数都有一种类型,包括函数的参数值类型和返回值类型。你可以把函数类型当做任何其他普通变量类型一样处理,这样就可以更简单地把函数当做别的函数的参数,也可以从其他函数中返回函数。函数的定义可以写在其他函数定义中,这样可以在嵌套函数范围内实现功能封装。 +Swift 统一的函数语法非常的灵活,可以用来表示任何函数,包括从最简单的没有参数名字的 C 风格函数,到复杂的带局部和外部参数名的 Objective-C 风格函数。参数可以提供默认值,以简化函数调用。参数也可以既当做传入参数,也当做传出参数,也就是说,一旦函数执行结束,传入的参数值将被修改。 + +在 Swift 中,每个函数都有一个由函数的参数值类型和返回值类型组成的类型。你可以把函数类型当做任何其他普通变量类型一样处理,这样就可以更简单地把函数当做别的函数的参数,也可以从其他函数中返回函数。函数的定义可以写在其他函数定义中,这样可以在嵌套函数范围内实现功能封装。 -## 函数的定义与调用(Defining and Calling Functions) +## 函数的定义与调用 (Defining and Calling Functions) -当你定义一个函数时,你可以定义一个或多个有名字和类型的值,作为函数的输入(称为*参数,parameters*),也可以定义某种类型的值作为函数执行结束的输出(称为*返回类型,return type*)。 +当你定义一个函数时,你可以定义一个或多个有名字和类型的值,作为函数的输入(称为*参数*,*parameters*),也可以定义某种类型的值作为函数执行结束时的输出(称为 *返回* 类型,*return* type)。 -每个函数有个*函数名*,用来描述函数执行的任务。要使用一个函数时,你用函数名“调用”,并传给它匹配的输入值(称作*实参,arguments*)。一个函数的实参必须与函数参数表里参数的顺序一致。 +每个函数有个函数名,用来描述函数执行的任务。要使用一个函数时,用函数名来“调用”这个函数,并传给它匹配的输入值(称作 *实参* ,*arguments*)。函数的实参必须与函数参数表里参数的顺序一致。 + + +下面例子中的函数的名字是`sayHello(_:)`,之所以叫这个名字,是因为这个函数用一个人的名字当做输入,并返回向这个人问候的语句。为了完成这个任务,你需要定义一个输入参数——一个叫做 `personName` 的 `String` 值,和一个包含给这个人问候语的 `String` 类型的返回值: -在下面例子中的函数叫做`"sayHello(_:)"`,之所以叫这个名字,是因为这个函数用一个人的名字当做输入,并返回给这个人的问候语。为了完成这个任务,你定义一个输入参数-一个叫做 `personName` 的 `String` 值,和一个包含给这个人问候语的 `String` 类型的返回值: ```swift -func sayHello(personName: String) -> String { - let greeting = "Hello, " + personName + "!" +func greet(person: String) -> String { + let greeting = "Hello, " + person + "!" return greeting } ``` 所有的这些信息汇总起来成为函数的*定义*,并以 `func` 作为前缀。指定函数返回类型时,用返回箭头 `->`(一个连字符后跟一个右尖括号)后跟返回类型的名称的方式来表示。 -该定义描述了函数做什么,它期望接收什么和执行结束时它返回的结果是什么类型。这样的定义使得函数可以在别的地方以一种清晰的方式被调用: +该定义描述了函数的功能,它期望接收什么作为参数和执行结束时它返回的结果是什么类型。这样的定义使得函数可以在别的地方以一种清晰的方式被调用: ```swift -print(sayHello("Anna")) -// prints "Hello, Anna!" -print(sayHello("Brian")) -// prints "Hello, Brian!" +print(greet(person: "Anna")) +// 打印 "Hello, Anna!" +print(greet(person: "Brian")) +// 打印 "Hello, Brian!" ``` -调用 `sayHello(_:)` 函数时,在圆括号中传给它一个 `String` 类型的实参,例如 `sayHello("Anna")`。因为这个函数返回一个 `String` 类型的值,`sayHello` 可以被包含在 `print(_:separator:terminator:)` 的调用中,用来输出这个函数的返回值,正如上面所示。 +调用 `sayHello(_:)` 函数时,在圆括号中传给它一个 `String` 类型的实参,例如 `sayHello("Anna")`。正如上面所示,因为这个函数返回一个 `String` 类型的值,所以`sayHello` 可以被包含在 `print(_:separator:terminator:)` 的调用中,用来输出这个函数的返回值。 + +>注意 +`print(_:separator:terminator:)` 函数的第一个参数并没有设置一个标签,而其他的参数因为已经有了默认值,因此是可选的。关于这些函数语法上的变化详见下方关于 函数参数标签和参数名 以及 默认参数值。 + 在 `sayHello(_:)` 的函数体中,先定义了一个新的名为 `greeting` 的 `String` 常量,同时,把对 `personName` 的问候消息赋值给了 `greeting` 。然后用 `return` 关键字把这个问候返回出去。一旦 `return greeting` 被调用,该函数结束它的执行并返回 `greeting` 的当前值。 @@ -65,100 +74,101 @@ print(sayHello("Brian")) 为了简化这个函数的定义,可以将问候消息的创建和返回写成一句: ```swift -func sayHelloAgain(personName: String) -> String { - return "Hello again, " + personName + "!" +func greetAgain(person: String) -> String { + return "Hello again, " + person + "!" } -print(sayHelloAgain("Anna")) -// prints "Hello again, Anna!" +print(greetAgain(person: "Anna")) +// 打印 "Hello again, Anna!" ``` -## 函数参数与返回值(Function Parameters and Return Values) +## 函数参数与返回值 (Function Parameters and Return Values) -函数参数与返回值在 Swift 中极为灵活。你可以定义任何类型的函数,包括从只带一个未名参数的简单函数到复杂的带有表达性参数名和不同参数选项的复杂函数。 +函数参数与返回值在 Swift 中非常的灵活。你可以定义任何类型的函数,包括从只带一个未名参数的简单函数到复杂的带有表达性参数名和不同参数选项的复杂函数。 -### 无参函数(Functions Without Parameters) + +### 无参数函数 (Functions Without Parameters) -函数可以没有参数。下面这个函数就是一个无参函数,当被调用时,它返回固定的 `String` 消息: +函数可以没有参数。下面这个函数就是一个无参数函数,当被调用时,它返回固定的 `String` 消息: ```swift func sayHelloWorld() -> String { return "hello, world" } print(sayHelloWorld()) -// prints "hello, world" +// 打印 "hello, world" ``` 尽管这个函数没有参数,但是定义中在函数名后还是需要一对圆括号。当被调用时,也需要在函数名后写一对圆括号。 + ### 多参数函数 (Functions With Multiple Parameters) 函数可以有多种输入参数,这些参数被包含在函数的括号之中,以逗号分隔。 -这个函数用一个人名和是否已经打过招呼作为输入,并返回对这个人的适当问候语: +下面这个函数用一个人名和是否已经打过招呼作为输入,并返回对这个人的适当问候语: ```swift -func sayHello(personName: String, alreadyGreeted: Bool) -> String { +func greet(person: String, alreadyGreeted: Bool) -> String { if alreadyGreeted { - return sayHelloAgain(personName) + return greetAgain(person: person) } else { - return sayHello(personName) + return greet(person: person) } } -print(sayHello("Tim", alreadyGreeted: true)) -// prints "Hello again, Tim!" +print(greet(person: "Tim", alreadyGreeted: true)) +// 打印 "Hello again, Tim!" ``` -你通过在括号内传递一个`String`参数值和一个标识为`alreadyGreeted`的`Bool`值,使用逗号分隔来调用`sayHello(_:alreadyGreeted:)`函数。 - -当调用超过一个参数的函数时,第一个参数后的参数根据其对应的参数名称标记,函数参数命名在[函数参数名称(Function Parameter Names)](#Function_Parameter_Names)有更详细的描述。 +你可以通过在括号内使用逗号分隔来传递一个`String`参数值和一个标识为`alreadyGreeted`的`Bool`值,来调用`sayHello(_:alreadyGreeted:)`函数。注意这个函数和上面`greet(person:)`是不同的。虽然它们都有着同样的名字`greet`,但是`greet(person:alreadyGreeted:)`函数需要两个参数,而`greet(person:)`只需要一个参数。 -### 无返回值函数(Functions Without Return Values) +### 无返回值函数 (Functions Without Return Values) -函数可以没有返回值。下面是 `sayHello(_:)` 函数的另一个版本,叫 `sayGoodbye(_:)`,这个函数直接输出 `String` 值,而不是返回它: +函数可以没有返回值。下面是 `sayHello(_:)` 函数的另一个版本,叫 `sayGoodbye(_:)`,这个函数直接打印一个`String`值,而不是返回它: ```swift -func sayGoodbye(personName: String) { - print("Goodbye, \(personName)!") +func greet(person: String) { + print("Hello, \(person)!") } -sayGoodbye("Dave") -// prints "Goodbye, Dave!" +greet(person: "Dave") +// 打印 "Hello, Dave!" ``` 因为这个函数不需要返回值,所以这个函数的定义中没有返回箭头(->)和返回类型。 -> 注意 -> 严格上来说,虽然没有返回值被定义,`sayGoodbye(_:)` 函数依然返回了值。没有定义返回类型的函数会返回特殊的值,叫 `Void`。它其实是一个空的元组(tuple),没有任何元素,可以写成`()`。 +>注意 +严格上来说,虽然没有返回值被定义,`sayGoodbye(_:)` 函数依然返回了值。没有定义返回类型的函数会返回一个特殊的`Void`值。它其实是一个空的元组(tuple),没有任何元素,可以写成()。 + 被调用时,一个函数的返回值可以被忽略: ```swift -func printAndCount(stringToPrint: String) -> Int { - print(stringToPrint) - return stringToPrint.characters.count +func printAndCount(string: String) -> Int { + print(string) + return string.characters.count } -func printWithoutCounting(stringToPrint: String) { - printAndCount(stringToPrint) +func printWithoutCounting(string: String) { + let _ = printAndCount(string: string) } -printAndCount("hello, world") -// prints "hello, world" and returns a value of 12 -printWithoutCounting("hello, world") -// prints "hello, world" but does not return a value - +printAndCount(string: "hello, world") +// 打印 "hello, world" 并且返回值 12 +printWithoutCounting(string: "hello, world") +// 打印 "hello, world" 但是没有返回任何值 ``` 第一个函数 `printAndCount(_:)`,输出一个字符串并返回 `Int` 类型的字符数。第二个函数 `printWithoutCounting`调用了第一个函数,但是忽略了它的返回值。当第二个函数被调用时,消息依然会由第一个函数输出,但是返回值不会被用到。 -> 注意 -> 返回值可以被忽略,但定义了有返回值的函数必须返回一个值,如果在函数定义底部没有返回任何值,将导致编译错误(compile-time error)。 +>注意 +返回值可以被忽略,但定义了有返回值的函数必须返回一个值,如果在函数定义底部没有返回任何值,将导致编译时错误(compile-time error)。 + -### 多重返回值函数(Functions with Multiple Return Values) +### 多重返回值函数 (Functions with Multiple Return Values) 你可以用元组(tuple)类型让多个值作为一个复合值从函数中返回。 -下面的这个例子中,定义了一个名为`minMax(_:)`的函数,作用是在一个`Int`数组中找出最小值与最大值。 +下例中定义了一个名为 `minMax(_:)` 的函数,作用是在一个 `Int` 类型的数组中找出最小值与最大值。 ```swift func minMax(array: [Int]) -> (min: Int, max: Int) { @@ -175,31 +185,33 @@ func minMax(array: [Int]) -> (min: Int, max: Int) { } ``` -`minMax(_:)`函数返回一个包含两个`Int`值的元组,这些值被标记为`min`和`max`,以便查询函数的返回值时可以通过名字访问它们。 + `minMax(_:)` 函数返回一个包含两个 `Int` 值的元组,这些值被标记为 `min` 和 `max` ,以便查询函数的返回值时可以通过名字访问它们。 -`minMax(_:)`的函数体中,在开始的时候设置两个工作变量`currentMin`和`currentMax`的值为数组中的第一个数。然后函数会遍历数组中剩余的值并检查该值是否比`currentMin`和`currentMax`更小或更大。最后数组中的最小值与最大值作为一个包含两个`Int`值的元组返回。 +在 `minMax(_:)` 的函数体中,在开始的时候设置两个工作变量 `currentMin` 和 `currentMax` 的值为数组中的第一个数。然后函数会遍历数组中剩余的值并检查该值是否比 `currentMin` 和 `currentMax` 更小或更大。最后数组中的最小值与最大值作为一个包含两个 `Int` 值的元组返回。 -因为元组的成员值已被命名,因此可以通过点语法来检索找到的最小值与最大值: +因为元组的成员值已被命名,因此可以通过 `.` 语法来检索找到的最小值与最大值: ```swift -let bounds = minMax([8, -6, 2, 109, 3, 71]) +let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) print("min is \(bounds.min) and max is \(bounds.max)") -// prints "min is -6 and max is 109" +// 打印 "min is -6 and max is 109" ``` 需要注意的是,元组的成员不需要在元组从函数中返回时命名,因为它们的名字已经在函数返回类型中指定了。 -###可选元组返回类型(Optional Tuple Return Types) +### 可选元组返回类型 (Optional Tuple Return Types) -如果函数返回的元组类型有可能整个元组都“没有值”,你可以使用*可选的(Optional)* 元组返回类型反映整个元组可以是`nil`的事实。你可以通过在元组类型的右括号后放置一个问号来定义一个可选元组,例如`(Int, Int)?`或`(String, Int, Bool)?` +如果函数返回的元组类型有可能整个元组都“没有值”,你可以使用可选的( `optional` ) 元组返回类型反映整个元组可以是`nil`的事实。你可以通过在元组类型的右括号后放置一个问号来定义一个可选元组,例如 `(Int, Int)?` 或 `(String, Int, Bool)?` -> 注意 -> 可选元组类型如`(Int, Int)?`与元组包含可选类型如`(Int?, Int?)`是不同的.可选的元组类型,整个元组是可选的,而不只是元组中的每个元素值。 +>注意 +可选元组类型如 `(Int, Int)?` 与元组包含可选类型如 `(Int?, Int?)` 是不同的.可选的元组类型,整个元组是可选的,而不只是元组中的每个元素值。 -前面的`minMax(_:)`函数返回了一个包含两个`Int`值的元组。但是函数不会对传入的数组执行任何安全检查,如果`array`参数是一个空数组,如上定义的`minMax(_:)`在试图访问`array[0]`时会触发一个运行时错误。 -为了安全地处理这个“空数组”问题,将`minMax(_:)`函数改写为使用可选元组返回类型,并且当数组为空时返回`nil`: +前面的 `minMax(_:)` 函数返回了一个包含两个 `Int` 值的元组。但是函数不会对传入的数组执行任何安全检查,如果 `array` 参数是一个空数组,如上定义的 `minMax(_:)` 在试图访问 `array[0]` 时会触发一个运行时错误(runtime error)。 + +为了安全地处理这个“空数组”问题,将 `minMax(_:)` 函数改写为使用可选元组返回类型,并且当数组为空时返回 `nil`: + ```swift func minMax(array: [Int]) -> (min: Int, max: Int)? { @@ -217,105 +229,97 @@ func minMax(array: [Int]) -> (min: Int, max: Int)? { } ``` -你可以使用可选绑定来检查`minMax(_:)`函数返回的是一个实际的元组值还是`nil`: +你可以使用可选绑定来检查 `minMax(_:)` 函数返回的是一个存在的元组值还是 `nil`: ```swift -if let bounds = minMax([8, -6, 2, 109, 3, 71]) { +if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) { print("min is \(bounds.min) and max is \(bounds.max)") } -// prints "min is -6 and max is 109" +// 打印 "min is -6 and max is 109" ``` - -## 函数参数名称(Function Parameter Names) -函数参数都有一个*外部参数名(external parameter name)*和一个*局部参数名(local parameter name)*。外部参数名用于在函数调用时标注传递给函数的参数,局部参数名在函数的实现内部使用。 + +## 函数参数标签和参数名称 (Function Argument Labels and Parameter Names) + +每个函数参数都有一个参数标签( argument label )以及一个参数名称( parameter name )。参数标签在调用函数的时候使用;调用的时候需要将函数的参数标签写在对应的参数前面。参数名称在函数的实现中使用。默认情况下,函数参数使用参数名称来作为它们的参数标签。 ```swift func someFunction(firstParameterName: Int, secondParameterName: Int) { - // function body goes here - // firstParameterName and secondParameterName refer to - // the argument values for the first and second parameters + // In the function body, firstParameterName and secondParameterName + // refer to the argument values for the first and second parameters. +} +someFunction(firstParameterName: 1, secondParameterName: 2) +``` + +所有的参数都必须有一个独一无二的名字。虽然多个参数拥有同样的参数标签是可能的,但是一个唯一的函数标签能够使你的代码更具可读性。 + + +### 参数标签 (Specifying Argument Labels) + +你可以在函数名称前指定它的参数标签,中间以空格分隔: + +```swift +func someFunction(argumentLabel parameterName: Int) { + // In the function body, parameterName refers to the argument value + // for that parameter. +} +``` + +这个版本的 `greet(person:)` 函数,接收一个人的名字和他的家乡,并且返回一句问候: + +```swift +func greet(person: String, from hometown: String) -> String { + return "Hello \(person)! Glad you could visit from \(hometown)." +} +print(greet(person: "Bill", from: "Cupertino")) +// Prints "Hello Bill! Glad you could visit from Cupertino." +``` + +参数标签的使用能够让一个函数在调用时更有表达力,更类似自然语言,并且仍保持了函数内部的可读性以及清晰的意图。 + + +### 忽略参数标签(Omitting Argument Labels) + +如果你不希望为某个参数添加一个标签,可以使用一个下划线(`_`)来代替一个明确的参数标签。 + +```swift +func someFunction(_ firstParameterName: Int, secondParameterName: Int) { + // In the function body, firstParameterName and secondParameterName + // refer to the argument values for the first and second parameters. } someFunction(1, secondParameterName: 2) ``` -一般情况下,第一个参数省略其外部参数名,第二个以及随后的参数使用其局部参数名作为外部参数名。所有参数必须有独一无二的局部参数名。尽管多个参数可以有相同的外部参数名,但不同的外部参数名能让你的代码更有可读性。 - - -### 指定外部参数名(Specifying External Parameter Names) - -你可以在局部参数名前指定外部参数名,中间以空格分隔: - -```swift -func someFunction(externalParameterName localParameterName: Int) { - // function body goes here, and can use localParameterName - // to refer to the argument value for that parameter -} -``` - -> 注意 -> 如果你提供了外部参数名,那么函数在被调用时,必须使用外部参数名。 - -这个版本的`sayHello(_:)`函数,接收两个人的名字,会同时返回对他俩的问候: - -```swift -func sayHello(to person: String, and anotherPerson: String) -> String { - return "Hello \(person) and \(anotherPerson)!" -} -print(sayHello(to: "Bill", and: "Ted")) -// prints "Hello Bill and Ted!" -``` - -为每个参数指定外部参数名后,在你调用`sayHello(to:and:)`函数时两个外部参数名都必须写出来。 - -使用外部函数名可以使函数以一种更富有表达性的类似句子的方式调用,并使函数体意图清晰,更具可读性。 - -### 忽略外部参数名(Omitting External Parameter Names) - -如果你不想为第二个及后续的参数设置外部参数名,用一个下划线(`_`)代替一个明确的参数名。 - -```swift -func someFunction(firstParameterName: Int, _ secondParameterName: Int) { - // function body goes here - // firstParameterName and secondParameterName refer to - // the argument values for the first and second parameters -} -someFunction(1, 2) -``` - -> 注意 -> 因为第一个参数默认忽略其外部参数名称,显式地写下划线是多余的。 +如果一个参数有一个标签,那么在调用的时候必须使用标签来标记这个参数。 -### 默认参数值(Default Parameter Values) +### 默认参数值 (Default Parameter Values) -你可以在函数体中为每个参数定义`默认值(Deafult Values)`。当默认值被定义后,调用这个函数时可以忽略这个参数。 +你可以在函数体中通过给参数赋值来为任意一个参数定义默认值(Deafult Values)。当默认值被定义后,调用这个函数时可以忽略这个参数。 ```swift -func someFunction(parameterWithDefault: Int = 12) { - // function body goes here - // if no arguments are passed to the function call, - // value of parameterWithDefault is 12 +func someFunction(parameterWithoutDefault: Int, parameterWithDefault: Int = 12) { + // If you omit the second argument when calling this function, then + // the value of parameterWithDefault is 12 inside the function body. } -someFunction(6) // parameterWithDefault is 6 -someFunction() // parameterWithDefault is 12 +someFunction(parameterWithoutDefault: 3, parameterWithDefault: 6) // parameterWithDefault is 6 +someFunction(parameterWithoutDefault: 4) // parameterWithDefault is 12 ``` -> 注意 -> 将带有默认值的参数放在函数参数列表的最后。这样可以保证在函数调用时,非默认参数的顺序是一致的,同时使得相同的函数在不同情况下调用时显得更为清晰。 +将不带有默认值的参数放在函数参数列表的最前。一般来说,没有默认值的参数更加的重要,将不带默认值的参数放在最前保证在函数调用时,非默认参数的顺序是一致的,同时也使得相同的函数在不同情况下调用时显得更为清晰。 -### 可变参数(Variadic Parameters) +### 可变参数 (Variadic Parameters) -一个`可变参数(variadic parameter)`可以接受零个或多个值。函数调用时,你可以用可变参数来指定函数参数可以被传入不确定数量的输入值。通过在变量类型名后面加入`(...)`的方式来定义可变参数。 +一个可变参数(variadic parameter)可以接受零个或多个值。函数调用时,你可以用可变参数来指定函数参数可以被传入不确定数量的输入值。通过在变量类型名后面加入(`...`)的方式来定义可变参数。 可变参数的传入值在函数体中变为此类型的一个数组。例如,一个叫做 `numbers` 的 `Double...` 型可变参数,在函数体内可以当做一个叫 `numbers` 的 `[Double]` 型的数组常量。 -下面的这个函数用来计算一组任意长度数字的`算术平均数(arithmetic mean)`: +下面的这个函数用来计算一组任意长度数字的 *算术平均数(arithmetic mean)*: ```swift -func arithmeticMean(numbers: Double...) -> Double { +func arithmeticMean(_ numbers: Double...) -> Double { var total: Double = 0 for number in numbers { total += number @@ -328,35 +332,33 @@ arithmeticMean(3, 8.25, 18.75) // returns 10.0, which is the arithmetic mean of these three numbers ``` -> 注意 -> 一个函数最多只能有一个可变参数。 - -如果函数有一个或多个带默认值的参数,而且还有一个可变参数,那么把可变参数放在参数表的最后。 +>注意 +一个函数最多只能拥有一个可变参数。 + ### 输入输出参数(In-Out Parameters) -函数参数默认是常量。试图在函数体中更改参数值将会导致编译错误。这意味着你不能错误地更改参数值。如果你想要一个函数可以修改参数的值,并且想要在这些修改在函数调用结束后仍然存在,那么就应该把这个参数定义为输入输出参数(In-Out Parameters)。 +函数参数默认是常量。试图在函数体中更改参数值将会导致编译错误(compile-time error)。这意味着你不能错误地更改参数值。如果你想要一个函数可以修改参数的值,并且想要在这些修改在函数调用结束后仍然存在,那么就应该把这个参数定义为输入输出参数(In-Out Parameters)。 + +定义一个输入输出参数时,在参数定义前加 `inout` 关键字。一个`输入输出参数`有传入函数的值,这个值被函数修改,然后被传出函数,替换原来的值。想获取更多的关于输入输出参数的细节和相关的编译器优化,请查看`输入输出参数`一节。 + +你只能传递变量给输入输出参数。你不能传入常量或者字面量(literal value),因为这些量是不能被修改的。当传入的参数作为输入输出参数时,需要在参数名前加 `&` 符,表示这个值可以被函数修改。 + +>注意 +输入输出参数不能有默认值,而且可变参数不能用 `inout` 标记。 -定义一个输入输出参数时,在参数定义前加 inout 关键字。一个输入输出参数有传入函数的值,这个值被函数修改,然后被传出函数,替换原来的值。想获取更多的关于输入输出参数的细节和相关的编译器优化,请查看[输入输出参数](../chapter3/05_Declarations.html#function_declaration)一节。 - - -你只能传递变量给输入输出参数。你不能传入常量或者字面量(literal value),因为这些量是不能被修改的。当传入的参数作为输入输出参数时,需要在参数名前加`&`符,表示这个值可以被函数修改。 - -> 注意 -> 输入输出参数不能有默认值,而且可变参数不能用 `inout` 标记。 - -下面是例子,`swapTwoInts(_:_:)` 函数,有两个分别叫做 `a` 和 `b` 的输入输出参数: +下例中,`swapTwoInts(_:_:)` 函数有两个分别叫做 `a` 和 `b` 的输入输出参数: ```swift -func swapTwoInts(inout a: Int, inout _ b: Int) { +func swapTwoInts(_ a: inout Int, _ b: inout Int) { let temporaryA = a a = b b = temporaryA } ``` -这个 `swapTwoInts(_:_:)` 函数简单地交换 `a` 与 `b` 的值。该函数先将 `a` 的值存到一个临时常量 `temporaryA` 中,然后将 `b` 的值赋给 `a`,最后将 `temporaryA` 赋值给 `b`。 +`swapTwoInts(_:_:)` 函数简单地交换 `a` 与 `b` 的值。该函数先将 `a` 的值存到一个临时常量 `temporaryA` 中,然后将 `b` 的值赋给 `a`,最后将 `temporaryA` 赋值给 `b`。 你可以用两个 `Int` 型的变量来调用 `swapTwoInts(_:_:)`。需要注意的是,`someInt` 和 `anotherInt` 在传入 `swapTwoInts(_:_:)` 函数前,都加了 `&` 的前缀: @@ -365,31 +367,32 @@ var someInt = 3 var anotherInt = 107 swapTwoInts(&someInt, &anotherInt) print("someInt is now \(someInt), and anotherInt is now \(anotherInt)") -// prints "someInt is now 107, and anotherInt is now 3" +// Prints "someInt is now 107, and anotherInt is now 3" ``` 从上面这个例子中,我们可以看到 `someInt` 和 `anotherInt` 的原始值在 `swapTwoInts(_:_:)` 函数中被修改,尽管它们的定义在函数体外。 -> 注意 -> 输入输出参数和返回值是不一样的。上面的 `swapTwoInts` 函数并没有定义任何返回值,但仍然修改了 `someInt` 和 `anotherInt` 的值。输入输出参数是函数对函数体外产生影响的另一种方式。 +>注意 +输入输出参数和返回值是不一样的。上面的 `swapTwoInts` 函数并没有定义任何返回值,但仍然修改了 `someInt` 和 `anotherInt` 的值。输入输出参数是函数对函数体外产生影响的另一种方式。 + -## 函数类型(Function Types) +## 函数类型 (Function Types) -每个函数都有种特定的函数类型,由函数的参数类型和返回类型组成。 +每个函数都有种特定的`函数类型`,函数的类型由函数的参数类型和返回类型组成。 例如: ```swift -func addTwoInts(a: Int, _ b: Int) -> Int { +func addTwoInts(_ a: Int, _ b: Int) -> Int { return a + b } -func multiplyTwoInts(a: Int, _ b: Int) -> Int { +func multiplyTwoInts(_ a: Int, _ b: Int) -> Int { return a * b } ``` -这个例子中定义了两个简单的数学函数:`addTwoInts` 和 `multiplyTwoInts`。这两个函数都接受两个 `Int` 值, 返回一个`Int`值。 +这个例子中定义了两个简单的数学函数:`addTwoInts` 和 `multiplyTwoInts`。这两个函数都接受两个 `Int` 值, 返回一个 `Int` 值。 这两个函数的类型是 `(Int, Int) -> Int`,可以解读为“这个函数类型有两个 `Int` 型的参数并返回一个 `Int` 型的值。”。 @@ -404,7 +407,7 @@ func printHelloWorld() { 这个函数的类型是:`() -> Void`,或者叫“没有参数,并返回 `Void` 类型的函数”。 -### 使用函数类型(Using Function Types) +### 使用函数类型 (Using Function Types) 在 Swift 中,使用函数类型就像使用其他类型一样。例如,你可以定义一个类型为函数的常量或变量,并将适当的函数赋值给它: @@ -412,76 +415,77 @@ func printHelloWorld() { var mathFunction: (Int, Int) -> Int = addTwoInts ``` -这个可以解读为: +这段代码可以被解读为: -“定义一个叫做 `mathFunction` 的变量,类型是‘一个有两个 `Int` 型的参数并返回一个 `Int` 型的值的函数’,并让这个新变量指向 `addTwoInts` 函数”。 +”定义一个叫做 `mathFunction` 的变量,类型是‘一个有两个 `Int` 型的参数并返回一个 `Int` 型的值的函数’,并让这个新变量指向 `addTwoInts` 函数”。 -`addTwoInts` 和 `mathFunction` 有同样的类型,所以这个赋值过程在 Swift 类型检查中是允许的。 +`addTwoInts` 和 `mathFunction` 有同样的类型,所以这个赋值过程在 Swift 类型检查(type-check)中是允许的。 现在,你可以用 `mathFunction` 来调用被赋值的函数了: ```swift print("Result: \(mathFunction(2, 3))") -// prints "Result: 5" +// Prints "Result: 5" ``` 有相同匹配类型的不同函数可以被赋值给同一个变量,就像非函数类型的变量一样: + ```swift mathFunction = multiplyTwoInts print("Result: \(mathFunction(2, 3))") -// prints "Result: 6" +// Prints "Result: 6" ``` 就像其他类型一样,当赋值一个函数给常量或变量时,你可以让 Swift 来推断其函数类型: ```swift let anotherMathFunction = addTwoInts -// anotherMathFunction is inferred to be of type (Int, Int) -> Int +// anotherMathFunction 被推断为 (Int, Int) -> Int 类型 ``` -### 函数类型作为参数类型(Function Types as Parameter Types) +### 函数类型作为参数类型 (Function Types as Parameter Types) -你可以用`(Int, Int) -> Int`这样的函数类型作为另一个函数的参数类型。这样你可以将函数的一部分实现留给函数的调用者来提供。 +你可以用 `(Int, Int) -> Int` 这样的函数类型作为另一个函数的参数类型。这样你可以将函数的一部分实现留给函数的调用者来提供。 下面是另一个例子,正如上面的函数一样,同样是输出某种数学运算结果: ```swift -func printMathResult(mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) { +func printMathResult(_ mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) { print("Result: \(mathFunction(a, b))") } printMathResult(addTwoInts, 3, 5) -// prints "Result: 8" +// 打印 "Result: 8" ``` -这个例子定义了 `printMathResult(_:_:_:)` 函数,它有三个参数:第一个参数叫 `mathFunction`,类型是`(Int, Int) -> Int`,你可以传入任何这种类型的函数;第二个和第三个参数叫 `a` 和 `b`,它们的类型都是 `Int`,这两个值作为已给出的函数的输入值。 +这个例子定义了 `printMathResult(_:_:_:)` 函数,它有三个参数:第一个参数叫 `mathFunction`,类型是 `(Int, Int) -> Int`,你可以传入任何这种类型的函数;第二个和第三个参数叫 `a` 和 `b`,它们的类型都是 `Int`,这两个值作为已给出的函数的输入值。 -当 `printMathResult(_:_:_:)` 被调用时,它被传入 `addTwoInts` 函数和整数`3`和`5`。它用传入`3`和`5`调用 `addTwoInts`,并输出结果:`8`。 +当 `printMathResult(_:_:_:)` 被调用时,它被传入 `addTwoInts` 函数和整数 `3` 和 `5`。它用传入 `3` 和 `5` 调用 `addTwoInts`,并输出结果:`8`。 -`printMathResult(_:_:_:)` 函数的作用就是输出另一个适当类型的数学函数的调用结果。它不关心传入函数是如何实现的,它只关心这个传入的函数类型是正确的。这使得 `printMathResult(_:_:_:)` 能以一种类型安全(type-safe)的方式将一部分功能转给调用者实现。 +`printMathResult(_:_:_:)` 函数的作用就是输出另一个适当类型的数学函数的调用结果。它不关心传入函数是如何实现的,只关心传入的函数是不是一个正确的类型。这使得 `printMathResult(_:_:_:)` 能以一种类型安全(type-safe)的方式将一部分功能转给调用者实现。 -### 函数类型作为返回类型(Function Types as Return Types) +### 函数类型作为返回类型 (Function Types as Return Types) -你可以用函数类型作为另一个函数的返回类型。你需要做的是在返回箭头(`->`)后写一个完整的函数类型。 +你可以用函数类型作为另一个函数的返回类型。你需要做的是在返回箭头(->)后写一个完整的函数类型。 -下面的这个例子中定义了两个简单函数,分别是 `stepForward` 和`stepBackward`。`stepForward` 函数返回一个比输入值大一的值。`stepBackward` 函数返回一个比输入值小一的值。这两个函数的类型都是 `(Int) -> Int`: +下面的这个例子中定义了两个简单函数,分别是 `stepForward` 和 `stepBackward`。`stepForward`函数返回一个比输入值大 `1` 的值。`stepBackward` 函数返回一个比输入值小 `1` 的值。这两个函数的类型都是 `(Int) -> Int`: ```swift -func stepForward(input: Int) -> Int { +func stepForward(_ input: Int) -> Int { return input + 1 } -func stepBackward(input: Int) -> Int { +func stepBackward(_ input: Int) -> Int { return input - 1 } ``` -下面这个叫做 `chooseStepFunction(_:)` 的函数,它的返回类型是 `(Int) -> Int` 类型的函数。`chooseStepFunction(_:)` 根据布尔值 `backwards` 来返回 `stepForward(_:)` 函数或 `stepBackward(_:)` 函数: +如下名为 `chooseStepFunction(_:)` 的函数,它的返回类型是 `(Int) -> Int` 类型的函数。`chooseStepFunction(_:)` 根据布尔值 `backwards` 来返回 `stepForward(_:)` 函数或 `stepBackward(_:)` 函数: ```swift -func chooseStepFunction(backwards: Bool) -> (Int) -> Int { - return backwards ? stepBackward : stepForward +func chooseStepFunction(backward: Bool) -> (Int) -> Int { + return backward ? stepBackward : stepForward } ``` @@ -489,13 +493,14 @@ func chooseStepFunction(backwards: Bool) -> (Int) -> Int { ```swift var currentValue = 3 -let moveNearerToZero = chooseStepFunction(currentValue > 0) -// moveNearerToZero now refers to the stepBackward() function +let moveNearerToZero = chooseStepFunction(backward: currentValue > 0) +// moveNearerToZero 现在指向 stepBackward() 函数。 ``` -上面这个例子中计算出从 `currentValue` 逐渐接近到`0`是需要向正数走还是向负数走。`currentValue` 的初始值是`3`,这意味着 `currentValue > 0` 是真的(`true`),这将使得 `chooseStepFunction(_:)` 返回 `stepBackward(_:)` 函数。一个指向返回的函数的引用保存在了 `moveNearerToZero` 常量中。 +上面这个例子中计算出从 `currentValue` 逐渐接近到0是需要向正数走还是向负数走。`currentValue` 的初始值是 `3`,这意味着 `currentValue > 0` 为真(true),这将使得 `chooseStepFunction(_:)` 返回 `stepBackward(_:)` 函数。一个指向返回的函数的引用保存在了 `moveNearerToZero` 常量中。 + +现在,moveNearerToZero 指向了正确的函数,它可以被用来数到零: -现在,`moveNearerToZero` 指向了正确的函数,它可以被用来数到`0`: ```swift print("Counting to zero:") @@ -512,22 +517,22 @@ print("zero!") ``` -## 嵌套函数(Nested Functions) +## 嵌套函数 (Nested Functions) -这章中你所见到的所有函数都叫全局函数(global functions),它们定义在全局域中。你也可以把函数定义在别的函数体中,称作嵌套函数(nested functions)。 +到目前为止本章中你所见到的所有函数都叫`全局`函数(global functions),它们定义在`全局域`中。你也可以把函数定义在别的函数体中,称作 `嵌套函数`(nested functions)。 默认情况下,嵌套函数是对外界不可见的,但是可以被它们的外围函数(enclosing function)调用。一个外围函数也可以返回它的某一个嵌套函数,使得这个函数可以在其他域中被使用。 你可以用返回嵌套函数的方式重写 `chooseStepFunction(_:)` 函数: ```swift -func chooseStepFunction(backwards: Bool) -> (Int) -> Int { +func chooseStepFunction(backward: Bool) -> (Int) -> Int { func stepForward(input: Int) -> Int { return input + 1 } func stepBackward(input: Int) -> Int { return input - 1 } - return backwards ? stepBackward : stepForward + return backward ? stepBackward : stepForward } var currentValue = -4 -let moveNearerToZero = chooseStepFunction(currentValue > 0) +let moveNearerToZero = chooseStepFunction(backward: currentValue > 0) // moveNearerToZero now refers to the nested stepForward() function while currentValue != 0 { print("\(currentValue)... ")
发布日期语法变更记录
2014-06-3
    -
  • - 苹果全球开发者大会WWDC2014召开,发布了苹果最新的开发语言Swift,并释放出XCode6 Beta1版本 -