diff --git a/README.md b/README.md index 352c17a8..24b42d6a 100755 --- a/README.md +++ b/README.md @@ -3,26 +3,59 @@ 中文版Apple官方Swift教程《The Swift Programming Language》 -中文Swift社区[Swiftist](http://swiftist.org/),新社区,正在建设中,感兴趣的朋友可以一起参与进来。 +[英文原版](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/index.html#//apple_ref/doc/uid/TP40014097-CH3-ID0) -**如果想帮忙翻译或者校对,请加QQ群:364279588,谢谢!** +**如果想帮忙翻译或者校对,请加QQ群:480616051,谢谢!** # 在线阅读 -使用Gitbook制作,可以直接[在线阅读](http://numbbbbb.github.io/the-swift-programming-language-in-chinese/)。 - -# 电子书下载 - -CocoaChina精校PDF→[点我下载](http://vdisk.weibo.com/s/EhsPPzRRQ5CZ/1402621206) - -其他格式可以通过PDF转换 +使用Gitbook制作,可以直接[在线阅读](http://swiftguide.cn/)。 # 当前阶段 -文章已经全部翻译完成,当前阶段为自由校对阶段,可以随意提issue和pr。 +已经更新到Swift 2.0,对应苹果官方文档7.23的更新。 +# 2.0译者记录 -# 译者记录 +- About Swift ([xtymichael](https://github.com/xtymichael)) +- A Swift Tour([xtymichael](https://github.com/xtymichael)) +- The Basics([xtymichael](https://github.com/xtymichael)) +- Basic Operators([AlanMelody](https://github.com/AlanMelody)) +- Strings and Characters([DianQK](https://github.com/DianQK)) +- Collection Types([AlanMelody](https://github.com/AlanMelody)) +- Control Flow([AlanMelody](https://github.com/AlanMelody)) +- Functions([dreamkidd](https://github.com/dreamkidd)) +- Closures([100mango](https://github.com/100mango)) +- Enumerations([futantan](https://github.com/futantan)) +- Classes and Structures([SkyJean](https://github.com/SkyJean)) +- Properties([yangsiy](https://github.com/yangsiy)) +- Methods([DianQK](https://github.com/DianQK)) +- Subscripts([shanksyang](https://github.com/shanksyang)) +- Inheritance([shanksyang](https://github.com/shanksyang)) +- Initialization([chenmingbiao](https://github.com/chenmingbiao)) +- Deinitialization([chenmingbiao](https://github.com/chenmingbiao)) +- Automatic Reference Counting([Channe](https://github.com/Channe)) +- Optional Chaining([lyojo](https://github.com/lyojo)) +- Error Handling([lyojo](https://github.com/lyojo)) +- Type Casting([yangsiy](https://github.com/yangsiy)) +- Nested Types([SergioChan](https://github.com/SergioChan)) +- Extensions([shanksyang](https://github.com/shanksyang)) +- Protocols([futantan](https://github.com/futantan)) +- Generics([SergioChan](https://github.com/SergioChan)) +- Access Control([mmoaay](https://github.com/mmoaay)) +- Advanced Operators([buginux](https://github.com/buginux)) +- About the Language Reference([KYawn](https://github.com/KYawn)) +- Lexical Structure([buginux](https://github.com/buginux)) +- Types([EudeMorgen](https://github.com/EudeMorgen)) +- Expressions([EudeMorgen](https://github.com/EudeMorgen)) +- Statements([littledogboy](https://github.com/littledogboy)) +- Declarations([Lenhoon](https://github.com/Lenhoon)) +- Attributes([KYawn](https://github.com/KYawn)) +- Patterns([ray16897188](https://github.com/ray16897188)) +- Generic Parameters and Arguments([wardenNScaiyi](https://github.com/wardenNScaiyi)) +- Summary of the Grammar([miaosiqi](https://github.com/miaosiqi)) + +# 1.0译者记录 * 欢迎使用 Swift * 关于 Swift ([numbbbbb]) @@ -171,4 +204,4 @@ CocoaChina精校PDF→[点我下载](http://vdisk.weibo.com/s/EhsPPzRRQ5CZ/14026 [Lin-H]:https://github.com/Lin-H [takalard]:https://github.com/takalard [dabing1022]:https://github.com/dabing1022 -[marsprince]:https://github.com/marsprince \ No newline at end of file +[marsprince]:https://github.com/marsprince diff --git a/config.json b/config.json new file mode 100644 index 00000000..b4f3ff0e --- /dev/null +++ b/config.json @@ -0,0 +1,9 @@ +{ + "name": "The Swift Programming Language 中文版", + "introduction": "中文版《The Swift Programming Language》", + "path": { + "content": "source", + "toc": "source/SUMMARY.md", + "readme": "source/README.md" + } +} diff --git a/cover/background.jpg b/cover/background.jpg new file mode 100755 index 00000000..7726503e Binary files /dev/null and b/cover/background.jpg differ diff --git a/cover/cover.jpg b/cover/cover.jpg new file mode 100644 index 00000000..e247cc81 Binary files /dev/null and b/cover/cover.jpg differ diff --git a/cover/logo.png b/cover/logo.png new file mode 100644 index 00000000..c350c165 Binary files /dev/null and b/cover/logo.png differ diff --git a/index.html b/index.html index c4ca2b39..380bd5fd 100644 --- a/index.html +++ b/index.html @@ -3,7 +3,7 @@
- + diff --git a/source-tw/chapter1/02_a_swift_tour.md b/source-tw/chapter1/02_a_swift_tour.md index aec9e0de..891d7bfa 100644 --- a/source-tw/chapter1/02_a_swift_tour.md +++ b/source-tw/chapter1/02_a_swift_tour.md @@ -301,7 +301,7 @@ func lessThanTen(number: Int) -> Bool { return number < 10 } var numbers = [20, 19, 7, 12] -hasAnyMatches(numbers, lessThanTen) +hasAnyMatches(numbers, condition: lessThanTen) ``` 函數實際上是一種特殊的閉包,你可以使用`{}`來創建一個匿名閉包。使用`in`將參數和返回值類型聲明與閉包涵數體進行分離。 @@ -649,7 +649,7 @@ let bDescription = b.simpleDescription 注意聲明`SimpleStructure`時候`mutating`關鍵字用來標記一個會修改結構體的方法。`SimpleClass`的聲明不需要標記任何方法因為類中的方法經常會修改類。 -使用`extension`來為現有的類型添加功能,比如新的方法和參數。你可以使用擴展來改造定義在別處,甚至是從外部庫或者框架引入的一個類型,使得這個類型遵循某個協議。 +使用`extension`來為現有的類型添加功能,比如新的方法和計算屬性。你可以使用擴展來改造定義在別處,甚至是從外部庫或者框架引入的一個類型,使得這個類型遵循某個協議。 ```swift extension Int: ExampleProtocol { diff --git a/source-tw/chapter2/11_Methods.md b/source-tw/chapter2/11_Methods.md index 9e35a555..47c3a802 100644 --- a/source-tw/chapter2/11_Methods.md +++ b/source-tw/chapter2/11_Methods.md @@ -225,7 +225,7 @@ ovenLight.next() ```swift class SomeClass { - class func someTypeMethod() { + static func someTypeMethod() { // type method implementation goes here } } diff --git a/source/README.md b/source/README.md index ee97b3f0..9b5da07a 100755 --- a/source/README.md +++ b/source/README.md @@ -1,89 +1,78 @@ -> Swift 兴趣交流群:`305014012`,307017261(已满) -> [Swift 开发者社区](http://swiftist.org) +# 2.0 新的开始 + +> Swift 兴趣交流群:`131595168`, `146932759`, `151336833`, `153549217`. **加入一个群即可,请勿重复添加** + +> Swift 开发者社区 + +> Swift 资源汇总 + +> Swift 优秀newsletter -> 如果你觉得这个项目不错,请[点击Star一下](https://github.com/numbbbbb/the-swift-programming-language-in-chinese),您的支持是我们最大的动力。 +> 如果您觉得这个项目不错,请点击Star一下,您的支持是我们最大的动力。 - -> 关于文档中翻译错误,逻辑错误以及疑难问题答疑,请关注["@老码团队"](http://weibo.com/u/5241713117 -)官方微博,会有技术人员统一收集答疑 +## 1 +开源项目完成难,维护更难。 -# The Swift Programming Language 中文版#### +大家看到的是发布时的瞩目和荣耀,却没有看到项目本身质量不高、错误频出。这并不是翻译者和校对者的问题,他们已经付出了足够的努力。真正的问题在我,没有建立起长期的维护团队,因此后期的校对和更新都难以实施。 -###这一次,让中国和世界同步 +1.0发布之后,我们就再也没能和苹果的文档同步。语法错误、编译不通过、语言不通顺,阅读量直线下降,最低时每天只有不到1000人访问。 -现在是6月12日凌晨4:38,我用了整整一晚上的时间来进行最后的校对,终于可以在12日拿出一个可以发布的版本。 +6月9日,calvingit发了一个issue“准备翻译2.0版本吗”,我没有回复,应该已经没人关注这个项目了吧,我想。 -9天时间,1317个 Star,310个 Fork,超过30人参与翻译和校对工作,项目最高排名GitHub总榜第4。 +## 2 -设想过很多遍校对完成时的场景,仰天大笑还是泪流满面?真正到了这一刻才发现,疲倦已经不允许我有任何情绪。 +我错了。 -说实话,刚开始发起项目的时候完全没想到会发展成今天这样,我一度计划自己一个人翻译完整本书。万万没想到,会有这么多的人愿意加入并贡献出自己的力量。 + -coverxit发给我最后一份文档的时候说,我要去背单词了,我问他,周末要考六级?他说是的。 + -pp-prog告诉我,这几天太累了,校对到一半睡着了,醒来又继续做。2点17分,发给我校对完成的文档。 + -lifedim说他平时12点就会睡,1点47分,发给我校对后的文档。 + -团队里每个人都有自己的事情,上班、上学、创业,但是我们只用了9天就完成整本书的翻译。我不知道大家付出了多少,牺牲了多少,但是我知道,他们的付出必将被这些文字记录下来,即使再过10年,20年,依然熠熠生辉,永不被人遗忘。 + -全体人员名单(排名不分先后): +在我没有任何回复的情况下,不到一天时间,有五位朋友报名。看到这些回复的时候我真的很惊讶,也很感动,无论这个项目存在多少问题,只要有人关注,有人愿意为它付出,那我还有什么理由放弃呢? -- [numbbbbb](https://github.com/numbbbbb) -- [stanzhai](https://github.com/stanzhai) -- [coverxit](https://github.com/coverxit) -- [wh1100717](https://github.com/wh1100717) -- [TimothyYe](https://github.com/TimothyYe) -- [honghaoz](https://github.com/honghaoz) -- [lyuka](https://github.com/lyuka) -- [JaySurplus](https://github.com/JaySurplus) -- [Hawstein](https://github.com/Hawstein) -- [geek5nan](https://github.com/geek5nan) -- [yankuangshi](https://github.com/yankuangshi) -- [xielingwang](https://github.com/xielingwang) -- [yulingtianxia](https://github.com/yulingtianxia) -- [twlkyao](https://github.com/twlkyao) -- [dabing1022](https://github.com/dabing1022) -- [vclwei](https://github.com/vclwei) -- [fd5788](https://github.com/fd5788) -- [siemenliu](https://github.com/siemenliu) -- [youkugems](https://github.com/youkugems) -- [haolloyin](https://github.com/haolloyin) -- [wxstars](https://github.com/wxstars) -- [IceskYsl](https://github.com/IceskYsl) -- [sg552](https://github.com/sg552) -- [superkam](https://github.com/superkam) -- [zac1st1k](https://github.com/zac1st1k) -- [bzsy](https://github.com/bzsy) -- [pyanfield](https://github.com/pyanfield) -- [ericzyh](https://github.com/ericzyh) -- [peiyucn](https://github.com/peiyucn) -- [sunfiled](https://github.com/sunfiled) -- [lzw120](https://github.com/lzw120) -- [viztor](https://github.com/viztor) -- [wongzigii](https://github.com/wongzigii) -- [umcsdon](https://github.com/umcsdon) -- [zq54zquan](https://github.com/zq54zquan) -- [xiehurricane](https://github.com/xiehurricane) -- [Jasonbroker](https://github.com/Jasonbroker) -- [tualatrix](https://github.com/tualatrix) -- [pp-prog](https://github.com/pp-prog) -- [088haizi](https://github.com/088haizi) -- [baocaixiong](https://github.com/baocaixiong) -- [yeahdongcn](https://github.com/yeahdongcn) -- [shinyzhu](https://github.com/shinyzhu) -- [lslxdx](https://github.com/lslxdx) -- [Evilcome](https://github.com/Evilcome) -- [zqp](https://github.com/zqp) -- [NicePiao](https://github.com/NicePiao) -- [LunaticM](https://github.com/LunaticM) -- [menlongsheng](https://github.com/menlongsheng) -- [lifedim](https://github.com/lifedim) -- [happyming](https://github.com/happyming) -- [bruce0505](https://github.com/bruce0505) -- [Lin-H](https://github.com/Lin-H) -- [takalard](https://github.com/takalard) -- [dabing1022](https://github.com/dabing1022) -- [marsprince](https://github.com/marsprince) +6月28日8点55分,Swift 2.0翻译正式启动。按下发送按钮后,我不停的刷新页面,半个小时过去了,一个回复都没有。“还是不行啊”“如果再过一个小时没人回复我就把issue删掉”,类似的念头不断出现,又不断消失。 + +9:35,xtymichael第一个回复,而且一下就认领了三篇!接下来就是不断的回复认领,到中午已经有超过一半章节被认领。 + +第二天早晨,37个章节全部认领完毕。 + +## 3 + +经过一个多月的努力,我们终于完成了文档的更新。听起来似乎没什么,确实,从1到n总是没有从0到1那么振奋人心。不过真正参与了才知道,修改往往比创造更麻烦,一个需要耐心,一个需要激情,前者往往得不到应有的重视。 + +但是我还是想尽最大可能去感谢他们,这个项目能走到今天,靠的不是我,是那个issue,是那些回复,是这几十个兄弟在工作学习的空闲敲下的每一个字符。而我能做的,只是在每篇文章的开头,那个所有人都会忽略的地方,加上他们的ID。 + +下次你再打开这篇文档,可以多看看那些列在最上方的ID,哪怕不去follow和star,只是看一眼就好。他们的所有努力和付出,就存在于这短暂的一瞥中。 + +Swift 2.0 参与者名单(按照章节顺序): +- [xtymichael](https://github.com/xtymichael) +- [AlanMelody](https://github.com/AlanMelody) +- [DianQK](https://github.com/DianQK) +- [dreamkidd](https://github.com/dreamkidd) +- [100mango](https://github.com/100mango) +- [futantan](https://github.com/futantan) +- [SkyJean](https://github.com/SkyJean) +- [yangsiy](https://github.com/yangsiy) +- [shanksyang](https://github.com/shanksyang) +- [chenmingbiao](https://github.com/chenmingbiao) +- [Channe](https://github.com/Channe) +- [lyojo](https://github.com/lyojo) +- [SergioChan](https://github.com/SergioChan) +- [mmoaay](https://github.com/mmoaay) +- [buginux](https://github.com/buginux) +- [KYawn](https://github.com/KYawn) +- [EudeMorgen](https://github.com/EudeMorgen) +- [littledogboy](https://github.com/littledogboy) +- [Lenhoon](https://github.com/Lenhoon) +- [ray16897188](https://github.com/ray16897188) +- [wardenNScaiyi](https://github.com/wardenNScaiyi) +- [miaosiqi](https://github.com/miaosiqi) + +最后,感谢极客学院提供的wiki系统,在国内访问起来速度很快,优化后的样式看起来也更舒服。 \ No newline at end of file diff --git a/source/SUMMARY.md b/source/SUMMARY.md index d9e426c6..36344074 100755 --- a/source/SUMMARY.md +++ b/source/SUMMARY.md @@ -4,6 +4,7 @@ * [关于 Swift](chapter1/01_swift.md) * [Swift 初见](chapter1/02_a_swift_tour.md) * [Swift 版本历史记录](chapter1/03_revision_history.md) + * [Swift 1.0 发布内容](v1.0.md) * [Swift 教程](chapter2/chapter2.md) * [基础部分](chapter2/01_The_Basics.md) * [基本运算符](chapter2/02_Basic_Operators.md) @@ -30,7 +31,7 @@ * [泛型](chapter2/23_Generics.md) * [权限控制](chapter2/24_Access_Control.md) * [高级操作符](chapter2/25_Advanced_Operators.md) -* [语言参考](chapter3/chapter3.md) +* 语言参考 * [关于语言参考](chapter3/01_About_the_Language_Reference.md) * [词法结构](chapter3/02_Lexical_Structure.md) * [类型](chapter3/03_Types.md) @@ -41,7 +42,7 @@ * [模式](chapter3/07_Patterns.md) * [泛型参数](chapter3/08_Generic_Parameters_and_Arguments.md) * [语法总结](chapter3/09_Summary_of_the_Grammar.md) -* [苹果官方Blog官方翻译](chapter4/chapter4.md) +* 苹果官方Blog官方翻译 * [Access Control 权限控制的黑与白](chapter4/01_Access_Control.md) * [造个类型不是梦-白话Swift类型创建](chapter4/02_Type_Custom.md) * [WWDC里面的那个“大炮打气球”](chapter4/03_Ballons.md) diff --git a/source/chapter1/01_swift.md b/source/chapter1/01_swift.md index d00378e4..1b5d9928 100755 --- a/source/chapter1/01_swift.md +++ b/source/chapter1/01_swift.md @@ -1,16 +1,21 @@ +# 关于 Swift(About Swift) +----------------- + +> 1.0 > 翻译:[numbbbbb](https://github.com/numbbbbb) > 校对:[yeahdongcn](https://github.com/yeahdongcn) -# 关于 Swift ------------------ +> 2.0 +> 翻译+校对:[xtymichael](https://github.com/xtymichael) Swift 是一种新的编程语言,用于编写 iOS,OS X 和 watchOS应用程序。Swift 结合了 C 和 Objective-C 的优点并且不受 C 兼容性的限制。Swift 采用安全的编程模式并添加了很多新特性,这将使编程更简单,更灵活,也更有趣。Swift 是基于成熟而且倍受喜爱的 Cocoa 和 Cocoa Touch 框架,它的降临将重新定义软件开发。 -Swift 的开发从很久之前就开始了。为了给 Swift 打好基础,苹果公司改进了编译器,调试器和框架结构。我们使用自动引用计数(Automatic Reference Counting, ARC)来简化内存管理。我们在 Foundation 和 Cocoa 的基础上构建框架栈并将其标准化。Objective-C 本身支持块、集合语法和模块,所以框架可以轻松支持现代编程语言技术。正是得益于这些基础工作,我们现在才能发布这样一个用于未来苹果软件开发的新语言。 +Swift 的开发从很久之前就开始了。为了给 Swift 打好基础,苹果公司改进了编译器,调试器和框架结构。我们使用自动引用计数(Automatic Reference Counting, ARC)来简化内存管理。我们在 Foundation 和 Cocoa 的基础上构建框架栈使其完全现代化和标准化。 +Objective-C 本身支持块、集合语法和模块,所以框架可以轻松支持现代编程语言技术。正是得益于这些基础工作,我们现在才能发布这样一个用于未来苹果软件开发的新语言。 Objective-C 开发者对 Swift 并不会感到陌生。它采用了 Objective-C 的命名参数以及动态对象模型,可以无缝对接到现有的 Cocoa 框架,并且可以兼容 Objective-C 代码。在此基础之上,Swift 还有许多新特性并且支持过程式编程和面向对象编程。 -Swift 对于初学者来说也很友好。它是第一个既满足工业标准又像脚本语言一样充满表现力和趣味的编程语言。它支持代码预览,这个革命性的特性可以允许程序员在不编译和运行应用程序的前提下运行 Swift 代码并实时查看结果。 +Swift 对于初学者来说也很友好。它是第一个既满足工业标准又像脚本语言一样充满表现力和趣味的脚本语言。它支持代码预览,这个革命性的特性可以允许程序员在不编译和运行应用程序的前提下运行 Swift 代码并实时查看结果。 Swift 将现代编程语言的精华和苹果工程师文化的智慧结合了起来。编译器对性能进行了优化,编程语言对开发进行了优化,两者互不干扰,鱼与熊掌兼得。Swift 既可以用于开发 “hello, world” 这样的小程序,也可以用于开发一套完整的操作系统。所有的这些特性让 Swift 对于开发者和苹果来说都是一项值得的投资。 diff --git a/source/chapter1/02_a_swift_tour.md b/source/chapter1/02_a_swift_tour.md index 43f8fa10..6d91a479 100755 --- a/source/chapter1/02_a_swift_tour.md +++ b/source/chapter1/02_a_swift_tour.md @@ -1,9 +1,13 @@ +# Swift 初见(A Swift Tour) + +--- + +> 1.0 > 翻译:[numbbbbb](https://github.com/numbbbbb) > 校对:[shinyzhu](https://github.com/shinyzhu), [stanzhai](https://github.com/stanzhai) -# Swift 初见 - ---- +> 2.0 +> 翻译+校对:[xtymichael](https://github.com/xtymichael) 本页内容包括: @@ -18,16 +22,16 @@ 通常来说,编程语言教程中的第一个程序应该在屏幕上打印“Hello, world”。在 Swift 中,可以用一行代码实现: ```swift -print("Hello, world") +print("Hello, world!") ``` -如果你写过 C 或者 Objective-C 代码,那你应该很熟悉这种形式——在 Swift 中,这行代码就是一个完整的程序。你不需要为了输入输出或者字符串处理导入一个单独的库。全局作用域中的代码会被自动当做程序的入口点,所以你也不需要`main`函数。你同样不需要在每个语句结尾写上分号。 +如果你写过 C 或者 Objective-C 代码,那你应该很熟悉这种形式——在 Swift 中,这行代码就是一个完整的程序。你不需要为了输入输出或者字符串处理导入一个单独的库。全局作用域中的代码会被自动当做程序的入口点,所以你也不需要`main()`函数。你同样不需要在每个语句结尾写上分号。 这个教程会通过一系列编程例子来让你对 Swift 有初步了解,如果你有什么不理解的地方也不用担心——任何本章介绍的内容都会在后面的章节中详细讲解。 > 注意: > 为了获得最好的体验,在 Xcode 当中使用代码预览功能。代码预览功能可以让你编辑代码并实时看到运行结果。 -> 打开Playground +> 下载Playground ## 简单值 @@ -40,7 +44,7 @@ myVariable = 50 let myConstant = 42 ``` -常量或者变量的类型必须和你赋给它们的值一样。然而,声明时类型是可选的,声明的同时赋值的话,编译器会自动推断类型。在上面的例子中,编译器推断出`myVariable`是一个整数(integer)因为它的初始值是整数。 +常量或者变量的类型必须和你赋给它们的值一样。然而,你不用明确地声明类型,声明的同时赋值的话,编译器会自动推断类型。在上面的例子中,编译器推断出`myVariable`是一个整数(integer)因为它的初始值是整数。 如果初始值没有提供足够的信息(或者没有初始值),那你需要在变量后面声明类型,用冒号分割。 @@ -75,14 +79,12 @@ let fruitSummary = "I have \(apples + oranges) pieces of fruit." > 练习: > 使用`\()`来把一个浮点计算转换成字符串,并加上某人的名字,和他打个招呼。 -使用方括号`[]`来创建数组和字典,并使用下标或者键(key)来访问元素。 +使用方括号`[]`来创建数组和字典,并使用下标或者键(key)来访问元素。最后一个元素后面允许有个逗号。 ```swift var shoppingList = ["catfish", "water", "tulips", "blue paint"] shoppingList[1] = "bottle of water" -``` -```swift var occupations = [ "Malcolm": "Captain", "Kaylee": "Mechanic", @@ -107,7 +109,7 @@ occupations = [:] ## 控制流 -使用`if`和`switch`来进行条件操作,使用`for-in`、`for`、`while`和`do-while`来进行循环。包裹条件和循环变量括号可以省略,但是语句体的大括号是必须的。 +使用`if`和`switch`来进行条件操作,使用`for-in`、`for`、`while`和`repeat-while`来进行循环。包裹条件和循环变量括号可以省略,但是语句体的大括号是必须的。 ```swift let individualScores = [75, 43, 103, 87, 12] @@ -124,7 +126,7 @@ print(teamScore) 在`if`语句中,条件必须是一个布尔表达式——这意味着像`if score { ... }`这样的代码将报错,而不会隐形地与 0 做对比。 -你可以一起使用`if`和`let`来处理值缺失的情况。有些变量的值是可选的。一个可选的值可能是一个具体的值或者是`nil`,表示值缺失。在类型后面加一个问号来标记这个变量的值是可选的。 +你可以一起使用`if`和`let`来处理值缺失的情况。这些值可由可选值来代表。一个可选的值是一个具体的值或者是`nil`以表示值缺失。在类型后面加一个问号来标记这个变量的值是可选的。 ```swift var optionalString: String? = "Hello" @@ -148,20 +150,21 @@ if let name = optionalName { let vegetable = "red pepper" switch vegetable { case "celery": - let vegetableComment = "Add some raisins and make ants on a log." + print("Add some raisins and make ants on a log.") case "cucumber", "watercress": - let vegetableComment = "That would make a good tea sandwich." + print("That would make a good tea sandwich.") case let x where x.hasSuffix("pepper"): - let vegetableComment = "Is it a spicy \(x)?" + print("Is it a spicy \(x)?") default: - let vegetableComment = "Everything tastes good in soup." + print("Everything tastes good in soup.") } ``` > 练习: > 删除`default`语句,看看会有什么错误? -声明'let'可用于匹配某部分固定值的模式 + +注意`let`在上述例子的等式中是如何使用的,它将匹配等式的值赋给常量`x`。 运行`switch`中匹配到的子句之后,程序会退出`switch`语句,并不会继续向下运行,所以不需要在每个子句结尾写`break`。 @@ -185,9 +188,9 @@ print(largest) ``` > 练习: -> 添加另一个变量来记录哪种类型的数字是最大的。 +> 添加另一个变量来记录现在和之前最大数字的类型。 -使用`while`来重复运行一段代码直到不满足条件。循环条件可以在开头也可以在结尾。 +使用`while`来重复运行一段代码直到不满足条件。循环条件也可以在结尾,保证能至少循环一次。 ```swift var n = 2 @@ -197,7 +200,7 @@ while n < 100 { print(n) var m = 2 -do { +repeat { m = m * 2 } while m < 100 print(m) @@ -224,7 +227,7 @@ print(secondForLoop) ## 函数和闭包 -使用`func`来声明一个函数,使用名字和参数来调用函数。使用`->`来指定函数返回值。 +使用`func`来声明一个函数,使用名字和参数来调用函数。使用`->`来指定函数返回值的类型。 ```swift func greet(name: String, day: String) -> String { @@ -319,10 +322,10 @@ func lessThanTen(number: Int) -> Bool { return number < 10 } var numbers = [20, 19, 7, 12] -hasAnyMatches(numbers, lessThanTen) +hasAnyMatches(numbers, condition: lessThanTen) ``` -函数实际上是一种特殊的闭包,你可以使用`{}`来创建一个匿名闭包。使用`in`将参数和返回值类型声明与闭包函数体进行分离。 +函数实际上是一种特殊的闭包:它是一段能之后被调取的代码。闭包中的代码能访问闭包所建作用域中能得到的变量和函数,即使闭包是在一个不同的作用域被执行的 - 你已经在嵌套函数例子中所看到。你可以使用`{}`来创建一个匿名闭包。使用`in`将参数和返回值类型声明与闭包函数体进行分离。 ```swift numbers.map({ @@ -335,17 +338,17 @@ numbers.map({ > 练习: > 重写闭包,对所有奇数返回0。 -有很多种创建闭包的方法。如果一个闭包的类型已知,比如作为一个回调函数,你可以忽略参数的类型和返回值。单个语句闭包会把它语句的值当做结果返回。 +有很多种创建更简洁的闭包的方法。如果一个闭包的类型已知,比如作为一个回调函数,你可以忽略参数的类型和返回值。单个语句闭包会把它语句的值当做结果返回。 ```swift let mappedNumbers = numbers.map({ number in 3 * number }) -mappedNumbers +print(mappedNumbers) ``` -你可以通过参数位置而不是参数名字来引用参数——这个方法在非常短的闭包中非常有用。当一个闭包作为最后一个参数传给一个函数的时候,它可以直接跟在括号后面。 +你可以通过参数位置而不是参数名字来引用参数——这个方法在非常短的闭包中非常有用。当一个闭包作为最后一个参数传给一个函数的时候,它可以直接跟在括号后面。当一个闭包是传给函数的唯一参数,你可以完全忽略括号。 ```swift -let sortedNumbers = sorted(numbers) { $0 > $1 } +let sortedNumbers = numbers.sort { $0 > $1 } print(sortedNumbers) ``` @@ -423,9 +426,9 @@ test.simpleDescription() ``` > 练习: -> 创建`NamedShape`的另一个子类`Circle`,构造器接收两个参数,一个是半径一个是名称,实现`area`和`describe`方法。 +> 创建`NamedShape`的另一个子类`Circle`,构造器接收两个参数,一个是半径一个是名称,在子类`Circle`中实现`area()`和`simpleDescription()`方法。 -属性可以有 getter 和 setter 。 +除了储存简单的属性之外,属性可以有 getter 和 setter 。 ```swift class EquilateralTriangle: NamedShape { @@ -438,12 +441,12 @@ class EquilateralTriangle: NamedShape { } var perimeter: Double { - get { - return 3.0 * sideLength - } - set { - sideLength = newValue / 3.0 - } + get { + return 3.0 * sideLength + } + set { + sideLength = newValue / 3.0 + } } override func simpleDescription() -> String { @@ -471,14 +474,14 @@ print(triangle.sideLength) ```swift class TriangleAndSquare { var triangle: EquilateralTriangle { - willSet { - square.sideLength = newValue.sideLength - } + willSet { + square.sideLength = newValue.sideLength + } } var square: Square { - willSet { - triangle.sideLength = newValue.sideLength - } + willSet { + triangle.sideLength = newValue.sideLength + } } init(size: Double, name: String) { square = Square(sideLength: size, name: name) @@ -486,23 +489,10 @@ class TriangleAndSquare { } } var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape") -triangleAndSquare.square.sideLength -triangleAndSquare.triangle.sideLength +print(triangleAndSquare.square.sideLength) +print(triangleAndSquare.triangle.sideLength) triangleAndSquare.square = Square(sideLength: 50, name: "larger square") -triangleAndSquare.triangle.sideLength -``` - -类中的方法和一般的函数有一个重要的区别,函数的参数名只在函数内部使用,但是方法的参数名需要在调用的时候显式说明(除了第一个参数)。默认情况下,方法的参数名和它在方法内部的名字一样,不过你也可以定义第二个名字,这个名字被用在方法内部。 - -```swift -class Counter { - var count: Int = 0 - func incrementBy(amount: Int, numberOfTimes times: Int) { - count += amount * times - } -} -var counter = Counter() -counter.incrementBy(2, numberOfTimes: 7) +print(triangleAndSquare.triangle.sideLength) ``` 处理变量的可选值时,你可以在操作(比如方法、属性和子脚本)之前加`?`。如果`?`之前的值是`nil`,`?`后面的东西都会被忽略,并且整个表达式返回`nil`。否则,`?`之后的东西都会被运行。在这两种情况下,整个表达式的值也是一个可选值。 @@ -544,9 +534,9 @@ let aceRawValue = ace.rawValue > 练习: > 写一个函数,通过比较它们的原始值来比较两个`Rank`值。 -在上面的例子中,枚举原始值的类型是`Int`,所以你只需要设置第一个原始值。剩下的原始值会按照顺序赋值。你也可以使用字符串或者浮点数作为枚举的原始值。 +在上面的例子中,枚举原始值的类型是`Int`,所以你只需要设置第一个原始值。剩下的原始值会按照顺序赋值。你也可以使用字符串或者浮点数作为枚举的原始值。使用`rawValue`属性来访问一个枚举成员的原始值。 -使用'rawValue'在原始值和枚举值之间进行转换。 +使用`init?(rawValue:)`初始化构造器在原始值和枚举值之间进行转换。 ```swift if let convertedRank = Rank(rawValue: 3) { @@ -554,7 +544,7 @@ if let convertedRank = Rank(rawValue: 3) { } ``` -枚举的成员值是实际值,并不是原始值的另一种表达方法。实际上,如果原始值没有意义,你不需要设置。 +枚举的成员值是实际值,并不是原始值的另一种表达方法。实际上,以防原始值没有意义,你不需要设置。 ```swift enum Suit { @@ -571,19 +561,17 @@ enum Suit { return "clubs" } } - } let hearts = Suit.Hearts let heartsDescription = hearts.simpleDescription() ``` > 练习: -> 给`Suit`添加一个`color`方法,对`spades`和`clubs`返回“black”,对`hearts`和`diamonds`返回“red”。 +> 给`Suit`添加一个`color()`方法,对`spades`和`clubs`返回“black”,对`hearts`和`diamonds`返回“red”。 注意,有两种方式可以引用`Hearts`成员:给`hearts`常量赋值时,枚举成员`Suit.Hearts`需要用全名来引用,因为常量没有显式指定类型。在`switch`里,枚举成员使用缩写`.Hearts`来引用,因为`self`的值已经知道是一个`suit`。已知变量类型的情况下你可以使用缩写。 -使用`struct`来创建一个结构体。结构体和类有很多相同的地方,比如方法和构造器。它们之间最大的一个区别就是 -结构体是传值,类是传引用。 +使用`struct`来创建一个结构体。结构体和类有很多相同的地方,比如方法和构造器。它们之间最大的一个区别就是结构体是传值,类是传引用。 ```swift struct Card { @@ -609,10 +597,10 @@ enum ServerResponse { case Result(String, String) case Error(String) } - + let success = ServerResponse.Result("6:00 am", "8:09 pm") let failure = ServerResponse.Error("Out of cheese.") - + switch success { case let .Result(sunrise, sunset): let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)." @@ -624,7 +612,7 @@ case let .Error(error): > 练习: > 给`ServerResponse`和`switch`添加第三种情况。 -注意如何从`ServerResponse`中提取日升和日落时间。 +注意如何从`ServerResponse`中提取日升和日落时间并用得到的值用来和`switch`的情况作比较。 ## 协议和扩展 @@ -666,20 +654,20 @@ let bDescription = b.simpleDescription > 练习: > 写一个实现这个协议的枚举。 -注意声明`SimpleStructure`时候`mutating`关键字用来标记一个会修改结构体的方法。`SimpleClass`的声明不需要标记任何方法因为类中的方法经常会修改类。 +注意声明`SimpleStructure`时候`mutating`关键字用来标记一个会修改结构体的方法。`SimpleClass`的声明不需要标记任何方法,因为类中的方法通常可以修改类属性(类的性质)。 -使用`extension`来为现有的类型添加功能,比如新的方法和参数。你可以使用扩展在别处修改定义,甚至是从外部库或者框架引入的一个类型,使得这个类型遵循某个协议。 +使用`extension`来为现有的类型添加功能,比如新的方法和计算属性。你可以使用扩展在别处修改定义,甚至是从外部库或者框架引入的一个类型,使得这个类型遵循某个协议。 ```swift extension Int: ExampleProtocol { var simpleDescription: String { - return "The number \(self)" + return "The number \(self)" } mutating func adjust() { self += 42 } } -7.simpleDescription +print(7.simpleDescription) ``` > 练习: @@ -689,8 +677,8 @@ extension Int: ExampleProtocol { ```swift let protocolValue: ExampleProtocol = a -protocolValue.simpleDescription -// protocolValue.anotherProperty // Uncomment to see the error +print(protocolValue.simpleDescription) +// print(protocolValue.anotherProperty) // Uncomment to see the error ``` 即使`protocolValue`变量运行时的类型是`simpleClass`,编译器会把它的类型当做`ExampleProtocol`。这表示你不能调用类在它实现的协议之外实现的方法或者属性。 @@ -701,32 +689,32 @@ protocolValue.simpleDescription 在尖括号里写一个名字来创建一个泛型函数或者类型。 ```swift -func repeat| Character | +D U+0044 |
+ o U+006F |
+ g U+0067 |
+ ‼ U+203C |
+ 🐶 U+1F436 |
+ |||||
| UTF-8 Code Unit |
+ 68 | +111 | +103 | +226 | +128 | +188 | +240 | +159 | +144 | +182 | +
| Position | +0 | +1 | +2 | +3 | +4 | +5 | +6 | +7 | +8 | +9 | +
| Character | +D U+0044 |
+ o U+006F |
+ g U+0067 |
+ ‼ U+203C |
+ 🐶 U+1F436 |
+ |
| UTF-16 Code Unit |
+ 68 | +111 | +103 | +8252 | +55357 | +56374 | +
| Position | +0 | +1 | +2 | +3 | +4 | +5 | +
| Character | +D U+0044 |
+ o U+006F |
+ g U+0067 |
+ ‼ U+203C |
+ 🐶 U+1F436 |
+
| UTF-16 Code Unit |
+ 68 | +111 | +103 | +8252 | +128054 | +
| Position | +0 | +1 | +2 | +3 | +4 | +
Set类型被桥接到Fundation中的NSSet类
-> 关于使用Fundation和Cocoa中集合的知识,请看Swift与Cocoa和Objective-C使用
+> 注意:
+> Swift的`Set`类型被桥接到`Fundation`中的`NSSet`类。
+> 关于使用`Fundation`和`Cocoa`中`Set`的知识,请看 [*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)。
+
+
+#### 集合类型的哈希值
+
+一个类型为了存储在集合中,该类型必须是可哈希化的--也就是说,该类型必须提供一个方法来计算它的哈希值。一个哈希值是`Int`类型的,相等的对象哈希值必须相同,比如`a==b`,因此必须`a.hashValue == b.hashValue`。
+
+Swift 的所有基本类型(比如`String`,`Int`,`Double`和`Bool`)默认都是可哈希化的,可以作为集合的值的类型或者字典的键的类型。没有关联值的枚举成员值(在[枚举](./08_Enumerations.html)有讲述)默认也是可哈希化的。
+
+> 注意:
+> 你可以使用你自定义的类型作为集合的值的类型或者是字典的键的类型,但你需要使你的自定义类型符合 Swift 标准库中的`Hashable`协议。符合`Hashable`协议的类型需要提供一个类型为`Int`的可读属性`hashValue`。由类型的`hashValue`属性返回的值不需要在同一程序的不同执行周期或者不同程序之间保持相同。
+
+> 因为`Hashable`协议符合`Equatable`协议,所以符合该协议的类型也必须提供一个"是否相等"运算符(`==`)的实现。这个`Equatable`协议要求任何符合`==`实现的实例间都是一种相等的关系。也就是说,对于`a,b,c`三个值来说,`==`的实现必须满足下面三种情况:
+
+> * `a == a`(自反性)
+> * `a == b`意味着`b == a`(对称性)
+> * `a == b && b == c`意味着`a == c`(传递性)
+
+关于符合协议的更多信息,请看[协议](./22_Protocols.html)。
-### Set类型语法
+### 集合类型语法
-Swift中的Set类型被写为```Set
@@ -128,28 +133,28 @@ default:
对于库存跟踪系统来说,能够把 UPC-A 码作为三个整型值的元组,和把 QR 码作为一个任何长度的字符串存储起来是方便的。
-在 Swift 中,用来定义两种商品条码的枚举是这样子的:
+在 Swift 中,使用如下方式定义两种商品条码的枚举:
```swift
enum Barcode {
- case UPCA(Int, Int, Int)
+ case UPCA(Int, Int, Int, Int)
case QRCode(String)
}
```
以上代码可以这么理解:
-“定义一个名为`Barcode`的枚举类型,它可以是`UPCA`的一个相关值(`Int`,`Int`,`Int`),或者`QRCode`的一个字符串类型(`String`)相关值。”
+“定义一个名为`Barcode`的枚举类型,它可以是`UPCA`的一个相关值(`Int`,`Int`,`Int`,`Int`),或者是`QRCode`的一个字符串类型(`String`)相关值。”
这个定义不提供任何`Int`或`String`的实际值,它只是定义了,当`Barcode`常量和变量等于`Barcode.UPCA`或`Barcode.QRCode`时,相关值的类型。
然后可以使用任何一种条码类型创建新的条码,如:
```swift
-var productBarcode = Barcode.UPCA(8, 85909_51226, 3)
+var productBarcode = Barcode.UPCA(8, 85909, 51226, 3)
```
-以上例子创建了一个名为`productBarcode`的新变量,并且赋给它一个`Barcode.UPCA`的相关元组值`(8, 8590951226, 3)`。提供的“标识符”值在整数字中有一个下划线,使其便于阅读条形码。
+以上例子创建了一个名为`productBarcode`的变量,并且赋给它一个`Barcode.UPCA`的相关元组值`(8, 85909, 51226, 3)`。
同一个商品可以被分配给一个不同类型的条形码,如:
@@ -163,32 +168,32 @@ productBarcode = .QRCode("ABCDEFGHIJKLMNOP")
```swift
switch productBarcode {
-case .UPCA(let numberSystem, let identifier, let check):
- println("UPC-A with value of \(numberSystem), \(identifier), \(check).")
+case .UPCA(let numberSystem, let manufacturer, let product, let check):
+ print("UPC-A: \(numberSystem), \(manufacturer), \(product), \(check).")
case .QRCode(let productCode):
- println("QR code with value of \(productCode).")
+ print("QR code: \(productCode).")
}
-// 输出 "QR code with value of ABCDEFGHIJKLMNOP.”
+// 输出 "QR code: ABCDEFGHIJKLMNOP."
```
如果一个枚举成员的所有相关值被提取为常量,或者它们全部被提取为变量,为了简洁,你可以只放置一个`var`或者`let`标注在成员名称前:
```swift
switch productBarcode {
-case let .UPCA(numberSystem, identifier, check):
- println("UPC-A with value of \(numberSystem), \(identifier), \(check).")
+case let .UPCA(numberSystem, manufacturer, product, check):
+ print("UPC-A: \(numberSystem), \(manufacturer), \(product), \(check).")
case let .QRCode(productCode):
- println("QR code with value of \(productCode).")
+ print("QR code: \(productCode).")
}
-// 输出 "QR code with value of ABCDEFGHIJKLMNOP."
+// 输出 "QR code: ABCDEFGHIJKLMNOP."
```
## 原始值(Raw Values)
-在[Associated Values](#raw_values)小节的条形码例子中演示了一个枚举的成员如何声明它们存储不同类型的相关值。作为相关值的替代,枚举成员可以被默认值(称为原始值)预先填充,其中这些原始值具有相同的类型。
+在[相关值](#raw_values)小节的条形码例子中演示了一个枚举的成员如何声明它们存储不同类型的相关值。作为相关值的另一种选择,枚举成员可以被默认值(称为原始值)赋值,其中这些原始值具有相同的类型。
-这里是一个枚举成员存储原始 ASCII 值的例子:
+这里是一个枚举成员存储 ASCII 码的例子:
```swift
enum ASCIIControlCharacter: Character {
@@ -198,11 +203,19 @@ enum ASCIIControlCharacter: Character {
}
```
-在这里,称为`ASCIIControlCharacter`的枚举的原始值类型被定义为字符型`Character`,并被设置了一些比较常见的 ASCII 控制字符。字符值的描述请详见字符串和字符[`Strings and Characters`](03_Strings_and_Characters.html)部分。
+在这里,`ASCIIControlCharacter`的枚举类型的原始值类型被定义为字符型`Character`,并被设置了一些比较常见的 ASCII 控制字符。字符值的描述请详见[字符串和字符](./03_Strings_and_Characters.html)部分。
-注意,原始值和相关值是不相同的。当你开始在你的代码中定义枚举的时候原始值是被预先填充的值,像上述三个 ASCII 码。对于一个特定的枚举成员,它的原始值始终是相同的。相关值是当你在创建一个基于枚举成员的新常量或变量时才会被设置,并且每次当你这么做得时候,它的值可以是不同的。
-原始值可以是字符串,字符,或者任何整型值或浮点型值。每个原始值在它的枚举声明中必须是唯一的。当整型值被用于原始值,如果其他枚举成员没有值时,它们会自动递增。
+原始值可以是字符串,字符,或者任何整型值或浮点型值。每个原始值在它的枚举声明中必须是唯一的。
+
+>注意:
+>原始值和相关值是不相同的。当你开始在你的代码中定义枚举的时候原始值是被预先填充的值,像上述三个 ASCII 码。对于一个特定的枚举成员,它的原始值始终是相同的。相关值是当你在创建一个基于枚举成员的新常量或变量时才会被设置,并且每次当你这么做得时候,它的值可以是不同的。
+
+### 原始值的隐式赋值(Implicitly Assigned Raw Values)
+
+在使用原始值为整数或者字符串类型的枚举时,不需要显式的为每一个成员赋值,这时,Swift将会自动为你赋值。
+
+例如,当使用整数作为原始值时,隐式赋值的值依次递增1。如果第一个值没有被赋初值,将会被自动置为0。
下面的枚举是对之前`Planet`这个枚举的一个细化,利用原始整型值来表示每个 planet 在太阳系中的顺序:
@@ -212,23 +225,46 @@ enum Planet: Int {
}
```
-自动递增意味着`Planet.Venus`的原始值是`2`,依次类推。
+在上面的例子中,`Plant.Mercury`赋了初值`1`,`Planet.Venus`会拥有隐式赋值`2`,依次类推。
+
+当使用字符串作为枚举类型的初值时,每个枚举成员的隐式初值则为该成员的名称。
+
+下面的例子是`CompassPoint`枚举类型的精简版,使用字符串作为初值类型,隐式初始化为咩个方向的名称:
+
+```swift
+enum CompassPoint: String {
+ case North, South, East, West
+}
+```
+
+上面例子中,`CompassPoint.South`拥有隐式初值`South`,依次类推。
使用枚举成员的`rawValue`属性可以访问该枚举成员的原始值:
```swift
let earthsOrder = Planet.Earth.rawValue
-// earthsOrder is 3
-```
+// earthsOrder 值为 3
-通过参数为`rawValue`构造函数创建特定原始值的枚举。这个例子通过原始值`7`识别`Uranus`:
+let sunsetDirection = CompassPoint.West.rawValue
+// sunsetDirection 值为 "West"
+```
+### 使用原始值来初始化(Initializing from a Raw Value)
+
+### 使用原始值初始化枚举变量(Initializing from a Raw Value)
+
+如果在定义枚举类型的时候使用了原始值,那么将会自动获得一个初始化方法,这个方法将原始值类型作为参数,返回枚举成员或者`nil`。你可以使用这种初始化方法来创建一个新的枚举变量。
+
+这个例子通过原始值`7`从而创建枚举成员:
```swift
let possiblePlanet = Planet(rawValue: 7)
-// possiblePlanet is of type Planet? and equals Planet.Uranus
+// possiblePlanet 类型为 Planet? 值为 Planet.Uranus
```
-然而,并非所有可能的`Int`值都可以找到一个匹配的行星。正因为如此,构造函数可以返回一个***可选***的枚举成员。在上面的例子中,`possiblePlanet`是`Planet?`类型,或“可选的`Planet`”。
+然而,并非所有可能的`Int`值都可以找到一个匹配的行星。正因为如此,构造函数可以返回一个*可选*的枚举成员。在上面的例子中,`possiblePlanet`是`Planet?`类型,或“可选的`Planet`”。
+
+>注意:
+>原始值构造器是一个可失败构造器,因为并不是每一个原始值都有与之对应的枚举成员。更多信息请参见[可失败构造器](../chapter3/05_Declarations#failable_initializers)
如果你试图寻找一个位置为9的行星,通过参数为`rawValue`构造函数返回的可选`Planet`值将是`nil`:
@@ -237,15 +273,71 @@ let positionToFind = 9
if let somePlanet = Planet(rawValue: positionToFind) {
switch somePlanet {
case .Earth:
- println("Mostly harmless")
+ print("Mostly harmless")
default:
- println("Not a safe place for humans")
+ print("Not a safe place for humans")
}
} else {
- println("There isn't a planet at position \(positionToFind)")
+ print("There isn't a planet at position \(positionToFind)")
}
// 输出 "There isn't a planet at position 9
```
这个范例使用可选绑定(optional binding),通过原始值`9`试图访问一个行星。`if let somePlanet = Planet(rawValue: 9)`语句获得一个可选`Planet`,如果可选`Planet`可以被获得,把`somePlanet`设置成该可选`Planet`的内容。在这个范例中,无法检索到位置为`9`的行星,所以`else`分支被执行。
+
+## 递归枚举(Recursive Enumerations)
+
+在对操作符进行描述的时候,使用枚举类型来对数据建模很方便,因为需要考虑的情况固定可枚举。操作符可以将两个由数字组成的算数表达式连接起来,例如,将`5`连接成复杂一些的表达式`5+4`.
+
+算数表达式的一个重要特性是,表达式可以嵌套使用。例如,表达式`(5 + 4) * 2`乘号右边是一个数字,左边则是另一个表达式。因为数据是嵌套的,因而用来存储数据的枚举类型也许要支持这种嵌套————这表示枚举类型需要支持递归。
+
+`递归枚举(recursive enumeration)`是一种枚举类型,表示它的枚举中,有一个或多个枚举成员拥有该枚举的其他成员作为相关值。使用递归枚举时,编译器会插入一个中间层。你可以在枚举成员前加上`indirect`来表示这成员可递归。
+
+例如,下面的例子中,枚举类型存储了简单的算数表达式:
+
+
+```swift
+enum ArithmeticExpression {
+ case Number(Int)
+ indirect case Addition(ArithmeticExpression, ArithmeticExpression)
+ indirect case Multiplication(ArithmeticExpression, ArithmeticExpression)
+}
+```
+
+你也可以在枚举类型开头加上`indirect`关键字来表示它的所有成员都是可递归的:
+
+```swift
+indirect enum ArithmeticExpression {
+ case Number(Int)
+ case Addition(ArithmeticExpression, ArithmeticExpression)
+ case Multiplication(ArithmeticExpression, ArithmeticExpression)
+}
+```
+
+上面定义的枚举类型可以存储三种算数表达式:纯数字、两个表达式的相加、两个表达式相乘。`Addition` 和 `Multiplication`成员的相关值也是算数表达式————这些相关值使得嵌套表达式成为可能。
+
+递归函数可以很直观地使用具有递归性质的数据结构。例如,下面是一个计算算数表达式的函数:
+
+```swift
+func evaluate(expression: ArithmeticExpression) -> Int {
+ switch expression {
+ case .Number(let value):
+ return value
+ case .Addition(let left, let right):
+ return evaluate(left) + evaluate(right)
+ case .Multiplication(let left, let right):
+ 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 f95de7d5..74257884 100755
--- a/source/chapter2/09_Classes_and_Structures.md
+++ b/source/chapter2/09_Classes_and_Structures.md
@@ -1,7 +1,11 @@
+# 类和结构体(Classes and Structures)
+
+> 1.0
> 翻译:[JaySurplus](https://github.com/JaySurplus)
> 校对:[sg552](https://github.com/sg552)
-# 类和结构体
+> 2.0
+> 翻译+校对:[SkyJean](https://github.com/SkyJean)
本页包含内容:
@@ -9,9 +13,10 @@
- [结构体和枚举是值类型](#structures_and_enumerations_are_value_types)
- [类是引用类型](#classes_are_reference_types)
- [类和结构体的选择](#choosing_between_classes_and_structures)
-- [集合(collection)类型的赋值与复制行为](#assignment_and_copy_behavior_for_collection_types)
+- [字符串(String)、数组(Array)、和字典(Dictionary)类型的赋值与复制行为](#assignment_and_copy_behavior_for_strings_arrays_and_dictionaries)
-类和结构体是人们构建代码所用的一种通用且灵活的构造体。为了在类和结构体中实现各种功能,我们必须要严格按照常量、变量以及函数所规定的语法规则来定义属性和添加方法。
+
+类和结构体是人们构建代码所用的一种通用且灵活的构造体。我们可以使用完全相同的语法规则来为类和结构体定义属性(常量、变量)和添加方法,从而扩展类和结构体的功能。
与其他编程语言所不同的是,Swift 并不要求你为自定义类和结构去创建独立的接口和实现文件。你所要做的是在一个单一文件中定义一个类或者结构体,系统将会自动生成面向其它代码的外部接口。
@@ -28,9 +33,9 @@ Swift 中类和结构体有很多共同点。共同处在于:
* 定义附属脚本用于访问值
* 定义构造器用于生成初始化值
* 通过扩展以增加默认实现的功能
-* 符合协议以对某类提供标准功能
+* 实现协议以提供某种标准功能
-更多信息请参见 [属性](10_Properties.html),[方法](11_Methods.html),[下标脚本](12_Subscripts.html),[初始过程](14_Initialization.html),[扩展](20_Extensions.html),和[协议](21_Protocols.html)。
+更多信息请参见 [属性](./10_Properties.html),[方法](./11_Methods.html),[下标脚本](./12_Subscripts.html),[初始过程](./14_Initialization.html),[扩展](./20_Extensions.html),和[协议](./21_Protocols.html)。
与结构体相比,类还有如下的附加功能:
@@ -39,7 +44,7 @@ Swift 中类和结构体有很多共同点。共同处在于:
* 解构器允许一个类实例释放任何其所被分配的资源
* 引用计数允许对一个类的多次引用
-更多信息请参见[继承](http://),[类型转换](http://),[初始化](http://),和[自动引用计数](http://)。
+更多信息请参见[继承](./13_Inheritance.html),[类型转换](./20_Type_Casting.html),[析构过程](./15_Deinitialization),和[自动引用计数](./16_Automatic_Reference_Counting)。
> 注意:
结构体总是通过被复制的方式在代码中传递,因此请不要使用引用计数。
@@ -77,7 +82,7 @@ class VideoMode {
在上面的示例中我们定义了一个名为`Resolution`的结构体,用来描述一个显示器的像素分辨率。这个结构体包含了两个名为`width`和`height`的存储属性。存储属性是捆绑和存储在类或结构体中的常量或变量。当这两个属性被初始化为整数`0`的时候,它们会被推断为`Int`类型。
-在上面的示例中我们还定义了一个名为`VideoMode`的类,用来描述一个视频显示器的特定模式。这个类包含了四个储存属性变量。第一个是`分辨率`,它被初始化为一个新的`Resolution`结构体的实例,具有`Resolution`的属性类型。新`VideoMode`实例同时还会初始化其它三个属性,它们分别是,初始值为`false`(意为“non-interlaced video”)的`interlaced`,回放帧率初始值为`0.0`的`frameRate`和值为可选`String`的`name`。`name`属性会被自动赋予一个默认值`nil`,意为“没有`name`值”,因为它是一个可选类型。
+在上面的示例中我们还定义了一个名为`VideoMode`的类,用来描述一个视频显示器的特定模式。这个类包含了四个储存属性变量。第一个是`分辨率`,它被初始化为一个新的`Resolution`结构体的实例,具有`Resolution`的属性类型。新`VideoMode`实例同时还会初始化其它三个属性,它们分别是,初始值为`false`(意为“非隔行扫描视频”)的`interlaced`,回放帧率初始值为`0.0`的`frameRate`和值为可选`String`的`name`。`name`属性会被自动赋予一个默认值`nil`,意为“没有`name`值”,因为它是一个可选类型。
### 类和结构体实例
@@ -90,14 +95,14 @@ let someResolution = Resolution()
let someVideoMode = VideoMode()
```
-结构体和类都使用构造器语法来生成新的实例。构造器语法的最简单形式是在结构体或者类的类型名称后跟随一个空括弧,如`Resolution()`或`VideoMode()`。通过这种方式所创建的类或者结构体实例,其属性均会被初始化为默认值。[构造过程](14_Initialization.html)章节会对类和结构体的初始化进行更详细的讨论。
+结构体和类都使用构造器语法来生成新的实例。构造器语法的最简单形式是在结构体或者类的类型名称后跟随一对空括号,如`Resolution()`或`VideoMode()`。通过这种方式所创建的类或者结构体实例,其属性均会被初始化为默认值。[构造过程](./14_Initialization.html)章节会对类和结构体的初始化进行更详细的讨论。
### 属性访问
通过使用*点语法*(*dot syntax*),你可以访问实例中所含有的属性。其语法规则是,实例名后面紧跟属性名,两者通过点号(.)连接:
```swift
-println("The width of someResolution is \(someResolution.width)")
+print("The width of someResolution is \(someResolution.width)")
// 输出 "The width of someResolution is 0"
```
@@ -106,7 +111,7 @@ println("The width of someResolution is \(someResolution.width)")
你也可以访问子属性,如`VideoMode`中`Resolution`属性的`width`属性:
```swift
-println("The width of someVideoMode is \(someVideoMode.resolution.width)")
+print("The width of someVideoMode is \(someVideoMode.resolution.width)")
// 输出 "The width of someVideoMode is 0"
```
@@ -114,31 +119,31 @@ println("The width of someVideoMode is \(someVideoMode.resolution.width)")
```swift
someVideoMode.resolution.width = 1280
-println("The width of someVideoMode is now \(someVideoMode.resolution.width)")
+print("The width of someVideoMode is now \(someVideoMode.resolution.width)")
// 输出 "The width of someVideoMode is now 1280"
```
> 注意:
与 Objective-C 语言不同的是,Swift 允许直接设置结构体属性的子属性。上面的最后一个例子,就是直接设置了`someVideoMode`中`resolution`属性的`width`这个子属性,以上操作并不需要重新设置`resolution`属性。
-### 结构体类型的成员逐一构造器(Memberwise Initializers for structure Types)
+### 结构体类型的成员逐一构造器(Memberwise Initializers for Structure Types)
-所有结构体都有一个自动生成的成员逐一构造器,用于初始化新结构体实例中成员的属性。新实例中各个属性的初始值可以通过属性的名称传递到成员逐一构造器之中:
+所有结构体都有一个自动生成的*成员逐一构造器*,用于初始化新结构体实例中成员的属性。新实例中各个属性的初始值可以通过属性的名称传递到成员逐一构造器之中:
```swift
let vga = Resolution(width:640, height: 480)
```
-与结构体不同,类实例没有默认的成员逐一构造器。[构造过程](14_Initialization.html)章节会对构造器进行更详细的讨论。
+与结构体不同,类实例没有默认的成员逐一构造器。[构造过程](./14_Initialization.html)章节会对构造器进行更详细的讨论。
## 结构体和枚举是值类型
-值类型被赋予给一个变量,常数或者本身被传递给一个函数的时候,实际上操作的是其的拷贝。
+*值类型*被赋予给一个变量、常量或者本身被传递给一个函数的时候,实际上操作的是其的*拷贝*。
-在之前的章节中,我们已经大量使用了值类型。实际上,在 Swift 中,所有的基本类型:整数(Integer)、浮点数(floating-point)、布尔值(Booleans)、字符串(string)、数组(array)和字典(dictionaries),都是值类型,并且都是以结构体的形式在后台所实现。
+在之前的章节中,我们已经大量使用了值类型。实际上,在 Swift 中,所有的基本类型:整数(Integer)、浮点数(floating-point)、布尔值(Boolean)、字符串(string)、数组(array)和字典(dictionary),都是值类型,并且都是以结构体的形式在后台所实现。
-在 Swift 中,所有的结构体和枚举都是值类型。这意味着它们的实例,以及实例中所包含的任何值类型属性,在代码中传递的时候都会被复制。
+在 Swift 中,所有的结构体和枚举类型都是值类型。这意味着它们的实例,以及实例中所包含的任何值类型属性,在代码中传递的时候都会被复制。
请看下面这个示例,其使用了前一个示例中`Resolution`结构体:
@@ -160,18 +165,18 @@ cinema.width = 2048
这里,将会显示`cinema`的`width`属性确已改为了`2048`:
```swift
-println("cinema is now \(cinema.width) pixels wide")
+print("cinema is now \(cinema.width) pixels wide")
// 输出 "cinema is now 2048 pixels wide"
```
然而,初始的`hd`实例中`width`属性还是`1920`:
```swift
-println("hd is still \(hd.width ) pixels wide")
+print("hd is still \(hd.width ) pixels wide")
// 输出 "hd is still 1920 pixels wide"
```
-在将`hd`赋予给`cinema`的时候,实际上是将`hd`中所存储的`值(values)`进行拷贝,然后将拷贝的数据存储到新的`cinema`实例中。结果就是两个完全独立的实例碰巧包含有相同的数值。由于两者相互独立,因此将`cinema`的`width`修改为`2048`并不会影响`hd`中的宽(width)。
+在将`hd`赋予给`cinema`的时候,实际上是将`hd`中所存储的`值(values)`进行拷贝,然后将拷贝的数据存储到新的`cinema`实例中。结果就是两个完全独立的实例碰巧包含有相同的数值。由于两者相互独立,因此将`cinema`的`width`修改为`2048`并不会影响`hd`中的`width`的值。
枚举也遵循相同的行为准则:
@@ -183,7 +188,7 @@ var currentDirection = CompassPoint.West
let rememberedDirection = currentDirection
currentDirection = .East
if rememberedDirection == .West {
- println("The remembered direction is still .West")
+ print("The remembered direction is still .West")
}
// 输出 "The remembered direction is still .West"
```
@@ -219,7 +224,7 @@ alsoTenEighty.frameRate = 30.0
下面,通过查看`tenEighty`的`frameRate`属性,我们会发现它正确的显示了基本`VideoMode`实例的新帧率,其值为`30.0`:
```swift
-println("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
+print("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
// 输出 "The frameRate property of theEighty is now 30.0"
```
@@ -238,7 +243,7 @@ println("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
```swift
if tenEighty === alsoTenEighty {
- println("tenEighty and alsoTenEighty refer to the same Resolution instance.")
+ print("tenEighty and alsoTenEighty refer to the same Resolution instance.")
}
//输出 "tenEighty and alsoTenEighty refer to the same Resolution instance."
```
@@ -248,11 +253,11 @@ if tenEighty === alsoTenEighty {
* “等价于”表示两个类类型(class type)的常量或者变量引用同一个类实例。
* “等于”表示两个实例的值“相等”或“相同”,判定时要遵照类设计者定义定义的评判标准,因此相比于“相等”,这是一种更加合适的叫法。
-当你在定义你的自定义类和结构体的时候,你有义务来决定判定两个实例“相等”的标准。在章节[运算符函数(Operator Functions)](24_Advanced_Operators.html#operator_functions)中将会详细介绍实现自定义“等于”和“不等于”运算符的流程。
+当你在定义你的自定义类和结构体的时候,你有义务来决定判定两个实例“相等”的标准。在章节[等价操作符](./24_Advanced_Operators.html#equivalence_operators)中将会详细介绍实现自定义“等于”和“不等于”运算符的流程。
### 指针
-如果你有 C,C++ 或者 Objective-C 语言的经验,那么你也许会知道这些语言使用指针来引用内存中的地址。一个 Swift 常量或者变量引用一个引用类型的实例与 C 语言中的指针类似,不同的是并不直接指向内存中的某个地址,而且也不要求你使用星号(*)来表明你在创建一个引用。Swift 中这些引用与其它的常量或变量的定义方式相同。
+如果你有 C,C++ 或者 Objective-C 语言的经验,那么你也许会知道这些语言使用*指针*来引用内存中的地址。一个 Swift 常量或者变量引用一个引用类型的实例与 C 语言中的指针类似,不同的是并不直接指向内存中的某个地址,而且也不要求你使用星号(*)来表明你在创建一个引用。Swift 中这些引用与其它的常量或变量的定义方式相同。
## 类和结构体的选择
@@ -268,7 +273,7 @@ if tenEighty === alsoTenEighty {
* 任何在结构体中储存的值类型属性,也将会被拷贝,而不是被引用。
* 结构体不需要去继承另一个已存在类型的属性或者行为。
-合适的结构体候选者包括:
+举例来说,以下情境中适合使用结构体:
* 几何形状的大小,封装一个`width`属性和`height`属性,两者均为`Double`类型。
* 一定范围内的路径,封装一个`start`属性和`length`属性,两者均为`Int`类型。
@@ -276,15 +281,14 @@ if tenEighty === alsoTenEighty {
在所有其它案例中,定义一个类,生成一个它的实例,并通过引用来管理和传递。实际中,这意味着绝大部分的自定义数据构造都应该是类,而非结构体。
-
-## 集合(Collection)类型的赋值和拷贝行为
+
+## 字符串(String)、数组(Array)、和字典(Dictionary)类型的赋值与复制行为
-Swift 中`字符串(String)`,`数组(Array)`和`字典(Dictionary)`类型均以结构体的形式实现。这意味着String,Array,Dictionary类型数据被赋值给新的常量(或变量),或者被传入函数(或方法)中时,它们的值会发生拷贝行为(值传递方式)。
+Swift 中`字符串(String)`,`数组(Array)`和`字典(Dictionary)`类型均以结构体的形式实现。这意味着String,Array,Dictionary类型数据被赋值给新的常量或变量,或者被传入函数或方法中时,它们的值会发生拷贝行为(值传递方式)。
Objective-C中`字符串(NSString)`,`数组(NSArray)`和`字典(NSDictionary)`类型均以类的形式实现,这与Swfit中以值传递方式是不同的。NSString,NSArray,NSDictionary在发生赋值或者传入函数(或方法)时,不会发生值拷贝,而是传递已存在实例的引用。
> 注意:
-以上是对于数组,字典,字符串和其它值的`拷贝`的描述。
+以上是对于字符串、数组、字典和其它值的`拷贝`的描述。
在你的代码中,拷贝好像是确实是在有拷贝行为的地方产生过。然而,在 Swift 的后台中,只有确有必要,`实际(actual)`拷贝才会被执行。Swift 管理所有的值拷贝以确保性能最优化的性能,所以你也没有必要去避免赋值以保证最优性能。(实际赋值由系统管理优化)
-
diff --git a/source/chapter2/10_Properties.md b/source/chapter2/10_Properties.md
index bf305032..1a341cc0 100755
--- a/source/chapter2/10_Properties.md
+++ b/source/chapter2/10_Properties.md
@@ -1,8 +1,12 @@
-> 翻译:[shinyzhu](https://github.com/shinyzhu)
-> 校对:[pp-prog](https://github.com/pp-prog)
-
# 属性 (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)
本页包含内容:
@@ -12,18 +16,18 @@
- [全局变量和局部变量(Global and Local Variables)](#global_and_local_variables)
- [类型属性(Type Properties)](#type_properties)
-**属性**将值跟特定的类、结构或枚举关联。存储属性存储常量或变量作为实例的一部分,计算属性计算(而不是存储)一个值。计算属性可以用于类、结构体和枚举里,存储属性只能用于类和结构体。
+*属性*将值跟特定的类、结构或枚举关联。存储属性存储常量或变量作为实例的一部分,而计算属性计算(不是存储)一个值。计算属性可以用于类、结构体和枚举,存储属性只能用于类和结构体。
-存储属性和计算属性通常用于特定类型的实例,但是,属性也可以直接用于类型本身,这种属性称为类型属性。
+存储属性和计算属性通常与特定类型的实例关联。但是,属性也可以直接作用于类型本身,这种属性称为类型属性。
-另外,还可以定义属性观察器来监控属性值的变化,以此来触发一个自定义的操作。属性观察器可以添加到自己写的存储属性上,也可以添加到从父类继承的属性上。
+另外,还可以定义属性观察器来监控属性值的变化,以此来触发一个自定义的操作。属性观察器可以添加到自己定义的存储属性上,也可以添加到从父类继承的属性上。
## 存储属性
-简单来说,一个存储属性就是存储在特定类或结构体的实例里的一个常量或变量,存储属性可以是*变量存储属性*(用关键字`var`定义),也可以是*常量存储属性*(用关键字`let`定义)。
+简单来说,一个存储属性就是存储在特定类或结构体的实例里的一个常量或变量。存储属性可以是*变量存储属性*(用关键字`var`定义),也可以是*常量存储属性*(用关键字`let`定义)。
-可以在定义存储属性的时候指定默认值,请参考[构造过程](../chapter2/14_Initialization.html)一章的[默认属性值](../chapter2/14_Initialization.html#default_property_values)一节。也可以在构造过程中设置或修改存储属性的值,甚至修改常量存储属性的值,请参考[构造过程](../chapter2/14_Initialization.html)一章的[在初始化阶段修改常量存储属性](../chapter2/14_Initialization.html#modifying_constant_properties_during_initialization)一节。
+可以在定义存储属性的时候指定默认值,请参考[默认属性值](./14_Initialization.html#default_property_values)一节。也可以在构造过程中设置或修改存储属性的值,甚至修改常量存储属性的值,请参考[在初始化阶段修改常量存储属性](./14_Initialization.html#assigning_constant_properties_during_initialization)一节。
下面的例子定义了一个名为`FixedLengthRange`的结构体,它描述了一个在创建后无法修改值域宽度的区间:
@@ -38,12 +42,12 @@ rangeOfThreeItems.firstValue = 6
// 该区间现在表示整数6,7,8
```
-`FixedLengthRange`的实例包含一个名为`firstValue`的变量存储属性和一个名为`length`的常量存储属性。在上面的例子中,`length`在创建实例的时候被赋值,因为它是一个常量存储属性,所以之后无法修改它的值。
+`FixedLengthRange`的实例包含一个名为`firstValue`的变量存储属性和一个名为`length`的常量存储属性。在上面的例子中,`length`在创建实例的时候被初始化,因为它是一个常量存储属性,所以之后无法修改它的值。
-### 常量和存储属性
+### 常量结构体的存储属性
-如果创建了一个结构体的实例并赋值给一个常量,则无法修改实例的任何属性,即使定义了变量存储属性:
+如果创建了一个结构体的实例并将其赋值给一个常量,则无法修改该实例的任何属性,即使定义了变量存储属性:
```swift
let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
@@ -52,11 +56,11 @@ rangeOfFourItems.firstValue = 6
// 尽管 firstValue 是个变量属性,这里还是会报错
```
-因为`rangeOfFourItems`声明成了常量(用`let`关键字),即使`firstValue`是一个变量属性,也无法再修改它了。
+因为`rangeOfFourItems`被声明成了常量(用`let`关键字),即使`firstValue`是一个变量属性,也无法再修改它了。
这种行为是由于结构体(struct)属于*值类型*。当值类型的实例被声明为常量的时候,它的所有属性也就成了常量。
-属于*引用类型*的类(class)则不一样,把一个引用类型的实例赋给一个常量后,仍然可以修改实例的变量属性。
+属于*引用类型*的类(class)则不一样。把一个引用类型的实例赋给一个常量后,仍然可以修改该实例的变量属性。
### 延迟存储属性
@@ -64,11 +68,11 @@ rangeOfFourItems.firstValue = 6
延迟存储属性是指当第一次被调用的时候才会计算其初始值的属性。在属性声明前使用`lazy`来标示一个延迟存储属性。
> 注意:
-> 必须将延迟存储属性声明成变量(使用`var`关键字),因为属性的值在实例构造完成之前可能无法得到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟属性。
+> 必须将延迟存储属性声明成变量(使用`var`关键字),因为属性的初始值可能在实例构造完成之后才会得到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟属性。
-延迟属性很有用,当属性的值依赖于在实例的构造过程结束前无法知道具体值的外部因素时,或者当属性的值需要复杂或大量计算时,可以只在需要的时候来计算它。
+延迟属性很有用,当属性的值依赖于在实例的构造过程结束后才会知道具体值的外部因素时,或者当获得属性的初始值需要复杂或大量计算时,可以只在需要的时候计算它。
-下面的例子使用了延迟存储属性来避免复杂类的不必要的初始化。例子中定义了`DataImporter`和`DataManager`两个类,下面是部分代码:
+下面的例子使用了延迟存储属性来避免复杂类中不必要的初始化。例子中定义了`DataImporter`和`DataManager`两个类,下面是部分代码:
```swift
class DataImporter {
@@ -87,37 +91,39 @@ class DataManager {
}
let manager = DataManager()
-manager.data.append("Some data")
+manager.data.append("Some data")
manager.data.append("Some more data")
// DataImporter 实例的 importer 属性还没有被创建
```
`DataManager`类包含一个名为`data`的存储属性,初始值是一个空的字符串(`String`)数组。虽然没有写出全部代码,`DataManager`类的目的是管理和提供对这个字符串数组的访问。
-`DataManager`的一个功能是从文件导入数据,该功能由`DataImporter`类提供,`DataImporter`需要消耗不少时间完成初始化:因为它的实例在初始化时可能要打开文件,还要读取文件内容到内存。
+`DataManager`的一个功能是从文件导入数据。该功能由`DataImporter`类提供,`DataImporter`完成初始化需要消耗不少时间:因为它的实例在初始化时可能要打开文件,还要读取文件内容到内存。
-`DataManager`也可能不从文件中导入数据。所以当`DataManager`的实例被创建时,没必要创建一个`DataImporter`的实例,更明智的是当用到`DataImporter`的时候才去创建它。
+`DataManager`也可能不从文件中导入数据就完成了管理数据的功能。所以当`DataManager`的实例被创建时,没必要创建一个`DataImporter`的实例,更明智的是当第一次用到`DataImporter`的时候才去创建它。
由于使用了`lazy`,`importer`属性只有在第一次被访问的时候才被创建。比如访问它的属性`fileName`时:
```swift
-println(manager.importer.fileName)
+print(manager.importer.fileName)
// DataImporter 实例的 importer 属性现在被创建了
// 输出 "data.txt”
```
+> 注意:
+> 如果一个被标记为`lazy`的属性在没有初始化时就同时被多个线程访问,则无法保证该属性只会被初始化一次。
+
### 存储属性和实例变量
-如果您有过 Objective-C 经验,应该知道Objective-C为类实例存储值和引用提供两种方法。对于属性来说,也可以使用实例变量作为属性值的后端存储。
+如果您有过 Objective-C 经验,应该知道 Objective-C 为类实例存储值和引用提供两种方法。对于属性来说,也可以使用实例变量作为属性值的后端存储。
-Swift 编程语言中把这些理论统一用属性来实现。Swift 中的属性没有对应的实例变量,属性的后端存储也无法直接访问。这就避免了不同场景下访问方式的困扰,同时也将属性的定义简化成一个语句。
-一个类型中属性的全部信息——包括命名、类型和内存管理特征——都在唯一一个地方(类型定义中)定义。
+Swift 编程语言中把这些理论统一用属性来实现。Swift 中的属性没有对应的实例变量,属性的后端存储也无法直接访问。这就避免了不同场景下访问方式的困扰,同时也将属性的定义简化成一个语句。一个类型中属性的全部信息——包括命名、类型和内存管理特征——都在唯一一个地方(类型定义中)定义。
## 计算属性
-除存储属性外,类、结构体和枚举可以定义*计算属性*,计算属性不直接存储值,而是提供一个 getter 来获取值,一个可选的 setter 来间接设置其他属性或变量的值。
+除存储属性外,类、结构体和枚举可以定义*计算属性*。计算属性不直接存储值,而是提供一个 getter 和一个可选的 setter,来间接获取和设置其他属性或变量的值。
```swift
struct Point {
@@ -130,38 +136,38 @@ 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)
- }
+ 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)
-println("square.origin is now at (\(square.origin.x), \(square.origin.y))")
+print("square.origin is now at (\(square.origin.x), \(square.origin.y))")
// 输出 "square.origin is now at (10.0, 10.0)”
```
-这个例子定义了 3 个几何形状的结构体:
+这个例子定义了 3 个结构体来描述几何形状:
- `Point`封装了一个`(x, y)`的坐标
-- `Size`封装了一个`width`和`height`
+- `Size`封装了一个`width`和一个`height`
- `Rect`表示一个有原点和尺寸的矩形
-`Rect`也提供了一个名为`center`的计算属性。一个矩形的中心点可以从原点和尺寸来算出,所以不需要将它以显式声明的`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`的值,从而实现移动正方形到新的位置。
@@ -175,15 +181,15 @@ 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)
- }
+ 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)
+ }
}
}
```
@@ -193,63 +199,61 @@ struct AlternativeRect {
只有 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
+ return width * height * depth
}
}
let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
-println("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
+print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
// 输出 "the volume of fourByFiveByTwo is 40.0"
```
-这个例子定义了一个名为`Cuboid`的结构体,表示三维空间的立方体,包含`width`、`height`和`depth`属性,还有一个名为`volume`的只读计算属性用来返回立方体的体积。设置`volume`的值毫无意义,因为通过`width`、`height`和`depth`就能算出`volume`。然而,`Cuboid`提供一个只读计算属性来让外部用户直接获取体积是很有用的。
+这个例子定义了一个名为`Cuboid`的结构体,表示三维空间的立方体,包含`width`、`height`和`depth`属性。结构体还有一个名为`volume`的只读计算属性用来返回立方体的体积。设置`volume`的值毫无意义,因为无法确定修改`width`、`height`和`depth`三者中的哪些值来匹配新的`volume`,从而造成歧义。然而,`Cuboid`提供一个只读计算属性来让外部用户直接获取体积是很有用的。
## 属性观察器
*属性观察器*监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,甚至新的值和现在的值相同的时候也不例外。
-可以为除了延迟存储属性之外的其他存储属性添加属性观察器,也可以通过重载属性的方式为继承的属性(包括存储属性和计算属性)添加属性观察器。属性重载请参考[继承](chapter/13_Inheritance.html)一章的[重载](chapter/13_Inheritance.html#overriding)。
+可以为除了延迟存储属性之外的其他存储属性添加属性观察器,也可以通过重载属性的方式为继承的属性(包括存储属性和计算属性)添加属性观察器。属性重载请参考[重载](./13_Inheritance.html#overriding)。
> 注意:
-> 不需要为无法重载的计算属性添加属性观察器,因为可以通过 setter 直接监控和响应值的变化。
+> 不需要为非重载的计算属性添加属性观察器,因为可以通过它的 setter 直接监控和响应值的变化。
可以为属性添加如下的一个或全部观察器:
-- `willSet`在设置新的值之前调用
+- `willSet`在新的值被设置之前调用
- `didSet`在新的值被设置之后立即调用
-`willSet`观察器会将新的属性值作为固定参数传入,在`willSet`的实现代码中可以为这个参数指定一个名称,如果不指定则参数仍然可用,这时使用默认名称`newValue`表示。
+`willSet`观察器会将新的属性值作为常量参数传入,在`willSet`的实现代码中可以为这个参数指定一个名称,如果不指定则参数仍然可用,这时使用默认名称`newValue`表示。
类似地,`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 {
var totalSteps: Int = 0 {
- willSet(newTotalSteps) {
- println("About to set totalSteps to \(newTotalSteps)")
- }
- didSet {
- if totalSteps > oldValue {
- println("Added \(totalSteps - oldValue) steps")
+ willSet(newTotalSteps) {
+ print("About to set totalSteps to \(newTotalSteps)")
+ }
+ didSet {
+ if totalSteps > oldValue {
+ print("Added \(totalSteps - oldValue) steps")
+ }
}
- }
}
}
let stepCounter = StepCounter()
@@ -270,19 +274,19 @@ stepCounter.totalSteps = 896
例子中的`willSet`观察器将表示新值的参数自定义为`newTotalSteps`,这个观察器只是简单的将新的值输出。
-`didSet`观察器在`totalSteps`的值改变后被调用,它把新的值和旧的值进行对比,如果总的步数增加了,就输出一个消息表示增加了多少步。`didSet`没有提供自定义名称,所以默认值`oldValue`表示旧值的参数名。
+`didSet`观察器在`totalSteps`的值改变后被调用,它把新的值和旧的值进行对比,如果总的步数增加了,就输出一个消息表示增加了多少步。`didSet`没有为旧的值提供自定义名称,所以默认值`oldValue`表示旧值的参数名。
> 注意:
-> 如果在`didSet`观察器里为属性赋值,这个值会替换观察器之前设置的值。
+> 如果在一个属性的`didSet`观察器里为它赋值,这个值会替换该观察器之前设置的值。
##全局变量和局部变量
-计算属性和属性观察器所描述的模式也可以用于*全局变量*和*局部变量*,全局变量是在函数、方法、闭包或任何类型之外定义的变量,局部变量是在函数、方法或闭包内部定义的变量。
+计算属性和属性观察器所描述的模式也可以用于*全局变量*和*局部变量*。全局变量是在函数、方法、闭包或任何类型之外定义的变量。局部变量是在函数、方法或闭包内部定义的变量。
前面章节提到的全局或局部变量都属于存储型变量,跟存储属性类似,它提供特定类型的存储空间,并允许读取和写入。
-另外,在全局或局部范围都可以定义计算型变量和为存储型变量定义观察器,计算型变量跟计算属性一样,返回一个计算的值而不是存储值,声明格式也完全一样。
+另外,在全局或局部范围都可以定义计算型变量和为存储型变量定义观察器。计算型变量跟计算属性一样,返回一个计算的值而不是存储值,声明格式也完全一样。
> 注意:
> 全局的常量或变量都是延迟计算的,跟[延迟存储属性](#lazy_stored_properties)相似,不同的地方在于,全局的常量或变量不需要标记`lazy`特性。
@@ -297,9 +301,7 @@ stepCounter.totalSteps = 896
类型属性用于定义特定类型所有实例共享的数据,比如所有实例都能用的一个常量(就像 C 语言中的静态常量),或者所有实例都能访问的一个变量(就像 C 语言中的静态变量)。
-对于值类型(指结构体和枚举)可以定义存储型和计算型类型属性,对于类(class)则只能定义计算型类型属性。
-
-值类型的存储型类型属性可以是变量或常量,计算型类型属性跟实例的计算属性一样定义成变量属性。
+值类型的存储型类型属性可以是变量或常量,计算型类型属性跟实例的计算属性一样只能定义成变量属性。
> 注意:
> 跟实例的存储属性不同,必须给存储型类型属性指定默认值,因为类型本身无法在初始化过程中使用构造器给类型属性赋值。
@@ -307,26 +309,30 @@ stepCounter.totalSteps = 896
###类型属性语法
-在 C 或 Objective-C 中,静态常量和静态变量的定义是通过特定类型加上`global`关键字。在 Swift 编程语言中,类型属性是作为类型定义的一部分写在类型最外层的花括号内,因此它的作用范围也就在类型支持的范围内。
+在 C 或 Objective-C 中,与某个类型关联的静态常量和静态变量,是作为全局(*global*)静态变量定义的。但是在 Swift 编程语言中,类型属性是作为类型定义的一部分写在类型最外层的花括号内,因此它的作用范围也就在类型支持的范围内。
-使用关键字`static`来定义值类型的类型属性,关键字`class`来为类(class)定义类型属性。下面的例子演示了存储型和计算型类型属性的语法:
+使用关键字`static`来定义类型属性。在为类(class)定义计算型类型属性时,可以使用关键字`class`来支持子类对父类的实现进行重写。下面的例子演示了存储型和计算型类型属性的语法:
```swift
struct SomeStructure {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
- // 这里返回一个 Int 值
+ return 1
}
}
enum SomeEnumeration {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
- // 这里返回一个 Int 值
+ return 6
}
}
class SomeClass {
- class var computedTypeProperty: Int {
- // 这里返回一个 Int 值
+ static var storedTypeProperty = "Some value."
+ static var computedTypeProperty: Int {
+ return 27
+ }
+ class var overrideableComputedTypeProperty: Int {
+ return 107
}
}
```
@@ -337,17 +343,18 @@ class SomeClass {
###获取和设置类型属性的值
-跟实例的属性一样,类型属性的访问也是通过点运算符来进行,但是,类型属性是通过类型本身来获取和设置,而不是通过实例。比如:
+跟实例的属性一样,类型属性的访问也是通过点运算符来进行。但是,类型属性是通过类型本身来获取和设置,而不是通过实例。比如:
```swift
-println(SomeClass.computedTypeProperty)
-// 输出 "42"
-
-println(SomeStructure.storedTypeProperty)
+print(SomeStructure.storedTypeProperty)
// 输出 "Some value."
SomeStructure.storedTypeProperty = "Another value."
-println(SomeStructure.storedTypeProperty)
+print(SomeStructure.storedTypeProperty)
// 输出 "Another value.”
+print(SomeEnumeration.computedTypeProperty)
+// 输出 "6"
+print(SomeClass.computedTypeProperty)
+// 输出 "27"
```
下面的例子定义了一个结构体,使用两个存储型类型属性来表示多个声道的声音电平值,每个声道有一个 0 到 10 之间的整数表示声音电平值。
@@ -356,23 +363,23 @@ println(SomeStructure.storedTypeProperty)
-上面所描述的声道模型使用`AudioChannel`结构体来表示:
+上面所描述的声道模型使用`AudioChannel`结构体的实例来表示:
```swift
struct AudioChannel {
static let thresholdLevel = 10
static var maxInputLevelForAllChannels = 0
var currentLevel: Int = 0 {
- didSet {
- if currentLevel > AudioChannel.thresholdLevel {
- // 将新电平值设置为阀值
- currentLevel = AudioChannel.thresholdLevel
+ didSet {
+ if currentLevel > AudioChannel.thresholdLevel {
+ // 将新电平值设置为阀值
+ currentLevel = AudioChannel.thresholdLevel
+ }
+ if currentLevel > AudioChannel.maxInputLevelForAllChannels {
+ // 存储当前电平值作为新的最大输入电平
+ AudioChannel.maxInputLevelForAllChannels = currentLevel
+ }
}
- if currentLevel > AudioChannel.maxInputLevelForAllChannels {
- // 存储当前电平值作为新的最大输入电平
- AudioChannel.maxInputLevelForAllChannels = currentLevel
- }
- }
}
}
```
@@ -383,10 +390,10 @@ struct AudioChannel {
`AudioChannel`也定义了一个名为`currentLevel`的实例存储属性,表示当前声道现在的电平值,取值为 0 到 10。
-属性`currentLevel`包含`didSet`属性观察器来检查每次新设置后的属性值,有如下两个检查:
+属性`currentLevel`包含`didSet`属性观察器来检查每次新设置后的属性值,它有如下两个检查:
- 如果`currentLevel`的新值大于允许的阈值`thresholdLevel`,属性观察器将`currentLevel`的值限定为阈值`thresholdLevel`。
-- 如果修正后的`currentLevel`值大于任何之前任意`AudioChannel`实例中的值,属性观察器将新值保存在静态属性`maxInputLevelForAllChannels`中。
+- 如果前一个修正后的`currentLevel`值大于任何之前任意`AudioChannel`实例中的值,属性观察器将新值保存在静态类型属性`maxInputLevelForAllChannels`中。
> 注意:
> 在第一个检查过程中,`didSet`属性观察器将`currentLevel`设置成了不同的值,但这时不会再次调用属性观察器。
@@ -402,9 +409,9 @@ var rightChannel = AudioChannel()
```swift
leftChannel.currentLevel = 7
-println(leftChannel.currentLevel)
+print(leftChannel.currentLevel)
// 输出 "7"
-println(AudioChannel.maxInputLevelForAllChannels)
+print(AudioChannel.maxInputLevelForAllChannels)
// 输出 "7"
```
@@ -412,8 +419,8 @@ println(AudioChannel.maxInputLevelForAllChannels)
```swift
rightChannel.currentLevel = 11
-println(rightChannel.currentLevel)
+print(rightChannel.currentLevel)
// 输出 "10"
-println(AudioChannel.maxInputLevelForAllChannels)
+print(AudioChannel.maxInputLevelForAllChannels)
// 输出 "10"
```
diff --git a/source/chapter2/11_Methods.md b/source/chapter2/11_Methods.md
index 2f925d5c..79cbd413 100755
--- a/source/chapter2/11_Methods.md
+++ b/source/chapter2/11_Methods.md
@@ -1,9 +1,13 @@
-> 翻译:[pp-prog](https://github.com/pp-prog)
-> 校对:[zqp](https://github.com/zqp)
-
# 方法(Methods)
-----------------
+> 1.0
+> 翻译:[pp-prog](https://github.com/pp-prog)
+> 校对:[zqp](https://github.com/zqp)
+
+> 2.0
+> 翻译+校对:[DianQK](https://github.com/DianQK)
+
本页包含内容:
- [实例方法(Instance Methods)](#instance_methods)
@@ -14,19 +18,19 @@
结构体和枚举能够定义方法是 Swift 与 C/Objective-C 的主要区别之一。在 Objective-C 中,类是唯一能定义方法的类型。但在 Swift 中,你不仅能选择是否要定义一个类/结构体/枚举,还能灵活的在你创建的类型(类/结构体/枚举)上定义方法。
-## 实例方法(Instance Methods)
+## 实例方法 (Instance Methods)
-**实例方法**是属于某个特定类、结构体或者枚举类型实例的方法。实例方法提供访问和修改实例属性的方法或提供与实例目的相关的功能,并以此来支撑实例的功能。实例方法的语法与函数完全一致,详情参见[函数](../charpter2/06_Functions.md)。
+**实例方法**是属于某个特定类、结构体或者枚举类型实例的方法。实例方法提供访问和修改实例属性的方法或提供与实例目的相关的功能,并以此来支撑实例的功能。实例方法的语法与函数完全一致,详情参见[函数](./06_Functions.md)。
实例方法要写在它所属的类型的前后大括号之间。实例方法能够隐式访问它所属类型的所有的其他实例方法和属性。实例方法只能被它所属的类的某个特定实例调用。实例方法不能脱离于现存的实例而被调用。
-下面的例子,定义一个很简单的类`Counter`,`Counter`能被用来对一个动作发生的次数进行计数:
+下面的例子,定义一个很简单的`Counter`类,`Counter`能被用来对一个动作发生的次数进行计数:
```swift
class Counter {
var count = 0
func increment() {
- count++
+ ++count
}
func incrementBy(amount: Int) {
count += amount
@@ -60,13 +64,13 @@ class Counter {
### 方法的局部参数名称和外部参数名称(Local and External Parameter Names for Methods)
-函数参数可以同时有一个局部名称(在函数体内部使用)和一个外部名称(在调用函数时使用),详情参见[函数的外部参数名](06_Functions.html)。方法参数也一样(因为方法就是函数,只是这个函数与某个类型相关联了)。但是,方法和函数的局部名称和外部名称的默认行为是不一样的。
+函数参数可以同时有一个局部名称(在函数体内部使用)和一个外部名称(在调用函数时使用),详情参见[指定外部参数名](./06_Functions.html#specifying_external_parameter_names)。方法参数也一样(因为方法就是函数,只是这个函数与某个类型相关联了)。
-Swift 中的方法和 Objective-C 中的方法极其相似。像在 Objective-C 中一样,Swift 中方法的名称通常用一个介词指向方法的第一个参数,比如:`with`,`for`,`by`等等。前面的`Counter`类的例子中`incrementBy`方法就是这样的。介词的使用让方法在被调用时能像一个句子一样被解读。和函数参数不同,对于方法的参数,Swift 使用不同的默认处理方式,这可以让方法命名规范更容易写。
+Swift 中的方法和 Objective-C 中的方法极其相似。像在 Objective-C 中一样,Swift 中方法的名称通常用一个介词指向方法的第一个参数,比如:`with`,`for`,`by`等等。前面的`Counter`类的例子中`incrementBy(_:)`方法就是这样的。介词的使用让方法在被调用时能像一个句子一样被解读。
-具体来说,Swift 默认仅给方法的第一个参数名称一个局部参数名称;默认同时给第二个和后续的参数名称局部参数名称和外部参数名称。这个约定与典型的命名和调用约定相适应,与你在写 Objective-C 的方法时很相似。这个约定还让表达式方法在调用时不需要再限定参数名称。
+具体来说,Swift 默认仅给方法的第一个参数名称一个局部参数名称;默认同时给第二个和后续的参数名称局部参数名称和外部参数名称。这个约定与典型的命名和调用约定相适应,与你在写 Objective-C 的方法时很相似。这个约定还让表达式方法在调用时不需要再限定参数名称。
-看看下面这个`Counter`的另一个版本(它定义了一个更复杂的`incrementBy`方法):
+看看下面这个`Counter`的另一个版本(它定义了一个更复杂的`incrementBy(_:)`方法):
```swift
class Counter {
@@ -77,35 +81,28 @@ class Counter {
}
```
-`incrementBy`方法有两个参数: `amount`和`numberOfTimes`。默认情况下,Swift 只把`amount`当作一个局部名称,但是把`numberOfTimes`即看作局部名称又看作外部名称。下面调用这个方法:
+`incrementBy(_:numverOfTimes:)`方法有两个参数: `amount`和`numberOfTimes`。默认情况下,Swift 只把`amount`当作一个局部名称,但是把`numberOfTimes`即看作局部名称又看作外部名称。下面调用这个方法:
```swift
let counter = Counter()
counter.incrementBy(5, numberOfTimes: 3)
-// counter value is now 15
-```
-
-你不必为第一个参数值再定义一个外部变量名:因为从函数名`incrementBy`已经能很清楚地看出它的作用。但是第二个参数,就要被一个外部参数名称所限定,以便在方法被调用时明确它的作用。
-
-这种默认的行为能够有效的处理方法(method),类似于在参数`numberOfTimes`前写一个井号(`#`):
-
-```swift
-func incrementBy(amount: Int, #numberOfTimes: Int) {
- count += amount * numberOfTimes
-}
+// counter 的值现在是 15
```
+你不必为第一个参数值再定义一个外部变量名:因为从函数名`incrementBy(_numberOfTimes:)`已经能很清楚地看出它的作用。但是第二个参数,就要被一个外部参数名称所限定,以便在方法被调用时明确它的作用。
这种默认行为使上面代码意味着:在 Swift 中定义方法使用了与 Objective-C 同样的语法风格,并且方法将以自然表达式的方式被调用。
-
+
+
### 修改方法的外部参数名称(Modifying External Parameter Name Behavior for Methods)
-有时为方法的第一个参数提供一个外部参数名称是非常有用的,尽管这不是默认的行为。你可以自己添加一个显式的外部名称或者用一个井号(`#`)作为第一个参数的前缀来把这个局部名称当作外部名称使用。
+有时为方法的第一个参数提供一个外部参数名称是非常有用的,尽管这不是默认的行为。你可以自己添加一个显式的外部名称作为第一个参数的前缀来把这个局部名称当作外部名称使用。
相反,如果你不想为方法的第二个及后续的参数提供一个外部名称,可以通过使用下划线(`_`)作为该参数的显式外部名称,这样做将覆盖默认行为。
-
-## `self`属性(The self Property)
+
+
+### self 属性(The self Property)
类型的每一个实例都有一个隐含属性叫做`self`,`self`完全等同于该实例本身。你可以在一个实例的实例方法中使用这个隐含的`self`属性来引用当前实例。
@@ -132,14 +129,14 @@ struct Point {
}
let somePoint = Point(x: 4.0, y: 5.0)
if somePoint.isToTheRightOfX(1.0) {
- println("This point is to the right of the line where x == 1.0")
+ print("This point is to the right of the line where x == 1.0")
}
-// 输出 "This point is to the right of the line where x == 1.0"(这个点在x等于1.0这条线的右边)
+// 打印输出: This point is to the right of the line where x == 1.0
```
如果不使用`self`前缀,Swift 就认为两次使用的`x`都指的是名称为`x`的函数参数。
-
+
### 在实例方法中修改值类型(Modifying Value Types from Within Instance Methods)
结构体和枚举是**值类型**。一般情况下,值类型的属性不能在它的实例方法中被修改。
@@ -158,21 +155,21 @@ struct Point {
}
var somePoint = Point(x: 1.0, y: 1.0)
somePoint.moveByX(2.0, y: 3.0)
-println("The point is now at (\(somePoint.x), \(somePoint.y))")
-// 输出 "The point is now at (3.0, 4.0)"
+print("The point is now at (\(somePoint.x), \(somePoint.y))")
+// 打印输出: "The point is now at (3.0, 4.0)"
```
-上面的`Point`结构体定义了一个变异方法(mutating method)`moveByX`,`moveByX`用来移动点。`moveByX`方法在被调用时修改了这个点,而不是返回一个新的点。方法定义时加上`mutating`关键字,这才让方法可以修改值类型的属性。
+上面的`Point`结构体定义了一个变异方法(mutating method)`moveByX(_:y:)`用来移动点。`moveByX`方法在被调用时修改了这个点,而不是返回一个新的点。方法定义时加上`mutating`关键字,这才让方法可以修改值类型的属性。
-注意:不能在结构体类型常量上调用变异方法,因为常量的属性不能被改变,即使想改变的是常量的变量属性也不行,详情参见[存储属性和实例变量](10_Properties.html#global_and_local_variables)
+注意:不能在结构体类型常量上调用变异方法,因为常量的属性不能被改变,即使想改变的是常量的变量属性也不行,详情参见[存储属性和实例变量](./10_Properties.html#global_and_local_variables):
```swift
let fixedPoint = Point(x: 3.0, y: 3.0)
fixedPoint.moveByX(2.0, y: 3.0)
-// this will report an error
+// 这里将会抛出一个错误
```
-
+
### 在变异方法中给self赋值(Assigning to self Within a Mutating Method)
变异方法能够赋给隐含属性`self`一个全新的实例。上面`Point`的例子可以用下面的方式改写:
@@ -186,7 +183,7 @@ struct Point {
}
```
-新版的变异方法`moveByX`创建了一个新的结构(它的 x 和 y 的值都被设定为目标值)。调用这个版本的方法和调用上个版本的最终结果是一样的。
+新版的变异方法`moveByX(_:y:)`创建了一个新的结构(它的 x 和 y 的值都被设定为目标值)。调用这个版本的方法和调用上个版本的最终结果是一样的。
枚举的变异方法可以把`self`设置为相同的枚举类型中不同的成员:
@@ -214,9 +211,9 @@ ovenLight.next()
上面的例子中定义了一个三态开关的枚举。每次调用`next`方法时,开关在不同的电源状态(`Off`,`Low`,`High`)之前循环切换。
-## 类型方法(Type Methods)
+## 类型方法 (Type Methods)
-实例方法是被类型的某个实例调用的方法。你也可以定义类型本身调用的方法,这种方法就叫做**类型方法**。声明类的类型方法,在方法的`func`关键字之前加上关键字`class`;声明结构体和枚举的类型方法,在方法的`func`关键字之前加上关键字`static`。
+实例方法是被类型的某个实例调用的方法。你也可以定义类型本身调用的方法,这种方法就叫做**类型方法**。声明结构体和枚举的类型方法,在方法的`func`关键字之前加上关键字`static`。类可能会用关键字`class`来允许子类重写父类的实现方法。
> 注意:
> 在 Objective-C 里面,你只能为 Objective-C 的类定义类型方法(type-level methods)。在 Swift 中,你可以为所有的类、结构体和枚举定义类型方法:每一个类型方法都被它所支持的类型显式包含。
@@ -225,7 +222,7 @@ ovenLight.next()
```swift
class SomeClass {
- class func someTypeMethod() {
+ static func someTypeMethod() {
// type method implementation goes here
}
}
@@ -242,22 +239,22 @@ SomeClass.someTypeMethod()
```swift
struct LevelTracker {
- static var highestUnlockedLevel = 1
- static func unlockLevel(level: Int) {
- if level > highestUnlockedLevel { highestUnlockedLevel = level }
- }
- static func levelIsUnlocked(level: Int) -> Bool {
- return level <= highestUnlockedLevel
- }
- var currentLevel = 1
- mutating func advanceToLevel(level: Int) -> Bool {
- if LevelTracker.levelIsUnlocked(level) {
- currentLevel = level
- return true
- } else {
- return false
+ static var highestUnlockedLevel = 1
+ static func unlockLevel(level: Int) {
+ if level > highestUnlockedLevel { highestUnlockedLevel = level }
+ }
+ static func levelIsUnlocked(level: Int) -> Bool {
+ return level <= highestUnlockedLevel
+ }
+ var currentLevel = 1
+ mutating func advanceToLevel(level: Int) -> Bool {
+ if LevelTracker.levelIsUnlocked(level) {
+ currentLevel = level
+ return true
+ } else {
+ return false
+ }
}
- }
}
```
@@ -285,25 +282,25 @@ class Player {
}
```
-`Player`类创建一个新的`LevelTracker`实例来监测这个用户的发展进度。它提供了`completedLevel`方法:一旦玩家完成某个指定等级就调用它。这个方法为所有玩家解锁下一等级,并且将当前玩家的进度更新为下一等级。(我们忽略了`advanceToLevel`返回的布尔值,因为之前调用`LevelTracker.unlockLevel`时就知道了这个等级已经被解锁了)。
+`Player`类创建一个新的`LevelTracker`实例来监测这个用户的进度。它提供了`completedLevel`方法:一旦玩家完成某个指定等级就调用它。这个方法为所有玩家解锁下一等级,并且将当前玩家的进度更新为下一等级。(我们忽略了`advanceToLevel`返回的布尔值,因为之前调用`LevelTracker.unlockLevel`时就知道了这个等级已经被解锁了)。
你还可以为一个新的玩家创建一个`Player`的实例,然后看这个玩家完成等级一时发生了什么:
```swift
var player = Player(name: "Argyrios")
player.completedLevel(1)
-println("highest unlocked level is now \(LevelTracker.highestUnlockedLevel)")
-// 输出 "highest unlocked level is now 2"(最高等级现在是2)
+print("highest unlocked level is now \(LevelTracker.highestUnlockedLevel)")
+// 打印输出:highest unlocked level is now 2
```
-如果你创建了第二个玩家,并尝试让它开始一个没有被任何玩家解锁的等级,那么这次设置玩家当前等级的尝试将会失败:
+如果你创建了第二个玩家,并尝试让他开始一个没有被任何玩家解锁的等级,那么这次设置玩家当前等级的尝试将会失败:
```swift
player = Player(name: "Beto")
if player.tracker.advanceToLevel(6) {
- println("player is now on level 6")
+ print("player is now on level 6")
} else {
- println("level 6 has not yet been unlocked")
+ print("level 6 has not yet been unlocked")
}
-// 输出 "level 6 has not yet been unlocked"(等级6还没被解锁)
+// 打印输出:level 6 has not yet been unlocked
```
diff --git a/source/chapter2/12_Subscripts.md b/source/chapter2/12_Subscripts.md
index 7bef609e..a09466c4 100755
--- a/source/chapter2/12_Subscripts.md
+++ b/source/chapter2/12_Subscripts.md
@@ -1,19 +1,22 @@
-> 翻译:[siemenliu](https://github.com/siemenliu)
-> 校对:[zq54zquan](https://github.com/zq54zquan)
-
-
# 下标脚本(Subscripts)
-----------------
+> 1.0
+> 翻译:[siemenliu](https://github.com/siemenliu)
+> 校对:[zq54zquan](https://github.com/zq54zquan)
+
+> 2.0
+> 翻译+校对:[shanksyang](https://github.com/shanksyang)
+
本页包含内容:
- [下标脚本语法](#subscript_syntax)
- [下标脚本用法](#subscript_usage)
- [下标脚本选项](#subscript_options)
-*下标脚本* 可以定义在类(Class)、结构体(structure)和枚举(enumeration)这些目标中,可以认为是访问对象、集合或序列的快捷方式,不需要再调用实例的特定的赋值和访问方法。举例来说,用下标脚本访问一个数组(Array)实例中的元素可以这样写 `someArray[index]` ,访问字典(Dictionary)实例中的元素可以这样写 `someDictionary[key]`。
+*下标脚本* 可以定义在类(Class)、结构体(structure)和枚举(enumeration)这些目标中,可以认为是访问集合(collection),列表(list)或序列(sequence的快捷方式,使用下标脚本的索引设置和获取值,不需要再调用实例的特定的赋值和访问方法。举例来说,用下标脚本访问一个数组(Array)实例中的元素可以这样写 `someArray[index]` ,访问字典(Dictionary)实例中的元素可以这样写 `someDictionary[key]`。
-对于同一个目标可以定义多个下标脚本,通过索引值类型的不同来进行重载,而且索引值的个数可以是多个。
+对于同一个目标可以定义多个下标脚本,通过索引值类型的不同来进行重载,下标脚本不限于单个纬度,你可以定义多个入参的下标脚本满足自定义类型的需求。
> 译者:这里附属脚本重载在本小节中原文并没有任何演示
@@ -54,7 +57,7 @@ struct TimesTable {
}
}
let threeTimesTable = TimesTable(multiplier: 3)
-println("3的6倍是\(threeTimesTable[6])")
+print("3的6倍是\(threeTimesTable[6])")
// 输出 "3的6倍是18"
```
@@ -63,7 +66,7 @@ println("3的6倍是\(threeTimesTable[6])")
你可以通过下标脚本来得到结果,比如`threeTimesTable[6]`。这条语句访问了`threeTimesTable`的第六个元素,返回`6`的`3`倍即`18`。
>注意:
-> `TimesTable`例子是基于一个固定的数学公式。它并不适合开放写权限来对`threeTimesTable[someIndex]`进行赋值操作,这也是为什么附属脚本只定义为只读的原因。
+> `TimesTable`例子是基于一个固定的数学公式。它并不适合对`threeTimesTable[someIndex]`进行赋值操作,这也是为什么附属脚本只定义为只读的原因。
## 下标脚本用法
@@ -77,9 +80,9 @@ var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
numberOfLegs["bird"] = 2
```
-上例定义一个名为`numberOfLegs`的变量并用一个字典字面量初始化出了包含三对键值的字典实例。`numberOfLegs`的字典存放值类型推断为`Dictionarysome text
`"或者"``"。 +默认情况下,闭包赋值给了`asHTML`属性,这个闭包返回一个代表 HTML 标签的字符串。如果`text`值存在,该标签就包含可选值`text`;如果`text`不存在,该标签就不包含文本。对于段落元素,根据`text`是`"some text"`还是`nil`,闭包会返回"`some text
`"或者"``"。 可以像实例方法那样去命名、使用`asHTML`属性。然而,由于`asHTML`是闭包而不是实例方法,如果你想改变特定元素的 HTML 处理的话,可以用自定义的闭包来取代默认值。 @@ -440,7 +451,7 @@ class HTMLElement { ```swift var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world") -println(paragraph!.asHTML()) +print(paragraph!.asHTML()) // prints"hello, world" ``` @@ -451,7 +462,7 @@ println(paragraph!.asHTML())  -实例的`asHTML`属性持有闭包的强引用。但是,闭包在其闭包体内使用了`self`(引用了`self.name`和`self.text`),因此闭包捕获了`self`,这意味着闭包又反过来持有了`HTMLElement`实例的强引用。这样两个对象就产生了循环强引用。(更多关于闭包捕获值的信息,请参考[值捕获](07_Closures.html))。 +实例的`asHTML`属性持有闭包的强引用。但是,闭包在其闭包体内使用了`self`(引用了`self.name`和`self.text`),因此闭包捕获了`self`,这意味着闭包又反过来持有了`HTMLElement`实例的强引用。这样两个对象就产生了循环强引用。(更多关于闭包捕获值的信息,请参考[值捕获](./07_Closures.html#capturing_values))。 >注意: 虽然闭包多次使用了`self`,它只捕获`HTMLElement`实例的一个强引用。 @@ -470,38 +481,38 @@ paragraph = nil 在定义闭包时同时定义捕获列表作为闭包的一部分,通过这种方式可以解决闭包和类实例之间的循环强引用。捕获列表定义了闭包体内捕获一个或者多个引用类型的规则。跟解决两个类实例间的循环强引用一样,声明每个捕获的引用为弱引用或无主引用,而不是强引用。应当根据代码关系来决定使用弱引用还是无主引用。 >注意: -Swift 有如下要求:只要在闭包内使用`self`的成员,就要用`self.someProperty`或者`self.someMethod`(而不只是`someProperty`或`someMethod`)。这提醒你可能会不小心就捕获了`self`。 +Swift 有如下要求:只要在闭包内使用`self`的成员,就要用`self.someProperty`或者`self.someMethod`(而不只是`someProperty`或`someMethod`)。这提醒你可能会一不小心就捕获了`self`。 ###定义捕获列表 -捕获列表中的每个元素都是由`weak`或者`unowned`关键字和实例的引用(如`self`或`someInstance`)成对组成。每一对都在方括号中,通过逗号分开。 +捕获列表中的每一项都由一对元素组成,一个元素是`weak`或`unowned`关键字,另一个元素是类实例的引用(如`self`)或初始化过的变量(如`delegate = self.delegate!`)。这些项在方括号中用逗号分开。 -捕获列表放置在闭包参数列表和返回类型之前: +如果闭包有参数列表和返回类型,把捕获列表放在它们前面: ```swift lazy var someClosure: (Int, String) -> String = { - [unowned self] (index: Int, stringToProcess: String) -> String in + [unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in // closure body goes here } ``` -如果闭包没有指定参数列表或者返回类型,则可以通过上下文推断,那么可以捕获列表放在闭包开始的地方,跟着是关键字`in`: +如果闭包没有指明参数列表或者返回类型,即它们会通过上下文推断,那么可以把捕获列表和关键字`in`放在闭包最开始的地方: ```swift -lazy var someClosure: () -> String = { - [unowned self] in +lazy var someClosure: Void -> String = { + [unowned self, weak delegate = self.delegate!] in // closure body goes here } ``` ###弱引用和无主引用 -当闭包和捕获的实例总是互相引用时并且总是同时销毁时,将闭包内的捕获定义为无主引用。 +在闭包和捕获的实例总是互相引用时并且总是同时销毁时,将闭包内的捕获定义为无主引用。 -相反的,当捕获引用有时可能会是`nil`时,将闭包内的捕获定义为弱引用。弱引用总是可选类型,并且当引用的实例被销毁后,弱引用的值会自动置为`nil`。这使我们可以在闭包内检查它们是否存在。 +相反的,在被捕获的引用可能会变为`nil`时,将闭包内的捕获定义为弱引用。弱引用总是可选类型,并且当引用的实例被销毁后,弱引用的值会自动置为`nil`。这使我们可以在闭包体内检查它们是否存在。 >注意: -如果捕获的引用绝对不会置为`nil`,应该用无主引用,而不是弱引用。 +如果被捕获的引用绝对不会变为`nil`,应该用无主引用,而不是弱引用。 前面的`HTMLElement`例子中,无主引用是正确的解决循环强引用的方法。这样编写`HTMLElement`类来避免循环强引用: @@ -511,7 +522,7 @@ class HTMLElement { let name: String let text: String? - lazy var asHTML: () -> String = { + lazy var asHTML: Void -> String = { [unowned self] in if let text = self.text { return "<\(self.name)>\(text)\(self.name)>" @@ -526,19 +537,19 @@ class HTMLElement { } deinit { - println("\(name) is being deinitialized") + print("\(name) is being deinitialized") } } ``` -上面的`HTMLElement`实现和之前的实现一致,只是在`asHTML`闭包中多了一个捕获列表。这里,捕获列表是`[unowned self]`,表示“用无主引用而不是强引用来捕获`self`”。 +上面的`HTMLElement`实现和之前的实现一致,除了在`asHTML`闭包中多了一个捕获列表。这里,捕获列表是`[unowned self]`,表示“用无主引用而不是强引用来捕获`self`”。 和之前一样,我们可以创建并打印`HTMLElement`实例: ```swift var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world") -println(paragraph!.asHTML()) +print(paragraph!.asHTML()) // prints "hello, world
" ``` @@ -552,4 +563,3 @@ println(paragraph!.asHTML()) paragraph = nil // prints "p is being deinitialized" ``` - diff --git a/source/chapter2/17_Optional_Chaining.md b/source/chapter2/17_Optional_Chaining.md index c8e90811..43ee5426 100755 --- a/source/chapter2/17_Optional_Chaining.md +++ b/source/chapter2/17_Optional_Chaining.md @@ -1,105 +1,99 @@ -> 翻译:[Jasonbroker](https://github.com/Jasonbroker) -> 校对:[numbbbbb](https://github.com/numbbbbb), [stanzhai](https://github.com/stanzhai) +# 可空链式调用(Optional Chaining) -# Optional Chaining ----------------- -本页包含内容: +> 1.0 +> 翻译:[Jasonbroker](https://github.com/Jasonbroker) +> 校对:[numbbbbb](https://github.com/numbbbbb), [stanzhai](https://github.com/stanzhai) -- [可选链可替代强制解析](#optional_chaining_as_an_alternative_to_forced_unwrapping) -- [为可选链定义模型类](#defining_model_classes_for_optional_chaining) -- [通过可选链调用属性](#calling_properties_through_optional_chaining) -- [通过可选链调用方法](#calling_methods_through_optional_chaining) -- [使用可选链调用下标脚本](#calling_subscripts_through_optional_chaining) -- [连接多层链接](#linking_multiple_levels_of_chaining) -- [链接可选返回值的方法](#chaining_on_methods_with_optional_return_values) +> 2.0 +> 翻译+校对:[lyojo](https://github.com/lyojo) -可选链(Optional Chaining)是一种可以请求和调用属性、方法及下标脚本的过程,它的可选性体现于请求或调用的目标当前可能为空(`nil`)。如果可选的目标有值,那么调用就会成功;相反,如果选择的目标为空(`nil`),则这种调用将返回空(`nil`)。多次请求或调用可以被链接在一起形成一个链,如果任何一个节点为空(`nil`)将导致整个链失效。 -> 注意: -Swift 的可选链和 Objective-C 中的消息为空有些相像,但是 Swift 可以使用在任意类型中,并且失败与否可以被检测到。 +可空链式调用(Optional Chaining)是一种可以请求和调用属性、方法及下标的过程,它的可空性体现于请求或调用的目标当前可能为空(nil)。如果可空的目标有值,那么调用就会成功;如果选择的目标为空(nil),那么这种调用将返回空(nil)。多个连续的调用可以被链接在一起形成一个调用链,如果其中任何一个节点为空(nil)将导致整个链调用失败。 + +> +注意: +Swift 的可空链式调用和 Objective-C 中的消息为空有些相像,但是 Swift 可以使用在任意类型中,并且能够检查调用是否成功。 -## 可选链可替代强制解析 +##使用可空链式调用来强制展开 +通过在想调用非空的属性、方法、或下标的可空值(optional value)后面放一个问号,可以定义一个可空链。这一点很像在可空值后面放一个叹号(!)来强制展开其中值。它们的主要的区别在于当可空值为空时可空链式只是调用失败,然而强制展开将会触发运行时错误。 -通过在想调用的属性、方法、或下标脚本的可选值(`optional value`)(非空)后面放一个问号,可以定义一个可选链。这一点很像在可选值后面放一个叹号来强制拆得其封包内的值。它们的主要的区别在于当可选值为空时可选链即刻失败,然而一般的强制解析将会引发运行时错误。 +为了反映可空链式调用可以在空对象(nil)上调用,不论这个调用的属性、方法、下标等返回的值是不是可空值,它的返回结果都是一个可空值。你可以利用这个返回值来判断你的可空链式调用是否调用成功,如果调用有返回值则说明调用成功,返回`nil`则说明调用失败。 -为了反映可选链可以调用空(`nil`),不论你调用的属性、方法、下标脚本等返回的值是不是可选值,它的返回结果都是一个可选值。你可以利用这个返回值来检测你的可选链是否调用成功,有返回值即成功,返回nil则失败。 - -调用可选链的返回结果与原本的返回结果具有相同的类型,但是原本的返回结果被包装成了一个可选值,当可选链调用成功时,一个应该返回`Int`的属性将会返回`Int?`。 - -下面几段代码将解释可选链和强制解析的不同。 +特别地,可空链式调用的返回结果与原本的返回结果具有相同的类型,但是被包装成了一个可空类型值。当可空链式调用成功时,一个本应该返回`Int`的类型的结果将会返回`Int?`类型。 +下面几段代码将解释可空链式调用和强制展开的不同。 首先定义两个类`Person`和`Residence`。 ```swift class Person { - var residence: Residence? + var residence: Residence? } class Residence { - var numberOfRooms = 1 + var numberOfRooms = 1 } ``` -`Residence`具有一个`Int`类型的`numberOfRooms`,其值为 1。`Person`具有一个可选`residence`属性,它的类型是`Residence?`。 +`Residence`有一个`Int`类型的属性`numberOfRooms`,其默认值为1。`Person`具有一个可空的`residence`属性,其类型为`Residence?`。 -如果你创建一个新的`Person`实例,它的`residence`属性由于是被定义为可选型的,此属性将默认初始化为空: +如果创建一个新的`Person`实例,因为它的`residence`属性是可空的,`john`属性将初始化为`nil`: ```swift let john = Person() ``` -如果你想使用感叹号(`!`)强制解析获得这个人`residence`属性`numberOfRooms`属性值,将会引发运行时错误,因为这时没有可以供解析的`residence`值。 +如果使用叹号(!)强制展开获得这个`john`的`residence`属性中的`numberOfRooms`值,会触发运行时错误,因为这时没有可以展开的`residence`: ```swift let roomCount = john.residence!.numberOfRooms -//将导致运行时错误 +// this triggers a runtime error ``` -当`john.residence`不是`nil`时,会运行通过,且会将`roomCount` 设置为一个`int`类型的合理值。然而,如上所述,当`residence`为空时,这个代码将会导致运行时错误。 -可选链提供了一种另一种获得`numberOfRooms`的方法。利用可选链,使用问号来代替原来`!`的位置: +`john.residence`非空的时候,上面的调用成功,并且把`roomCount`设置为`Int`类型的房间数量。正如上面说到的,当`residence`为空的时候上面这段代码会触发运行时错误。 + +可空链式调用提供了一种另一种访问`numberOfRooms`的方法,使用问号(?)来代替原来叹号(!)的位置: ```swift if let roomCount = john.residence?.numberOfRooms { - println("John's residence has \(roomCount) room(s).") + print("John's residence has \(roomCount) room(s).") } else { - println("Unable to retrieve the number of rooms.") + print("Unable to retrieve the number of rooms.") } -// 打印 "Unable to retrieve the number of rooms. +// prints "Unable to retrieve the number of rooms." ``` -这告诉 Swift 来链接可选`residence?`属性,如果`residence`存在则取回`numberOfRooms`的值。 +在`residence`后面添加问号之后,Swift就会在`residence`不为空的情况下访问`numberOfRooms`。 -因为这种尝试获得`numberOfRooms`的操作有可能失败,可选链会返回`Int?`类型值,或者称作“可选`Int`”。当`residence`是空的时候(上例),选择`Int`将会为空,因此会出现无法访问`numberOfRooms`的情况。 +因为访问`numberOfRooms`有可能失败,可空链式调用会返回`Int?`类型,或称为“可空的Int”。如上例所示,当`residence`为`nil`的时候,可空的`Int`将会为`nil`,表明无法访问`numberOfRooms`。 -要注意的是,即使numberOfRooms是非可选`Int`(`Int?`)时这一点也成立。只要是通过可选链的请求就意味着最后`numberOfRooms`总是返回一个`Int?`而不是`Int`。 +要注意的是,即使`numberOfRooms`是不可空的`Int`时,这一点也成立。只要是通过可空链式调用就意味着最后`numberOfRooms`返回一个`Int?`而不是`Int`。 -你可以自己定义一个`Residence`实例给`john.residence`,这样它就不再为空了: +通过赋给`john.residence`一个`Residence`的实例变量: ```swift john.residence = Residence() ``` -`john.residence` 现在有了实际存在的实例而不是nil了。如果你想使用和前面一样的可选链来获得`numberOfRoooms`,它将返回一个包含默认值 1 的`Int?`: +这样`john.residence`不为`nil`了。现在就可以正常访问`john.residence.numberOfRooms`,其值为默认的1,类型为`Int?`: ```swift if let roomCount = john.residence?.numberOfRooms { - println("John's residence has \(roomCount) room(s).") + print("John's residence has \(roomCount) room(s).") } else { - println("Unable to retrieve the number of rooms.") + print("Unable to retrieve the number of rooms.") } -// 打印 "John's residence has 1 room(s)"。 +// prints "John's residence has 1 room(s)." ``` - -##为可选链定义模型类 +##为可空链式调用定义模型类 +通过使用可空链式调用可以调用多层属性,方法,和下标。这样可以通过各种模型向下访问各种子属性。并且判断能否访问子属性的属性,方法或下标。 -你可以使用可选链来多层调用属性,方法,和下标脚本。这让你可以利用它们之间的复杂模型来获取更底层的属性,并检查是否可以成功获取此类底层属性。 +下面这段代码定义了四个模型类,这些例子包括多层可空链式调用。为了方便说明,在`Person`和`Residence`的基础上增加了`Room`和`Address`,以及相关的属性,方法以及下标。 -后面的代码定义了四个将在后面使用的模型类,其中包括多层可选链。这些类是由上面的`Person`和`Residence`模型通过添加一个`Room`和一个`Address`类拓展来。 - -`Person`类定义与之前相同。 +Person类定义基本保持不变: ```swift class Person { @@ -107,32 +101,32 @@ class Person { } ``` -`Residence`类比之前复杂些。这次,它定义了一个变量 `rooms`,它被初始化为一个`Room[]`类型的空数组: +`Residence`类比之前复杂些,增加了一个`Room`类型的空数组`room`: ```swift class Residence { var rooms = [Room]() var numberOfRooms: Int { - return rooms.count + return rooms.count } subscript(i: Int) -> Room { - return rooms[i] + get { + return rooms[i] + } + set { + rooms[i] = newValue + } } func printNumberOfRooms() { - println("The number of rooms is \(numberOfRooms)") + print("The number of rooms is \(numberOfRooms)") } var address: Address? } ``` -因为`Residence`存储了一个`Room`实例的数组,它的`numberOfRooms`属性值不是一个固定的存储值,而是通过计算而来的。`numberOfRooms`属性值是由返回`rooms`数组的`count`属性值得到的。 +现在`Residence`有了一个存储`Room`类型的数组,`numberOfRooms`属性需要计算,而不是作为单纯的变量。计算后的`numberOfRooms`返回`rooms`数组的`count`属性值。现在的`Residence`还提供访问`rooms`数组的快捷方式, 通过可读写的下标来访问指定位置的数组元素。此外,还提供`printNumberOfRooms`方法,这个方法的作用就是输出这个房子中房间的数量。最后,`Residence`定义了一个可空属性`address`,其类型为`Address?`。`Address`类的定义在下面会说明。 -为了能快速访问`rooms`数组,`Residence`定义了一个只读的下标脚本,通过插入数组的元素角标就可以成功调用。如果该角标存在,下标脚本则将该元素返回。 - -`Residence`中也提供了一个`printNumberOfRooms`的方法,即简单的打印房间个数。 - -最后,`Residence`定义了一个可选属性叫`address`(`address?`)。`Address`类的属性将在后面定义。 -用于`rooms`数组的`Room`类是一个很简单的类,它只有一个`name`属性和一个设定`room`名的初始化器。 +类`Room`是一个简单类,只包含一个属性`name`,以及一个初始化函数: ```swift class Room { @@ -141,8 +135,7 @@ class Room { } ``` - -这个模型中的最终类叫做`Address`。它有三个类型是`String?`的可选属性。前面两个可选属性`buildingName`和 `buildingNumber`作为地址的一部分,是定义某个建筑物的两种方式。第三个属性`street`,用于命名地址的街道名: +最后一个类是`Address`,这个类有三个`String?`类型的可空属性。`buildingName`以及`buildingNumber`属性表示建筑的名称和号码,用来表示某个特定的建筑。第三个属性表示建筑所在街道的名称: ```swift class Address { @@ -150,9 +143,9 @@ class Address { var buildingNumber: String? var street: String? func buildingIdentifier() -> String? { - if buildingName { + if buildingName != nil { return buildingName - } else if buildingNumber { + } else if buildingNumber != nil { return buildingNumber } else { return nil @@ -161,161 +154,201 @@ class Address { } ``` -`Address`类还提供了一个`buildingIdentifier`的方法,它的返回值类型为`String?`。这个方法检查`buildingName`和`buildingNumber`的属性,如果`buildingName`有值则将其返回,或者如果`buildingNumber`有值则将其返回,再或如果没有一个属性有值,返回空。 +类`Address`提供`buildingIdentifier()`方法,返回值为`String?`。 如果`buildingName`不为空则返回`buildingName`, 如果`buildingNumber`不为空则返回`buildingNumber`。如果这两个属性都为空则返回`nil`。 - -##通过可选链调用属性 +##通过可空链式调用访问属性 +正如[使用可空链式调用来强制展开](#optional_chaining_as_an_alternative_to_forced_unwrapping)中所述,可以通过可空链式调用访问属性的可空值,并且判断访问是否成功。 -正如上面“ [可选链可替代强制解析](#optional_chaining_as_an_alternative_to_forced_unwrapping)”中所述,你可以利用可选链的可选值获取属性,并且检查属性是否获取成功。然而,你不能使用可选链为属性赋值。 - -使用上述定义的类来创建一个人实例,并再次尝试后去它的`numberOfRooms`属性: +下面的代码创建了一个`Person`实例,然后访问`numberOfRooms`属性: ```swift let john = Person() if let roomCount = john.residence?.numberOfRooms { - println("John's residence has \(roomCount) room(s).") + print("John's residence has \(roomCount) room(s).") } else { - println("Unable to retrieve the number of rooms.") + print("Unable to retrieve the number of rooms.") } -// 打印 "Unable to retrieve the number of rooms。 +// prints "Unable to retrieve the number of rooms." ``` -由于`john.residence`是空,所以这个可选链和之前一样失败了,但是没有运行时错误。 +因为`john.residence`为`nil`,所以毫无疑问这个可空链式调用失败。 - -##通过可选链调用方法 - -你可以使用可选链的来调用可选值的方法并检查方法调用是否成功。即使这个方法没有返回值,你依然可以使用可选链来达成这一目的。 - -`Residence`的`printNumberOfRooms`方法会打印`numberOfRooms`的当前值。方法如下: +通过可空链式调用来设定属性值: ```swift -func printNumberOfRooms(){ - println(“The number of rooms is \(numberOfRooms)”) -} +let someAddress = Address() +someAddress.buildingNumber = "29" +someAddress.street = "Acacia Road" +john.residence?.address = someAddress ``` -这个方法没有返回值。但是,没有返回值类型的函数和方法有一个隐式的返回值类型`Void`(参见Function Without Return Values)。 +在这个例子中,通过`john.residence`来设定`address`属性也是不行的,因为`john.residence`为`nil`。 -如果你利用可选链调用此方法,这个方法的返回值类型将是`Void?`,而不是`Void`,因为当通过可选链调用方法时返回值总是可选类型(optional type)。即使这个方法本身没有定义返回值,你也可以使用`if`语句来检查是否能成功调用`printNumberOfRooms`方法:如果方法通过可选链调用成功,`printNumberOfRooms`的隐式返回值将会是`Void`,如果没有成功,将返回`nil`: +##通过可空链式调用来调用方法 +可以通过可空链式调用来调用方法,并判断是否调用成功,即使这个方法没有返回值。 +`Residence`中的`printNumberOfRooms()`方法输出当前的`numberOfRooms`值: ```swift -if john.residence?.printNumberOfRooms?() { - println("It was possible to print the number of rooms.") -} else { - println("It was not possible to print the number of rooms.") +func printNumberOfRooms() { + print("The number of rooms is \(numberOfRooms)") } -// 打印 "It was not possible to print the number of rooms."。 ``` - -##使用可选链调用下标脚本 +这个方法没有返回值。但是没有返回值的方法隐式返回`Void`类型,如[无返回值函数](./06_Functions.html#functions_without_return_values)中所述。这意味着没有返回值的方法也会返回()或者空的元组。 -你可以使用可选链来尝试从下标脚本获取值并检查下标脚本的调用是否成功,然而,你不能通过可选链来设置下标脚本。 +如果在可空值上通过可空链式调用来调用这个方法,这个方法的返回类型为`Void?`,而不是`Void`,因为通过可空链式调用得到的返回值都是可空的。这样我们就可以使用`if`语句来判断能否成功调用`printNumberOfRooms()`方法,即使方法本身没有定义返回值。通过返回值是否为`nil`可以判断调用是否成功: -> 注意: -当你使用可选链来获取下标脚本的时候,你应该将问号放在下标脚本括号的前面而不是后面。可选链的问号一般直接跟在表达语句的后面。 +```swift +if john.residence?.printNumberOfRooms() != nil { + print("It was possible to print the number of rooms.") +} else { + print("It was not possible to print the number of rooms.") +} +// prints "It was not possible to print the number of rooms." +``` -下面这个例子用在`Residence`类中定义的下标脚本来获取`john.residence`数组中第一个房间的名字。因为`john.residence`现在是`nil`,下标脚本的调用失败了。 +同样的,可以判断通过可空链式调用来给属性赋值是否成功。在上面的例子中,我们尝试给`john.residence`中的`address`属性赋值,即使`residence`为`nil`。通过可空链式调用给属性赋值会返回`Void?`,通过判断返回值是否为`nil`可以知道赋值是否成功: + +```swift +if (john.residence?.address = someAddress) != nil { + print("It was possible to set the address.") +} else { + print("It was not possible to set the address.") +} +// prints "It was not possible to set the address." +``` + + +##通过可空链式调用来访问下标 +通过可空链式调用,我们可以用下标来对可空值进行读取或写入,并且判断下标调用是否成功。 +> +注意: +当通过可空链式调用访问可空值的下标的时候,应该将问号放在下标方括号的前面而不是后面。可空链式调用的问号一般直接跟在可空表达式的后面。 + +下面这个例子用下标访问`john.residence`中`rooms`数组中第一个房间的名称,因为`john.residence`为`nil`,所以下标调用毫无疑问失败了: ```swift if let firstRoomName = john.residence?[0].name { - println("The first room name is \(firstRoomName).") + print("The first room name is \(firstRoomName).") } else { - println("Unable to retrieve the first room name.") + print("Unable to retrieve the first room name.") } -// 打印 "Unable to retrieve the first room name."。 +// prints "Unable to retrieve the first room name." ``` -在下标脚本调用中可选链的问号直接跟在`john.residence`的后面,在下标脚本括号的前面,因为`john.residence`是可选链试图获得的可选值。 -如果你创建一个`Residence`实例给`john.residence`,且在他的`rooms`数组中有一个或多个`Room`实例,那么你可以使用可选链通过`Residence`下标脚本来获取在`rooms`数组中的实例了: +在这个例子中,问号直接放在`john.residence`的后面,并且在方括号的前面,因为`john.residence`是可空值。 + +类似的,可以通过下标,用可空链式调用来赋值: + +```swift +john.residence?[0] = Room(name: "Bathroom") +``` + + +这次赋值同样会失败,因为`residence`目前是`nil`。 + +如果你创建一个`Residence`实例,添加一些`Room`实例并赋值给`john.residence`,那就可以通过可选链和下标来访问数组中的元素: ```swift let johnsHouse = Residence() -johnsHouse.rooms += Room(name: "Living Room") -johnsHouse.rooms += Room(name: "Kitchen") +johnsHouse.rooms.append(Room(name: "Living Room")) +johnsHouse.rooms.append(Room(name: "Kitchen")) john.residence = johnsHouse if let firstRoomName = john.residence?[0].name { - println("The first room name is \(firstRoomName).") + print("The first room name is \(firstRoomName).") } else { - println("Unable to retrieve the first room name.") + print("Unable to retrieve the first room name.") } -// 打印 "The first room name is Living Room."。 +// prints "The first room name is Living Room." ``` - -##连接多层链接 +##访问可空类型的下标: +如果下标返回可空类型值,比如Swift中`Dictionary`的`key`下标。可以在下标的闭合括号后面放一个问号来链接下标的可空返回值: + +```swift +var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]] +testScores["Dave"]?[0] = 91 +testScores["Bev"]?[0]++ +testScores["Brian"]?[0] = 72 +// the "Dave" array is now [91, 82, 84] and the "Bev" array is now [80, 94, 81] +``` + +上面的例子中定义了一个`testScores`数组,包含了两个键值对, 把`String`类型的`key`映射到一个整形数组。这个例子用可空链式调用把“Dave”数组中第一个元素设为91,把”Bev”数组的第一个元素+1,然后尝试把”Brian”数组中的第一个元素设为72。前两个调用是成功的,因为这两个`key`存在。但是key“Brian”在字典中不存在,所以第三个调用失败。 + +##多层链接 +可以通过多个链接多个可空链式调用来向下访问属性,方法以及下标。但是多层可空链式调用不会添加返回值的可空性。 -你可以将多层可选链连接在一起,可以掘取模型内更下层的属性方法和下标脚本。然而多层可选链不能再添加比已经返回的可选值更多的层。 也就是说: -如果你试图获得的类型不是可选类型,由于使用了可选链它将变成可选类型。 -如果你试图获得的类型已经是可选类型,由于可选链它也不会提高可选性。 ++ 如果你访问的值不是可空的,通过可空链式调用将会放回可空值。 ++ 如果你访问的值已经是可空的,通过可空链式调用不会变得“更”可空。 因此: -如果你试图通过可选链获得`Int`值,不论使用了多少层链接返回的总是`Int?`。 -相似的,如果你试图通过可选链获得`Int?`值,不论使用了多少层链接返回的总是`Int?`。 ++ 通过可空链式调用访问一个`Int`值,将会返回`Int?`,不过进行了多少次可空链式调用。 ++ 类似的,通过可空链式调用访问`Int?`值,并不会变得更加可空。 -下面的例子试图获取`john`的`residence`属性里的`address`的`street`属性。这里使用了两层可选链来联系`residence`和`address`属性,它们两者都是可选类型: +下面的例子访问`john`中的`residence`中的`address`中的`street`属性。这里使用了两层可空链式调用,`residence`以及`address`,这两个都是可空值。 ```swift if let johnsStreet = john.residence?.address?.street { - println("John's street name is \(johnsStreet).") + print("John's street name is \(johnsStreet).") } else { - println("Unable to retrieve the address.") + print("Unable to retrieve the address.") } -// 打印 "Unable to retrieve the address.”。 +// prints "Unable to retrieve the address." ``` -`john.residence`的值现在包含一个`Residence`实例,然而`john.residence.address`现在是`nil`,因此`john.residence?.address?.street`调用失败。 +`john.residence`包含`Residence`实例,但是`john.residence.address`为`nil`。因此,不能访问`john.residence?.address?.street`。 -从上面的例子发现,你试图获得`street`属性值。这个属性的类型是`String?`。因此尽管在可选类型属性前使用了两层可选链,`john.residence?.address?.street`的返回值类型也是`String?`。 +需要注意的是,上面的例子中,`street`的属性为`String?`。`john.residence?.address?.street`的返回值也依然是`String?`,即使已经进行了两次可空的链式调用。 -如果你为`Address`设定一个实例来作为`john.residence.address`的值,并为`address`的`street`属性设定一个实际值,你可以通过多层可选链来得到这个属性值。 +如果把`john.residence.address`指向一个实例,并且为`address`中的`street`属性赋值,我们就能过通过可空链式调用来访问`street`属性。 ```swift let johnsAddress = Address() johnsAddress.buildingName = "The Larches" johnsAddress.street = "Laurel Street" -john.residence!.address = johnsAddress -``` - -```swift +john.residence?.address = johnsAddress + if let johnsStreet = john.residence?.address?.street { - println("John's street name is \(johnsStreet).") + print("John's street name is \(johnsStreet).") } else { - println("Unable to retrieve the address.") + print("Unable to retrieve the address.") } -// 打印 "John's street name is Laurel Street."。 +// prints "John's street name is Laurel Street." ``` -值得注意的是,“`!`”符号在给`john.residence.address`分配`address`实例时的使用。`john.residence`属性是一个可选类型,因此你需要在它获取`address`属性之前使用`!`解析以获得它的实际值。 +在上面的例子中,因为`john.residence`是一个可用的`Residence`实例,所以对`john.residence`的`address`属性赋值成功。 - -##链接可选返回值的方法 - -前面的例子解释了如何通过可选链来获得可选类型属性值。你也可以通过可选链调用一个返回可选类型值的方法并按需链接该方法的返回值。 - -下面的例子通过可选链调用了`Address`类中的`buildingIdentifier` 方法。这个方法的返回值类型是`String?`。如上所述,这个方法在可选链调用后最终的返回值类型依然是`String?`: +##对返回可空值的函数进行链接 +上面的例子说明了如何通过可空链式调用来获取可空属性值。我们还可以通过可空链式调用来调用返回可空值的方法,并且可以继续对可空值进行链接。 +在下面的例子中,通过可空链式调用来调用`Address`的`buildingIdentifier()`方法。这个方法返回`String?`类型。正如上面所说,通过可空链式调用的方法的最终返回值还是`String?`: + ```swift if let buildingIdentifier = john.residence?.address?.buildingIdentifier() { - println("John's building identifier is \(buildingIdentifier).") + print("John's building identifier is \(buildingIdentifier).") } -// 打印 "John's building identifier is The Larches."。 +// prints "John's building identifier is The Larches." ``` -如果你还想进一步对方法返回值执行可选链,将可选链问号符放在方法括号的后面: +如果要进一步对方法的返回值进行可空链式调用,在方法`buildingIdentifier()`的圆括号后面加上问号: ```swift -if let upper = john.residence?.address?.buildingIdentifier()?.uppercaseString { - println("John's uppercase building identifier is \(upper).") +if let beginsWithThe = + john.residence?.address?.buildingIdentifier()?.hasPrefix("The") { + if beginsWithThe { + print("John's building identifier begins with \"The\".") + } else { + print("John's building identifier does not begin with \"The\".") + } } -// 打印 "John's uppercase building identifier is THE LARCHES."。 +// prints "John's building identifier begins with "The"." ``` -> 注意: -在上面的例子中,你将可选链问号符放在括号后面是因为你想要链接的可选值是`buildingIdentifier`方法的返回值,不是`buildingIdentifier`方法本身。 +> +注意: +在上面的例子中在,在方法的圆括号后面加上问号是因为`buildingIdentifier()`的返回值是可空值,而不是方法本身是可空的。 diff --git a/source/chapter2/18_Error_Handling.md b/source/chapter2/18_Error_Handling.md index c1d1e62e..33d29562 100644 --- a/source/chapter2/18_Error_Handling.md +++ b/source/chapter2/18_Error_Handling.md @@ -1,2 +1,177 @@ -# 错误处理 ------------------ \ No newline at end of file +# 错误处理(Error Handling) +----------------- + +> 2.0 +> 翻译+校对:[lyojo](https://github.com/lyojo) + +错误处理是响应错误以及从错误中返回的过程。swift提供第一类错误支持,包括在运行时抛出,捕获,传送和控制可回收错误。 + +一些函数和方法不能总保证能够执行所有代码或产生有用的输出。可空类型用来表示值可能为空,但是当函数执行失败的时候,可空通常可以用来确定执行失败的原因,因此代码可以正确地响应失败。在Swift中,这叫做抛出函数或者抛出方法。 + +举个例子,考虑到一个从磁盘上的一个文件读取以及处理数据的任务,有几种情况可能会导致这个任务失败,包括指定路径的文件不存在,文件不具有可读属性,或者文件没有被编码成合适的格式。区分这些错误可以让程序解决并且修复这些错误,并且,如果可能的话,把这些错误报告给用户。 + +> +注意: +Swift中的错误处理涉及到错误处理样式,这会用到Cocoa中的NSError和Objective-C。更多信息请参见:[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)中的[错误处理](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html#//apple_ref/doc/uid/TP40014216-CH7-ID10)。 + +###错误的表示: +在Swift中,错误用符合`ErrorType`协议的值表示。 +Swift枚举特别适合把一系列相关的错误组合在一起,同时可以把一些相关的值和错误关联在一起。因此编译器会为实现`ErrorType`协议的Swift枚举类型自动实现相应合成。 + +比如说,你可以这样表示操作自动贩卖机会出现的错误: + +```swift +enum VendingMachineError: ErrorType { + case InvalidSelection + case InsufficientFunds(required: Double) + case OutOfStock +} +``` + +在这种情况下,自动贩卖机可能会因为以下原因失败: +请求的物品不存在,用`InvalidSelection`表示。 +请求的物品的价格高于已投入金额,用`InsufficientFunds`表示。相关的双精度值表示还需要多少钱来完成此次交易。 +请求的物品已经卖完了,用`OutOfStock`表示。 + + +错误抛出 +通过在函数或方法声明的参数后面加上`throws`关键字,表明这个函数或方法可以抛出错误。如果指定一个返回值,可以把`throws`关键字放在返回箭头(->)的前面。除非明确地指出,一个函数,方法或者闭包就不能抛出错误。 + +```swift +func canThrowErrors() throws -> String + +func cannotThrowErrors() -> String +``` + +在抛出函数体的任意一个地方,可以通过`throw`语句抛出错误。在下面的例子中,如果请求的物品不存在,或者卖完了,或者超出投入金额,`vend(itemNamed:)`函数会抛出一个错误: + +```swift +struct Item { + var price: Double + var count: Int +} + +var inventory = [ + "Candy Bar": Item(price: 1.25, count: 7), + "Chips": Item(price: 1.00, count: 4), + "Pretzels": Item(price: 0.75, count: 11) +] +var amountDeposited = 1.00 + +func vend(itemNamed name: String) throws { + guard var item = inventory[name] else { + throw VendingMachineError.InvalidSelection + } + + guard item.count > 0 else { + throw VendingMachineError.OutOfStock + } + + if amountDeposited >= item.price { + // Dispense the snack + amountDeposited -= item.price + --item.count + inventory[name] = item + } else { + let amountRequired = item.price - amountDeposited + throw VendingMachineError.InsufficientFunds(required: amountRequired) + } +} +``` + +首先,`guard`语句用来把绑定`item`常量和`count`变量到在库存中对应的值。如果物品不在库存中,将会抛出`InvalidSelection`错误。然后,物品是否可获取有物品的剩余数量决定。如果`count`小于等于0,将会抛出`OutOfStock`错误。最后,把请求物品的价格和已经投入的金额进行比较,如果如果投入的金额大于物品的价格,将会从投入的金额从减去物品的价格,然后库存中该物品的数量减1,然后返回请求的物品。否则,将会计算还需要多少钱,然后把这个值作为`InsufficientFunds`错误的关联值。因为`throw`语句会马上改变程序流程,当所有的购买条件(物品存在,库存足够以及投入金额足够)都满足的时候,物品才会出售。 + +当调用一个抛出函数的时候,在调用前面加上`try`。这个关键字表明函数可以抛出错误,而且在`try`后面代码将不会执行。 + +```swift +let favoriteSnacks = [ + "Alice": "Chips", + "Bob": "Licorice", + "Eve": "Pretzels", +] +func buyFavoriteSnack(person: String) throws { + let snackName = favoriteSnacks[person] ?? "Candy Bar" + try vend(itemNamed: snackName) +} +``` + +`buyFavoriteSnack(_:)` 函数查找某个人的最喜欢的零食,然后尝试买给他。如果这个人在列表中没有喜欢的零食,就会购买`Candy Bar`。这个函数会调用`vend`函数,`vend`函数可能会抛出错误,所以在`vend`前面加上了`try`关键字。因为`buyFavoriteSnack`函数也是一个抛出函数,所以`vend`函数抛出的任何错误都会向上传递到`buyFavoriteSnack`被调用的地方。 + +###捕捉和处理错误 +使用do-catch语句来就捕获和处理错误 + +```swift +do { + + try function that throws + + statements + +} catch pattern { + + statements + +} +``` + +如果一个错误被抛出了,这个错误会被传递到外部域,直到被一个`catch`分句处理。一个`catch`分句包含一个`catch`关键字,跟着一个`pattern`来匹配错误和相应的执行语句。 + +类似`switch`语句,编译器会检查`catch`分句是否能够处理全部错误。如果能够处理所有错误情况,就认为这个错误被完全处理。否者,包含这个抛出函数的所在域就要处理这个错误,或者包含这个抛出函数的函数也用`throws`声明。为了保证错误被处理,用一个带`pattern`的`catch`分句来匹配所有错误。如果一个`catch`分句没有指定样式,这个分句会匹配并且绑定任何错误到一个本地`error`常量。更多关于`pattern`的信息,参见[模式](../chapter3/07_Patterns.html)。 + +```swift +do { + try vend(itemNamed: "Candy Bar") + // Enjoy delicious snack +} catch VendingMachineError.InvalidSelectio { + print("Invalid Selection") +} catch VendingMachineError.OutOfStock { + print("Out of Stock.") +} catch VendingMachineError.InsufficientFunds(let amountRequired) { + print("Insufficient funds. Please insert an additional $\(amountRequired).") +} +``` + +在上面的例子中,`vend(itemNamed:)` 函数在`try`表达式中被调用,因为这个函数会抛出错误。如果抛出了错误,程序执行流程马上转到`catch`分句,在`catch`分句中确定错误传递是否继续传送。如果没有抛出错误,将会执行在`do`语句中剩余的语句。 + +> 注意:Swift中的错误处理和其他语言中的异常处理很像,使用了`try`、`catch`和`throw`关键字。但是和这些语言——包括Objective-C——不同的是,Swift不会展开调用堆栈,那会带来很大的性能损耗。因此,在Swift中`throw`语句的性能可以做到几乎和`return`语句一样。 + +###禁止错误传播 +在运行时,有几种情况抛出函数事实上是不会抛出错误的。在这几种情况下,你可以用`forced-try`表达式来调用抛出函数或方法,即使用`try!`来代替`try`。 + +通过`try!`来调用抛出函数或方法禁止了错误传送,并且把调用包装在运行时断言,这样就不会抛出错误。如果错误真的抛出了,会触发运行时错误。 + +```swift +func willOnlyThrowIfTrue(value: Bool) throws { + if value { throw someError } +} + +do { + try willOnlyThrowIfTrue(false) +} catch { + // Handle Error +} + +try! willOnlyThrowIfTrue(false) +``` + +###收尾操作 +使用defer语句来在执行一系列的语句。这样不管有没有错误发生,都可以执行一些必要的收尾操作。包括关闭打开的文件描述符以及释放所有手动分配的内存。 + +`defer`语句把执行推迟到退出当前域的时候。`defer`语句包括`defer`关键字以及后面要执行的语句。被推迟的语句可能不包含任何将执行流程转移到外部的代码,比如`break`或者`return`语句,或者通过抛出一个错误。被推迟的操作的执行的顺序和他们定义的顺序相反,也就是说,在第一个`defer`语句中的代码在第二个`defer`语句中的代码之后执行。 + +```swift +func processFile(filename: String) throws { + if exists(filename) { + let file = open(filename) + defer { + close(file) + } + while let line = try file.readline() { + // Work with the file. + } + // close(file) is called here, at the end of the scope. + } +} +``` + +上面这个例子使用了`defer`语句来保证`open`有对应的`close`。这个调用不管是否有抛出都会执行。 diff --git a/source/chapter2/19_Nested_Types.md b/source/chapter2/19_Nested_Types.md old mode 100755 new mode 100644 index 0db7d1da..fe0bf822 --- a/source/chapter2/19_Nested_Types.md +++ b/source/chapter2/19_Nested_Types.md @@ -1,6 +1,10 @@ +> 1.0 > 翻译:[Lin-H](https://github.com/Lin-H) > 校对:[shinyzhu](https://github.com/shinyzhu) +> 2.0 +> 翻译+校对:[SergioChan](https://github.com/SergioChan) + # 嵌套类型 ----------------- @@ -41,15 +45,15 @@ struct BlackjackCard { case .Jack, .Queen, .King: return Values(first: 10, second: nil) default: - return Values(first: self.toRaw(), second: nil) + return Values(first: self.rawValue, second: nil) } } } - + // BlackjackCard 的属性和方法 let rank: Rank, suit: Suit var description: String { - var output = "suit is \(suit.toRaw())," + var output = "suit is \(suit.rawValue)," output += " value is \(rank.values.first)" if let second = rank.values.second { output += " or \(second)" @@ -63,20 +67,20 @@ struct BlackjackCard { 枚举型的`Rank`用来描述扑克牌从`Ace`~10,`J`,`Q`,`K`,13张牌,并分别用一个`Int`类型的值表示牌的面值。(这个`Int`类型的值不适用于`Ace`,`J`,`Q`,`K`的牌)。 -如上文所提到的,枚举型`Rank`在自己内部定义了一个嵌套结构体`Values`。这个结构体包含两个变量,只有`Ace`有两个数值,其余牌都只有一个数值。结构体`Values`中定义的两个属性: +如上文所提到的,枚举型`Rank`在自己内部定义了一个嵌套结构体`Values`。在这个结构体中,只有`Ace`有两个数值,其余牌都只有一个数值。结构体`Values`中定义的两个属性: `first`, 为` Int` `second`, 为 `Int?`, 或 “optional `Int`” -`Rank`定义了一个计算属性`values`,这个计算属性会根据牌的面值,用适当的数值去初始化`Values`实例,并赋值给`values`。对于`J`,`Q`,`K`,`Ace`会使用特殊数值,对于数字面值的牌使用`Int`类型的值。 +`Rank`定义了一个计算属性`values`,它将会返回一个结构体`Values`的实例。这个计算属性会根据牌的面值,用适当的数值去初始化`Values`实例,并赋值给`values`。对于`J`,`Q`,`K`,`Ace`会使用特殊数值,对于数字面值的牌使用`Int`类型的值。 `BlackjackCard`结构体自身有两个属性—`rank`与`suit`,也同样定义了一个计算属性`description`,`description`属性用`rank`和`suit`的中内容来构建对这张扑克牌名字和数值的描述,并用可选类型`second`来检查是否存在第二个值,若存在,则在原有的描述中增加对第二数值的描述。 -因为`BlackjackCard`是一个没有自定义构造函数的结构体,在[Memberwise Initializers for Structure Types](https://github.com/CocoaChina-editors/Welcome-to-Swift/blob/master/The%20Swift%20Programming%20Language/02Language%20Guide/14Initialization.md)中知道结构体有默认的成员构造函数,所以你可以用默认的`initializer`去初始化新的常量`theAceOfSpades`: +因为`BlackjackCard`是一个没有自定义构造函数的结构体,在[结构体的逐一成员构造器](./14_Initialization.html#memberwise_initializers_for_structure_types)中知道结构体有默认的成员构造函数,所以你可以用默认的`initializer`去初始化新的常量`theAceOfSpades`: ```swift let theAceOfSpades = BlackjackCard(rank: .Ace, suit: .Spades) -println("theAceOfSpades: \(theAceOfSpades.description)") +print("theAceOfSpades: \(theAceOfSpades.description)") // 打印出 "theAceOfSpades: suit is ♠, value is 1 or 11" ``` @@ -88,9 +92,8 @@ println("theAceOfSpades: \(theAceOfSpades.description)") 在外部对嵌套类型的引用,以被嵌套类型的名字为前缀,加上所要引用的属性名: ```swift -let heartsSymbol = BlackjackCard.Suit.Hearts.toRaw() +let heartsSymbol = BlackjackCard.Suit.Hearts.rawValue // 红心的符号 为 "♡" ``` -对于上面这个例子,这样可以使`Suit`, `Rank`, 和 `Values`的名字尽可能的短,因为它们的名字会自然的由被定义的上下文来限定。 - +对于上面这个例子,这样可以使`Suit`, `Rank`, 和 `Values`的名字尽可能的短,因为它们的名字会自然的由定义它们的上下文来限定。 diff --git a/source/chapter2/19_Type_Casting.md b/source/chapter2/19_Type_Casting.md new file mode 100644 index 00000000..538a53ad --- /dev/null +++ b/source/chapter2/19_Type_Casting.md @@ -0,0 +1,261 @@ +# 类型转换(Type Casting) +----------------- + +> 1.0 +> 翻译:[xiehurricane](https://github.com/xiehurricane) +> 校对:[happyming](https://github.com/happyming) + +> 2.0 +> 翻译+校对:[yangsiy](https://github.com/yangsiy) + +本页包含内容: + +- [定义一个类层次作为例子](#defining_a_class_hierarchy_for_type_casting) +- [检查类型](#checking_type) +- [向下转型(Downcasting)](#downcasting) +- [`Any`和`AnyObject`的类型转换](#type_casting_for_any_and_anyobject) + + +_类型转换_ 可以判断实例的类型,也可以将实例看做是其父类或者子类的实例。 + +类型转换在 Swift 中使用 `is` 和 `as` 操作符实现。这两个操作符提供了一种简单达意的方式去检查值的类型或者转换它的类型。 + +你也可以用它来检查一个类是否实现了某个协议,就像在 [检验协议的一致性](./22_Protocols.html#checking_for_protocol_conformance)部分讲述的一样。 + + +## 定义一个类层次作为例子 + +你可以将类型转换用在类和子类的层次结构上,检查特定类实例的类型并且转换这个类实例的类型成为这个层次结构中的其他类型。下面的三个代码段定义了一个类层次和一个包含了几个这些类实例的数组,作为类型转换的例子。 + +第一个代码片段定义了一个新的基础类 `MediaItem`。这个类为任何出现在数字媒体库的媒体项提供基础功能。特别的,它声明了一个 `String` 类型的 `name` 属性,和一个 `init name` 初始化器。(假定所有的媒体项都有个名称。) + +```swift +class MediaItem { + var name: String + init(name: String) { + self.name = name + } +} +``` + +下一个代码段定义了 `MediaItem` 的两个子类。第一个子类 `Movie` 封装了与电影相关的额外信息,在父类(或者说基类)的基础上增加了一个 `director`(导演)属性,和相应的初始化器。第二个子类 `Song`,在父类的基础上增加了一个 `artist`(艺术家)属性,和相应的初始化器: + +```swift +class Movie: MediaItem { + var director: String + init(name: String, director: String) { + self.director = director + super.init(name: name) + } +} + +class Song: MediaItem { + var artist: String + init(name: String, artist: String) { + self.artist = artist + super.init(name: name) + } +} +``` + +最后一个代码段创建了一个数组常量 `library`,包含两个 `Movie` 实例和三个 `Song` 实例。`library` 的类型是在它被初始化时根据它数组中所包含的内容推断来的。Swift的类型检测器能够推理出 `Movie` 和 `Song` 有共同的父类 `MediaItem`,所以它推断出 `[MediaItem]` 类作为 `library` 的类型。 + +```swift +let library = [ + Movie(name: "Casablanca", director: "Michael Curtiz"), + Song(name: "Blue Suede Shoes", artist: "Elvis Presley"), + Movie(name: "Citizen Kane", director: "Orson Welles"), + Song(name: "The One And Only", artist: "Chesney Hawkes"), + Song(name: "Never Gonna Give You Up", artist: "Rick Astley") +] +// the type of "library" is inferred to be [MediaItem] +``` + +在幕后 `library` 里存储的媒体项依然是 `Movie` 和 `Song` 类型的。但是,若你迭代它,依次取出的实例会是 `MediaItem` 类型的,而不是 `Movie` 和 `Song` 类型。为了让它们作为原本的类型工作,你需要检查它们的类型或者向下转换它们到其它类型,就像下面描述的一样。 + + +## 检查类型(Checking Type) + +用类型检查操作符(`is`)来检查一个实例是否属于特定子类型。若实例属于那个子类型,类型检查操作符返回 `true`,否则返回 `false`。 + +下面的例子定义了两个变量,`movieCount` 和 `songCount`,用来计算数组 `library` 中 `Movie` 和 `Song` 类型的实例数量。 + +```swift +var movieCount = 0 +var songCount = 0 + +for item in library { + if item is Movie { + ++movieCount + } else if item is Song { + ++songCount + } +} + +print("Media library contains \(movieCount) movies and \(songCount) songs") +// prints "Media library contains 2 movies and 3 songs" +``` + +示例迭代了数组 `library` 中的所有项。每一次,`for`-`in` 循环设置 +`item` 为数组中的下一个 `MediaItem`。 + +若当前 `MediaItem` 是一个 `Movie` 类型的实例,`item is Movie` 返回 +`true`,相反返回 `false`。同样的,`item is +Song` 检查item是否为 `Song` 类型的实例。在循环结束后,`movieCount` 和 `songCount` 的值就是被找到属于各自的类型的实例数量。 + + +## 向下转型(Downcasting) + +某类型的一个常量或变量可能在幕后实际上属于一个子类。当确定是这种情况时,你可以尝试向下转到它的子类型,用类型转换操作符(`as?` 或 `as!`) + +因为向下转型可能会失败,类型转型操作符带有两种不同形式。条件形式(conditional form) `as?` 返回一个你试图向下转成的类型的可选值(optional value)。强制形式 `as!` 把试图向下转型和强制解包(force-unwraps)结果作为一个混合动作。 + +当你不确定向下转型可以成功时,用类型转换的条件形式(`as?`)。条件形式的类型转换总是返回一个可选值(optional value),并且若下转是不可能的,可选值将是 `nil`。这使你能够检查向下转型是否成功。 + +只有你可以确定向下转型一定会成功时,才使用强制形式(`as!`)。当你试图向下转型为一个不正确的类型时,强制形式的类型转换会触发一个运行时错误。 + +下面的例子,迭代了 `library` 里的每一个 `MediaItem`,并打印出适当的描述。要这样做,`item` 需要真正作为 `Movie` 或 `Song` 的类型来使用,不仅仅是作为 `MediaItem`。为了能够在描述中使用 `Movie` 或 `Song` 的 `director` 或 `artist` 属性,这是必要的。 + +在这个示例中,数组中的每一个 `item` 可能是 `Movie` 或 `Song`。事前你不知道每个 `item` 的真实类型,所以这里使用条件形式的类型转换(`as?`)去检查循环里的每次下转。 + +```swift +for item in library { + if let movie = item as? Movie { + print("Movie: '\(movie.name)', dir. \(movie.director)") + } else if let song = item as? Song { + print("Song: '\(song.name)', by \(song.artist)") + } +} + +// Movie: 'Casablanca', dir. Michael Curtiz +// Song: 'Blue Suede Shoes', by Elvis Presley +// Movie: 'Citizen Kane', dir. Orson Welles +// Song: 'The One And Only', by Chesney Hawkes +// Song: 'Never Gonna Give You Up', by Rick Astley +``` + +示例首先试图将 `item` 下转为 `Movie`。因为 `item` 是一个 `MediaItem` +类型的实例,它可能是一个 `Movie`;同样,它也可能是一个 `Song`,或者仅仅是基类 +`MediaItem`。因为不确定,`as?`形式在试图下转时将返回一个可选值。`item as? Movie` 的返回值是 `Movie?` 或 “可选 `Movie`”类型。 + +当向下转型为 `Movie` 应用在两个 `Song` +实例时将会失败。为了处理这种情况,上面的例子使用了可选绑定(optional binding)来检查可选 `Movie` 真的包含一个值(这个是为了判断下转是否成功。)可选绑定是这样写的“`if let movie = item as? Movie`”,可以这样解读: + +“尝试将 `item` 转为 `Movie` 类型。若成功,设置一个新的临时常量 `movie` 来存储返回的可选 `Movie`” + +若向下转型成功,然后 `movie` 的属性将用于打印一个 `Movie` 实例的描述,包括它的导演的名字 `director` 。相近的原理被用来检测 `Song` 实例,当 `Song` 被找到时则打印它的描述(包含 `artist` 的名字)。 + +> 注意: +> 转换没有真的改变实例或它的值。潜在的根本的实例保持不变;只是简单地把它作为它被转换成的类来使用。 + + +## `Any`和`AnyObject`的类型转换 + +Swift为不确定类型提供了两种特殊类型别名: + +* `AnyObject`可以代表任何class类型的实例。 +* `Any`可以表示任何类型,包括方法类型(function types)。 + +> 注意: +> 只有当你明确的需要它的行为和功能时才使用`Any`和`AnyObject`。在你的代码里使用你期望的明确的类型总是更好的。 + + +### `AnyObject`类型 + +当在工作中使用 Cocoa APIs,我们一般会接收一个`[AnyObject]`类型的数组,或者说“一个任何对象类型的数组”。这是因为 Objective-C 没有明确的类型化数组。但是,你常常可以从 API 提供的信息中清晰地确定数组中对象的类型。 + +在这些情况下,你可以使用强制形式的类型转换(`as`)来下转在数组中的每一项到比 `AnyObject` 更明确的类型,不需要可选解析(optional unwrapping)。 + +下面的示例定义了一个 `[AnyObject]` 类型的数组并填入三个`Movie`类型的实例: + +```swift +let someObjects: [AnyObject] = [ + Movie(name: "2001: A Space Odyssey", director: "Stanley Kubrick"), + Movie(name: "Moon", director: "Duncan Jones"), + Movie(name: "Alien", director: "Ridley Scott") +] +``` + +因为知道这个数组只包含 `Movie` 实例,你可以直接用(`as!`)下转并解包到不可选的`Movie`类型: + +```swift +for object in someObjects { + let movie = object as! Movie + print("Movie: '\(movie.name)', dir. \(movie.director)") +} +// Movie: '2001: A Space Odyssey', dir. Stanley Kubrick +// Movie: 'Moon', dir. Duncan Jones +// Movie: 'Alien', dir. Ridley Scott +``` + +为了变为一个更短的形式,下转`someObjects`数组为`[Movie]`类型来代替下转数组中每一项的方式。 + +```swift +for movie in someObjects as! [Movie] { + print("Movie: '\(movie.name)', dir. \(movie.director)") +} +// Movie: '2001: A Space Odyssey', dir. Stanley Kubrick +// Movie: 'Moon', dir. Duncan Jones +// Movie: 'Alien', dir. Ridley Scott +``` + +### `Any`类型 + +这里有个示例,使用 `Any` 类型来和混合的不同类型一起工作,包括方法类型和非 `class` 类型。它创建了一个可以存储`Any`类型的数组 `things`。 + +```swift +var things = [Any]() + +things.append(0) +things.append(0.0) +things.append(42) +things.append(3.14159) +things.append("hello") +things.append((3.0, 5.0)) +things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman")) +things.append({ (name: String) -> String in "Hello, \(name)" }) +``` + +`things` 数组包含两个 `Int` 值,2个 `Double` 值,1个 `String` 值,一个元组 `(Double, Double)` ,电影“Ghostbusters”,和一个获取 `String` 值并返回另一个 `String` 值的闭包表达式。 + +你可以在 `switch` 表达式的cases中使用 `is` 和 `as` 操作符来发觉只知道是 `Any` 或 `AnyObject` 的常量或变量的类型。下面的示例迭代 `things` 数组中的每一项的并用`switch`语句查找每一项的类型。这几种 `switch` 语句的情形绑定它们匹配的值到一个规定类型的常量,让它们的值可以被打印: + +```swift +for thing in things { + switch thing { + case 0 as Int: + print("zero as an Int") + case 0 as Double: + print("zero as a Double") + case let someInt as Int: + print("an integer value of \(someInt)") + case let someDouble as Double where someDouble > 0: + print("a positive double value of \(someDouble)") + case is Double: + print("some other double value that I don't want to print") + case let someString as String: + print("a string value of \"\(someString)\"") + case let (x, y) as (Double, Double): + print("an (x, y) point at \(x), \(y)") + case let movie as Movie: + print("a movie called '\(movie.name)', dir. \(movie.director)") + case let stringConverter as String -> String: + print(stringConverter("Michael")) + default: + print("something else") + } +} + +// zero as an Int +// zero as a Double +// an integer value of 42 +// a positive double value of 3.14159 +// a string value of "hello" +// an (x, y) point at 3.0, 5.0 +// a movie called 'Ghostbusters', dir. Ivan Reitman +// Hello, Michael +``` + + +> 注意: +> 在一个switch语句的case中使用强制形式的类型转换操作符(as, 而不是 as?)来检查和转换到一个明确的类型。在 `switch` case 语句的内容中这种检查总是安全的。 diff --git a/source/chapter2/20_Nested_Types.md b/source/chapter2/20_Nested_Types.md new file mode 100755 index 00000000..18dfc46d --- /dev/null +++ b/source/chapter2/20_Nested_Types.md @@ -0,0 +1,99 @@ +# 嵌套类型(Nested Types) +----------------- + +> 1.0 +> 翻译:[Lin-H](https://github.com/Lin-H) +> 校对:[shinyzhu](https://github.com/shinyzhu) + +> 2.0 +> 翻译+校对:[SergioChan](https://github.com/SergioChan) + +本页包含内容: + +- [嵌套类型实例](#nested_types_in_action) +- [嵌套类型的引用](#referring_to_nested_types) + +枚举类型常被用于实现特定类或结构体的功能。也能够在有多种变量类型的环境中,方便地定义通用类或结构体来使用,为了实现这种功能,Swift允许你定义嵌套类型,可以在枚举类型、类和结构体中定义支持嵌套的类型。 + +要在一个类型中嵌套另一个类型,将需要嵌套的类型的定义写在被嵌套类型的区域{}内,而且可以根据需要定义多级嵌套。 + + +##嵌套类型实例 + +下面这个例子定义了一个结构体`BlackjackCard`(二十一点),用来模拟`BlackjackCard`中的扑克牌点数。`BlackjackCard`结构体包含2个嵌套定义的枚举类型`Suit` 和 `Rank`。 + +在`BlackjackCard`规则中,`Ace`牌可以表示1或者11,`Ace`牌的这一特征用一个嵌套在枚举型`Rank`的结构体`Values`来表示。 + +```swift +struct BlackjackCard { + // 嵌套定义枚举型Suit + enum Suit: Character { + case Spades = "♠", Hearts = "♡", Diamonds = "♢", Clubs = "♣" + } + + // 嵌套定义枚举型Rank + enum Rank: Int { + case Two = 2, Three, Four, Five, Six, Seven, Eight, Nine, Ten + case Jack, Queen, King, Ace + struct Values { + let first: Int, second: Int? + } + var values: Values { + switch self { + case .Ace: + return Values(first: 1, second: 11) + case .Jack, .Queen, .King: + return Values(first: 10, second: nil) + default: + return Values(first: self.rawValue, second: nil) + } + } + } + + // BlackjackCard 的属性和方法 + let rank: Rank, suit: Suit + var description: String { + var output = "suit is \(suit.rawValue)," + output += " value is \(rank.values.first)" + if let second = rank.values.second { + output += " or \(second)" + } + return output + } +} +``` + +枚举型的`Suit`用来描述扑克牌的四种花色,并分别用一个`Character`类型的值代表花色符号。 + +枚举型的`Rank`用来描述扑克牌从`Ace`~10,`J`,`Q`,`K`,13张牌,并分别用一个`Int`类型的值表示牌的面值。(这个`Int`类型的值不适用于`Ace`,`J`,`Q`,`K`的牌)。 + +如上文所提到的,枚举型`Rank`在自己内部定义了一个嵌套结构体`Values`。在这个结构体中,只有`Ace`有两个数值,其余牌都只有一个数值。结构体`Values`中定义的两个属性: + +- `first`为` Int` +- `second`为 `Int?` 或 “optional `Int`” + +`Rank`定义了一个计算属性`values`,它将会返回一个结构体`Values`的实例。这个计算属性会根据牌的面值,用适当的数值去初始化`Values`实例,并赋值给`values`。对于`J`,`Q`,`K`,`Ace`会使用特殊数值,对于数字面值的牌使用`Int`类型的值。 + +`BlackjackCard`结构体自身有两个属性—`rank`与`suit`,也同样定义了一个计算属性`description`,`description`属性用`rank`和`suit`的中内容来构建对这张扑克牌名字和数值的描述,并用可选类型`second`来检查是否存在第二个值,若存在,则在原有的描述中增加对第二数值的描述。 + +因为`BlackjackCard`是一个没有自定义构造函数的结构体,在[结构体的逐一成员构造器](./14_Initialization.html#memberwise_initializers_for_structure_types)中知道结构体有默认的成员构造函数,所以你可以用默认的`initializer`去初始化新的常量`theAceOfSpades`: + +```swift +let theAceOfSpades = BlackjackCard(rank: .Ace, suit: .Spades) +print("theAceOfSpades: \(theAceOfSpades.description)") +// 打印出 "theAceOfSpades: suit is ♠, value is 1 or 11" +``` + +尽管`Rank`和`Suit`嵌套在`BlackjackCard`中,但仍可被引用,所以在初始化实例时能够通过枚举类型中的成员名称单独引用。在上面的例子中`description`属性能正确得输出对`Ace`牌有1和11两个值。 + + +##嵌套类型的引用 + +在外部对嵌套类型的引用,以被嵌套类型的名字为前缀,加上所要引用的属性名: + +```swift +let heartsSymbol = BlackjackCard.Suit.Hearts.rawValue +// 红心的符号 为 "♡" +``` + +对于上面这个例子,这样可以使`Suit`, `Rank`, 和 `Values`的名字尽可能的短,因为它们的名字会自然的由定义它们的上下文来限定。 diff --git a/source/chapter2/20_Type_Casting.md b/source/chapter2/20_Type_Casting.md index cea8fe3d..8ecbbfb7 100644 --- a/source/chapter2/20_Type_Casting.md +++ b/source/chapter2/20_Type_Casting.md @@ -1,6 +1,10 @@ +> 1.0 > 翻译:[xiehurricane](https://github.com/xiehurricane) > 校对:[happyming](https://github.com/happyming) +> 2.0 +> 翻译+校对:[yangsiy](https://github.com/yangsiy) + # 类型转换(Type Casting) ----------------- @@ -14,16 +18,16 @@ _类型转换_可以判断实例的类型,也可以将实例看做是其父类或者子类的实例。 -类型转换在 Swift 中使用`is` 和 `as`操作符实现。这两个操作符提供了一种简单达意的方式去检查值的类型或者转换它的类型。 +类型转换在 Swift 中使用 `is` 和 `as` 操作符实现。这两个操作符提供了一种简单达意的方式去检查值的类型或者转换它的类型。 -你也可以用来检查一个类是否实现了某个协议,就像在 [Checking for Protocol Conformance](Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-XID_363)部分讲述的一样。 +你也可以用它来检查一个类是否实现了某个协议,就像在 [检验协议的一致性](./22_Protocols.html#checking_for_protocol_conformance)部分讲述的一样。 ## 定义一个类层次作为例子 -你可以将它用在类和子类的层次结构上,检查特定类实例的类型并且转换这个类实例的类型成为这个层次结构中的其他类型。这下面的三个代码段定义了一个类层次和一个包含了几个这些类实例的数组,作为类型转换的例子。 +你可以将类型转换用在类和子类的层次结构上,检查特定类实例的类型并且转换这个类实例的类型成为这个层次结构中的其他类型。下面的三个代码段定义了一个类层次和一个包含了几个这些类实例的数组,作为类型转换的例子。 -第一个代码片段定义了一个新的基础类`MediaItem`。这个类为任何出现在数字媒体库的媒体项提供基础功能。特别的,它声明了一个 `String` 类型的 `name` 属性,和一个`init name`初始化器。(它假定所有的媒体项都有个名称。) +第一个代码片段定义了一个新的基础类 `MediaItem`。这个类为任何出现在数字媒体库的媒体项提供基础功能。特别的,它声明了一个 `String` 类型的 `name` 属性,和一个 `init name` 初始化器。(假定所有的媒体项都有个名称。) ```swift class MediaItem { @@ -34,7 +38,7 @@ class MediaItem { } ``` -下一个代码段定义了 `MediaItem` 的两个子类。第一个子类`Movie`,在父类(或者说基类)的基础上增加了一个 `director`(导演) 属性,和相应的初始化器。第二个类在父类的基础上增加了一个 `artist`(艺术家) 属性,和相应的初始化器: +下一个代码段定义了 `MediaItem` 的两个子类。第一个子类 `Movie` 封装了与电影相关的额外信息,在父类(或者说基类)的基础上增加了一个 `director`(导演)属性,和相应的初始化器。第二个子类 `Song`,在父类的基础上增加了一个 `artist`(艺术家)属性,和相应的初始化器: ```swift class Movie: MediaItem { @@ -54,7 +58,7 @@ class Song: MediaItem { } ``` -最后一个代码段创建了一个数组常量 `library`,包含两个`Movie`实例和三个`Song`实例。`library`的类型是在它被初始化时根据它数组中所包含的内容推断来的。Swift 的类型检测器能够演绎出`Movie` 和 `Song` 有共同的父类 `MediaItem` ,所以它推断出 `[MediaItem]` 类作为 `library` 的类型。 +最后一个代码段创建了一个数组常量 `library`,包含两个 `Movie` 实例和三个 `Song` 实例。`library` 的类型是在它被初始化时根据它数组中所包含的内容推断来的。Swift的类型检测器能够推理出 `Movie` 和 `Song` 有共同的父类 `MediaItem`,所以它推断出 `[MediaItem]` 类作为 `library` 的类型。 ```swift let library = [ @@ -67,14 +71,14 @@ let library = [ // the type of "library" is inferred to be [MediaItem] ``` -在幕后`library` 里存储的媒体项依然是 `Movie` 和 `Song` 类型的,但是,若你迭代它,取出的实例会是 `MediaItem` 类型的,而不是 `Movie` 和 `Song` 类型的。为了让它们作为它们本来的类型工作,你需要检查它们的类型或者向下转换它们的类型到其它类型,就像下面描述的一样。 +在幕后 `library` 里存储的媒体项依然是 `Movie` 和 `Song` 类型的。但是,若你迭代它,依次取出的实例会是 `MediaItem` 类型的,而不是 `Movie` 和 `Song` 类型。为了让它们作为原本的类型工作,你需要检查它们的类型或者向下转换它们到其它类型,就像下面描述的一样。 ## 检查类型(Checking Type) -用类型检查操作符(`is`)来检查一个实例是否属于特定子类型。若实例属于那个子类型,类型检查操作符返回 `true` ,否则返回 `false` 。 +用类型检查操作符(`is`)来检查一个实例是否属于特定子类型。若实例属于那个子类型,类型检查操作符返回 `true`,否则返回 `false`。 -下面的例子定义了两个变量,`movieCount` 和 `songCount`,用来计算数组`library` 中 `Movie` 和 `Song` 类型的实例数量。 +下面的例子定义了两个变量,`movieCount` 和 `songCount`,用来计算数组 `library` 中 `Movie` 和 `Song` 类型的实例数量。 ```swift var movieCount = 0 @@ -88,38 +92,38 @@ for item in library { } } -println("Media library contains \(movieCount) movies and \(songCount) songs") +print("Media library contains \(movieCount) movies and \(songCount) songs") // prints "Media library contains 2 movies and 3 songs" ``` -示例迭代了数组 `library` 中的所有项。每一次, `for`-`in` 循环设置 +示例迭代了数组 `library` 中的所有项。每一次,`for`-`in` 循环设置 `item` 为数组中的下一个 `MediaItem`。 -若当前 `MediaItem` 是一个 `Movie` 类型的实例, `item is Movie` 返回 +若当前 `MediaItem` 是一个 `Movie` 类型的实例,`item is Movie` 返回 `true`,相反返回 `false`。同样的,`item is -Song`检查item是否为`Song`类型的实例。在循环结束后,`movieCount` 和 `songCount`的值就是被找到属于各自的类型的实例数量。 +Song` 检查item是否为 `Song` 类型的实例。在循环结束后,`movieCount` 和 `songCount` 的值就是被找到属于各自的类型的实例数量。 ## 向下转型(Downcasting) -某类型的一个常量或变量可能在幕后实际上属于一个子类。你可以相信,上面就是这种情况。你可以尝试向下转到它的子类型,用类型转换操作符(`as`) +某类型的一个常量或变量可能在幕后实际上属于一个子类。当确定是这种情况时,你可以尝试向下转到它的子类型,用类型转换操作符(`as?` 或 `as!`) -因为向下转型可能会失败,类型转型操作符带有两种不同形式。可选形式( optional form) `as?` 返回一个你试图下转成的类型的可选值(optional value)。强制形式 `as` 把试图向下转型和强制解包(force-unwraps)结果作为一个混合动作。 +因为向下转型可能会失败,类型转型操作符带有两种不同形式。条件形式(conditional form) `as?` 返回一个你试图向下转成的类型的可选值(optional value)。强制形式 `as!` 把试图向下转型和强制解包(force-unwraps)结果作为一个混合动作。 -当你不确定向下转型可以成功时,用类型转换的可选形式(`as?`)。可选形式的类型转换总是返回一个可选值(optional value),并且若下转是不可能的,可选值将是 `nil` 。这使你能够检查向下转型是否成功。 +当你不确定向下转型可以成功时,用类型转换的条件形式(`as?`)。条件形式的类型转换总是返回一个可选值(optional value),并且若下转是不可能的,可选值将是 `nil`。这使你能够检查向下转型是否成功。 -只有你可以确定向下转型一定会成功时,才使用强制形式。当你试图向下转型为一个不正确的类型时,强制形式的类型转换会触发一个运行时错误。 +只有你可以确定向下转型一定会成功时,才使用强制形式(`as!`)。当你试图向下转型为一个不正确的类型时,强制形式的类型转换会触发一个运行时错误。 -下面的例子,迭代了`library`里的每一个 `MediaItem` ,并打印出适当的描述。要这样做,`item`需要真正作为`Movie` 或 `Song`的类型来使用。不仅仅是作为 `MediaItem`。为了能够使用`Movie` 或 `Song`的 `director` 或 `artist`属性,这是必要的。 +下面的例子,迭代了 `library` 里的每一个 `MediaItem`,并打印出适当的描述。要这样做,`item` 需要真正作为 `Movie` 或 `Song` 的类型来使用,不仅仅是作为 `MediaItem`。为了能够在描述中使用 `Movie` 或 `Song` 的 `director` 或 `artist` 属性,这是必要的。 -在这个示例中,数组中的每一个`item`可能是 `Movie` 或 `Song`。 事前你不知道每个`item`的真实类型,所以这里使用可选形式的类型转换 (`as?`)去检查循环里的每次下转。 +在这个示例中,数组中的每一个 `item` 可能是 `Movie` 或 `Song`。事前你不知道每个 `item` 的真实类型,所以这里使用条件形式的类型转换(`as?`)去检查循环里的每次下转。 ```swift for item in library { if let movie = item as? Movie { - println("Movie: '\(movie.name)', dir. \(movie.director)") + print("Movie: '\(movie.name)', dir. \(movie.director)") } else if let song = item as? Song { - println("Song: '\(song.name)', by \(song.artist)") + print("Song: '\(song.name)', by \(song.artist)") } } @@ -131,18 +135,18 @@ for item in library { ``` 示例首先试图将 `item` 下转为 `Movie`。因为 `item` 是一个 `MediaItem` -类型的实例,它可能是一个`Movie`;同样,它可能是一个 `Song`,或者仅仅是基类 -`MediaItem`。因为不确定,`as?`形式在试图下转时将返还一个可选值。 `item as Movie` 的返回值是`Movie?`类型或 “optional `Movie`”。 +类型的实例,它可能是一个 `Movie`;同样,它也可能是一个 `Song`,或者仅仅是基类 +`MediaItem`。因为不确定,`as?`形式在试图下转时将返回一个可选值。`item as? Movie` 的返回值是 `Movie?` 或 “可选 `Movie`”类型。 当向下转型为 `Movie` 应用在两个 `Song` -实例时将会失败。为了处理这种情况,上面的例子使用了可选绑定(optional binding)来检查可选 `Movie`真的包含一个值(这个是为了判断下转是否成功。)可选绑定是这样写的“`if let movie = item as? Movie`”,可以这样解读: +实例时将会失败。为了处理这种情况,上面的例子使用了可选绑定(optional binding)来检查可选 `Movie` 真的包含一个值(这个是为了判断下转是否成功。)可选绑定是这样写的“`if let movie = item as? Movie`”,可以这样解读: -“尝试将 `item` 转为 `Movie`类型。若成功,设置一个新的临时常量 `movie` 来存储返回的可选`Movie`” +“尝试将 `item` 转为 `Movie` 类型。若成功,设置一个新的临时常量 `movie` 来存储返回的可选 `Movie`” -若向下转型成功,然后`movie`的属性将用于打印一个`Movie`实例的描述,包括它的导演的名字`director`。当`Song`被找到时,一个相近的原理被用来检测 `Song` 实例和打印它的描述。 +若向下转型成功,然后 `movie` 的属性将用于打印一个 `Movie` 实例的描述,包括它的导演的名字 `director` 。相近的原理被用来检测 `Song` 实例,当 `Song` 被找到时则打印它的描述(包含 `artist` 的名字)。 -> 注意: -转换没有真的改变实例或它的值。潜在的根本的实例保持不变;只是简单地把它作为它被转换成的类来使用。 +> 注意: +> 转换没有真的改变实例或它的值。潜在的根本的实例保持不变;只是简单地把它作为它被转换成的类来使用。 ## `Any`和`AnyObject`的类型转换 @@ -152,12 +156,13 @@ Swift为不确定类型提供了两种特殊类型别名: * `AnyObject`可以代表任何class类型的实例。 * `Any`可以表示任何类型,包括方法类型(function types)。 -> 注意: -只有当你明确的需要它的行为和功能时才使用`Any`和`AnyObject`。在你的代码里使用你期望的明确的类型总是更好的。 +> 注意: +> 只有当你明确的需要它的行为和功能时才使用`Any`和`AnyObject`。在你的代码里使用你期望的明确的类型总是更好的。 + ### `AnyObject`类型 -当需要在工作中使用 Cocoa APIs,它一般接收一个`[AnyObject]`类型的数组,或者说“一个任何对象类型的数组”。这是因为 Objective-C 没有明确的类型化数组。但是,你常常可以确定包含在仅从你知道的 API 信息提供的这样一个数组中的对象的类型。 +当在工作中使用 Cocoa APIs,我们一般会接收一个`[AnyObject]`类型的数组,或者说“一个任何对象类型的数组”。这是因为 Objective-C 没有明确的类型化数组。但是,你常常可以从 API 提供的信息中清晰地确定数组中对象的类型。 在这些情况下,你可以使用强制形式的类型转换(`as`)来下转在数组中的每一项到比 `AnyObject` 更明确的类型,不需要可选解析(optional unwrapping)。 @@ -171,23 +176,23 @@ let someObjects: [AnyObject] = [ ] ``` -因为知道这个数组只包含 `Movie` 实例,你可以直接用(`as`)下转并解包到不可选的`Movie`类型(ps:其实就是我们常用的正常类型,这里是为了和可选类型相对比)。 +因为知道这个数组只包含 `Movie` 实例,你可以直接用(`as!`)下转并解包到不可选的`Movie`类型: ```swift for object in someObjects { - let movie = object as Movie - println("Movie: '\(movie.name)', dir. \(movie.director)") + let movie = object as! Movie + print("Movie: '\(movie.name)', dir. \(movie.director)") } // Movie: '2001: A Space Odyssey', dir. Stanley Kubrick // Movie: 'Moon', dir. Duncan Jones // Movie: 'Alien', dir. Ridley Scott ``` -为了变为一个更短的形式,下转`someObjects`数组为`[Movie]`类型来代替下转每一项方式。 +为了变为一个更短的形式,下转`someObjects`数组为`[Movie]`类型来代替下转数组中每一项的方式。 ```swift for movie in someObjects as! [Movie] { - println("Movie: '\(movie.name)', dir. \(movie.director)") + print("Movie: '\(movie.name)', dir. \(movie.director)") } // Movie: '2001: A Space Odyssey', dir. Stanley Kubrick // Movie: 'Moon', dir. Duncan Jones @@ -196,7 +201,7 @@ for movie in someObjects as! [Movie] { ### `Any`类型 -这里有个示例,使用 `Any` 类型来和混合的不同类型一起工作,包括非`class`类型。它创建了一个可以存储`Any`类型的数组 `things`。 +这里有个示例,使用 `Any` 类型来和混合的不同类型一起工作,包括方法类型和非 `class` 类型。它创建了一个可以存储`Any`类型的数组 `things`。 ```swift var things = [Any]() @@ -211,33 +216,33 @@ things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman")) things.append({ (name: String) -> String in "Hello, \(name)" }) ``` -`things` 数组包含两个 `Int` 值,2个 `Double` 值,1个 `String` 值,一个元组 `(Double, Double)` ,Ivan Reitman 导演的电影“Ghostbusters”。 +`things` 数组包含两个 `Int` 值,2个 `Double` 值,1个 `String` 值,一个元组 `(Double, Double)` ,电影“Ghostbusters”,和一个获取 `String` 值并返回另一个 `String` 值的闭包表达式。 -你可以在 `switch` `cases`里用`is` 和 `as` 操作符来发觉只知道是 `Any` 或 `AnyObject`的常量或变量的类型。 下面的示例迭代 `things`数组中的每一项的并用`switch`语句查找每一项的类型。这几种`switch`语句的情形绑定它们匹配的值到一个规定类型的常量,让它们可以打印它们的值: +你可以在 `switch` 表达式的cases中使用 `is` 和 `as` 操作符来发觉只知道是 `Any` 或 `AnyObject` 的常量或变量的类型。下面的示例迭代 `things` 数组中的每一项的并用`switch`语句查找每一项的类型。这几种 `switch` 语句的情形绑定它们匹配的值到一个规定类型的常量,让它们的值可以被打印: ```swift for thing in things { switch thing { case 0 as Int: - println("zero as an Int") + print("zero as an Int") case 0 as Double: - println("zero as a Double") + print("zero as a Double") case let someInt as Int: - println("an integer value of \(someInt)") + print("an integer value of \(someInt)") case let someDouble as Double where someDouble > 0: - println("a positive double value of \(someDouble)") + print("a positive double value of \(someDouble)") case is Double: - println("some other double value that I don't want to print") + print("some other double value that I don't want to print") case let someString as String: - println("a string value of \"\(someString)\"") + print("a string value of \"\(someString)\"") case let (x, y) as (Double, Double): - println("an (x, y) point at \(x), \(y)") + print("an (x, y) point at \(x), \(y)") case let movie as Movie: - println("a movie called '\(movie.name)', dir. \(movie.director)") + print("a movie called '\(movie.name)', dir. \(movie.director)") case let stringConverter as String -> String: - println(stringConverter("Michael")) + print(stringConverter("Michael")) default: - println("something else") + print("something else") } } @@ -252,5 +257,5 @@ for thing in things { ``` -> 注意: -在一个switch语句的case中使用强制形式的类型转换操作符(as, 而不是 as?)来检查和转换到一个明确的类型。在 switch case 语句的内容中这种检查总是安全的。 +> 注意: +> 在一个switch语句的case中使用强制形式的类型转换操作符(as, 而不是 as?)来检查和转换到一个明确的类型。在 `switch` case 语句的内容中这种检查总是安全的。 diff --git a/source/chapter2/21_Extensions.md b/source/chapter2/21_Extensions.md index 3e085b4a..47770030 100644 --- a/source/chapter2/21_Extensions.md +++ b/source/chapter2/21_Extensions.md @@ -1,8 +1,12 @@ -> 翻译:[lyuka](https://github.com/lyuka) +# 扩展(Extensions) +---- + +> 1.0 +> 翻译:[lyuka](https://github.com/lyuka) > 校对:[Hawstein](https://github.com/Hawstein) -#扩展(Extensions) ----- +> 2.0 +> 翻译+校对:[shanksyang](https://github.com/shanksyang) 本页包含内容: @@ -13,22 +17,23 @@ - [下标](#subscripts) - [嵌套类型](#nested_types) -*扩展*就是向一个已有的类、结构体或枚举类型添加新功能(functionality)。这包括在没有权限获取原始源代码的情况下扩展类型的能力(即*逆向建模*)。扩展和 Objective-C 中的分类(categories)类似。(不过与Objective-C不同的是,Swift 的扩展没有名字。) +*扩展*就是向一个已有的类、结构体、枚举类型或者协议类型添加新功能(functionality)。这包括在没有权限获取原始源代码的情况下扩展类型的能力(即*逆向建模*)。扩展和 Objective-C 中的分类(categories)类似。(不过与 Objective-C 不同的是,Swift 的扩展没有名字。) Swift 中的扩展可以: -- 添加计算型属性和计算静态属性 +- 添加计算型属性和计算型静态属性 - 定义实例方法和类型方法 - 提供新的构造器 - 定义下标 - 定义和使用新的嵌套类型 - 使一个已有类型符合某个协议 +在 Swift 中,你甚至可以对一个协议(Protocol)进行扩展,提供协议需要的实现,或者添加额外的功能能够对合适的类型带来额外的好处。你可以从[协议扩展](./22_Protocols.html#protocol_extensions)获取更多的细节。 >注意: -如果你定义了一个扩展向一个已有类型添加新功能,那么这个新功能对该类型的所有已有实例中都是可用的,即使它们是在你的这个扩展的前面定义的。 - +扩展可以对一个类型添加新的功能,但是不能重写已有的功能。 + ## 扩展语法(Extension Syntax) 声明一个扩展使用关键字`extension`: @@ -38,7 +43,6 @@ extension SomeType { // 加到SomeType的新功能写到这里 } ``` - 一个扩展可以扩展一个已有类型,使其能够适配一个或多个协议(protocol)。当这种情况发生时,协议的名字应该完全按照类或结构体的名字的方式进行书写: ```swift @@ -47,12 +51,15 @@ extension SomeType: SomeProtocol, AnotherProctocol { } ``` -按照这种方式添加的协议遵循者(protocol conformance)被称之为[在扩展中添加协议遵循者](21_Protocols.html#adding_protocol_conformance_with_an_extension) +按照这种方式添加的协议遵循者(protocol conformance)被称之为[在扩展中添加协议遵循者](./22_Protocols.html#adding_protocol_conformance_with_an_extension) + +>注意: +如果你定义了一个扩展向一个已有类型添加新功能,那么这个新功能对该类型的所有已有实例中都是可用的,即使它们是在你的这个扩展的前面定义的。 ## 计算型属性(Computed Properties) -扩展可以向已有类型添加计算型实例属性和计算型类型属性。下面的例子向 Swift 的内建`Double`类型添加了5个计算型实例属性,从而提供与距离单位协作的基本支持。 +扩展可以向已有类型添加计算型实例属性和计算型类型属性。下面的例子向 Swift 的内建`Double`类型添加了5个计算型实例属性,从而提供与距离单位协作的基本支持: ```swift extension Double { @@ -63,10 +70,10 @@ extension Double { var ft: Double { return self / 3.28084 } } let oneInch = 25.4.mm -println("One inch is \(oneInch) meters") +print("One inch is \(oneInch) meters") // 打印输出:"One inch is 0.0254 meters" let threeFeet = 3.ft -println("Three feet is \(threeFeet) meters") +print("Three feet is \(threeFeet) meters") // 打印输出:"Three feet is 0.914399970739201 meters" ``` @@ -80,11 +87,10 @@ println("Three feet is \(threeFeet) meters") ```swift let aMarathon = 42.km + 195.m -println("A marathon is \(aMarathon) meters long") +print("A marathon is \(aMarathon) meters long") // 打印输出:"A marathon is 42195.0 meters long" ``` - >注意: 扩展可以添加新的计算属性,但是不可以添加存储属性,也不可以向已有属性添加属性观测器(property observers)。 @@ -93,13 +99,12 @@ println("A marathon is \(aMarathon) meters long") 扩展可以向已有类型添加新的构造器。这可以让你扩展其它类型,将你自己的定制类型作为构造器参数,或者提供该类型的原始实现中没有包含的额外初始化选项。 -扩展能向类中添加新的便利构造器,但是它们不能向类中添加新的指定构造器或析构函数。指定构造器和析构函数必须总是由原始的类实现来提供。 +扩展能向类中添加新的便利构造器,但是它们不能向类中添加新的指定构造器或析构器。指定构造器和析构器必须总是由原始的类实现来提供。 > 注意: 如果你使用扩展向一个值类型添加一个构造器,在该值类型已经向所有的存储属性提供默认值,而且没有定义任何定制构造器(custom initializers)时,你可以在值类型的扩展构造器中调用默认构造器(default initializers)和逐一成员构造器(memberwise initializers)。 > -正如在[值类型的构造器代理](14_Initialization.html#initializer_delegation_for_value_types)中描述的,如果你已经把构造器写成值类型原始实现的一部分,上述规则不再适用。 - +正如在[值类型的构造器代理](./14_Initialization.html#initializer_delegation_for_value_types)中描述的,如果你已经把构造器写成值类型原始实现的一部分,上述规则不再适用。 下面的例子定义了一个用于描述几何矩形的定制结构体`Rect`。这个例子同时定义了两个辅助结构体`Size`和`Point`,它们都把`0.0`作为所有属性的默认值: @@ -115,8 +120,7 @@ struct Rect { var size = Size() } ``` - -因为结构体`Rect`提供了其所有属性的默认值,所以正如默认构造器中描述的,它可以自动接受一个默认的构造器和一个成员级构造器。这些构造器可以用于构造新的`Rect`实例: +因为结构体`Rect`提供了其所有属性的默认值,所以正如[默认构造器](./14_Initialization.html#default_initializers)中描述的,它可以自动接受一个默认构造器和一个逐一成员构造器。这些构造器可以用于构造新的`Rect`实例: ```swift let defaultRect = Rect() @@ -135,8 +139,7 @@ extension Rect { } } ``` - -这个新的构造器首先根据提供的`center`和`size`值计算一个合适的原点。然后调用该结构体自动的成员构造器`init(origin:size:)`,该构造器将新的原点和大小存到了合适的属性中: +这个新的构造器首先根据提供的`center`和`size`值计算一个合适的原点。然后调用该结构体自动的逐一成员构造器`init(origin:size:)`,该构造器将新的原点和大小存到了合适的属性中: ```swift let centerRect = Rect(center: Point(x: 4.0, y: 4.0), @@ -169,7 +172,7 @@ extension Int { ```swift 3.repetitions({ - println("Hello!") + print("Hello!") }) // Hello! // Hello! @@ -180,7 +183,7 @@ extension Int { ```swift 3.repetitions{ - println("Goodbye!") + print("Goodbye!") } // Goodbye! // Goodbye! @@ -250,52 +253,49 @@ extension Int { 扩展可以向已有的类、结构体和枚举添加新的嵌套类型: ```swift -extension Character { +extension Int { enum Kind { - case Vowel, Consonant, Other + case Negative, Zero, Positive } var kind: Kind { - switch String(self).lowercaseString { - case "a", "e", "i", "o", "u": - return .Vowel - case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", - "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z": - return .Consonant + switch self { + case 0: + return .Zero + case let x where x > 0: + return .Positive default: - return .Other + return .Negative } } } ``` -该例子向`Character`添加了新的嵌套枚举。这个名为`Kind`的枚举表示特定字符的类型。具体来说,就是表示一个标准的拉丁脚本中的字符是元音还是辅音(不考虑口语和地方变种),或者是其它类型。 +该例子向`Int`添加了新的嵌套枚举。这个名为`Kind`的枚举表示特定整数的类型。具体来说,就是表示整数是正数,零或者负数。 -这个例子还向`Character`添加了一个新的计算实例属性,即`kind`,用来返回合适的`Kind`枚举成员。 +这个例子还向`Int`添加了一个新的计算实例属性,即`kind`,用来返回合适的`Kind`枚举成员。 + +现在,这个嵌套枚举可以和一个`Int`值联合使用了: -现在,这个嵌套枚举可以和一个`Character`值联合使用了: ```swift -func printLetterKinds(word: String) { - println("'\(word)' is made up of the following kinds of letters:") - for character in word { - switch character.kind { - case .Vowel: - print("vowel ") - case .Consonant: - print("consonant ") - case .Other: - print("other ") +func printIntegerKinds(numbers: [Int]) { + for number in numbers { + switch number.kind { + case .Negative: + print("- ", appendNewline: false) + case .Zero: + print("0 ", appendNewline: false) + case .Positive: + print("+ ", appendNewline: false) } } - print("\n") + print("") } -printLetterKinds("Hello") -// 'Hello' is made up of the following kinds of letters: -// consonant vowel consonant consonant vowel +printIntegerKinds([3, 19, -27, 0, -6, 0, 7]) +// prints "+ + - 0 - 0 +" ``` -函数`printLetterKinds`的输入是一个`String`值并对其字符进行迭代。在每次迭代过程中,考虑当前字符的`kind`计算属性,并打印出合适的类别描述。所以`printLetterKinds`就可以用来打印一个完整单词中所有字母的类型,正如上述单词`"hello"`所展示的。 - ->注意: -由于已知`character.kind`是`Character.Kind`型,所以`Character.Kind`中的所有成员值都可以使用`switch`语句里的形式简写,比如使用 `.Vowel`代替`Character.Kind.Vowel` +函数`printIntegerKinds`的输入是一个`Int`数组值并对其字符进行迭代。在每次迭代过程中,考虑当前字符的`kind`计算属性,并打印出合适的类别描述。 +>注意: +由于已知`number.kind `是`Int.Kind`型,所以`Int.Kind`中的所有成员值都可以使用`switch`语句里的形式简写,比如使用 `. Negative`代替`Int.Kind.Negative`。 diff --git a/source/chapter2/22_Protocols.md b/source/chapter2/22_Protocols.md index d17abb5d..ae8f74b0 100644 --- a/source/chapter2/22_Protocols.md +++ b/source/chapter2/22_Protocols.md @@ -1,15 +1,19 @@ -> 翻译:[geek5nan](https://github.com/geek5nan) +# 协议(Protocols) +----------------- + +> 1.0 +> 翻译:[geek5nan](https://github.com/geek5nan) > 校对:[dabing1022](https://github.com/dabing1022) -# 协议 ------------------ +> 2.0 +> 翻译+校对:[futantan](https://github.com/futantan) 本页包含内容: - [协议的语法(Protocol Syntax)](#protocol_syntax) - [对属性的规定(Property Requirements)](#property_requirements) - [对方法的规定(Method Requirements)](#method_requirements) -- [对突变方法的规定(Mutating Method Requirements)](#mutating_method_requirements) +- [对Mutating方法的规定(Mutating Method Requirements)](#mutating_method_requirements) - [对构造器的规定(Initializer Requirements)](#initializer_requirements) - [协议类型(Protocols as Types)](#protocols_as_types) - [委托(代理)模式(Delegation)](#delegation) @@ -21,15 +25,15 @@ - [协议合成(Protocol Composition)](#protocol_composition) - [检验协议的一致性(Checking for Protocol Conformance)](#checking_for_protocol_conformance) - [对可选协议的规定(Optional Protocol Requirements)](#optional_protocol_requirements) +- [协议扩展(Protocol Extensions)](#protocol_extensions) -`协议(Protocol)`用于定义完成某项任务或功能所必须的方法和属性,协议实际上并不提供这些功能或任务的具体`实现(Implementation)`--而只用来描述这些实现应该是什么样的。类,结构体,枚举通过提供协议所要求的方法,属性的具体实现来`采用(adopt)`协议。任意能够满足协议要求的类型被称为协议的`遵循者`。 -`协议`可以要求其`遵循者`提供特定的实例属性,实例方法,类方法,操作符或下标脚本等。 +`协议`定义了一个蓝图,规定了用来实现某一特定工作或者功能所必需的方法和属性。类,结构体或枚举类型都可以遵循协议,并提供具体实现来完成协议定义的方法和功能。任意能够满足协议要求的类型被称为`遵循(conform)`这个协议。 ## 协议的语法 -`协议`的定义方式与`类,结构体,枚举`的定义都非常相似,如下所示: +协议的定义方式与类,结构体,枚举的定义非常相似。 ```swift protocol SomeProtocol { @@ -37,7 +41,7 @@ protocol SomeProtocol { } ``` -在类型名称后加上`协议名称`,中间以冒号`:`分隔即可实现协议;实现多个协议时,各协议之间用逗号`,`分隔,如下所示: +要使类遵循某个协议,需要在类型名称后加上协议名称,中间以冒号`:`分隔,作为类型定义的一部分。遵循多个协议时,各协议之间用逗号`,`分隔。 ```swift struct SomeStructure: FirstProtocol, AnotherProtocol { @@ -45,7 +49,7 @@ struct SomeStructure: FirstProtocol, AnotherProtocol { } ``` -如果一个类在含有`父类`的同时也采用了协议,应当把`父类`放在所有的`协议`之前,如下所示: +如果类在遵循协议的同时拥有父类,应该将父类名放在协议名之前,以逗号分隔。 ```swift class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol { @@ -56,11 +60,11 @@ class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol { ## 对属性的规定 -协议可以规定其`遵循者`提供特定名称与类型的`实例属性(instance property)`或`类属性(type property)`,而不管其是`存储型属性(stored property)`还是`计算型属性(calculate property)`。此外也可以指定属性是只读的还是可读写的。 +协议可以规定其`遵循者`提供特定名称和类型的`实例属性(instance property)`或`类属性(type property)`,而不指定是`存储型属性(stored property)`还是`计算型属性(calculate property)`。此外还必须指明是只读的还是可读可写的。 -如果协议要求属性是可读写的,那么这个属性不能是常量`存储型属性`或只读`计算型属性`;如果协议要求属性是只读的(gettable),那么`计算型属性`或`存储型属性`都能满足协议对属性的规定,在你的代码中,即使为只读属性实现了写方法(settable)也依然有效。 +如果协议规定属性是可读可写的,那么这个属性不能是常量或只读的计算属性。如果协议只要求属性是只读的(gettable),那个属性不仅可以是只读的,如果你代码需要的话,也可以是可写的。 -协议中的属性经常被加以`var`前缀声明其为变量属性,在声明后加上`{ set get }`来表示属性是可读写的,只读的属性则写作`{ get }`,如下所示: +协议中的通常用var来声明属性,在类型声明后加上`{ set get }`来表示属性是可读可写的,只读属性则用`{ get }`来表示。 ```swift protocol SomeProtocol { @@ -69,15 +73,15 @@ protocol SomeProtocol { } ``` -如下所示,通常在协议的定义中使用`class`前缀表示该属性为类成员;在枚举和结构体实现协议时中,需要使用`static`关键字作为前缀。 +在协议中定义类属性(type property)时,总是使用`static`关键字作为前缀。当协议的遵循者是类时,可以使用`class`或`static`关键字来声明类属性,但是在协议的定义中,仍然要使用`static`关键字。 ```swift protocol AnotherProtocol { - class var someTypeProperty: Int { get set } + static var someTypeProperty: Int { get set } } ``` -如下所示,这是一个含有一个实例属性要求的协议: +如下所示,这是一个含有一个实例属性要求的协议。 ```swift protocol FullyNamed { @@ -85,9 +89,9 @@ protocol FullyNamed { } ``` -`FullyNamed`协议定义了任何拥有`fullName`的类型。它并不指定具体类型,而只是要求类型必须提供一个`fullName`。任何`FullyNamed`类型都得有一个只读的`fullName`属性,类型为`String`。 +`FullyNamed`协议除了要求协议的遵循者提供fullName属性外,对协议对遵循者的类型并没有特别的要求。这个协议表示,任何遵循`FullyNamed`协议的类型,都具有一个可读的`String`类型实例属性`fullName`。 -如下所示,这是一个实现了`FullyNamed`协议的简单结构体: +下面是一个遵循`FullyNamed`协议的简单结构体。 ```swift struct Person: FullyNamed{ @@ -97,47 +101,44 @@ let john = Person(fullName: "John Appleseed") //john.fullName 为 "John Appleseed" ``` -这个例子中定义了一个叫做`Person`的结构体,用来表示具有指定名字的人。从第一行代码中可以看出,它采用了`FullyNamed`协议。 +这个例子中定义了一个叫做`Person`的结构体,用来表示具有名字的人。从第一行代码中可以看出,它遵循了`FullyNamed`协议。 -`Person`结构体的每一个实例都有一个叫做`fullName`,`String`类型的存储型属性,这正好匹配了`FullyNamed`协议的要求,也就意味着,`Person`结构体完整的`遵循`了协议。(如果协议要求未被完全满足,在编译时会报错) +`Person`结构体的每一个实例都有一个叫做`fullName`,`String`类型的存储型属性。这正好满足了`FullyNamed`协议的要求,也就意味着,`Person`结构体完整的`遵循`了协议。(如果协议要求未被完全满足,在编译时会报错) -这有一个更为复杂的类,它采用并实现了`FullyNamed`协议,如下所示: +下面是一个更为复杂的类,它采用并遵循了`FullyNamed`协议: ```swift class Starship: FullyNamed { - var prefix: String? - var name: String - init(name: String, prefix: String? = nil ) { - self.name = name - self.prefix = prefix - } - var fullName: String { - return (prefix != nil ? prefix! + " " : " ") + name - } + var prefix: String? + var name: String + init(name: String, prefix: String? = nil) { + self.name = name + self.prefix = prefix + } + var fullName: String { + return (prefix != nil ? prefix! + " " : "") + name + } } var ncc1701 = Starship(name: "Enterprise", prefix: "USS") -// ncc1701.fullName == "USS Enterprise" +// ncc1701.fullName is "USS Enterprise" ``` -`Starship`类把`fullName`属性实现为只读的`计算型属性`。每一个`Starship`类的实例都有一个名为`name`的必备属性和一个名为`prefix`的可选属性。 当`prefix`存在时,将`prefix`插入到`name`之前来为`Starship`构建`fullName`,`prefix`不存在时,则将直接用`name`构建`fullName` +Starship类把`fullName`属性实现为只读的计算型属性。每一个`Starship`类的实例都有一个名为`name`的属性和一个名为`prefix`的可选属性。 当`prefix`存在时,将`prefix`插入到`name`之前来为Starship构建`fullName`,`prefix`不存在时,则将直接用`name`构建`fullName`。 ## 对方法的规定 -`协议`可以要求其`遵循者`实现某些指定的`实例方法`或`类方法`。这些方法作为协议的一部分,像普通的方法一样清晰的放在协议的定义中,而不需要大括号和方法体。 +协议可以要求其遵循者实现某些指定的实例方法或类方法。这些方法作为协议的一部分,像普通的方法一样放在协议的定义中,但是不需要大括号和方法体。可以在协议中定义具有可变参数的方法,和普通方法的定义方式相同。但是在协议的方法定义中,不支持参数默认值。 ->注意: ->协议中的方法支持`变长参数(variadic parameter)`,不支持`参数默认值(default value)`。 - -如下所示,协议中类方法的定义与类属性的定义相似,在协议定义的方法前置`class`关键字来表示。当在`枚举`或`结构体`实现类方法时,需要使用`static`关键字来代替。 +正如对属性的规定中所说的,在协议中定义类方法的时候,总是使用`static`关键字作为前缀。当协议的遵循者是类的时候,虽然你可以在类的实现中使用`class`或者`static`来实现类方法,但是在协议中声明类方法,仍然要使用`static`关键字。 ```swift protocol SomeProtocol { - class func someTypeMethod() + static func someTypeMethod() } ``` -如下所示,定义了含有一个实例方法的的协议。 +下面的例子定义了含有一个实例方法的协议。 ```swift protocol RandomNumberGenerator { @@ -145,7 +146,7 @@ protocol RandomNumberGenerator { } ``` -`RandomNumberGenerator`协议要求其`遵循者`必须拥有一个名为`random`, 返回值类型为`Double`的实例方法。 (尽管这里并未指明,但是我们假设返回值在[0,1]区间内)。 +`RandomNumberGenerator`协议要求其遵循者必须拥有一个名为`random`, 返回值类型为`Double`的实例方法。尽管这里并未指明,但是我们假设返回值在[0,1)区间内。 `RandomNumberGenerator`协议并不在意每一个随机数是怎样生成的,它只强调这里有一个随机数生成器。 @@ -164,24 +165,26 @@ class LinearCongruentialGenerator: RandomNumberGenerator { } } let generator = LinearCongruentialGenerator() -println("Here's a random number: \(generator.random())") +print("Here's a random number: \(generator.random())") // 输出 : "Here's a random number: 0.37464991998171" -println("And another one: \(generator.random())") +print("And another one: \(generator.random())") // 输出 : "And another one: 0.729023776863283" ``` -## 对突变方法的规定 +## 对Mutating方法的规定 -有时不得不在方法中更改实例的所属类型。在基于`值类型(value types)`(结构体,枚举)的实例方法中,将`mutating`关键字作为函数的前缀,写在`func`之前,表示可以在该方法中修改实例及其属性的所属类型。这一过程在[Modifyting Value Types from Within Instance Methods](1)章节中有详细描述。 +有时需要在方法中改变它的实例。例如,值类型(结构体,枚举)的实例方法中,将`mutating`关键字作为函数的前缀,写在`func`之前,表示可以在该方法中修改它所属的实例及其实例属性的值。这一过程在[在实例方法中修改值类型](./11_Methods.html#modifying_value_types_from_within_instance_methods)章节中有详细描述。 -如果协议中的实例方法打算改变其`遵循者`实例的类型,那么在协议定义时需要在方法前加`mutating`关键字,才能使`结构体,枚举`来采用并满足协议中对方法的规定。 +如果你在协议中定义了一个方法旨在改变遵循该协议的实例,那么在协议定义时需要在方法前加`mutating`关键字。这使得结构和枚举遵循协议并满足此方法要求。 ->注意: ->用`类`实现协议中的`mutating`方法时,不用写`mutating`关键字;用`结构体`,`枚举`实现协议中的`mutating`方法时,必须写`mutating`关键字。 +>注意: +>用类实现协议中的`mutating`方法时,不用写`mutating`关键字;用结构体,枚举实现协议中的`mutating`方法时,必须写`mutating`关键字。 -如下所示,`Togglable`协议含有名为`toggle`的突变实例方法。根据名称推测,`toggle`方法应该是用于切换或恢复其`遵循者`实例或其属性的类型。 +如下所示,`Togglable`协议含有名为`toggle`的实例方法。根据名称推测,`toggle()`方法将通过改变实例属性,来切换遵循该协议的实例的状态。 + +`toggle()`方法在定义的时候,使用`mutating`关键字标记,这表明当它被调用时该方法将会改变协议遵循者实例的状态。 ```swift protocol Togglable { @@ -189,9 +192,9 @@ protocol Togglable { } ``` -当使用`枚举`或`结构体`来实现`Togglabl`协议时,需要提供一个带有`mutating`前缀的`toggle`方法。 +当使用`枚举`或`结构体`来实现`Togglable`协议时,需要提供一个带有`mutating`前缀的`toggle`方法。 -如下所示,`OnOffSwitch`枚举`遵循`了`Togglable`协议,`On`,`Off`两个成员用于表示当前状态。枚举的`toggle`方法被标记为`mutating`,用以匹配`Togglabel`协议的规定。 +下面定义了一个名为`OnOffSwitch`的枚举类型。这个枚举类型在两种状态之间进行切换,用枚举成员`On`和`Off`表示。枚举类型的`toggle`方法被标记为`mutating`以满足`Togglable`协议的要求。 ```swift enum OnOffSwitch: Togglable { @@ -213,7 +216,7 @@ lightSwitch.toggle() ## 对构造器的规定 -协议可以要求它的遵循类型实现特定的构造器。你可以像书写普通的构造器那样,在协议的定义里写下构造器的需求,但不需要写花括号和构造器的实体: +协议可以要求它的遵循者实现指定的构造器。你可以像书写普通的构造器那样,在协议的定义里写下构造器的声明,但不需要写花括号和构造器的实体: ```swift protocol SomeProtocol { @@ -221,9 +224,9 @@ protocol SomeProtocol { } ``` -**协议构造器规定在类中的实现** +### 协议构造器规定在类中的实现 -你可以在遵循该协议的类中实现构造器,并指定其为类的特定构造器或者便捷构造器。在这两种情况下,你都必须给构造器实现标上"required"修饰符: +你可以在遵循该协议的类中实现构造器,并指定其为类的指定构造器(designated initializer)或者便利构造器(convenience initializer)。在这两种情况下,你都必须给构造器实现标上"required"修饰符: ```swift class SomeClass: SomeProtocol { @@ -235,11 +238,10 @@ class SomeClass: SomeProtocol { 使用`required`修饰符可以保证:所有的遵循该协议的子类,同样能为构造器规定提供一个显式的实现或继承实现。 -关于`required`构造器的更多内容,请参考`required`构造器 +关于`required`构造器的更多内容,请参考[必要构造器](./14_Initialization.html#required_initializers)。 ->注意 -> ->如果类已经被“final”修饰符所标示,你就不需要在协议构造器规定的实现中使用"required"修饰符。因为final类不能有子类。关于`final`修饰符的更多内容,请参见防止重写 +>注意 +>如果类已经被标记为`final`,那么不需要在协议构造器的实现中使用`required`修饰符。因为final类不能有子类。关于`final`修饰符的更多内容,请参见[防止重写](./13_Inheritance.html#preventing_overrides)。 如果一个子类重写了父类的指定构造器,并且该构造器遵循了某个协议的规定,那么该构造器的实现需要被同时标示`required`和`override`修饰符 @@ -251,39 +253,39 @@ protocol SomeProtocol { class SomeSuperClass { init() { - //协议定义 + // 构造器的实现 } } - + class SomeSubClass: SomeSuperClass, SomeProtocol { - // "required" from SomeProtocol conformance; "override" from SomeSuperClass + // 因为遵循协议,需要加上"required"; 因为继承自父类,需要加上"override" required override init() { // 构造器实现 } } ``` -**可失败构造器的规定** +### 可失败构造器的规定 -可以通过给协议```Protocols```中添加可失败构造器来使遵循该协议的类型必须实现该可失败构造器。 +可以通过给协议`Protocols`中添加[可失败构造器](./14_Initialization.html#failable_initializers)来使遵循该协议的类型必须实现该可失败构造器。 -如果在协议中定义一个可失败构造器,则在遵顼该协议的类型中必须添加同名同参数的可失败构造器或非可失败构造器。 -如果在协议中定义一个非可失败构造器,则在遵循该协议的类型中必须添加同名同参数的非可失败构造器或隐式解析类型的可失败构造器(`init!`)。 +如果在协议中定义一个可失败构造器,则在遵顼该协议的类型中必须添加同名同参数的可失败构造器或非可失败构造器。如果在协议中定义一个非可失败构造器,则在遵循该协议的类型中必须添加同名同参数的非可失败构造器或隐式解析类型的可失败构造器(`init!`)。 ## 协议类型 -尽管`协议`本身并不实现任何功能,但是`协议`可以被当做类型来使用。 +尽管协议本身并不实现任何功能,但是协议可以被当做类型来使用。 -使用场景: +协议可以像其他普通类型一样使用,使用场景: -* `协议类型`作为函数、方法或构造器中的参数类型或返回值类型 -* `协议类型`作为常量、变量或属性的类型 -* `协议类型`作为数组、字典或其他容器中的元素类型 +* 作为函数、方法或构造器中的参数类型或返回值类型 +* 作为常量、变量或属性的类型 +* 作为数组、字典或其他容器中的元素类型 -> 注意: 协议是一种类型,因此协议类型的名称应与其他类型(Int,Double,String)的写法相同,使用驼峰式写法 +> 注意 +> 协议是一种类型,因此协议类型的名称应与其他类型(Int,Double,String)的写法相同,使用大写字母开头的驼峰式写法,例如(`FullyNamed`和`RandomNumberGenerator`) 如下所示,这个示例中将协议当做类型来使用 @@ -301,20 +303,20 @@ class Dice { } ``` -例子中又一个`Dice`类,用来代表桌游中的拥有N个面的骰子。`Dice`的实例含有`sides`和`generator`两个属性,前者是整型,用来表示骰子有几个面,后者为骰子提供一个随机数生成器。 +例子中定义了一个`Dice`类,用来代表桌游中的拥有N个面的骰子。`Dice`的实例含有`sides`和`generator`两个属性,前者是整型,用来表示骰子有几个面,后者为骰子提供一个随机数生成器。 `generator`属性的类型为`RandomNumberGenerator`,因此任何遵循了`RandomNumberGenerator`协议的类型的实例都可以赋值给`generator`,除此之外,无其他要求。 -`Dice`类中也有一个`构造器(initializer)`,用来进行初始化操作。构造器中含有一个名为`generator`,类型为`RandomNumberGenerator`的形参。在调用构造方法时创建`Dice`的实例时,可以传入任何遵循`RandomNumberGenerator`协议的实例给generator。 +`Dice`类中也有一个构造器(initializer),用来进行初始化操作。构造器中含有一个名为`generator`,类型为`RandomNumberGenerator`的形参。在调用构造方法时创建`Dice`的实例时,可以传入任何遵循`RandomNumberGenerator`协议的实例给generator。 -`Dice`类也提供了一个名为`roll`的实例方法用来模拟骰子的面值。它先使用`generator`的`random`方法来创建一个[0-1]区间内的随机数种子,然后加工这个随机数种子生成骰子的面值。generator被认为是遵循了`RandomNumberGenerator`的类型,因而保证了`random`方法可以被调用。 +`Dice`类也提供了一个名为`roll`的实例方法用来模拟骰子的面值。它先使用`generator`的`random()`方法来创建一个[0,1)区间内的随机数,然后使用这个随机数生成正确的骰子面值。因为generator遵循了`RandomNumberGenerator`协议,因而保证了`random`方法可以被调用。 -如下所示,这里展示了如何使用`LinearCongruentialGenerator`的实例作为随机数生成器创建一个六面骰子: +下面的例子展示了如何使用`LinearCongruentialGenerator`的实例作为随机数生成器创建一个六面骰子: ```swift -var d6 = Dice(sides: 6,generator: LinearCongruentialGenerator()) +var d6 = Dice(sides: 6, generator: LinearCongruentialGenerator()) for _ in 1...5 { - println("Random dice roll is \(d6.roll())") + print("Random dice roll is \(d6.roll())") } //输出结果 //Random dice roll is 3 @@ -327,13 +329,9 @@ for _ in 1...5 { ## 委托(代理)模式 -委托是一种设计模式(*译者注: 想起了那年 UITableViewDelegate 中的奔跑,那是我逝去的Objective-C。。。*),它允许`类`或`结构体`将一些需要它们负责的功能`交由(委托)`给其他的类型的实例。 +委托是一种设计模式,它允许`类`或`结构体`将一些需要它们负责的功能`交由(委托)`给其他的类型的实例。委托模式的实现很简单: 定义协议来封装那些需要被委托的函数和方法, 使其`遵循者`拥有这些被委托的`函数和方法`。委托模式可以用来响应特定的动作或接收外部数据源提供的数据,而无需要知道外部数据源的类型信息。 -委托模式的实现很简单: 定义`协议`来`封装`那些需要被委托的`函数和方法`, 使其`遵循者`拥有这些被委托的`函数和方法`。 - -委托模式可以用来响应特定的动作或接收外部数据源提供的数据,而无需要知道外部数据源的所属类型(*译者注:只要求外部数据源`遵循`某协议*)。 - -下文是两个基于骰子游戏的协议: +下面的例子是两个基于骰子游戏的协议: ```swift protocol DiceGame { @@ -348,9 +346,9 @@ protocol DiceGameDelegate { } ``` -`DiceGame`协议可以在任意含有骰子的游戏中实现,`DiceGameDelegate`协议可以用来追踪`DiceGame`的游戏过程 - -如下所示,`SnakesAndLadders`是`Snakes and Ladders`(译者注:[Control Flow](2)章节有该游戏的详细介绍)游戏的新版本。新版本使用`Dice`作为骰子,并且实现了`DiceGame`和`DiceGameDelegate`协议,后者用来记录游戏的过程: +`DiceGame`协议可以在任意含有骰子的游戏中实现。`DiceGameDelegate`协议可以用来追踪`DiceGame`的游戏过程 + +如下所示,`SnakesAndLadders`是`Snakes and Ladders`([Control Flow](./05_Control_Flow.html)章节有该游戏的详细介绍)游戏的新版本。新版本使用`Dice`作为骰子,并且实现了`DiceGame`和`DiceGameDelegate`协议,后者用来记录游戏的过程: ```swift class SnakesAndLadders: DiceGame { @@ -385,15 +383,15 @@ class SnakesAndLadders: DiceGame { } ``` -这个版本的游戏封装到了`SnakesAndLadders`类中,该类采用了`DiceGame`协议,并且提供了`dice`属性和`play`实例方法用来`遵循`协议。(`dice`属性在构造之后就不在改变,且协议只要求`dice`为只读的,因此将`dice`声明为常量属性。) +这个版本的游戏封装到了`SnakesAndLadders`类中,该类遵循了`DiceGame`协议,并且提供了相应的可读的`dice`属性和`play`实例方法。(`dice`属性在构造之后就不再改变,且协议只要求`dice`为只读的,因此将`dice`声明为常量属性。) -在`SnakesAndLadders`类的`构造器(initializer)`初始化游戏。所有的游戏逻辑被转移到了`play`方法中,`play`方法使用协议规定的`dice`属性提供骰子摇出的值。 +游戏使用`SnakesAndLadders`类的`构造器(initializer)`初始化游戏。所有的游戏逻辑被转移到了协议中的`play`方法,`play`方法使用协议规定的`dice`属性提供骰子摇出的值。 -> 注意:`delegate`并不是游戏的必备条件,因此`delegate`被定义为遵循`DiceGameDelegate`协议的可选属性,`delegate`使用`nil`作为初始值。 +注意:`delegate`并不是游戏的必备条件,因此`delegate`被定义为遵循`DiceGameDelegate`协议的可选属性。因为`delegate`是可选值,因此在初始化的时候被自动赋值为`nil`。随后,可以在游戏中为`delegate`设置适当的值。 `DicegameDelegate`协议提供了三个方法用来追踪游戏过程。被放置于游戏的逻辑中,即`play()`方法内。分别在游戏开始时,新一轮开始时,游戏结束时被调用。 -因为`delegate`是一个遵循`DiceGameDelegate`的可选属性,因此在`play()`方法中使用了`可选链`来调用委托方法。 若`delegate`属性为`nil`, 则delegate所调用的方法失效。若`delegate`不为`nil`,则方法能够被调用 +因为`delegate`是一个遵循`DiceGameDelegate`的可选属性,因此在`play()`方法中使用了`可选链`来调用委托方法。 若`delegate`属性为`nil`, 则delegate所调用的方法失效,并不会产生错误。若`delegate`不为`nil`,则方法能够被调用 如下所示,`DiceGameTracker`遵循了`DiceGameDelegate`协议 @@ -403,25 +401,25 @@ class DiceGameTracker: DiceGameDelegate { func gameDidStart(game: DiceGame) { numberOfTurns = 0 if game is SnakesAndLadders { - println("Started a new game of Snakes and Ladders") + print("Started a new game of Snakes and Ladders") } - println("The game is using a \(game.dice.sides)-sided dice") + print("The game is using a \(game.dice.sides)-sided dice") } func game(game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) { ++numberOfTurns - println("Rolled a \(diceRoll)") + print("Rolled a \(diceRoll)") } func gameDidEnd(game: DiceGame) { - println("The game lasted for \(numberOfTurns) turns") + print("The game lasted for \(numberOfTurns) turns") } } ``` -`DiceGameTracker`实现了`DiceGameDelegate`协议规定的三个方法,用来记录游戏已经进行的轮数。 当游戏开始时,`numberOfTurns`属性被赋值为0; 在每新一轮中递加; 游戏结束后,输出打印游戏的总轮数。 +`DiceGameTracker`实现了`DiceGameDelegate`协议规定的三个方法,用来记录游戏已经进行的轮数。 当游戏开始时,`numberOfTurns`属性被赋值为0; 在每新一轮中递增; 游戏结束后,输出打印游戏的总轮数。 `gameDidStart`方法从`game`参数获取游戏信息并输出。`game`在方法中被当做`DiceGame`类型而不是`SnakeAndLadders`类型,所以方法中只能访问`DiceGame`协议中的成员。当然了,这些方法也可以在类型转换之后调用。在上例代码中,通过`is`操作符检查`game`是否为 `SnakesAndLadders`类型的实例,如果是,则打印出相应的内容。 -无论当前进行的是何种游戏,`game`都遵循`DiceGame`协议以确保`game`含有`dice`属性,因此在`gameDidStart`方法中可以通过传入的`game`参数来访问`dice`属性,进而打印出`dice`的`sides`属性的值。 +无论当前进行的是何种游戏,`game`都遵循`DiceGame`协议以确保`game`含有`dice`属性,因此在`gameDidStart(_:)`方法中可以通过传入的`game`参数来访问`dice`属性,进而打印出`dice`的`sides`属性的值。 `DiceGameTracker`的运行情况,如下所示: @@ -442,11 +440,12 @@ game.play() ## 在扩展中添加协议成员 -即便无法修改源代码,依然可以通过`扩展(Extension)`来扩充已存在类型(*译者注: 类,结构体,枚举等*)。`扩展`可以为已存在的类型添加`属性`,`方法`,`下标脚本`,`协议`等成员。详情请在[扩展](4)章节中查看。 +即便无法修改源代码,依然可以通过扩展(Extension)来扩充已存在类型(*译者注: 类,结构体,枚举等*)。扩展可以为已存在的类型添加属性,方法,下标脚本,协议等成员。详情请在[扩展](./21_Extensions.html)章节中查看。 -> 注意: 通过`扩展`为已存在的类型`遵循`协议时,该类型的所有实例也会随之添加协议中的方法 +> 注意 +> 通过扩展为已存在的类型遵循协议时,该类型的所有实例也会随之添加协议中的方法 -`TextRepresentable`协议含有一个`asText`,如下所示: +例如`TextRepresentable`协议,任何想要表示一些文本内容的类型都可以遵循该协议。这些想要表示的内容可以是类型本身的描述,也可以是当前内容的版本: ```swift protocol TextRepresentable { @@ -454,7 +453,7 @@ protocol TextRepresentable { } ``` -通过`扩展`为上一节中提到的`Dice`类遵循`TextRepresentable`协议 +可以通过扩展,为上一节中提到的`Dice`增加类遵循`TextRepresentable`协议的功能 ```swift extension Dice: TextRepresentable { @@ -463,16 +462,17 @@ extension Dice: TextRepresentable { } } ``` +现在,通过扩展使得`Dice`类型遵循了一个新的协议,这和`Dice`类型在定义的时候声明为遵循`TextRepresentable`协议的效果相同。在扩展的时候,协议名称写在类型名之后,以冒号隔开,在大括号内写明新添加的协议内容。 -从现在起,`Dice`类型的实例可被当作`TextRepresentable`类型: +现在所有`Dice`的实例都遵循了`TextRepresentable`协议: ```swift let d12 = Dice(sides: 12,generator: LinearCongruentialGenerator()) -println(d12.asText()) +print(d12.asText()) // 输出 "A 12-sided dice" ``` -`SnakesAndLadders`类也可以通过`扩展`的方式来遵循协议: +同样`SnakesAndLadders`类也可以通过`扩展`的方式来遵循`TextRepresentable`协议: ```swift extension SnakesAndLadders: TextRepresentable { @@ -480,14 +480,14 @@ extension SnakesAndLadders: TextRepresentable { return "A game of Snakes and Ladders with \(finalSquare) squares" } } -println(game.asText()) +print(game.asText()) // 输出 "A game of Snakes and Ladders with 25 squares" ``` ## 通过扩展补充协议声明 -当一个类型已经实现了协议中的所有要求,却没有声明时,可以通过`扩展`来补充协议声明: +当一个类型已经实现了协议中的所有要求,却没有声明为遵循该协议时,可以通过扩展(空的扩展体)来补充协议声明: ```swift struct Hamster { @@ -504,26 +504,27 @@ extension Hamster: TextRepresentable {} ```swift let simonTheHamster = Hamster(name: "Simon") let somethingTextRepresentable: TextRepresentable = simonTheHamster -println(somethingTextRepresentable.asText()) +print(somethingTextRepresentable.asText()) // 输出 "A hamster named Simon" ``` -> 注意: 即使满足了协议的所有要求,类型也不会自动转变,因此你必须为它做出明显的协议声明 +> 注意 +> 即使满足了协议的所有要求,类型也不会自动转变,因此你必须为它做出显式的协议声明 ## 集合中的协议类型 -协议类型可以被集合使用,表示集合中的元素均为协议类型: +协议类型可以在集合使用,表示集合中的元素均为协议类型,下面的例子创建了一个类型为`TextRepresentable`的数组: ```swift let things: [TextRepresentable] = [game,d12,simonTheHamster] ``` -如下所示,`things`数组可以被直接遍历,并调用其中元素的`asText()`函数: +如下所示,`things`数组可以被直接遍历,并打印每个元素的文本表示: ```swift for thing in things { - println(thing.asText()) + print(thing.asText()) } // A game of Snakes and Ladders with 25 squares // A 12-sided dice @@ -535,7 +536,7 @@ for thing in things { ## 协议的继承 -协议能够继承一到多个其他协议。语法与类的继承相似,多个协议间用逗号`,`分隔 +协议能够继承一个或多个其他协议,可以在继承的协议基础上增加新的内容要求。协议的继承语法与类的继承相似,多个被继承的协议间用逗号分隔: ```swift protocol InheritingProtocol: SomeProtocol, AnotherProtocol { @@ -551,9 +552,9 @@ protocol PrettyTextRepresentable: TextRepresentable { } ``` -遵循`PrettyTextRepresentable`协议的同时,也需要遵循`TextRepresentable`协议。 +例子中定义了一个新的协议`PrettyTextRepresentable`,它继承自`TextRepresentable`协议。任何遵循`PrettyTextRepresentable`协议的类型在满足该协议的要求时,也必须满足`TextRepresentable`协议的要求。在这个例子中,`PrettyTextRepresentable`协议要求其遵循者提供一个返回值为`String`类型的`asPrettyText`方法。 -如下所示,用`扩展`为`SnakesAndLadders`遵循`PrettyTextRepresentable`协议: +如下所示,扩展`SnakesAndLadders`,让其遵循`PrettyTextRepresentable`协议: ```swift extension SnakesAndLadders: PrettyTextRepresentable { @@ -574,43 +575,44 @@ extension SnakesAndLadders: PrettyTextRepresentable { } ``` -在`for in`中迭代出了`board`数组中的每一个元素: +上述扩展使得`SnakesAndLadders`遵循了`PrettyTextRepresentable`协议,并为每个`SnakesAndLadders`类型提供了了协议要求的`asPrettyText()`方法。每个`PrettyTextRepresentable`类型同时也是`TextRepresentable`类型,所以在`asPrettyText`的实现中,可以调用`asText()`方法。之后在每一行加上换行符,作为输出的开始。然后遍历数组中的元素,输出一个几何图形来表示遍历的结果: -* 当从数组中迭代出的元素的值大于0时,用`▲`表示 -* 当从数组中迭代出的元素的值小于0时,用`▼`表示 -* 当从数组中迭代出的元素的值等于0时,用`○`表示 +* 当从数组中取出的元素的值大于0时,用`▲`表示 +* 当从数组中取出的元素的值小于0时,用`▼`表示 +* 当从数组中取出的元素的值等于0时,用`○`表示 任意`SankesAndLadders`的实例都可以使用`asPrettyText()`方法。 ```swift -println(game.asPrettyText()) +print(game.asPrettyText()) // A game of Snakes and Ladders with 25 squares: // ○ ○ ▲ ○ ○ ▲ ○ ○ ▲ ▲ ○ ○ ○ ▼ ○ ○ ○ ○ ▼ ○ ○ ▼ ○ ▼ ○ ``` ## 类专属协议 -你可以在协议的继承列表中,通过添加“class”关键字,限制协议只能适配到类(class)类型。(结构体或枚举不能遵循该协议)。该“class”关键字必须是第一个出现在协议的继承列表中,其后,才是其他继承协议。 +你可以在协议的继承列表中,通过添加`class`关键字,限制协议只能适配到类(class)类型。(结构体或枚举不能遵循该协议)。该`class`关键字必须是第一个出现在协议的继承列表中,其后,才是其他继承协议。 ```swift protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol { // class-only protocol definition goes here } ``` -在以上例子中,协议SomeClassOnlyProtocol只能被类(class)类型适配。如果尝试让结构体或枚举类型适配该协议,则会出现编译错误。 ->注意 -> ->当协议需求定义的行为,要求(或假设)它的遵循类型必须是引用语义而非值语义时,应该采用类专属协议。关于引用语义,值语义的更多内容,请查看结构体和枚举是值类型和类是引用类型 +在以上例子中,协议`SomeClassOnlyProtocol`只能被类(class)类型适配。如果尝试让结构体或枚举类型适配该协议,则会出现编译错误。 + + +>注意 +>当协议想要定义的行为,要求(或假设)它的遵循类型必须是引用语义而非值语义时,应该采用类专属协议。关于引用语义,值语义的更多内容,请查看[结构体和枚举是值类型](./09_Classes_and_Structures.html#structures_and_enumerations_are_value_types)和[类是引用类型](./09_Classes_and_Structures.html#classes_are_reference_types)。 ## 协议合成 -一个协议可由多个协议采用`protocol\`。例如,class 不是合法的标识符,但可以使用 \`class\`。反引号不属于标识符的一部分,\`x\` 和 `x` 表示同一标识符。
-
-闭包(*closure*)中如果没有明确指定参数名称,参数将被隐式命名为 $0、$1、$2... 这些命名在闭包作用域内是合法的标识符。
-
-> 标识符语法
-> *标识符* → [*标识符头(Head)*](LexicalStructure.html#identifier_head) [*标识符字符列表*](LexicalStructure.html#identifier_characters) _可选_
-> *标识符* → **`** [*标识符头(Head)*](LexicalStructure.html#identifier_head) [*标识符字符列表*](LexicalStructure.html#identifier_characters) _可选_ **`**
-> *标识符* → [*隐式参数名*](LexicalStructure.html#implicit_parameter_name)
-> *标识符列表* → [*标识符*](LexicalStructure.html#identifier) | [*标识符*](LexicalStructure.html#identifier) **,** [*标识符列表*](LexicalStructure.html#identifier_list)
-> *标识符头(Head)* → Upper- or lowercase letter A through Z
-> *标识符头(Head)* → U+00A8, U+00AA, U+00AD, U+00AF, U+00B2–U+00B5, or U+00B7–U+00BA
-> *标识符头(Head)* → U+00BC–U+00BE, U+00C0–U+00D6, U+00D8–U+00F6, or U+00F8–U+00FF
-> *标识符头(Head)* → U+0100–U+02FF, U+0370–U+167F, U+1681–U+180D, or U+180F–U+1DBF
-> *标识符头(Head)* → U+1E00–U+1FFF
-> *标识符头(Head)* → U+200B–U+200D, U+202A–U+202E, U+203F–U+2040, U+2054, or U+2060–U+206F
-> *标识符头(Head)* → U+2070–U+20CF, U+2100–U+218F, U+2460–U+24FF, or U+2776–U+2793
-> *标识符头(Head)* → U+2C00–U+2DFF or U+2E80–U+2FFF
-> *标识符头(Head)* → U+3004–U+3007, U+3021–U+302F, U+3031–U+303F, or U+3040–U+D7FF
-> *标识符头(Head)* → U+F900–U+FD3D, U+FD40–U+FDCF, U+FDF0–U+FE1F, or U+FE30–U+FE44
-> *标识符头(Head)* → U+FE47–U+FFFD
-> *标识符头(Head)* → U+10000–U+1FFFD, U+20000–U+2FFFD, U+30000–U+3FFFD, or U+40000–U+4FFFD
-> *标识符头(Head)* → U+50000–U+5FFFD, U+60000–U+6FFFD, U+70000–U+7FFFD, or U+80000–U+8FFFD
-> *标识符头(Head)* → U+90000–U+9FFFD, U+A0000–U+AFFFD, U+B0000–U+BFFFD, or U+C0000–U+CFFFD
-> *标识符头(Head)* → U+D0000–U+DFFFD or U+E0000–U+EFFFD
-> *标识符字符* → 数值 0 到 9
-> *标识符字符* → U+0300–U+036F, U+1DC0–U+1DFF, U+20D0–U+20FF, or U+FE20–U+FE2F
-> *标识符字符* → [*标识符头(Head)*](LexicalStructure.html#identifier_head)
-> *标识符字符列表* → [*标识符字符*](LexicalStructure.html#identifier_character) [*标识符字符列表*](LexicalStructure.html#identifier_characters) _可选_
-> *隐式参数名* → **$** [*十进制数字列表*](LexicalStructure.html#decimal_digits)
-
-
-## 关键字
-
-被保留的关键字(*keywords*)不允许用作标识符,除非被反引号转义,参见 [标识符](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/doc/uid/TP40014097-CH30-XID_796)。
-
-* **用作声明的关键字:** *class*、*deinit*、*enum*、*extension*、*func*、*import*、*init*、*let*、*protocol*、*static*、*struct*、*subscript*、*typealias*、*var*
-* **用作语句的关键字:** *break*、*case*、*continue*、*default*、*do*、*else*、*fallthrough*、*if*、*in*、*for*、*return*、*switch*、*where*、*while*
-* **用作表达和类型的关键字:** *as*、*dynamicType*、*is*、*new*、*super*、*self*、*Self*、*Type*、*\_\_COLUMN\_\_*、*\_\_FILE\_\_*、*\_\_FUNCTION\_\_*、*\_\_LINE\_\_*
-* **特定上下文中被保留的关键字:** *associativity*、*didSet*、*get*、*infix*、*inout*、*left*、*mutating*、*none*、*nonmutating*、*operator*、*override*、*postfix*、
- *precedence*、*prefix*、*right*、*set*、*unowned*、*unowned(safe)*、*unowned(unsafe)*、*weak*、*willSet*,这些关键字在特定上下文之外可以被用于标识符。
-
-
-## 字面量
-
-字面值表示整型、浮点型数字或文本类型的值,举例如下:
-
-```swift
-42 // 整型字面量
-3.14159 // 浮点型字面量
-"Hello, world!" // 文本型字面量
-```
-
-> 字面量语法
-> *字面量* → [*整型字面量*](LexicalStructure.html#integer_literal) | [*浮点数字面量*](LexicalStructure.html#floating_point_literal) | [*字符串字面量*](LexicalStructure.html#string_literal)
-
-### 整型字面量
-
-整型字面量(*integer literals*)表示未指定精度整型数的值。整型字面量默认用十进制表示,可以加前缀来指定其他的进制,二进制字面量加 `0b`,八进制字面量加 `0o`,十六进制字面量加 `0x`。
-
-十进制字面量包含数字 `0` 至 `9`。二进制字面量只包含 `0` 或 `1`,八进制字面量包含数字 `0` 至 `7`,十六进制字面量包含数字 `0` 至 `9` 以及字母 `A` 至 `F` (大小写均可)。
-
-负整数的字面量在数字前加减号 `-`,比如 `-42`。
-
-允许使用下划线 `_` 来增加数字的可读性,下划线不会影响字面量的值。整型字面量也可以在数字前加 `0`,同样不会影响字面量的值。
-
-```swift
-1000_000 // 等于 1000000
-005 // 等于 5
-```
-
-除非特殊指定,整型字面量的默认类型为 Swift 标准库类型中的 `Int`。Swift 标准库还定义了其他不同长度以及是否带符号的整数类型,请参考 [整数类型](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_411)。
-
-> 整型字面量语法
-> *整型字面量* → [*二进制字面量*](LexicalStructure.html#binary_literal)
-> *整型字面量* → [*八进制字面量*](LexicalStructure.html#octal_literal)
-> *整型字面量* → [*十进制字面量*](LexicalStructure.html#decimal_literal)
-> *整型字面量* → [*十六进制字面量*](LexicalStructure.html#hexadecimal_literal)
-> *二进制字面量* → **0b** [*二进制数字*](LexicalStructure.html#binary_digit) [*二进制字面量字符列表*](LexicalStructure.html#binary_literal_characters) _可选_
-> *二进制数字* → 数值 0 到 1
-> *二进制字面量字符* → [*二进制数字*](LexicalStructure.html#binary_digit) | **_**
-> *二进制字面量字符列表* → [*二进制字面量字符*](LexicalStructure.html#binary_literal_character) [*二进制字面量字符列表*](LexicalStructure.html#binary_literal_characters) _可选_
-> *八进制字面量* → **0o** [*八进字数字*](LexicalStructure.html#octal_digit) [*八进制字符列表*](LexicalStructure.html#octal_literal_characters) _可选_
-> *八进字数字* → 数值 0 到 7
-> *八进制字符* → [*八进字数字*](LexicalStructure.html#octal_digit) | **_**
-> *八进制字符列表* → [*八进制字符*](LexicalStructure.html#octal_literal_character) [*八进制字符列表*](LexicalStructure.html#octal_literal_characters) _可选_
-> *十进制字面量* → [*十进制数字*](LexicalStructure.html#decimal_digit) [*十进制字符列表*](LexicalStructure.html#decimal_literal_characters) _可选_
-> *十进制数字* → 数值 0 到 9
-> *十进制数字列表* → [*十进制数字*](LexicalStructure.html#decimal_digit) [*十进制数字列表*](LexicalStructure.html#decimal_digits) _可选_
-> *十进制字符* → [*十进制数字*](LexicalStructure.html#decimal_digit) | **_**
-> *十进制字符列表* → [*十进制字符*](LexicalStructure.html#decimal_literal_character) [*十进制字符列表*](LexicalStructure.html#decimal_literal_characters) _可选_
-> *十六进制字面量* → **0x** [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制字面量字符列表*](LexicalStructure.html#hexadecimal_literal_characters) _可选_
-> *十六进制数字* → 数值 0 到 9, a through f, or A through F
-> *十六进制字符* → [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) | **_**
-> *十六进制字面量字符列表* → [*十六进制字符*](LexicalStructure.html#hexadecimal_literal_character) [*十六进制字面量字符列表*](LexicalStructure.html#hexadecimal_literal_characters) _可选_
-
-### 浮点型字面量
-
-浮点型字面量(*floating-point literals*)表示未指定精度浮点数的值。
-
-浮点型字面量默认用十进制表示(无前缀),也可以用十六进制表示(加前缀 `0x`)。
-
-十进制浮点型字面量(*decimal floating-point literals*)由十进制数字串后跟小数部分或指数部分(或两者皆有)组成。十进制小数部分由小数点 `.` 后跟十进制数字串组成。指数部分由大写或小写字母 `e` 后跟十进制数字串组成,这串数字表示 `e` 之前的数量乘以 10 的几次方。例如:`1.25e2` 表示 `1.25 ⨉ 10^2`,也就是 `125.0`;同样,`1.25e-2` 表示 `1.25 ⨉ 10^-2`,也就是 `0.0125`。
-
-十六进制浮点型字面量(*hexadecimal floating-point literals*)由前缀 `0x` 后跟可选的十六进制小数部分以及十六进制指数部分组成。十六进制小数部分由小数点后跟十六进制数字串组成。指数部分由大写或小写字母 `p` 后跟十进制数字串组成,这串数字表示 `p` 之前的数量乘以 2 的几次方。例如:`0xFp2` 表示 `15 ⨉ 2^2`,也就是 `60`;同样,`0xFp-2` 表示 `15 ⨉ 2^-2`,也就是 `3.75`。
-
-与整型字面量不同,负的浮点型字面量由一元运算符减号 `-` 和浮点型字面量组成,例如 `-42.0`。这代表一个表达式,而不是一个浮点整型字面量。
-
-允许使用下划线 `_` 来增强可读性,下划线不会影响字面量的值。浮点型字面量也可以在数字前加 `0`,同样不会影响字面量的值。
-
-```swift
-10_000.56 // 等于 10000.56
-005000.76 // 等于 5000.76
-```
-
-除非特殊指定,浮点型字面量的默认类型为 Swift 标准库类型中的 `Double`,表示64位浮点数。Swift 标准库也定义 `Float` 类型,表示32位浮点数。
-
-> 浮点型字面量语法
-> *浮点数字面量* → [*十进制字面量*](LexicalStructure.html#decimal_literal) [*十进制分数*](LexicalStructure.html#decimal_fraction) _可选_ [*十进制指数*](LexicalStructure.html#decimal_exponent) _可选_
-> *浮点数字面量* → [*十六进制字面量*](LexicalStructure.html#hexadecimal_literal) [*十六进制分数*](LexicalStructure.html#hexadecimal_fraction) _可选_ [*十六进制指数*](LexicalStructure.html#hexadecimal_exponent)
-> *十进制分数* → **.** [*十进制字面量*](LexicalStructure.html#decimal_literal)
-> *十进制指数* → [*浮点数e*](LexicalStructure.html#floating_point_e) [*正负号*](LexicalStructure.html#sign) _可选_ [*十进制字面量*](LexicalStructure.html#decimal_literal)
-> *十六进制分数* → **.** [*十六进制字面量*](LexicalStructure.html#hexadecimal_literal) _可选_
-> *十六进制指数* → [*浮点数p*](LexicalStructure.html#floating_point_p) [*正负号*](LexicalStructure.html#sign) _可选_ [*十六进制字面量*](LexicalStructure.html#hexadecimal_literal)
-> *浮点数e* → **e** | **E**
-> *浮点数p* → **p** | **P**
-> *正负号* → **+** | **-**
-
-### 文本型字面量
-
-文本型字面量(*string literal*)由双引号中的字符串组成,形式如下:
-
-```swift
-"characters"
-```
-
-文本型字面量中不能包含未转义的双引号 `"`、未转义的反斜线`\`、回车符(*carriage return*)或换行符(*line feed*)。
-
-可以在文本型字面量中使用的转义特殊符号如下:
-
-* 空字符(Null Character)`\0`
-* 反斜线(Backslash)`\\`
-* 水平 Tab (Horizontal Tab)`\t`
-* 换行符(Line Feed)`\n`
-* 回车符(Carriage Return)`\r`
-* 双引号(Double Quote)`\"`
-* 单引号(Single Quote)`\'`
-
-字符也可以用以下方式表示:
-
-* `\x` 后跟两位十六进制数字
-* `\u` 后跟四位十六进制数字
-* `\U` 后跟八位十六进制数字
-
-后跟的数字表示一个 Unicode 码点。
-
-文本型字面量允许在反斜线小括号 `\()` 中插入表达式的值。插入表达式(*interpolated expression*)不能包含未转义的双引号 `"`、反斜线 `\`、回车符或者换行符。表达式值的类型必须在 *String* 类中有对应的初始化方法。
-
-例如,以下所有文本型字面量的值相同:
-
-```swift
-"1 2 3"
-"1 2 \(3)"
-"1 2 \(1 + 2)"
-var x = 3; "1 2 \(x)"
-```
-
-文本型字面量的默认类型为 `String`。组成字符串的字符类型为 `Character`。更多有关 `String` 和 `Character` 的信息请参照 [字符串和字符](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html#//apple_ref/doc/uid/TP40014097-CH7-XID_368)。
-
-> 字符型字面量语法
-> *字符串字面量* → **"** [*引用文本*](LexicalStructure.html#quoted_text) **"**
-> *引用文本* → [*引用文本条目*](LexicalStructure.html#quoted_text_item) [*引用文本*](LexicalStructure.html#quoted_text) _可选_
-> *引用文本条目* → [*转义字符*](LexicalStructure.html#escaped_character)
-> *引用文本条目* → **\(** [*表达式*](..\chapter3\04_Expressions.html#expression) **)**
-> *引用文本条目* → 除了", \, U+000A, or U+000D的所有Unicode的字符
-> *转义字符* → **\0** | **\\** | **\t** | **\n** | **\r** | **\"** | **\'**
-> *转义字符* → **\x** [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit)
-> *转义字符* → **\u** [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit)
-> *转义字符* → **\U** [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit)
-
-
-## 运算符
-
-Swift 标准库定义了许多可供使用的运算符,其中大部分在 [基础运算符](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html#//apple_ref/doc/uid/TP40014097-CH6-XID_70) 和 [高级运算符](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-XID_28) 中进行了阐述。这里将描述哪些字符能用作运算符。
-
-运算符由一个或多个以下字符组成:
-`/`、`=`、`-`、`+`、`!`、`*`、`%`、`<`、`>`、`&`、`|`、`^`、`~`、`.`。也就是说,标记 `=`, `->`、`//`、`/*`、`*/`、`.` 以及一元前缀运算符 `&` 属于保留字,这些标记不能被重写或用于自定义运算符。
-
-运算符两侧的空白被用来区分该运算符是否为前缀运算符(*prefix operator*)、后缀运算符(*postfix operator*)或二元运算符(*binary operator*)。规则总结如下:
-
-* 如果运算符两侧都有空白或两侧都无空白,将被看作二元运算符。例如:`a+b` 和 `a + b` 中的运算符 `+` 被看作二元运算符。
-* 如果运算符只有左侧空白,将被看作前缀一元运算符。例如 `a ++b` 中的 `++` 被看作前缀一元运算符。
-* 如果运算符只有右侧空白,将被看作后缀一元运算符。例如 `a++ b` 中的 `++` 被看作后缀一元运算符。
-* 如果运算符左侧没有空白并紧跟 `.`,将被看作后缀一元运算符。例如 `a++.b` 中的 `++` 被看作后缀一元运算符(同理, `a++ . b` 中的 `++` 是后缀一元运算符而 `a ++ .b` 中的 `++` 不是).
-
-鉴于这些规则,运算符前的字符 `(`、`[` 和 `{` ;运算符后的字符 `)`、`]` 和 `}` 以及字符 `,`、`;` 和 `:` 都将用于空白检测。
-
-以上规则需注意一点,如果运算符 `!` 或 `?` 左侧没有空白,则不管右侧是否有空白都将被看作后缀运算符。如果将 `?` 用作可选类型(*optional type*)修饰,左侧必须无空白。如果用于条件运算符 `? :`,必须两侧都有空白。
-
-在特定构成中 ,以 `<` 或 `>` 开头的运算符会被分离成两个或多个标记,剩余部分以同样的方式会被再次分离。因此,在 `Dictionary\`class\`。反引号不属于标识符的一部分,\`x\` 和 `x` 表示同一标识符。
+
+闭包(*closure*)中如果没有明确指定参数名称,参数将被隐式命名为 `$0`、`$1`、`$2`等等。 这些命名在闭包作用域范围内是合法的标识符。
+
+> 标识符语法
+
+> *标识符* → [*头部标识符*](#identifier_head) [*标识符字符组*](#identifier_characters)可选
+> *标识符* → \`[*头部标识符*](#identifier_head) [*标识符字符组*](#identifier_characters)可选\`
+> *标识符* → [*隐式参数名*](#implicit_parameter_name)
+> *标识符列表* → [*标识符*](#identifier) | [*标识符*](#identifier) **,** [*标识符列表*](#identifier_list)
+
+> *头部标识符* → 大写或小写字母 A - Z
+> *头部标识符* → U+00A8, U+00AA, U+00AD, U+00AF, U+00B2–U+00B5, or U+00B7–U+00BA
+> *头部标识符* → U+00BC–U+00BE, U+00C0–U+00D6, U+00D8–U+00F6, or U+00F8–U+00FF
+> *头部标识符* → U+0100–U+02FF, U+0370–U+167F, U+1681–U+180D, or U+180F–U+1DBF
+> *头部标识符* → U+1E00–U+1FFF
+> *头部标识符* → U+200B–U+200D, U+202A–U+202E, U+203F–U+2040, U+2054, or U+2060–U+206F
+> *头部标识符* → U+2070–U+20CF, U+2100–U+218F, U+2460–U+24FF, or U+2776–U+2793
+> *头部标识符* → U+2C00–U+2DFF or U+2E80–U+2FFF
+> *头部标识符* → U+3004–U+3007, U+3021–U+302F, U+3031–U+303F, or U+3040–U+D7FF
+> *头部标识符* → U+F900–U+FD3D, U+FD40–U+FDCF, U+FDF0–U+FE1F, or U+FE30–U+FE44
+> *头部标识符* → U+FE47–U+FFFD
+> *头部标识符* → U+10000–U+1FFFD, U+20000–U+2FFFD, U+30000–U+3FFFD, or U+40000–U+4FFFD
+> *头部标识符* → U+50000–U+5FFFD, U+60000–U+6FFFD, U+70000–U+7FFFD, or U+80000–U+8FFFD
+> *头部标识符* → U+90000–U+9FFFD, U+A0000–U+AFFFD, U+B0000–U+BFFFD, or U+C0000–U+CFFFD
+> *头部标识符* → U+D0000–U+DFFFD or U+E0000–U+EFFFD
+> *标识符字符* → 数值 0 - 9
+> *标识符字符* → U+0300–U+036F, U+1DC0–U+1DFF, U+20D0–U+20FF, or U+FE20–U+FE2F
+> *标识符字符* → [*头部标识符*](#identifier_head)
+
+> *标识符字符组* → [*标识符字符*](#identifier_character) [*标识符字符列表*](#identifier_characters)可选
+
+> *隐式参数名* → **$** [*十进制数字列表*](#decimal_digits)
+
+
+## 关键字和符号
+
+下面这些被保留的关键字(*keywords*)不允许用作标识符,除非被反引号转义,具体描述请参考 [标识符](#identifiers)。
+
+* **用在声明中的关键字:** *class*、*deinit*、*enum*、*extension*、*func*、*import*、*init*、*let*、*protocol*、*static*、*struct*、*subscript*、*typealias*、*var*
+* **用在语句中的关键字:** *break*、*case*、*continue*、*default*、*do*、*else*、*fallthrough*、*if*、*in*、*for*、*return*、*switch*、*where*、*while*
+* **用在表达式和类型中的关键字:** *as*、*dynamicType*、*is*、*new*、*super*、*self*、*Self*、*Type*、*\_\_COLUMN\_\_*、*\_\_FILE\_\_*、*\_\_FUNCTION\_\_*、*\_\_LINE\_\_*
+* **用在模式中的关键字:** *\_*
+* **特定上下文中被保留的关键字:** *associativity*、*didSet*、*get*、*infix*、*inout*、*left*、*mutating*、*none*、*nonmutating*、*operator*、*override*、*postfix*、*precedence*、*prefix*、*right*、*set*、*unowned*、*unowned(safe)*、*unowned(unsafe)*、*weak*、*willSet*,这些关键字在特定上下文之外可以被用于标识符。
+
+以下标记被当作保留符号,不能用于自定义操作符:`(`、`)`、`{`、`}`、`[`、`]`、`.`、`,`、`:`、`;`、`=`、`@`、`#`、`&(作为前缀操作符)`、`->`、`` `、`?` 和 `!(作为后缀操作符)`。
+
+
+## 字面量
+
+字面量是用来表示源码中某种特定类型的值,比如一个数字或字符串。
+
+下面是字面量的一些示例:
+
+```swift
+42 // 整型字面量
+3.14159 // 浮点型字面量
+"Hello, world!" // 字符串型字面量
+true // 布尔型字面量
+```
+
+字面量本身并不包含类型信息。事实上,一个字面量会被解析为拥有无限的精度,然后 Swift 的类型推导会尝试去推导出这个字面量的类型。比如,在 `let x: Int8 = 42` 这个声明中,Swift 使用了显式类型标注(`: Int8`)来推导出 `42` 这个整型字面量的类型是 `Int8`。如果没有可用的类型信息, Swift 则会从标准库中定义的字面量类型中推导出一个默认的类型。整型字面量的默认类型是 `Int`,浮点型字面量的默认类型是 `Double`,字符串型字面量的默认类型是 `String`,布尔型字面量的默认类型是 `Bool`。比如,在 `let str = "Hello, world"` 这个声明中,字符串 `"Hello, world"`的默认推导类型就是 `String`。
+
+当为一个字面量值指定了类型标注的时候,这个注解的类型必须能通过这个字面量值实例化后得到。也就是说,这个类型必须遵守这些 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**
+
+### 整型字面量
+
+整型字面量(*integer literals*)表示未指定精度整型数的值。整型字面量默认用十进制表示,可以加前缀来指定其他的进制,二进制字面量加 `0b`,八进制字面量加 `0o`,十六进制字面量加 `0x`。
+
+十进制字面量包含数字 `0` 至 `9`。二进制字面量只包含 `0` 或 `1`,八进制字面量包含数字 `0` 至 `7`,十六进制字面量包含数字 `0` 至 `9` 以及字母 `A` 至 `F` (大小写均可)。
+
+负整数的字面量在整型字面量前加减号 `-`,比如 `-42`。
+
+整型字面面可以使用下划线 `_` 来增加数字的可读性,下划线会被系统忽略,因此不会影响字面量的值。同样地,也可以在数字前加 `0`,并不会影响字面量的值。
+
+除非特别指定,整型字面量的默认推导类型为 Swift 标准库类型中的 `Int`。Swift 标准库还定义了其他不同长度以及是否带符号的整数类型,请参考 [整数类型](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-ID323)。
+
+> 整型字面量语法
+> *整型字面量* → [*二进制字面量*](#binary_literal)
+> *整型字面量* → [*八进制字面量*](#octal_literal)
+> *整型字面量* → [*十进制字面量*](#decimal_literal)
+> *整型字面量* → [*十六进制字面量*](#hexadecimal_literal)
+
+> *二进制字面量* → **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`)。
+
+十进制浮点型字面量(*decimal floating-point literals*)由十进制数字串后跟小数部分或指数部分(或两者皆有)组成。十进制小数部分由小数点 `.` 后跟十进制数字串组成。指数部分由大写或小写字母 `e` 为前缀后跟十进制数字串组成,这串数字表示 `e` 之前的数量乘以 10 的几次方。例如:`1.25e2` 表示 `1.25 ⨉ 10^2`,也就是 `125.0`;同样,`1.25e-2` 表示 `1.25 ⨉ 10^-2`,也就是 `0.0125`。
+
+十六进制浮点型字面量(*hexadecimal floating-point literals*)由前缀 `0x` 后跟可选的十六进制小数部分以及十六进制指数部分组成。十六进制小数部分由小数点后跟十六进制数字串组成。指数部分由大写或小写字母 `p` 为前缀后跟十进制数字串组成,这串数字表示 `p` 之前的数量乘以 2 的几次方。例如:`0xFp2` 表示 `15 ⨉ 2^2`,也就是 `60`;同样,`0xFp-2` 表示 `15 ⨉ 2^-2`,也就是 `3.75`。
+
+负的浮点型字面量由一元运算符减号 `-` 和浮点型字面量组成,例如 `-42.5`。
+
+浮点型字面量允许使用下划线 `_` 来增强数字的可读性,下划线会被系统忽略,因此不会影响字面量的值。同样地,也可以在数字前加 `0`,并不会影响字面量的值。
+
+除非特别指定,浮点型字面量的默认推导类型为 Swift 标准库类型中的 `Double`,表示64位浮点数。Swift 标准库也定义了 `Float` 类型,表示32位浮点数。
+
+> 浮点型字面量语法
+> *浮点数字面量* → [*十进制字面量*](#decimal_literal) [*十进制分数*](#decimal_fraction)可选 [*十进制指数*](#decimal_exponent)可选
+> *浮点数字面量* → [*十六进制字面量*](#hexadecimal_literal) [*十六进制分数*](#hexadecimal_fraction)可选 [*十六进制指数*](#hexadecimal_exponent)
+
+> *十进制分数* → **.** [*十进制字面量*](#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**
+> *正负号* → **+** | **-**
+
+
+### 字符串型字面量
+
+字符串型字面量(*string literal*)由被包在双引号中的一串字符组成,形式如下:
+
+```
+"characters"
+```
+
+字符串型字面量中不能包含未转义的双引号 (`"`)、未转义的反斜线(`\`)、回车符(*carriage return*)或换行符(*line feed*)。
+
+可以在字符串字面量中使用的转义特殊符号如下:
+
+* 空字符(Null Character)`\0`
+* 反斜线(Backslash)`\\`
+* 水平制表符(Horizontal Tab)`\t`
+* 换行符(Line Feed)`\n`
+* 回车符(Carriage Return)`\r`
+* 双引号(Double Quote)`\"`
+* 单引号(Single Quote)`\'`
+* Unicode标量 `\u{n}`,n为一到八位的十六进制数字
+
+字符串字面量允许在反斜杠小括号 `\()` 中插入表达式的值。插入表达式(*interpolated expression*)不能包含未转义的双引号 `"`、未转义的反斜线 `\`、回车符或者换行符。表达式结果的类型必须在 *String* 类中有对应的初始化方法。
+
+例如,以下所有字符串字面量的值都是相同的:
+
+```
+"1 2 3"
+"1 2 \(3)"
+"1 2 \(1 + 2)"
+let x = 3; "1 2 \(x)"
+```
+
+字符串字面量的默认推导类型为 `String`。组成字符串的字符默认推导类型为 `Character`。更多有关 `String` 和 `Character` 的信息请参照 [字符串和字符](../chapter2/03_Strings_and_Characters.html)。
+
+> 字符型字面量语法
+> *字符串字面量* → **"** [*引用文本*](#quoted_text)可选 **"**
+
+> *引用文本* → [*引用文本条目*](#quoted_text_item) [*引用文本*](#quoted_text) 可选
+> *引用文本条目* → [*转义字符*](#escaped_character)
+> *引用文本条目* → **\(** [*表达式*](./04_Expressions.html) **)**
+> *引用文本条目* → **除了", \, U+000A, 或者 U+000D的所有Unicode的字符**
+> *转义字符* → **\0** | **\\** | **\t** | **\n** | **\r** | **\"** | **\'**
+> *转义字符* → **\u {** [*unicode标量数字*](#unicode_scalar_digits) **}**
+> *unicode标量数字* → 一到八位的十六进制数字
+
+
+## 运算符
+
+Swift 标准库定义了许多可供使用的运算符,其中大部分在 [基础运算符](../chapter2/02_Basic_Operators.html) 和 [高级运算符](../chapter2/25_Advanced_Operators.html) 中进行了阐述。这一小节将描述哪些字符能用于自定义运算符。
+
+自定义运算符可以由以下其中之一的 ASCII 字符 `/`、`=`、 `-`、`+`、`!`、`*`、`%`、`<`、`>`、`&`、`|`、`^`、`?` 以及 `~`, 或者后面语法中规定的任一个 Unicode 字符开始。在第一个字符之后,允许使用组合型 Unicode 字符。也可以使用两个或者多个的点号来自定义运算符(比如, `....`)。虽然可以自定义包含问号`?`的运算符,但是这个运算符不能只包含单独的一个问号。
+
+ 注意:
+ 以下这些标记 =, ->, //, /*, */, ., <(前缀运算符), &, and ?, ?(中缀运算符), >(后缀运算符), ! 以及 ? 是被系统保留的。这些标记不能被重载,也不能用于自定义操作符。
+
+运算符两侧的空白被用来区分该运算符是否为前缀运算符(*prefix operator*)、后缀运算符(*postfix operator*)或二元运算符(*binary operator*)。规则总结如下:
+
+* 如果运算符两侧都有空白或两侧都无空白,将被看作二元运算符。例如:`a+b` 和 `a + b` 中的运算符 `+` 被看作二元运算符。
+* 如果运算符只有左侧空白,将被看作前缀一元运算符。例如 `a ++b` 中的 `++` 被看作前缀一元运算符。
+* 如果运算符只有右侧空白,将被看作后缀一元运算符。例如 `a++ b` 中的 `++` 被看作后缀一元运算符。
+* 如果运算符左侧没有空白并紧跟 `.`,将被看作后缀一元运算符。例如 `a++.b` 中的 `++` 被看作后缀一元运算符(即上式被视为 `a++ .b` 而非 `a ++ .b`)。
+
+鉴于这些规则,运算符前的字符 `(`、`[` 和 `{` ;运算符后的字符 `)`、`]` 和 `}` 以及字符 `,`、`;` 和 `:` 都被视为空白。
+
+以上规则需注意一点,如果预定义运算符 `!` 或 `?` 左侧没有空白,则不管右侧是否有空白都将被看作后缀运算符。如果将 `?` 用作可选链(*optional-chaining*)操作符,左侧必须无空白。如果用于条件运算符 `? :`,必须两侧都有空白。
+
+在某些特定的构造中 ,以 `<` 或 `>` 开头的运算符会被分离成两个或多个标记,剩余部分以同样的方式会被再次分离。因此,在 `Dictionary`introduced=version number`
这里的`version number`由一个正的十进制整数或浮点数构成。 +- `iOS` +- `iOSApplicationExtension` +- `OSX` +- `OSXApplicationExtension` +- `watchOS` -- `deprecated`参数表示:特定的平台上,该声明被建议弃用的第一个版本。格式如下: +当然,你也可以用一个星号(*)来表示,该声明在上面提到的所有平台上都是有效的。 + +剩下的参数,可以以任何顺序出现,并且可以添加关于声明生命周期的附加信息,包括重要的里程碑。 + +- `unavailable`参数表示:该声明在特定的平台上是无效的 + +- `introduced`参数表示:该声明第一次被引入时所在平台的版本。格式如下: +
`introduced=version number`
这里的`version number`由一个正的十进制整数或浮点数构成。 + +- `deprecated`参数表示:该声明第一次被建议弃用时所在平台的版本。格式如下:
`deprecated=version number`
这里的`version number`由一个正的十进制整数或浮点数构成。 -- `obsoleted`参数表示:特定的平台上,该声明被弃用的第一个版本。格式如下: -
`deprecated=version number`
这里的`version number`由一个正的十进制整数或浮点数构成。 +- `obsoleted`参数表示:该声明第一次被弃用时所在平台的版本。当一个声明被弃用时,它就从此平台中被移除,不能再被使用。格式如下: +
`obsoleted=version number`
这里的`version number`由一个正的十进制整数或浮点数构成。 -- `message`参数用来提供文本信息,并在因使用建议弃用或者被弃用的声明而遇到警告或错误时,由编译器抛出。格式如下: +- `message`参数用来提供文本信息。当使用建议弃用或者被弃用的声明时,编译器会抛出错误或警告信息。格式如下:
`message=message`
这里的`message`由一个字符串文字构成。 -- `renamed`参数用来提供文本信息,用以表示被重命名的声明的新名字。当使用这个重命名的声明遇到错误时,该新名字会被编译器显示出来。格式如下: +- `renamed`参数用来提供文本信息,用以表示被重命名的声明的新名字。当使用这个重命名的声明遇到错误时,编译器会显示出该新名字。格式如下:
`renamed=new name`
这里的`new name`由一个字符串文字构成。 -你可以将`renamed`参数和`unavailable`参数以及类型别名声明组合使用,以向用户表示:在你的代码中,一个声明已经被重命名。当一个声明的名字在一个框架或者库的不同发布版本间发生变化时,这会相当管用。 +你可以将`renamed`参数和`unavailable`参数以及类型别名声明组合使用,以向用户表示:在你的代码中,一个声明已经被重命名。当一个声明的名字在一个框架或者库的不同发布版本间发生变化时,这会相当有用。 ```swift // First release @@ -58,50 +76,34 @@ protocol MyRenamedProtocol { // protocol definition } -@availability(*, unavailable, renamed="MyRenamedProtocol") +@available(*, unavailable, renamed="MyRenamedProtocol") typealias MyProtocol = MyRenamedProtocol ``` -你可以在一个单独的声明上使用多个`availability`特性,以详细说明该声明在不同平台上的有效性。编译器只有在当前的目标平台和`availability`特性中指定的平台匹配时,才会使用`availability`特性 +你可以在一个单独的声明上使用多个`available`特性,以详细说明该声明在不同平台上的有效性。编译器只有在当前的目标平台和`available`特性中指定的平台匹配时,才会使用`available`特性 -`autoclosure` +如果`available`特性除了平台名称参数外,只指定了一个`introduced `参数,那么可以使用以下简写语法代替: -这个属性通过把表达式自动封装成不带参数的闭包来延迟表达式的计算。这个属性使用在函数参数声明或者不带参数但是返回表达式类型的方法类型上。含有```autoclosure```属性的声明同时也具有```noescape```的特性,除非传递一个可选的参数属性```escaping```,请看[函数类型](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Types.html#//apple_ref/doc/uid/TP40014097-CH31-ID449)。 +@available(`platform name` `version number`, *) -`noescape` - -在函数或者方法声明上使用该属性表示参数将不会被存储用作后续的计算,其用来确保不会超出函数调用的声明周期。使用```noescape```声明属性的函数类型不需要显式的使用```self```,对于其属性或者方法来说。 - -`noreturn` - -该特性用于修饰函数或方法声明,表明该函数或方法的对应类型,`T`,是`@noreturn T`。你可以用这个特性修饰函数或方法的类型,这样一来,函数或方法就不会返回到它的调用者中去。 - -对于一个没有用`noreturn`特性标记的函数或方法,你可以将它重写(override)为用该特性标记的。相反,对于一个已经用`noreturn`特性标记的函数或方法,你则不可以将它重写为没使用该特性标记的。相同的规则试用于当你在一个comforming类型中实现一个协议方法时。 - -`NSApplicationMain` -在类上使用该属性表示该类是应用程序委托类,使用该属性与调用```NSApplicationMain```函数并且把该类的名字作为委托类的名字传递给函数的效果相同。 - -如果你不想使用这个属性,可以提供一个```main.swift```文件,并且提供一个```main```函数去调用```NSApplicationMain```函数。比如,如果你的应用程序使用一个派生于```NSApplication```的自定义子类作为主要类,你可以调用```NSApplicationMain```函数而不是使用该属性。 - - -`NSCopying` - -该特性用于修饰一个类的存储型变量属性。该特性将使属性的setter与属性值的一个副本合成,由`copyWithZone`方法返回,而不是属性本身的值。该属性的类型必需遵循`NSCopying`协议。 - -`NSCopying`特性的行为与Objective-C中的`copy`特性相似。 - -`NSManaged` - -该特性用于修饰`NSManagedObject`子类中的存储型变量属性,表明属性的存储和实现由Core Data在运行时基于相关实体描述动态提供。 +`available`特性的简写语法可以简明地表达出多个平台的可用性。尽管这两种形式在功能上是相同的,但请尽可能地使用简明语法形式。 +```swift +@available(iOS 8.0, OSX 10.10, *) +class MyClass { + // class definition +} +``` `objc` -该特性用于修饰任意可以在Objective-C中表示的声明,比如,非嵌套类,协议,类和协议中的属性和方法(包含getter和setter),初始化器,析构器,以及下标。`objc`特性告诉编译器该声明可以在Objective-C代码中使用。 +该特性用于修饰任何可以在Objective-C中表示的声明。比如,非嵌套类、协议、非泛型枚举(仅限整型值类型)、类和协议的属性和方法(包括`getter`和`setter`)、构造器、析构器以及下标。`objc`特性告诉编译器这个声明可以在Objective-C代码中使用。 -如果你将`objc`特性应用于一个类或协议,它也会隐式地应用于那个类或协议的成员。对于标记了`objc`特性的类,编译器会隐式地为它的子类添加`objc`特性。标记了`objc`特性的协议不能继承自没有标记`objc`的协议。 +标有`objc`特性的类必须继承自Objective-C中定义的类。如果你将`objc`特性应用于一个类或协议,它也会隐式地应用于那个类的成员或协议。对于标记了`objc`特性的类,编译器会隐式地为它的子类添加`objc`特性。标记了`objc`特性的协议不能继承没有标记`objc`的协议。 -`objc`特性有一个可选的参数,由标记符组成。当你想把`objc`所修饰的实体以一个不同的名字暴露给Objective-C,你就可以使用这个特性参数。你可以使用这个参数来命名类,协议,方法,getters,setters,以及初始化器。下面的例子把`ExampleClass`中`enabled`属性的getter暴露给Objective-C,名字是`isEnabled`,而不是它原来的属性名。 +如果你将`objc`特性应用于枚举,每一个枚举的`case`都会以枚举名称和`case`名称组合的方式暴露在Objective-C代码中。例如:一个名为`Venus`的`case`在`Planet`枚举中,这个`case`暴露在Objective-C代码中时叫做`PlanetVenus`。 + +`objc`特性有一个可选的参数,由标记符组成。当你想把`objc`所修饰的实体以一个不同的名字暴露给Objective-C时,你就可以使用这个特性参数。你可以使用这个参数来命名类,协议,方法,getters,setters,以及构造器。下面的例子把`ExampleClass`中`enabled`属性的getter暴露给Objective-C,名字是`isEnabled`,而不是它原来的属性名。 ```swift @objc @@ -114,17 +116,64 @@ class ExampleClass { } ``` -`optional` +`noescape` -用该特性修饰协议的属性,方法或下标成员,表示实现这些成员并不需要一致性类型(conforming type)。 +在函数或者方法声明上使用该特性,它表示参数将不会被存储用作后续的计算,其用来确保不会超出函数调用的生命周期。对于其属性或方法来说,使用`noescape`声明属性的函数类型不需要显式的使用`self.`。 -你只能用`optional`特性修饰那些标记了`objc`特性的协议。因此,只有类类型可以adopt和comform to那些包含可选成员需求的协议。更多关于如何使用`optional`特性以及如何访问可选协议成员的指导,例如,当你不确定一个conforming类型是否实现了它们,请见:[可选协议需求]()。 +`nonobjc` -`required` +该特性用于方法、属性、下标、或构造器的声明,这些声明本是可以在Objective-C代码中表示的。使用`nonobjc`特性告诉编译器这个声明不能在Objective-C代码中使用。 -用该特性修饰一个类的指定或便利初始化器,表示该类的所有子类都必需实现该初始化器。 +可以使用`nonobjc`特性解决标有`objc`的类中桥接方法的循环问题,该特性还允许标有`objc`的类的构造器和方法进行重载(overload)。 -加了该特性的指定初始化器必需显式地实现,而便利初始化器既可显式地实现,也可以在子类实现了超类所有指定初始化器后继承而来(或者当子类使用便利初始化器重写了指定初始化器)。 +标有`nonobjc`特性的方法不能重写(override)一个标有`objc`特性的方法。然而,标有`objc`特性的方法可以重写标有`nonobjc`特性的方法。同样,标有`nonobjc`特性的方法不能满足一个需要标有`@objc`特性的方法的协议。 + +`noreturn` + +该特性用于修饰函数或方法声明,表明该函数或方法的对应类型,`T`,是`@noreturn T`。你可以用这个特性修饰函数或方法的类型,这样一来,函数或方法就不会返回到它的调用者中去。 + +对于一个没有用`noreturn`特性标记的函数或方法,你可以将它重写为用该特性标记的。相反,对于一个已经用`noreturn`特性标记的函数或方法,你则不可以将它重写为没使用该特性标记的。当你在一个comforming类型中实现一个协议方法时,该规则同样适用。 + +`NSApplicationMain` + +在类上使用该特性表示该类是应用程序委托类,使用该特性与调用`NSApplicationMain(_:_:)`函数并且把该类的名字作为委托类的名字传递给函数的效果相同。 + +如果你不想使用这个特性,可以提供一个`main.swift`文件,并且提供一个`main`函数去调用`NSApplicationMain(_:_:)`函数。比如,如果你的应用程序使用一个派生于`NSApplication`的自定义子类作为主要类,你可以调用`NSApplicationMain`函数而不是使用该特性。 + +`NSCopying` + +该特性用于修饰一个类的存储型变量属性。该特性将使属性的setter与属性值的一个副本合成,这个值由`copyWithZone(_:)`方法返回,而不是属性本身的值。该属性的类型必需遵循`NSCopying`协议。 + +`NSCopying`特性的原理与Objective-C中的`copy`特性相似。 + +`NSManaged` + +该特性用于修饰`NSManagedObject`子类中的存储型变量属性,表明属性的存储和实现由Core Data在运行时基于相关实体描述动态提供。 + +`testable` + +该特性用于`import`声明可以测试的编译模块,它能访问任何标有`internal`权限标识符的实体,这和将它声明为`public`权限标识符有同样的效果。 + +`UIApplicationMain` + +在类上使用该特性表示该类是应用程序委托类,使用该特性与调用`UIApplicationMain(_:_:)`函数并且把该类的名字作为委托类的名字传递给函数的效果相同。 + +如果你不想使用这个特性,可以提供一个`main.swift`文件,并且提供一个`main`函数去调用`UIApplicationMain(_:_:)`函数。比如,如果你的应用程序使用一个派生于`UIApplication`的自定义子类作为主要类,你可以调用`UIApplicationMain`函数而不是使用该特性。 + +`warn_unused_result` + +该特性应用于方法或函数声明,当方法或函数被调用,但其结果未被使用时,该特性会让编译器会产生警告。 + +你可以使用这个特性提供一个警告信息,这个警告信息是关于不正确地使用未变异的方法的,这个方法也有一个对应的变异方法。 + +`warn_unused_result`有下面两个可选的参数。 + +- `message`参数用来提供警告信息,并在因当方法或函数被调用,但其结果未被使用时,显示警告信息。格式如下: +
`message=message`
这里的`message`由一个字符串文字构成。 + +- `mutable_variant`参数用于提供变异方法的名称,如果未变异方法以一个可变的值被调用而且其结果并未被使用时,应该使用此变异方法。格式如下(方法名有字符串构成):
`mutable_variant=method name`
+
+比如,Swift标准库提供了变异方法`sortInPlace()`和未变异方法`sort()`集合,它们的元素生成器符合`Comparable`协议。如果你调用了`sort()`方法,而没有使用它的结果,很有可能,你打算使用变异方法`sortInPlace()`替代。
### Interface Builder使用的声明特性
@@ -137,22 +186,36 @@ Interface Builder特性是Interface Builder用来与Xcode同步的声明特性
类型特性只能用于修饰类型。然而,你也可以用`noreturn`特性去修饰函数或方法声明。
-`auto_closure`
+`convention`
-这个特性通过自动地将表达式封闭到一个无参数闭包中来延迟表达式的求值。使用该特性修饰无参的函数或方法类型,返回表达式的类型。一个如何使用`auto_closure`特性的例子,见[函数类型]()
+该特性用于函数的类型,它指出函数调用的约定。
+
+`convention`特性有下面几个可选的参数。
+
+- `swift`参数用于表明一个Swift函数引用。这是Swift中标准的函数值调用约定。
+
+- `block`参数用于表明一个Objective-C兼容的块引用。函数值表示为一个块对象的引用,这是一个`id-`兼容的Objective-C对象,对象中嵌入了调用函数。调用函数使用C的调用约定。
+
+- `c`参数用于表明一个C函数引用。函数值没有上下文,这个函数也使用C的调用约定。
+
+使用C函数调用约定的函数也可用作使用Objective-C块调用约定的函数,同时使用Objective-C块调用约定的函数也可用作使用Swift函数调用约定的函数。然而,只有非泛型的全局函数和本地函数或者不使用任何本地变量的闭包可以被用作使用C函数调用约定的函数。
`noreturn`
该特性用于修饰函数或方法的类型,表明该函数或方法不会返回到它的调用者中去。你也可以用它标记函数或方法的声明,表示函数或方法的相应类型,`T`,是`@noreturn T`。
-> 特性语法
-> *特性* → **@** [*特性名*](..\chapter3\06_Attributes.html#attribute_name) [*特性参数子句*](..\chapter3\06_Attributes.html#attribute_argument_clause) _可选_
-> *特性名* → [*标识符*](LexicalStructure.html#identifier)
-> *特性参数子句* → **(** [*平衡令牌列表*](..\chapter3\06_Attributes.html#balanced_tokens) _可选_ **)**
-> *特性(Attributes)列表* → [*特色*](..\chapter3\06_Attributes.html#attribute) [*特性(Attributes)列表*](..\chapter3\06_Attributes.html#attributes) _可选_
-> *平衡令牌列表* → [*平衡令牌*](..\chapter3\06_Attributes.html#balanced_token) [*平衡令牌列表*](..\chapter3\06_Attributes.html#balanced_tokens) _可选_
-> *平衡令牌* → **(** [*平衡令牌列表*](..\chapter3\06_Attributes.html#balanced_tokens) _可选_ **)**
-> *平衡令牌* → **[** [*平衡令牌列表*](..\chapter3\06_Attributes.html#balanced_tokens) _可选_ **]**
-> *平衡令牌* → **{** [*平衡令牌列表*](..\chapter3\06_Attributes.html#balanced_tokens) _可选_ **}**
+> 特性语法
+> *特性* → **@** [*特性名*](#attribute_name) [*特性参数子句*](#attribute_argument_clause) _可选_
+> *特性名* → [*标识符*](02_Lexical_Structure.html#identifiers)
+> *特性参数子句* → **(** [*平衡令牌列表*](#balanced_tokens) _可选_ **)**
+> *特性(Attributes)列表* → [*特色*](#attribute) [*特性(Attributes)列表*](#attributes) _可选_
+> *平衡令牌列表* → [*平衡令牌*](#balanced_token) [*平衡令牌列表*](#balanced_tokens) _可选_
+> *平衡令牌* → **(** [*平衡令牌列表*](#balanced_tokens) _可选_ **)**
+> *平衡令牌* → **[** [*平衡令牌列表*](#balanced_tokens) _可选_ **]**
+> *平衡令牌* → **{** [*平衡令牌列表*](#balanced_tokens) _可选_ **}**
> *平衡令牌* → **任意标识符, 关键字, 字面量或运算符**
> *平衡令牌* → **任意标点除了(, ), [, ], {, 或 }**
+
+
+
+
diff --git a/source/chapter3/07_Patterns.md b/source/chapter3/07_Patterns.md
index 3448a392..056cdd22 100755
--- a/source/chapter3/07_Patterns.md
+++ b/source/chapter3/07_Patterns.md
@@ -1,8 +1,12 @@
-> 翻译:[honghaoz](https://github.com/honghaoz)
+# 模式(Patterns)
+-----------------
+
+> 1.0
+> 翻译:[honghaoz](https://github.com/honghaoz)
> 校对:[numbbbbb](https://github.com/numbbbbb), [stanzhai](https://github.com/stanzhai)
-# 模式(Patterns)
------------------
+> 2.0
+> 翻译+校对:[ray16897188](https://github.com/ray16897188)
本页内容包括:
@@ -11,28 +15,32 @@
- [值绑定模式(Value-Binding Pattern)](#value-binding_pattern)
- [元组模式(Tuple Pattern)](#tuple_pattern)
- [枚举用例模式(Enumeration Case Pattern)](#enumeration_case_pattern)
-- [类型转换模式(Type-Casting Patterns)](#type-casting_patterns)
+- [可选模式(Optional Pattern)](#optional_pattern)
+- [类型转换模式(Type-Casting Pattern)](#type-casting_pattern)
- [表达式模式(Expression Pattern)](#expression_pattern)
-模式(pattern)代表了单个值或者复合值的结构。例如,元组`(1, 2)`的结构是逗号分隔的,包含两个元素的列表。因为模式代表一种值的结构,而不是特定的某个值,你可以把模式和各种同类型的值匹配起来。比如,`(x, y)`可以匹配元组`(1, 2)`,以及任何含两个元素的元组。除了将模式与一个值匹配外,你可以从合成值中提取出部分或全部,然后分别把各个部分和一个常量或变量绑定起来。
+模式(pattern)代表了单个值或者复合值的结构。例如,元组`(1, 2)`的结构是逗号分隔的,包含两个元素的列表。因为模式代表一种值的结构,而不是特定的某个值,你可以把模式和各种同类型的值匹配起来。比如,`(x, y)`可以匹配元组`(1, 2)`,以及任何含两个元素的元组。除了将模式与一个值匹配外,你可以从复合值中提取出部分或全部,然后分别把各个部分和一个常量或变量绑定起来。
-在Swift中,模式出现在变量和常量的声明(在它们的左侧),`for-in`语句和`switch`语句(在它们的case标签)中。尽管任何模式都可以出现在`switch`语句的case标签中,但在其他情况下,只有通配符模式(wildcard pattern),标识符模式(identifier pattern)和包含这两种模式的模式才能出现。
+swift语言中模式有2个基本的分类:一类能成功和任何值的类型相匹配,另一类在运行时(runtime)和某特定值匹配时可能会失败。
-你可以为通配符模式(wildcard pattern),标识符模式(identifier pattern)和元组模式(tuple pattern)指定类型注释,用来限制这种模式只匹配某种类型的值。
+第一类模式用于解构简单变量,常量和可选绑定中的值。此类模式包括通配符模式(wildcard patterns),标识符模式(identifier patterns),以及任何包含了它们的值绑定模式(value binding patterns)或者元祖模式(tuple patterns)。你可以为这类模式指定一个类型标注(type annotation)从而限制它们只能匹配某种特定类型的值。
+
+第二类模式用于全模式匹配,这种情况下你用来相比较的值在运行时可能还不存在。此类模式包括枚举用例模式(enumeration case patterns),可选模式(optional patterns),表达式模式(expression patterns)和类型转换模式(type-casting patterns)。你在`switch`语句的case标签中,`do`语句的`catch`从句中,或者在`if, while, guard`和`for-in`语句的case条件句中使用这类模式。
> 模式(Patterns) 语法
-> *模式* → [*通配符模式*](..\chapter3\07_Patterns.html#wildcard_pattern) [*类型注解*](..\chapter3\03_Types.html#type_annotation) _可选_
-> *模式* → [*标识符模式*](..\chapter3\07_Patterns.html#identifier_pattern) [*类型注解*](..\chapter3\03_Types.html#type_annotati(Value Binding)on) _可选_
-> *模式* → [*值绑定模式*](..\chapter3\07_Patterns.html#value_binding_pattern)
-> *模式* → [*元组模式*](..\chapter3\07_Patterns.html#tuple_pattern) [*类型注解*](..\chapter3\03_Types.html#type_annotation) _可选_
-> *模式* → [*enum-case-pattern*](..\chapter3\07_Patterns.html#enum_case_pattern)
-> *模式* → [*type-casting-pattern*](..\chapter3\07_Patterns.html#type_casting_pattern)
-> *模式* → [*表达式模式*](..\chapter3\07_Patterns.html#expression_pattern)
+> *模式* → [*通配符模式*](../chapter3/07_Patterns.html#wildcard_pattern) [*类型标注*](../chapter3/03_Types.html#type_annotation) _可选_
+> *模式* → [*标识符模式*](../chapter3/07_Patterns.html#identifier_pattern) [*类型标注*](../chapter3/03_Types.html#type_annotati(Value Binding)on) _可选_
+> *模式* → [*值绑定模式*](../chapter3/07_Patterns.html#value_binding_pattern)
+> *模式* → [*元组模式*](../chapter3/07_Patterns.html#tuple_pattern) [*类型标注*](../chapter3/03_Types.html#type_annotation) _可选_
+> *模式* → [*枚举用例模式*](../chapter3/07_Patterns.html#enum_case_pattern)
+> *模式* → [*可选模式*](../chapter3/07_Patterns.html#optional_pattern)
+> *模式* → [*类型转换模式*](../chapter3/07_Patterns.html#type_casting_pattern)
+> *模式* → [*表达式模式*](../chapter3/07_Patterns.html#expression_pattern)
## 通配符模式(Wildcard Pattern)
-通配符模式匹配并忽略任何值,包含一个下划线(_)。当你不关心被匹配的值时,可以使用此模式。例如,下面这段代码进行了`1...3`的循环,并忽略了每次循环的值:
+通配符模式由一个下划线(_)构成,且匹配并忽略任何值。当你不在乎被匹配的值时可以使用该模式。例如,下面这段代码在闭区间`1...3`中循环,每次循环时忽略该区间内的当前值:
```swift
for _ in 1...3 {
@@ -46,7 +54,7 @@ for _ in 1...3 {
## 标识符模式(Identifier Pattern)
-标识符模式匹配任何值,并将匹配的值和一个变量或常量绑定起来。例如,在下面的常量申明中,`someValue`是一个标识符模式,匹配了类型是`Int`的`42`。
+标识符模式匹配任何值,并将匹配的值和一个变量或常量绑定起来。例如,在下面的常量声明中,`someValue`是一个标识符模式,匹配了类型是`Int`的`42`。
```swift
let someValue = 42
@@ -54,7 +62,7 @@ let someValue = 42
当匹配成功时,`42`被绑定(赋值)给常量`someValue`。
-当一个变量或常量申明的左边是标识符模式时,此时,标识符模式是隐式的值绑定模式(value-binding pattern)。
+如果一个变量或常量声明的左边的模式是一个标识符模式,那么这个标识符模式是一个隐式的值绑定模式(value-binding pattern)。
> 标识符模式语法
> *标识符模式* → [*标识符*](LexicalStructure.html#identifier)
@@ -62,33 +70,33 @@ let someValue = 42
## 值绑定模式(Value-Binding Pattern)
-值绑定模式绑定匹配的值到一个变量或常量。当绑定匹配值给常量时,用关键字`let`,绑定给变量时,用关键字`var`。
+值绑定模式把匹配到的值绑定给一个变量或常量名。把绑定匹配到的值绑定给常量时,用关键字`let`,绑定给变量时,用关键字`var`。
-标识符模式包含在值绑定模式中,绑定新的变量或常量到匹配的值。例如,你可以分解一个元组的元素,并把每个元素绑定到相应的标识符模式中。
+在值绑定模式中的标识符模式会把新命名的变量或常量与匹配值做绑定。例如,你可以拆开一个元组的元素,然后把每个元素绑定到其相应一个的标识符模式中。
```swift
let point = (3, 2)
switch point {
// Bind x and y to the elements of point.
case let (x, y):
- println("The point is at (\(x), \(y)).")
+ print("The point is at (\(x), \(y)).")
}
// prints "The point is at (3, 2).”
```
-在上面这个例子中,`let`将元组模式`(x, y)`分配到各个标识符模式。因为这种行为,`switch`语句中`case let (x, y):`和`case (let x, let y):`匹配的值是一样的。
+在上面这个例子中,`let`将元组模式`(x, y)`分配到各个标识符模式。正是由于这么做,`switch`语句中`case let (x, y):`和`case (let x, let y):`匹配到的值是一样的。
> 值绑定(Value Binding)模式语法
-> *值绑定模式* → **var** [*模式*](..\chapter3\07_Patterns.html#pattern) | **let** [*模式*](..\chapter3\07_Patterns.html#pattern)
+> *值绑定模式* → **var** [*模式*](../chapter3/07_Patterns.html#pattern) | **let** [*模式*](../chapter3/07_Patterns.html#pattern)
## 元组模式(Tuple Pattern)
-元组模式是逗号分隔的列表,包含一个或多个模式,并包含在一对圆括号中。元组模式匹配相应元组类型的值。
+元组模式是逗号分隔的,有零个或多个模式的列表,并被一对圆括号括起来。元组模式匹配相应元组类型的值。
-你可以使用类型注释来限制一个元组模式来匹配某种元组类型。例如,在常量申明`let (x, y): (Int, Int) = (1, 2)`中的元组模式`(x, y): (Int, Int)`,只匹配两个元素都是`Int`这种类型的元组。如果仅需要限制一个元组模式中的某几个元素,只需要直接对这几个元素提供类型注释即可。例如,在`let (x: String, y)`中的元组模式,只要某个元组类型是包含两个元素,且第一个元素类型是`String`,则被匹配。
+你可以使用类型标注去限制一个元组模式能匹配哪些种元组类型。例如,在常量声明`let (x, y): (Int, Int) = (1, 2)`中的元组模式`(x, y): (Int, Int)`只匹配两个元素都是`Int`这种类型的元组。如果仅需要限制一个元组模式中的某几个元素,只需要直接对这几个元素提供类型标注即可。例如,在`let (x: String, y)`中的元组模式可以和任何有两个元素,且第一个元素类型是`String`的元组类型匹配。
-当元组模式被用在`for-in`语句或者变量或常量申明时,它可以包含通配符模式,标识符模式或者其他包含这两种模式的模式。例如,下面这段代码是不正确的,因为`(x, 0)`中的元素`0`是一个表达式模式:
+当元组模式被用在`for-in`语句或者变量或常量声明时,它仅可以包含通配符模式,标识符模式,可选模式或者其他包含这些模式的元祖模式。比如下面这段代码就不正确,因为`(x, 0)`中的元素`0`是一个表达式模式:
```swift
let points = [(0, 0), (1, 0), (1, 1), (2, 0), (2, 1)]
@@ -98,7 +106,7 @@ for (x, 0) in points {
}
```
-对于只包含一个元素的元组,括号是不起作用的。模式匹配那个单个元素的类型。例如,下面是等效的:
+对于只包含一个元素的元组,括号是不起作用的。模式只匹配这个单个元素的类型。举例来说,下面3条语句是等效的:
```swift
let a = 2 // a: Int = 2
@@ -107,60 +115,95 @@ let (a): Int = 2 // a: Int = 2
```
> 元组模式语法
-> *元组模式* → **(** [*元组模式元素列表*](..\chapter3\07_Patterns.html#tuple_pattern_element_list) _可选_ **)**
-> *元组模式元素列表* → [*元组模式元素*](..\chapter3\07_Patterns.html#tuple_pattern_element) | [*元组模式元素*](..\chapter3\07_Patterns.html#tuple_pattern_element) **,** [*元组模式元素列表*](..\chapter3\07_Patterns.html#tuple_pattern_element_list)
-> *元组模式元素* → [*模式*](..\chapter3\07_Patterns.html#pattern)
+> *元组模式* → **(** [*元组模式元素列表*](../chapter3/07_Patterns.html#tuple_pattern_element_list) _可选_ **)**
+> *元组模式元素列表* → [*元组模式元素*](../chapter3/07_Patterns.html#tuple_pattern_element) | [*元组模式元素*](../chapter3/07_Patterns.html#tuple_pattern_element) **,** [*元组模式元素列表*](../chapter3/07_Patterns.html#tuple_pattern_element_list)
+> *元组模式元素* → [*模式*](../chapter3/07_Patterns.html#pattern)
## 枚举用例模式(Enumeration Case Pattern)
-枚举用例模式匹配现有的枚举类型的某种用例。枚举用例模式仅在`switch`语句中的`case`标签中出现。
+一个枚举用例模式匹配现有的某个枚举类型的某个用例(case)。枚举用例模式出现在`switch`语句中的case标签中,以及`if`,`while`,`guard`和`for-in`语句的case条件中。
如果你准备匹配的枚举用例有任何关联的值,则相应的枚举用例模式必须指定一个包含每个关联值元素的元组模式。关于使用`switch`语句来匹配包含关联值枚举用例的例子,请参阅`Associated Values`.
> 枚举用例模式语法
-> *enum-case-pattern* → [*类型标识*](..\chapter3\03_Types.html#type_identifier) _可选_ **.** [*枚举的case名*](..\chapter3\05_Declarations.html#enum_case_name) [*元组模式*](..\chapter3\07_Patterns.html#tuple_pattern) _可选_
+> *enum-case-pattern* → [*类型标识*](../chapter3/03_Types.html#type_identifier) _可选_ **.** [*枚举的case名*](../chapter3/05_Declarations.html#enum_case_name) [*元组模式*](../chapter3/07_Patterns.html#tuple_pattern) _可选_
+
+
+## 可选模式(Optional Pattern)
+
+可选模式与封装在一个`Optional(T)`或者一个`ExplicitlyUnwrappedOptional(T)`枚举中的`Some(T)`用例相匹配。可选模式由一个标识符模式和紧随其后的一个问号组成,在某些情况下表现为枚举用例模式。
+
+由于可选模式是`optional`和`ImplicitlyUnwrappedOptional`枚举用例模式的语法糖(syntactic sugar),下面的2种写法是一样的:
+
+```swift
+let someOptional: Int? = 42
+// Match using an enumeration case pattern
+if case .Some(let x) = someOptional {
+ print(x)
+}
+
+// Match using an optional pattern
+if case let x? = someOptional {
+ print(x)
+}
+```
+如果一个数组的元素是可选类型,可选模式为`for-in`语句提供了一种在该数组中迭代的简便方式,只为数组中的非空`non-nil`元素执行循环体。
+
+```swift
+let arrayOfOptionalInts: [Int?] = [nil, 2, 3, nil, 5]
+// Match only non-nil values
+for case let number? in arrayOfOptinalInts {
+ print("Found a \(number)")
+}
+//Found a 2
+//Found a 3
+//Found a 5
+
+```
+> 可选模式语法
+> *可选模式* → [*标识符模式*](../chapter3/03_Types.html#type_identifier) ?
## 类型转换模式(Type-Casting Patterns)
-有两种类型转换模式,`is`模式和`as`模式。这两种模式均只出现在`switch`语句中的`case`标签中。`is`模式和`as`模式有以下形式:
+有两种类型转换模式,`is`模式和`as`模式。这两种模式只出现在`switch`语句中的case标签中。`is`模式和`as`模式有以下形式:
> is `type`
> `pattern` as `type`
-`is`模式匹配一个值,如果这个值的类型在运行时(runtime)和`is`模式右边的指定类型(或者那个类型的子类)是一致的。`is`模式和`is`操作符一样,它们都进行类型转换,但是抛弃了返回的类型。
+`is`模式仅当一个值的类型在运行时(runtime)和`is`模式右边的指定类型一致 - 或者是该类型的子类 - 的情况下,才会匹配这个值。`is`模式和`is`操作符有相似表现,它们都进行类型转换,却舍弃返回的类型。
-`as`模式匹配一个值,如果这个值的类型在运行时(runtime)和`as`模式右边的指定类型(或者那个类型的子类)是一致的。一旦匹配成功,匹配的值的类型被转换成`as`模式左边指定的模式。
+`as`模式仅当一个值的类型在运行时(runtime)和`as`模式右边的指定类型一致 - 或者是该类型的子类 - 的情况下,才会匹配这个值。如果匹配成功,被匹配的值的类型被转换成`as`模式左边指定的模式。
关于使用`switch`语句来匹配`is`模式和`as`模式值的例子,请参阅`Type Casting for Any and AnyObject`。
> 类型转换模式语法
-> *type-casting-pattern* → [*is模式*](..\chapter3\07_Patterns.html#is_pattern) | [*as模式*](..\chapter3\07_Patterns.html#as_pattern)
-> *is模式* → **is** [*类型*](..\chapter3\03_Types.html#type)
-> *as模式* → [*模式*](..\chapter3\07_Patterns.html#pattern) **as** [*类型*](..\chapter3\03_Types.html#type)
+> *type-casting-pattern* → [*is模式*](../chapter3/07_Patterns.html#is_pattern) | [*as模式*](../chapter3/07_Patterns.html#as_pattern)
+> *is模式* → **is** [*类型*](../chapter3/03_Types.html#type)
+> *as模式* → [*模式*](../chapter3/07_Patterns.html#pattern) **as** [*类型*](../chapter3/03_Types.html#type)
## 表达式模式(Expression Pattern)
-表达式模式代表了一个表达式的值。这个模式只出现在`switch`语句中的`case`标签中。
+一个表达式模式代表了一个表达式的值。表达式模式只出现在`switch`语句中的`case`标签中。
-由表达式模式所代表的表达式用Swift标准库中的`~=`操作符与输入表达式的值进行比较。如果`~=`操作符返回`true`,则匹配成功。默认情况下,`~=`操作符使用`==`操作符来比较两个相同类型的值。它也可以匹配一个整数值与一个`Range`对象中的整数范围,正如下面这个例子所示:
+由表达式模式所代表的表达式与使用了Swift标准库中`~=`操作符的输入表达式的值进行比较。如果`~=`操作符返回`true`,则匹配成功。默认情况下,`~=`操作符使用`==`操作符来比较两个相同类型的值。它也可以将一个整型数值与一个`Range`对象中的一段整数区间做匹配,正如下面这个例子所示:
```swift
let point = (1, 2)
switch point {
case (0, 0):
- println("(0, 0) is at the origin.")
+ print("(0, 0) is at the origin.")
case (-2...2, -2...2):
- println("(\(point.0), \(point.1)) is near the origin.")
+ print("(\(point.0), \(point.1)) is near the origin.")
default:
- println("The point is at (\(point.0), \(point.1)).")
+ print("The point is at (\(point.0), \(point.1)).")
}
// prints "(1, 2) is near the origin.”
```
-你可以重载`~=`操作符来提供自定义的表达式行为。例如,你可以重写上面的例子,以实现用字符串表达的点来比较`point`表达式。
+你可以重载`~=`操作符来提供自定义的表达式匹配行为。比如你可以重写上面的例子,拿`point`表达式去比较字符串形式的点。
```swift
// Overload the ~= operator to match a string with an integer
@@ -169,14 +212,12 @@ func ~=(pattern: String, value: Int) -> Bool {
}
switch point {
case ("0", "0"):
- println("(0, 0) is at the origin.")
-case ("-2...2", "-2...2"):
- println("(\(point.0), \(point.1)) is near the origin.")
+ print("(0, 0) is at the origin.")
default:
- println("The point is at (\(point.0), \(point.1)).")
+ print("The point is at (\(point.0), \(point.1)).")
}
// prints "(1, 2) is near the origin.”
```
> 表达式模式语法
-> *表达式模式* → [*表达式*](..\chapter3\04_Expressions.html#expression)
\ No newline at end of file
+> *表达式模式* → [*表达式*](../chapter3/04_Expressions.html#expression)
diff --git a/source/chapter3/08_Generic_Parameters_and_Arguments.md b/source/chapter3/08_Generic_Parameters_and_Arguments.md
index 06a078b0..fc4657f8 100755
--- a/source/chapter3/08_Generic_Parameters_and_Arguments.md
+++ b/source/chapter3/08_Generic_Parameters_and_Arguments.md
@@ -1,36 +1,41 @@
-> 翻译:[fd5788](https://github.com/fd5788)
+# 泛型参数(Generic Parameters and Arguments)
+---------
+
+> 1.0
+> 翻译:[fd5788](https://github.com/fd5788)
> 校对:[yankuangshi](https://github.com/yankuangshi), [stanzhai](https://github.com/stanzhai)
-# 泛型参数
----------
+> 2.0
+> 翻译+校对:[wardenNScaiyi](https:github.com/wardenNScaiyi)
本页包含内容:
- [泛型形参子句](#generic_parameter)
- [泛型实参子句](#generic_argument)
-本节涉及泛型类型、泛型函数以及泛型构造器的参数,包括形参和实参。声明泛型类型、函数或构造器时,须指定相应的类型参数。类型参数相当于一个占位符,当实例化泛型类型、调用泛型函数或泛型构造器时,就用具体的类型实参替代之。
+本节涉及泛型类型、泛型函数以及泛型初始化器(**initializer**)的参数,包括形参和实参。声明泛型类型、函数或初始化器时,须指定相应的类型参数。类型参数相当于一个占位符,当实例化泛型类型、调用泛型函数或泛型初始化器时,就用具体的类型实参替代之。
-关于 Swift 语言的泛型概述,见[泛型](../charpter2/22_Generics.md)(第二部分第22章)。
+关于 Swift 语言的泛型概述,见[泛型](../chapter2/23_Generics.md)(第二部分第23章)。
## 泛型形参子句
-泛型形参子句指定泛型类型或函数的类型形参,以及这些参数的关联约束和要求。泛型形参子句用尖括号(<>)包住,并且有以下两种形式:
+泛型形参子句指定泛型类型或函数的类型形参,以及这些参数的关联约束和关联类型要求(**requirement**)。泛型形参子句用尖括号(<>)包住,并且有以下两种形式:
-> <`generic parameter list`>
-> <`generic parameter list` where `requirements`>
+> <`泛型形参列表`>
+> <`泛型形参列表` where `关联类型要求`>
-泛型形参列表中泛型形参用逗号分开,每一个采用以下形式:
+泛型形参列表中泛型形参用逗号分开,其中每一个采用以下形式:
-> `type parameter` : `constrain`
+> `类型形参` : `约束`
-泛型形参由两部分组成:类型形参及其后的可选约束。类型形参只是占位符类型(如T,U,V,KeyType,ValueType等)的名字而已。你可以在泛型类型、函数的其余部分或者构造器声明,以及函数或构造器的签名中使用它。
+泛型形参由两部分组成:类型形参及其后的可选约束。类型形参只是占位符类型(如 T,U,V,Key,Value 等)的名字而已。你可以在泛型类型、函数的其余部分或者初始化器声明,包括函数或初始化器的签名中使用它(与其任何相关类型)。
+
+约束用于指明该类型形参继承自某个类或者遵守某个协议或协议的一部分。例如,在下面的泛型函数中,泛型形参`T: Comparable`表示任何用于替代类型形参`T`的类型实参必须满足`Comparable`协议。
-约束用于指明该类型形参继承自某个类或者遵守某个协议或协议的一部分。例如,在下面的泛型中,泛型形参`T: Comparable`表示任何用于替代类型形参`T`的类型实参必须满足`Comparable`协议。
```swift
-func simpleMin