Merge branch 'numbbbbb/gh-pages' into develop

This commit is contained in:
Channe
2015-08-28 20:56:12 +08:00
60 changed files with 5849 additions and 4274 deletions

View File

@ -3,26 +3,59 @@
中文版Apple官方Swift教程《The Swift Programming Language》 中文版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/)。 使用Gitbook制作可以直接[在线阅读](http://swiftguide.cn/)。
# 电子书下载
CocoaChina精校PDF→[点我下载](http://vdisk.weibo.com/s/EhsPPzRRQ5CZ/1402621206)
其他格式可以通过PDF转换
# 当前阶段 # 当前阶段
文章已经全部翻译完成当前阶段为自由校对阶段可以随意提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
* 关于 Swift ([numbbbbb]) * 关于 Swift ([numbbbbb])
@ -171,4 +204,4 @@ CocoaChina精校PDF→[点我下载](http://vdisk.weibo.com/s/EhsPPzRRQ5CZ/14026
[Lin-H]:https://github.com/Lin-H [Lin-H]:https://github.com/Lin-H
[takalard]:https://github.com/takalard [takalard]:https://github.com/takalard
[dabing1022]:https://github.com/dabing1022 [dabing1022]:https://github.com/dabing1022
[marsprince]:https://github.com/marsprince [marsprince]:https://github.com/marsprince

9
config.json Normal file
View File

@ -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"
}
}

BIN
cover/background.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
cover/cover.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
cover/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -3,7 +3,7 @@
<head> <head>
<meta http-equiv="refresh" content="0; url=http://numbbbbb.gitbooks.io/-the-swift-programming-language-/content/" /> <meta http-equiv="refresh" content="0; url=http://wiki.jikexueyuan.com/project/swift/" />

View File

@ -1,89 +1,78 @@
> Swift 兴趣交流群:`305014012`307017261已满 # 2.0 新的开始
> [Swift 开发者社区](http://swiftist.org)
> Swift 兴趣交流群:`131595168`, `146932759`, `151336833`, `153549217`. **加入一个群即可,请勿重复添加**
> <a target='_blank' href="http://swiftist.org">Swift 开发者社区</a>
> <a target='_blank' href="https://github.com/ipader/SwiftGuide">Swift 资源汇总</a>
> <a target='_blank' href="http://swiftsandbox.io">Swift 优秀newsletter</a>
<!-- --> <!-- -->
> 如果觉得这个项目不错,请[点击Star一下](https://github.com/numbbbbb/the-swift-programming-language-in-chinese),您的支持是我们最大的动力。 > 如果觉得这个项目不错,请<a target='_blank' href="https://github.com/numbbbbb/the-swift-programming-language-in-chinese">点击Star一下</a>,您的支持是我们最大的动力。
<!-- --> ## 1
> 关于文档中翻译错误,逻辑错误以及疑难问题答疑,请关注["@老码团队"](http://weibo.com/u/5241713117
)官方微博,会有技术人员统一收集答疑
开源项目完成难,维护更难。
# The Swift Programming Language 中文版#### 大家看到的是发布时的瞩目和荣耀,却没有看到项目本身质量不高、错误频出。这并不是翻译者和校对者的问题,他们已经付出了足够的努力。真正的问题在我,没有建立起长期的维护团队,因此后期的校对和更新都难以实施。
###这一次,让中国和世界同步 1.0发布之后我们就再也没能和苹果的文档同步。语法错误、编译不通过、语言不通顺阅读量直线下降最低时每天只有不到1000人访问。
现在是6月12日凌晨4:38我用了整整一晚上的时间来进行最后的校对终于可以在12日拿出一个可以发布的版本 6月9日calvingit发了一个issue“准备翻译2.0版本吗”,我没有回复,应该已经没人关注这个项目了吧,我想
9天时间1317个 Star310个 Fork超过30人参与翻译和校对工作项目最高排名GitHub总榜第4。 ## 2
设想过很多遍校对完成时的场景,仰天大笑还是泪流满面?真正到了这一刻才发现,疲倦已经不允许我有任何情绪 我错了
说实话,刚开始发起项目的时候完全没想到会发展成今天这样,我一度计划自己一个人翻译完整本书。万万没想到,会有这么多的人愿意加入并贡献出自己的力量。 ![](https://raw.githubusercontent.com/numbbbbb/the-swift-programming-language-in-chinese/gh-pages/swift%202.0%E5%BC%95%E7%94%A8%E5%9B%BE%E7%89%87/1.png)
coverxit发给我最后一份文档的时候说我要去背单词了我问他周末要考六级他说是的。 ![](https://raw.githubusercontent.com/numbbbbb/the-swift-programming-language-in-chinese/gh-pages/swift%202.0%E5%BC%95%E7%94%A8%E5%9B%BE%E7%89%87/2.png)
pp-prog告诉我这几天太累了校对到一半睡着了醒来又继续做。2点17分发给我校对完成的文档。 ![](https://raw.githubusercontent.com/numbbbbb/the-swift-programming-language-in-chinese/gh-pages/swift%202.0%E5%BC%95%E7%94%A8%E5%9B%BE%E7%89%87/3.png)
lifedim说他平时12点就会睡1点47分发给我校对后的文档。 ![](https://raw.githubusercontent.com/numbbbbb/the-swift-programming-language-in-chinese/gh-pages/swift%202.0%E5%BC%95%E7%94%A8%E5%9B%BE%E7%89%87/4.png)
团队里每个人都有自己的事情上班、上学、创业但是我们只用了9天就完成整本书的翻译。我不知道大家付出了多少牺牲了多少但是我知道他们的付出必将被这些文字记录下来即使再过10年20年依然熠熠生辉永不被人遗忘。 ![](https://raw.githubusercontent.com/numbbbbb/the-swift-programming-language-in-chinese/gh-pages/swift%202.0%E5%BC%95%E7%94%A8%E5%9B%BE%E7%89%87/5.png)
全体人员名单(排名不分先后): 在我没有任何回复的情况下,不到一天时间,有五位朋友报名。看到这些回复的时候我真的很惊讶,也很感动,无论这个项目存在多少问题,只要有人关注,有人愿意为它付出,那我还有什么理由放弃呢?
- [numbbbbb](https://github.com/numbbbbb) 6月28日8点55分Swift 2.0翻译正式启动。按下发送按钮后我不停的刷新页面半个小时过去了一个回复都没有。“还是不行啊”“如果再过一个小时没人回复我就把issue删掉”类似的念头不断出现又不断消失。
- [stanzhai](https://github.com/stanzhai)
- [coverxit](https://github.com/coverxit) 9:35xtymichael第一个回复而且一下就认领了三篇接下来就是不断的回复认领到中午已经有超过一半章节被认领。
- [wh1100717](https://github.com/wh1100717)
- [TimothyYe](https://github.com/TimothyYe) 第二天早晨37个章节全部认领完毕。
- [honghaoz](https://github.com/honghaoz)
- [lyuka](https://github.com/lyuka) ## 3
- [JaySurplus](https://github.com/JaySurplus)
- [Hawstein](https://github.com/Hawstein) 经过一个多月的努力我们终于完成了文档的更新。听起来似乎没什么确实从1到n总是没有从0到1那么振奋人心。不过真正参与了才知道修改往往比创造更麻烦一个需要耐心一个需要激情前者往往得不到应有的重视。
- [geek5nan](https://github.com/geek5nan)
- [yankuangshi](https://github.com/yankuangshi) 但是我还是想尽最大可能去感谢他们这个项目能走到今天靠的不是我是那个issue是那些回复是这几十个兄弟在工作学习的空闲敲下的每一个字符。而我能做的只是在每篇文章的开头那个所有人都会忽略的地方加上他们的ID。
- [xielingwang](https://github.com/xielingwang)
- [yulingtianxia](https://github.com/yulingtianxia) 下次你再打开这篇文档可以多看看那些列在最上方的ID哪怕不去follow和star只是看一眼就好。他们的所有努力和付出就存在于这短暂的一瞥中。
- [twlkyao](https://github.com/twlkyao)
- [dabing1022](https://github.com/dabing1022) Swift 2.0 参与者名单(按照章节顺序):
- [vclwei](https://github.com/vclwei) - [xtymichael](https://github.com/xtymichael)
- [fd5788](https://github.com/fd5788) - [AlanMelody](https://github.com/AlanMelody)
- [siemenliu](https://github.com/siemenliu) - [DianQK](https://github.com/DianQK)
- [youkugems](https://github.com/youkugems) - [dreamkidd](https://github.com/dreamkidd)
- [haolloyin](https://github.com/haolloyin) - [100mango](https://github.com/100mango)
- [wxstars](https://github.com/wxstars) - [futantan](https://github.com/futantan)
- [IceskYsl](https://github.com/IceskYsl) - [SkyJean](https://github.com/SkyJean)
- [sg552](https://github.com/sg552) - [yangsiy](https://github.com/yangsiy)
- [superkam](https://github.com/superkam) - [shanksyang](https://github.com/shanksyang)
- [zac1st1k](https://github.com/zac1st1k) - [chenmingbiao](https://github.com/chenmingbiao)
- [bzsy](https://github.com/bzsy) - [Channe](https://github.com/Channe)
- [pyanfield](https://github.com/pyanfield) - [lyojo](https://github.com/lyojo)
- [ericzyh](https://github.com/ericzyh) - [SergioChan](https://github.com/SergioChan)
- [peiyucn](https://github.com/peiyucn) - [mmoaay](https://github.com/mmoaay)
- [sunfiled](https://github.com/sunfiled) - [buginux](https://github.com/buginux)
- [lzw120](https://github.com/lzw120) - [KYawn](https://github.com/KYawn)
- [viztor](https://github.com/viztor) - [EudeMorgen](https://github.com/EudeMorgen)
- [wongzigii](https://github.com/wongzigii) - [littledogboy](https://github.com/littledogboy)
- [umcsdon](https://github.com/umcsdon) - [Lenhoon](https://github.com/Lenhoon)
- [zq54zquan](https://github.com/zq54zquan) - [ray16897188](https://github.com/ray16897188)
- [xiehurricane](https://github.com/xiehurricane) - [wardenNScaiyi](https://github.com/wardenNScaiyi)
- [Jasonbroker](https://github.com/Jasonbroker) - [miaosiqi](https://github.com/miaosiqi)
- [tualatrix](https://github.com/tualatrix)
- [pp-prog](https://github.com/pp-prog) 最后,感谢<a target='_blank' href="http://wiki.jikexueyuan.com/">极客学院</a>提供的wiki系统在国内访问起来速度很快优化后的样式看起来也更舒服。
- [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)

View File

@ -4,6 +4,7 @@
* [关于 Swift](chapter1/01_swift.md) * [关于 Swift](chapter1/01_swift.md)
* [Swift 初见](chapter1/02_a_swift_tour.md) * [Swift 初见](chapter1/02_a_swift_tour.md)
* [Swift 版本历史记录](chapter1/03_revision_history.md) * [Swift 版本历史记录](chapter1/03_revision_history.md)
* [Swift 1.0 发布内容](v1.0.md)
* [Swift 教程](chapter2/chapter2.md) * [Swift 教程](chapter2/chapter2.md)
* [基础部分](chapter2/01_The_Basics.md) * [基础部分](chapter2/01_The_Basics.md)
* [基本运算符](chapter2/02_Basic_Operators.md) * [基本运算符](chapter2/02_Basic_Operators.md)
@ -30,7 +31,7 @@
* [泛型](chapter2/23_Generics.md) * [泛型](chapter2/23_Generics.md)
* [权限控制](chapter2/24_Access_Control.md) * [权限控制](chapter2/24_Access_Control.md)
* [高级操作符](chapter2/25_Advanced_Operators.md) * [高级操作符](chapter2/25_Advanced_Operators.md)
* [语言参考](chapter3/chapter3.md) * 语言参考
* [关于语言参考](chapter3/01_About_the_Language_Reference.md) * [关于语言参考](chapter3/01_About_the_Language_Reference.md)
* [词法结构](chapter3/02_Lexical_Structure.md) * [词法结构](chapter3/02_Lexical_Structure.md)
* [类型](chapter3/03_Types.md) * [类型](chapter3/03_Types.md)
@ -41,7 +42,7 @@
* [模式](chapter3/07_Patterns.md) * [模式](chapter3/07_Patterns.md)
* [泛型参数](chapter3/08_Generic_Parameters_and_Arguments.md) * [泛型参数](chapter3/08_Generic_Parameters_and_Arguments.md)
* [语法总结](chapter3/09_Summary_of_the_Grammar.md) * [语法总结](chapter3/09_Summary_of_the_Grammar.md)
* [苹果官方Blog官方翻译](chapter4/chapter4.md) * 苹果官方Blog官方翻译
* [Access Control 权限控制的黑与白](chapter4/01_Access_Control.md) * [Access Control 权限控制的黑与白](chapter4/01_Access_Control.md)
* [造个类型不是梦-白话Swift类型创建](chapter4/02_Type_Custom.md) * [造个类型不是梦-白话Swift类型创建](chapter4/02_Type_Custom.md)
* [WWDC里面的那个“大炮打气球”](chapter4/03_Ballons.md) * [WWDC里面的那个“大炮打气球”](chapter4/03_Ballons.md)

View File

@ -1,16 +1,21 @@
# 关于 SwiftAbout Swift
-----------------
> 1.0
> 翻译:[numbbbbb](https://github.com/numbbbbb) > 翻译:[numbbbbb](https://github.com/numbbbbb)
> 校对:[yeahdongcn](https://github.com/yeahdongcn) > 校对:[yeahdongcn](https://github.com/yeahdongcn)
# 关于 Swift > 2.0
----------------- > 翻译+校对:[xtymichael](https://github.com/xtymichael)
Swift 是一种新的编程语言,用于编写 iOSOS X 和 watchOS应用程序。Swift 结合了 C 和 Objective-C 的优点并且不受 C 兼容性的限制。Swift 采用安全的编程模式并添加了很多新特性这将使编程更简单更灵活也更有趣。Swift 是基于成熟而且倍受喜爱的 Cocoa 和 Cocoa Touch 框架,它的降临将重新定义软件开发。 Swift 是一种新的编程语言,用于编写 iOSOS 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 还有许多新特性并且支持过程式编程和面向对象编程。 Objective-C 开发者对 Swift 并不会感到陌生。它采用了 Objective-C 的命名参数以及动态对象模型,可以无缝对接到现有的 Cocoa 框架,并且可以兼容 Objective-C 代码。在此基础之上Swift 还有许多新特性并且支持过程式编程和面向对象编程。
Swift 对于初学者来说也很友好。它是第一个既满足工业标准又像脚本语言一样充满表现力和趣味的编程语言。它支持代码预览,这个革命性的特性可以允许程序员在不编译和运行应用程序的前提下运行 Swift 代码并实时查看结果。 Swift 对于初学者来说也很友好。它是第一个既满足工业标准又像脚本语言一样充满表现力和趣味的脚本语言。它支持代码预览,这个革命性的特性可以允许程序员在不编译和运行应用程序的前提下运行 Swift 代码并实时查看结果。
Swift 将现代编程语言的精华和苹果工程师文化的智慧结合了起来。编译器对性能进行了优化编程语言对开发进行了优化两者互不干扰鱼与熊掌兼得。Swift 既可以用于开发 “hello, world” 这样的小程序,也可以用于开发一套完整的操作系统。所有的这些特性让 Swift 对于开发者和苹果来说都是一项值得的投资。 Swift 将现代编程语言的精华和苹果工程师文化的智慧结合了起来。编译器对性能进行了优化编程语言对开发进行了优化两者互不干扰鱼与熊掌兼得。Swift 既可以用于开发 “hello, world” 这样的小程序,也可以用于开发一套完整的操作系统。所有的这些特性让 Swift 对于开发者和苹果来说都是一项值得的投资。

View File

@ -1,9 +1,13 @@
# Swift 初见A Swift Tour
---
> 1.0
> 翻译:[numbbbbb](https://github.com/numbbbbb) > 翻译:[numbbbbb](https://github.com/numbbbbb)
> 校对:[shinyzhu](https://github.com/shinyzhu), [stanzhai](https://github.com/stanzhai) > 校对:[shinyzhu](https://github.com/shinyzhu), [stanzhai](https://github.com/stanzhai)
# Swift 初见 > 2.0
> 翻译+校对:[xtymichael](https://github.com/xtymichael)
---
本页内容包括: 本页内容包括:
@ -27,7 +31,7 @@ print("Hello, world")
> 注意: > 注意:
> 为了获得最好的体验,在 Xcode 当中使用代码预览功能。代码预览功能可以让你编辑代码并实时看到运行结果。 > 为了获得最好的体验,在 Xcode 当中使用代码预览功能。代码预览功能可以让你编辑代码并实时看到运行结果。
> <a href="https://github.com/numbbbbb/the-swift-programming-language-in-chinese/raw/gh-pages/source/chapter1/GuidedTour.playground.zip">打开Playground</a> > <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/GuidedTour.playground.zip">下载Playground</a>
<a name="simple_values"></a> <a name="simple_values"></a>
## 简单值 ## 简单值
@ -40,7 +44,7 @@ myVariable = 50
let myConstant = 42 let myConstant = 42
``` ```
常量或者变量的类型必须和你赋给它们的值一样。然而,声明类型是可选的,声明的同时赋值的话,编译器会自动推断类型。在上面的例子中,编译器推断出`myVariable`是一个整数integer因为它的初始值是整数。 常量或者变量的类型必须和你赋给它们的值一样。然而,你不用明确地声明类型,声明的同时赋值的话,编译器会自动推断类型。在上面的例子中,编译器推断出`myVariable`是一个整数integer因为它的初始值是整数。
如果初始值没有提供足够的信息(或者没有初始值),那你需要在变量后面声明类型,用冒号分割。 如果初始值没有提供足够的信息(或者没有初始值),那你需要在变量后面声明类型,用冒号分割。
@ -107,7 +111,7 @@ occupations = [:]
<a name="control_flow"></a> <a name="control_flow"></a>
## 控制流 ## 控制流
使用`if``switch`来进行条件操作,使用`for-in``for``while``do-while`来进行循环。包裹条件和循环变量括号可以省略,但是语句体的大括号是必须的。 使用`if``switch`来进行条件操作,使用`for-in``for``while``repeat-while`来进行循环。包裹条件和循环变量括号可以省略,但是语句体的大括号是必须的。
```swift ```swift
let individualScores = [75, 43, 103, 87, 12] let individualScores = [75, 43, 103, 87, 12]
@ -124,7 +128,7 @@ print(teamScore)
`if`语句中,条件必须是一个布尔表达式——这意味着像`if score { ... }`这样的代码将报错,而不会隐形地与 0 做对比。 `if`语句中,条件必须是一个布尔表达式——这意味着像`if score { ... }`这样的代码将报错,而不会隐形地与 0 做对比。
你可以一起使用`if``let`来处理值缺失的情况。有些变量的值是可选的。一个可选的值可能是一个具体的值或者是`nil`表示值缺失。在类型后面加一个问号来标记这个变量的值是可选的。 你可以一起使用`if``let`来处理值缺失的情况。这些值可由可选值来代表。一个可选的值是一个具体的值或者是`nil`表示值缺失。在类型后面加一个问号来标记这个变量的值是可选的。
```swift ```swift
var optionalString: String? = "Hello" var optionalString: String? = "Hello"
@ -161,7 +165,7 @@ default:
> 练习: > 练习:
> 删除`default`语句,看看会有什么错误? > 删除`default`语句,看看会有什么错误?
声明'let'可用于匹配某部分固定值的模式 声明`let`可用于匹配某部分固定值的模式
运行`switch`中匹配到的子句之后,程序会退出`switch`语句,并不会继续向下运行,所以不需要在每个子句结尾写`break` 运行`switch`中匹配到的子句之后,程序会退出`switch`语句,并不会继续向下运行,所以不需要在每个子句结尾写`break`
@ -185,9 +189,9 @@ print(largest)
``` ```
> 练习: > 练习:
> 添加另一个变量来记录哪种类型的数字是最大的 > 添加另一个变量来记录现在和之前最大数字的类型
使用`while`来重复运行一段代码直到不满足条件。循环条件可以在开头也可以在结尾。 使用`while`来重复运行一段代码直到不满足条件。循环条件也可以在结尾,保证能至少循环一次
```swift ```swift
var n = 2 var n = 2
@ -197,7 +201,7 @@ while n < 100 {
print(n) print(n)
var m = 2 var m = 2
do { repeat {
m = m * 2 m = m * 2
} while m < 100 } while m < 100
print(m) print(m)
@ -224,7 +228,7 @@ print(secondForLoop)
<a name="functions_and_closures"></a> <a name="functions_and_closures"></a>
## 函数和闭包 ## 函数和闭包
使用`func`来声明一个函数,使用名字和参数来调用函数。使用`->`来指定函数返回值。 使用`func`来声明一个函数,使用名字和参数来调用函数。使用`->`来指定函数返回值的类型
```swift ```swift
func greet(name: String, day: String) -> String { func greet(name: String, day: String) -> String {
@ -322,7 +326,7 @@ var numbers = [20, 19, 7, 12]
hasAnyMatches(numbers, lessThanTen) hasAnyMatches(numbers, lessThanTen)
``` ```
函数实际上是一种特殊的闭包你可以使用`{}`来创建一个匿名闭包。使用`in`将参数和返回值类型声明与闭包函数体进行分离。 函数实际上是一种特殊的闭包:它是一段能之后被调取的代码。闭包中的代码能访问闭包所建作用域中能得到的变量和函数,即使闭包是在一个不同的作用域被执行的 - 你已经在嵌套函数例子中所看到。你可以使用`{}`来创建一个匿名闭包。使用`in`将参数和返回值类型声明与闭包函数体进行分离。
```swift ```swift
numbers.map({ numbers.map({
@ -335,14 +339,14 @@ numbers.map({
> 练习: > 练习:
> 重写闭包对所有奇数返回0。 > 重写闭包对所有奇数返回0。
有很多种创建闭包的方法。如果一个闭包的类型已知,比如作为一个回调函数,你可以忽略参数的类型和返回值。单个语句闭包会把它语句的值当做结果返回。 有很多种创建更简洁的闭包的方法。如果一个闭包的类型已知,比如作为一个回调函数,你可以忽略参数的类型和返回值。单个语句闭包会把它语句的值当做结果返回。
```swift ```swift
let mappedNumbers = numbers.map({ number in 3 * number }) let sortedNumbers = numbers.sort { $0 > $1 }
mappedNumbers print(sortedNumbers)
``` ```
你可以通过参数位置而不是参数名字来引用参数——这个方法在非常短的闭包中非常有用。当一个闭包作为最后一个参数传给一个函数的时候,它可以直接跟在括号后面。 你可以通过参数位置而不是参数名字来引用参数——这个方法在非常短的闭包中非常有用。当一个闭包作为最后一个参数传给一个函数的时候,它可以直接跟在括号后面。当一个闭包是传给函数的唯一参数,你可以完全忽略括号。
```swift ```swift
let sortedNumbers = sorted(numbers) { $0 > $1 } let sortedNumbers = sorted(numbers) { $0 > $1 }
@ -423,9 +427,9 @@ test.simpleDescription()
``` ```
> 练习: > 练习:
> 创建`NamedShape`的另一个子类`Circle`,构造器接收两个参数,一个是半径一个是名称,实现`area`和`describe`方法。 > 创建`NamedShape`的另一个子类`Circle`,构造器接收两个参数,一个是半径一个是名称,在子类`Circle`中实现`area()`和`simpleDescription()`方法。
属性可以有 getter 和 setter 。 除了储存简单的属性之外,属性可以有 getter 和 setter 。
```swift ```swift
class EquilateralTriangle: NamedShape { class EquilateralTriangle: NamedShape {
@ -473,12 +477,12 @@ class TriangleAndSquare {
var triangle: EquilateralTriangle { var triangle: EquilateralTriangle {
willSet { willSet {
square.sideLength = newValue.sideLength square.sideLength = newValue.sideLength
} }
} }
var square: Square { var square: Square {
willSet { willSet {
triangle.sideLength = newValue.sideLength triangle.sideLength = newValue.sideLength
} }
} }
init(size: Double, name: String) { init(size: Double, name: String) {
square = Square(sideLength: size, name: name) square = Square(sideLength: size, name: name)
@ -486,23 +490,10 @@ class TriangleAndSquare {
} }
} }
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape") var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
triangleAndSquare.square.sideLength print(triangleAndSquare.square.sideLength)
triangleAndSquare.triangle.sideLength print(triangleAndSquare.triangle.sideLength)
triangleAndSquare.square = Square(sideLength: 50, name: "larger square") triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
triangleAndSquare.triangle.sideLength print(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)
``` ```
处理变量的可选值时,你可以在操作(比如方法、属性和子脚本)之前加`?`。如果`?`之前的值是`nil``?`后面的东西都会被忽略,并且整个表达式返回`nil`。否则,`?`之后的东西都会被运行。在这两种情况下,整个表达式的值也是一个可选值。 处理变量的可选值时,你可以在操作(比如方法、属性和子脚本)之前加`?`。如果`?`之前的值是`nil``?`后面的东西都会被忽略,并且整个表达式返回`nil`。否则,`?`之后的东西都会被运行。在这两种情况下,整个表达式的值也是一个可选值。
@ -544,9 +535,9 @@ let aceRawValue = ace.rawValue
> 练习: > 练习:
> 写一个函数,通过比较它们的原始值来比较两个`Rank`值。 > 写一个函数,通过比较它们的原始值来比较两个`Rank`值。
在上面的例子中,枚举原始值的类型是`Int`,所以你只需要设置第一个原始值。剩下的原始值会按照顺序赋值。你也可以使用字符串或者浮点数作为枚举的原始值。 在上面的例子中,枚举原始值的类型是`Int`,所以你只需要设置第一个原始值。剩下的原始值会按照顺序赋值。你也可以使用字符串或者浮点数作为枚举的原始值。使用`rawValue`来访问一个枚举成员的原始值。
使用'rawValue'在原始值枚举值之间进行转换 使用`init?(rawValue:)`构造器来从原始值枚举一个例子
```swift ```swift
if let convertedRank = Rank(rawValue: 3) { if let convertedRank = Rank(rawValue: 3) {
@ -554,7 +545,7 @@ if let convertedRank = Rank(rawValue: 3) {
} }
``` ```
枚举的成员值是实际值,并不是原始值的另一种表达方法。实际上,如果原始值没有意义,你不需要设置。 枚举的成员值是实际值,并不是原始值的另一种表达方法。实际上,以防原始值没有意义,你不需要设置。
```swift ```swift
enum Suit { enum Suit {
@ -578,12 +569,11 @@ 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`。已知变量类型的情况下你可以使用缩写。 注意,有两种方式可以引用`Hearts`成员:给`hearts`常量赋值时,枚举成员`Suit.Hearts`需要用全名来引用,因为常量没有显式指定类型。在`switch`里,枚举成员使用缩写`.Hearts`来引用,因为`self`的值已经知道是一个`suit`。已知变量类型的情况下你可以使用缩写。
使用`struct`来创建一个结构体。结构体和类有很多相同的地方,比如方法和构造器。它们之间最大的一个区别就是 使用`struct`来创建一个结构体。结构体和类有很多相同的地方,比如方法和构造器。它们之间最大的一个区别就是结构体是传值,类是传引用。
结构体是传值,类是传引用。
```swift ```swift
struct Card { struct Card {
@ -624,7 +614,7 @@ case let .Error(error):
> 练习: > 练习:
> 给`ServerResponse`和`switch`添加第三种情况。 > 给`ServerResponse`和`switch`添加第三种情况。
注意如何从`ServerResponse`中提取日升和日落时间。 注意如何从`ServerResponse`中提取日升和日落时间并用得到的值用来和`switch`的情况作比较
<a name="protocols_and_extensions"></a> <a name="protocols_and_extensions"></a>
## 协议和扩展 ## 协议和扩展
@ -679,7 +669,7 @@ extension Int: ExampleProtocol {
self += 42 self += 42
} }
} }
7.simpleDescription print(7.simpleDescription)
``` ```
> 练习: > 练习:
@ -701,17 +691,17 @@ protocolValue.simpleDescription
在尖括号里写一个名字来创建一个泛型函数或者类型。 在尖括号里写一个名字来创建一个泛型函数或者类型。
```swift ```swift
func repeat<ItemType>(item: ItemType, times: Int) -> [ItemType] { func repeatItem<Item>(item: Item, numberOfTimes: Int) -> [Item] {
var result = [ItemType]() var result = [Item]()
for i in 0..<times { for _ in 0..<numberOfTimes {
result.append(item) result.append(item)
} }
return result return result
} }
repeat("knock", 4) repeatItem("knock", numberOfTimes:4)
``` ```
你也可以创建泛型类、枚举和结构体。 你也可以创建泛型函数、方法、类、枚举和结构体。
```swift ```swift
// Reimplement the Swift standard library's optional type // Reimplement the Swift standard library's optional type
@ -726,7 +716,7 @@ possibleInteger = .Some(100)
在类型名后面使用`where`来指定对类型的需求,比如,限定类型实现某一个协议,限定两个类型是相同的,或者限定某个类必须有一个特定的父类 在类型名后面使用`where`来指定对类型的需求,比如,限定类型实现某一个协议,限定两个类型是相同的,或者限定某个类必须有一个特定的父类
```swift ```swift
func anyCommonElements <T, U where T: SequenceType, U: SequenceType, T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, rhs: U) -> Bool { func anyCommonElements <T, U where T: SequenceType, U: SequenceType, T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, _ rhs: U) -> Bool {
for lhsItem in lhs { for lhsItem in lhs {
for rhsItem in rhs { for rhsItem in rhs {
if lhsItem == rhsItem { if lhsItem == rhsItem {
@ -740,6 +730,6 @@ anyCommonElements([1, 2, 3], [3])
``` ```
> 练习: > 练习:
> 修改`anyCommonElements`函数来创建一个函数,返回一个数组,内容是两个序列的共有元素。 > 修改`anyCommonElements(_:_:)`函数来创建一个函数,返回一个数组,内容是两个序列的共有元素。
简单起见,你可以忽略`where`,只在冒号后面写协议或者类名。` <T: Equatable>``<T where T: Equatable>`是等价的。 简单起见,你可以忽略`where`,只在冒号后面写协议或者类名。` <T: Equatable>``<T where T: Equatable>`是等价的。

View File

@ -1,10 +1,10 @@
> 翻译:[成都老码团队翻译组-Arya](http://weibo.com/littlekok/)
> 校对:[成都老码团队翻译组-Oberyn](http://weibo.com/u/5241713117)
# Swift 版本历史记录 # Swift 版本历史记录
--- ---
> 翻译:[成都老码团队翻译组-Arya](http://weibo.com/littlekok/)
> 校对:[成都老码团队翻译组-Oberyn](http://weibo.com/u/5241713117)
本页内容包括: 本页内容包括:
- [XCode6.4 Beta Swift语法文档更新](#xcode6_4_Beta) - [XCode6.4 Beta Swift语法文档更新](#xcode6_4_Beta)

View File

@ -1,26 +1,44 @@
# 基础部分The Basics
-----------------
> 1.0
> 翻译:[numbbbbb](https://github.com/numbbbbb), [lyuka](https://github.com/lyuka), [JaySurplus](https://github.com/JaySurplus) > 翻译:[numbbbbb](https://github.com/numbbbbb), [lyuka](https://github.com/lyuka), [JaySurplus](https://github.com/JaySurplus)
> 校对:[lslxdx](https://github.com/lslxdx) > 校对:[lslxdx](https://github.com/lslxdx)
# 基础部分 > 2.0
----------------- > 翻译+校对:[xtymichael](https://github.com/xtymichael)
本页包含内容: 本页包含内容:
- [常量和变量](#constants_and_variables) - [常量和变量](#constants_and_variables)
- [声明常量和变量](#declaring)
- [类型标注](#type_annotations)
- [常量和变量的命名](#naming)
- [输出常量和变量](#printing)
- [注释](#comments) - [注释](#comments)
- [分号](#semicolons) - [分号](#semicolons)
- [整数](#integers) - [整数](#integers)
- [整数范围](#integer_bounds)
- [Int](#Int)
- [UInt](#UInt)
- [浮点数](#floating-point_numbers) - [浮点数](#floating-point_numbers)
- [类型安全和类型推断](#type_safety_and_type_inference) - [类型安全和类型推断](#type_safety_and_type_inference)
- [数值型字面量](#numeric_literals) - [数值型字面量](#numeric_literals)
- [数值型类型转换](#numeric_type_conversion) - [数值型类型转换](#numeric_type_conversion)
- [整数转换](#integer_conversion)
- [数整数和浮点数转换](#integer_and_floating_point_conversion)
- [类型别名](#type_aliases) - [类型别名](#type_aliases)
- [布尔值](#booleans) - [布尔值](#booleans)
- [元组](#tuples) - [元组](#tuples)
- [可选](#optionals) - [可选](#optionals)
- [nil](#nil)
- [if 语句以及强制解析](#if)
- [可选绑定](#optional_binding)
- [隐式解析可选类型](#implicityly_unwrapped_optionals)
- [错误处理](#error_handling)
- [断言](#assertions) - [断言](#assertions)
Swift 是一门进行 iOS 和 OS X 应用开发的新语言。然而,如果你有 C 或者 Objective-C 开发经验的话,你会发现 Swift 的很多内容都是你熟悉的。 Swift 是一门开发 iOS, OS X 和 watchOS 应用的新语言。然而,如果你有 C 或者 Objective-C 开发经验的话,你会发现 Swift 的很多内容都是你熟悉的。
Swift 包含了 C 和 Objective-C 上所有基础数据类型,`Int`表示整型值;`Double``Float`表示浮点型值;`Bool`是布尔型值;`String`是文本型数据。Swift 还提供了三个基本的集合类型,`Array``Set``Dictionary`,详见[集合类型](04_Collection_Types.html)。 Swift 包含了 C 和 Objective-C 上所有基础数据类型,`Int`表示整型值;`Double``Float`表示浮点型值;`Bool`是布尔型值;`String`是文本型数据。Swift 还提供了三个基本的集合类型,`Array``Set``Dictionary`,详见[集合类型](04_Collection_Types.html)。
@ -37,6 +55,7 @@ Swift 是一门类型安全的语言,可选类型就是一个很好的例子
常量和变量把一个名字(比如`maximumNumberOfLoginAttempts`或者`welcomeMessage`)和一个指定类型的值(比如数字`10`或者字符串`"Hello"`)关联起来。常量的值一旦设定就不能改变,而变量的值可以随意更改。 常量和变量把一个名字(比如`maximumNumberOfLoginAttempts`或者`welcomeMessage`)和一个指定类型的值(比如数字`10`或者字符串`"Hello"`)关联起来。常量的值一旦设定就不能改变,而变量的值可以随意更改。
<a name="declaring"></a>
### 声明常量和变量 ### 声明常量和变量
常量和变量必须在使用前声明,用`let`来声明常量,用`var`来声明变量。下面的例子展示了如何用常量和变量来记录用户尝试登录的次数: 常量和变量必须在使用前声明,用`let`来声明常量,用`var`来声明变量。下面的例子展示了如何用常量和变量来记录用户尝试登录的次数:
@ -61,9 +80,10 @@ var x = 0.0, y = 0.0, z = 0.0
>注意: >注意:
如果你的代码中有不需要改变的值,请使用`let`关键字将它声明为常量。只将需要改变的值声明为变量。 如果你的代码中有不需要改变的值,请使用`let`关键字将它声明为常量。只将需要改变的值声明为变量。
<a name="type_annotations"></a>
### 类型标注 ### 类型标注
当你声明常量或者变量的时候可以加上_类型标注type annotation_,说明常量或者变量中要存储的值的类型。如果要添加类型标注,需要在常量或者变量名后面加上一个冒号和空格,然后加上类型名称。 当你声明常量或者变量的时候可以加上类型标注type annotation说明常量或者变量中要存储的值的类型。如果要添加类型标注需要在常量或者变量名后面加上一个冒号和空格然后加上类型名称。
这个例子给`welcomeMessage`变量添加了类型标注,表示这个变量可以存储`String`类型的值: 这个例子给`welcomeMessage`变量添加了类型标注,表示这个变量可以存储`String`类型的值:
@ -83,9 +103,16 @@ var welcomeMessage: String
welcomeMessage = "Hello" welcomeMessage = "Hello"
``` ```
你可以在一行中定义多个同样类型的变量,用逗号分割,并在最后一个变量名之后添加类型标注:
```swift
var red, green, blue: Double
```
> 注意: > 注意:
一般来说你很少需要写类型标注。如果你在声明常量或者变量的时候赋了一个初始值Swift可以推断出这个常量或者变量的类型请参考[类型安全和类型推断](#type_safety_and_type_inference)。在上面的例子中,没有给`welcomeMessage`赋初始值,所以变量`welcomeMessage`的类型是通过一个类型标注指定的,而不是通过初始值推断的。 一般来说你很少需要写类型标注。如果你在声明常量或者变量的时候赋了一个初始值Swift可以推断出这个常量或者变量的类型请参考[类型安全和类型推断](#type_safety_and_type_inference)。在上面的例子中,没有给`welcomeMessage`赋初始值,所以变量`welcomeMessage`的类型是通过一个类型标注指定的,而不是通过初始值推断的。
<a name="naming"></a>
### 常量和变量的命名 ### 常量和变量的命名
你可以用任何你喜欢的字符作为常量和变量名,包括 Unicode 字符: 你可以用任何你喜欢的字符作为常量和变量名,包括 Unicode 字符:
@ -119,35 +146,36 @@ languageName = "Swift++"
// 这会报编译时错误 - languageName 不可改变 // 这会报编译时错误 - languageName 不可改变
``` ```
<a name="printing"></a>
### 输出常量和变量 ### 输出常量和变量
你可以用`println`函数来输出当前常量或变量的值: 你可以用`print(_:)`函数来输出当前常量或变量的值:
```swift ```swift
println(friendlyWelcome) print(friendlyWelcome)
// 输出 "Bonjour!" // 输出 "Bonjour!"
``` ```
`println`是一个用来输出的全局函数,输出的内容会在最后换行。如果你用 Xcode`println`将会输出内容到“console”面板上。(另一种函数叫`print`,唯一区别是在输出内容最后不会换行。) `print(_:)`是一个用来输出的全局函数,输出的内容会在最后换行。如果你用 Xcode`print(_:)`将会输出内容到“console”面板上。(另一种函数叫`print(_:appendNewline:)`,唯一区别是在输出内容最后不会换行。)
`println`函数输出传入的`String`值: `print(_:)`函数输出传入的`String`值:
```swift ```swift
println("This is a string") print("This is a string")
// 输出 "This is a string" // 输出 "This is a string"
``` ```
与 Cocoa 里的`NSLog`函数类似的是,`println`函数可以输出更复杂的信息。这些信息可以包含当前常量和变量的值。 `print(_:)`函数可以输出更复杂的信息。这些信息可以包含当前常量和变量的值。
Swift 用_字符串插值string interpolation_的方式把常量名或者变量名当做占位符加入到长字符串中Swift 会用当前常量或变量的值替换这些占位符。将常量或变量名放入圆括号中,并在开括号前使用反斜杠将其转义: Swift 用字符串插值string interpolation的方式把常量名或者变量名当做占位符加入到长字符串中Swift 会用当前常量或变量的值替换这些占位符。将常量或变量名放入圆括号中,并在开括号前使用反斜杠将其转义:
```swift ```swift
println("The current value of friendlyWelcome is \(friendlyWelcome)") print("The current value of friendlyWelcome is \(friendlyWelcome)")
// 输出 "The current value of friendlyWelcome is Bonjour! // 输出 "The current value of friendlyWelcome is Bonjour!
``` ```
> 注意: > 注意:
字符串插值所有可用的选项,请参考[字符串插值](03_Strings_and_Characters.html#string_interpolation)。 字符串插值所有可用的选项,请参考[字符串插值](./03_Strings_and_Characters.html#string_interpolation)。
<a name="comments"></a> <a name="comments"></a>
## 注释 ## 注释
@ -181,7 +209,7 @@ Swift 中的注释与C 语言的注释非常相似。单行注释以双正斜杠
与其他大部分编程语言不同Swift 并不强制要求你在每条语句的结尾处使用分号(`;`),当然,你也可以按照你自己的习惯添加分号。有一种情况下必须要用分号,即你打算在同一行内写多条独立的语句: 与其他大部分编程语言不同Swift 并不强制要求你在每条语句的结尾处使用分号(`;`),当然,你也可以按照你自己的习惯添加分号。有一种情况下必须要用分号,即你打算在同一行内写多条独立的语句:
```swift ```swift
let cat = "🐱"; println(cat) let cat = "🐱"; print(cat)
// 输出 "🐱" // 输出 "🐱"
``` ```
@ -192,6 +220,7 @@ let cat = "🐱"; println(cat)
Swift 提供了81632和64位的有符号和无符号整数类型。这些整数类型和 C 语言的命名方式很像比如8位无符号整数类型是`UInt8`32位有符号整数类型是`Int32`。就像 Swift 的其他类型一样,整数类型采用大写命名法。 Swift 提供了81632和64位的有符号和无符号整数类型。这些整数类型和 C 语言的命名方式很像比如8位无符号整数类型是`UInt8`32位有符号整数类型是`Int32`。就像 Swift 的其他类型一样,整数类型采用大写命名法。
<a name="integer_bounds"></a>
### 整数范围 ### 整数范围
你可以访问不同整数类型的`min`和`max`属性来获取对应类型的最大值和最小值: 你可以访问不同整数类型的`min`和`max`属性来获取对应类型的最大值和最小值:
@ -199,9 +228,10 @@ Swift 提供了81632和64位的有符号和无符号整数类型。这些
```swift ```swift
let minValue = UInt8.min // minValue 为 0是 UInt8 类型 let minValue = UInt8.min // minValue 为 0是 UInt8 类型
let maxValue = UInt8.max // maxValue 为 255是 UInt8 类型 let maxValue = UInt8.max // maxValue 为 255是 UInt8 类型
``` ```
`min`和`max`所传回值的类型,正是其所对的整数类型(如上例UInt8, 所传回的类型是UInt8),可用在表达式中相同类型值旁。 `min`和`max`所传回值的类型,正是其所对的整数类型(如上例UInt8, 所传回的类型是UInt8),可用在表达式中相同类型值旁。
<a name="Int"></a>
### Int ### Int
一般来说你不需要专门指定整数的长度。Swift 提供了一个特殊的整数类型`Int`,长度与当前平台的原生字长相同: 一般来说你不需要专门指定整数的长度。Swift 提供了一个特殊的整数类型`Int`,长度与当前平台的原生字长相同:
@ -211,6 +241,7 @@ let maxValue = UInt8.max // maxValue 为 255是 UInt8 类型
除非你需要特定长度的整数,一般来说使用`Int`就够了。这可以提高代码一致性和可复用性。即使是在32位平台上`Int`可以存储的整数范围也可以达到`-2147483648`~`2147483647`,大多数时候这已经足够大了。 除非你需要特定长度的整数,一般来说使用`Int`就够了。这可以提高代码一致性和可复用性。即使是在32位平台上`Int`可以存储的整数范围也可以达到`-2147483648`~`2147483647`,大多数时候这已经足够大了。
<a name="UInt"></a>
### UInt ### UInt
Swift 也提供了一个特殊的无符号类型`UInt`,长度与当前平台的原生字长相同: Swift 也提供了一个特殊的无符号类型`UInt`,长度与当前平台的原生字长相同:
@ -237,15 +268,15 @@ Swift 也提供了一个特殊的无符号类型`UInt`,长度与当前平台
<a name="type_safety_and_type_inference"></a> <a name="type_safety_and_type_inference"></a>
## 类型安全和类型推断 ## 类型安全和类型推断
Swift 是一个_类型安全type safe_的语言。类型安全的语言可以让你清楚地知道代码要处理的值的类型。如果你的代码需要一个`String`,你绝对不可能不小心传进去一个`Int`。 Swift 是一个类型安全type safe的语言。类型安全的语言可以让你清楚地知道代码要处理的值的类型。如果你的代码需要一个`String`,你绝对不可能不小心传进去一个`Int`。
由于 Swift 是类型安全的,所以它会在编译你的代码时进行_类型检查type checks_,并把不匹配的类型标记为错误。这可以让你在开发的时候尽早发现并修复错误。 由于 Swift 是类型安全的所以它会在编译你的代码时进行类型检查type checks并把不匹配的类型标记为错误。这可以让你在开发的时候尽早发现并修复错误。
当你要处理不同类型的值时类型检查可以帮你避免错误。然而这并不是说你每次声明常量和变量的时候都需要显式指定类型。如果你没有显式指定类型Swift 会使用_类型推断type inference_来选择合适的类型。有了类型推断,编译器可以在编译代码的时候自动推断出表达式的类型。原理很简单,只要检查你赋的值即可。 当你要处理不同类型的值时类型检查可以帮你避免错误。然而这并不是说你每次声明常量和变量的时候都需要显式指定类型。如果你没有显式指定类型Swift 会使用_类型推断type inference来选择合适的类型。有了类型推断编译器可以在编译代码的时候自动推断出表达式的类型。原理很简单只要检查你赋的值即可。
因为有类型推断,和 C 或者 Objective-C 比起来 Swift 很少需要声明类型。常量和变量虽然需要明确类型,但是大部分工作并不需要你自己来完成。 因为有类型推断,和 C 或者 Objective-C 比起来 Swift 很少需要声明类型。常量和变量虽然需要明确类型,但是大部分工作并不需要你自己来完成。
当你声明常量或者变量并赋初值的时候类型推断非常有用。当你在声明常量或者变量的时候赋给它们一个_字面量literal value 或 literal_即可触发类型推断。(字面量就是会直接出现在你代码中的值,比如`42`和`3.14159`。) 当你声明常量或者变量并赋初值的时候类型推断非常有用。当你在声明常量或者变量的时候赋给它们一个字面量literal value 或 literal即可触发类型推断。字面量就是会直接出现在你代码中的值比如`42`和`3.14159`。)
例如,如果你给一个新常量赋值`42`并且没有标明类型Swift 可以推断出常量类型是`Int`,因为你给它赋的初始值看起来像一个整数: 例如,如果你给一个新常量赋值`42`并且没有标明类型Swift 可以推断出常量类型是`Int`,因为你给它赋的初始值看起来像一个整数:
@ -291,7 +322,7 @@ let octalInteger = 0o21 // 八进制的17
let hexadecimalInteger = 0x11 // 十六进制的17 let hexadecimalInteger = 0x11 // 十六进制的17
``` ```
浮点字面量可以是十进制(没有前缀)或者是十六进制(前缀是`0x`)。小数点两边必须有至少一个十进制数字(或者是十六进制的数字)。浮点字面量还有一个可选的_指数exponent_,在十进制浮点数中通过大写或者小写的`e`来指定,在十六进制浮点数中通过大写或者小写的`p`来指定。 浮点字面量可以是十进制(没有前缀)或者是十六进制(前缀是`0x`。小数点两边必须有至少一个十进制数字或者是十六进制的数字。浮点字面量还有一个可选的指数exponent在十进制浮点数中通过大写或者小写的`e`来指定,在十六进制浮点数中通过大写或者小写的`p`来指定。
如果一个十进制数的指数为`exp`那这个数相当于基数和10^exp的乘积 如果一个十进制数的指数为`exp`那这个数相当于基数和10^exp的乘积
* `1.25e2` 表示 1.25 × 10^2等于 `125.0`。 * `1.25e2` 表示 1.25 × 10^2等于 `125.0`。
@ -321,8 +352,10 @@ let justOverOneMillion = 1_000_000.000_000_1
## 数值型类型转换 ## 数值型类型转换
通常来讲,即使代码中的整数常量和变量已知非负,也请使用`Int`类型。总是使用默认的整数类型可以保证你的整数常量和变量可以直接被复用并且可以匹配整数类字面量的类型推断。 通常来讲,即使代码中的整数常量和变量已知非负,也请使用`Int`类型。总是使用默认的整数类型可以保证你的整数常量和变量可以直接被复用并且可以匹配整数类字面量的类型推断。
只有在必要的时候才使用其他整数类型,比如要处理外部的长度明确的数据或者为了优化性能、内存占用等等。使用显式指定长度的类型可以及时发现值溢出并且可以暗示正在处理特殊数据。 只有在必要的时候才使用其他整数类型,比如要处理外部的长度明确的数据或者为了优化性能、内存占用等等。使用显式指定长度的类型可以及时发现值溢出并且可以暗示正在处理特殊数据。
<a name="integer_conversion"></a>
### 整数转换 ### 整数转换
不同整数类型的变量和常量可以存储不同范围的数字。`Int8`类型的常量或者变量可以存储的数字范围是`-128`~`127`,而`UInt8`类型的常量或者变量能存储的数字范围是`0`~`255`。如果数字超出了常量或者变量可存储的范围,编译的时候会报错: 不同整数类型的变量和常量可以存储不同范围的数字。`Int8`类型的常量或者变量可以存储的数字范围是`-128`~`127`,而`UInt8`类型的常量或者变量能存储的数字范围是`0`~`255`。如果数字超出了常量或者变量可存储的范围,编译的时候会报错:
@ -346,8 +379,9 @@ let twoThousandAndOne = twoThousand + UInt16(one)
现在两个数字的类型都是`UInt16`,可以进行相加。目标常量`twoThousandAndOne`的类型被推断为`UInt16`,因为它是两个`UInt16`值的和。 现在两个数字的类型都是`UInt16`,可以进行相加。目标常量`twoThousandAndOne`的类型被推断为`UInt16`,因为它是两个`UInt16`值的和。
`SomeType(ofInitialValue)`是调用 Swift 构造器并传入一个初始值的默认方法。在语言内部,`UInt16`有一个构造器,可以接受一个`UInt8`类型的值,所以这个构造器可以用现有的`UInt8`来创建一个新的`UInt16`。注意,你并不能传入任意类型的值,只能传入`UInt16`内部有对应构造器的值。不过你可以扩展现有的类型来让它可以接收其他类型的值(包括自定义类型),请参考[扩展](20_Extensions.html)。 `SomeType(ofInitialValue)`是调用 Swift 构造器并传入一个初始值的默认方法。在语言内部,`UInt16`有一个构造器,可以接受一个`UInt8`类型的值,所以这个构造器可以用现有的`UInt8`来创建一个新的`UInt16`。注意,你并不能传入任意类型的值,只能传入`UInt16`内部有对应构造器的值。不过你可以扩展现有的类型来让它可以接收其他类型的值(包括自定义类型),请参考[扩展](./20_Extensions.html)。
<a name="integer_and_floating_point_conversion"></a>
### 整数和浮点数转换 ### 整数和浮点数转换
整数和浮点数的转换必须显式指定类型: 整数和浮点数的转换必须显式指定类型:
@ -376,7 +410,7 @@ let integerPi = Int(pi)
<a name="type_aliases"></a> <a name="type_aliases"></a>
## 类型别名 ## 类型别名
_类型别名type aliases_就是给现有类型定义另一个名字。你可以使用`typealias`关键字来定义类型别名。 类型别名type aliases就是给现有类型定义另一个名字。你可以使用`typealias`关键字来定义类型别名。
当你想要给现有类型起一个更有意义的名字时,类型别名非常有用。假设你正在处理特定长度的外部资源的数据: 当你想要给现有类型起一个更有意义的名字时,类型别名非常有用。假设你正在处理特定长度的外部资源的数据:
@ -396,7 +430,7 @@ var maxAmplitudeFound = AudioSample.min
<a name="booleans"></a> <a name="booleans"></a>
## 布尔值 ## 布尔值
Swift 有一个基本的_布尔Boolean_类型,叫做`Bool`。布尔值指_逻辑上的logical_因为它们只能是真或者假。Swift 有两个布尔常量,`true`和`false` Swift 有一个基本的布尔Boolean类型叫做`Bool`。布尔值指逻辑上的logical因为它们只能是真或者假。Swift 有两个布尔常量,`true`和`false`
```swift ```swift
let orangesAreOrange = true let orangesAreOrange = true
@ -409,14 +443,14 @@ let turnipsAreDelicious = false
```swift ```swift
if turnipsAreDelicious { if turnipsAreDelicious {
println("Mmm, tasty turnips!") print("Mmm, tasty turnips!")
} else { } else {
println("Eww, turnips are horrible.") print("Eww, turnips are horrible.")
} }
// 输出 "Eww, turnips are horrible." // 输出 "Eww, turnips are horrible."
``` ```
条件语句,例如`if`,请参考[控制流](05_Control_Flow.html)。 条件语句,例如`if`,请参考[控制流](./05_Control_Flow.html)。
如果你在需要使用`Bool`类型的地方使用了非布尔值Swift 的类型安全机制会报错。下面的例子会报告一个编译时错误: 如果你在需要使用`Bool`类型的地方使用了非布尔值Swift 的类型安全机制会报错。下面的例子会报告一个编译时错误:
@ -436,16 +470,16 @@ if i == 1 {
} }
``` ```
`i == 1`的比较结果是`Bool`类型,所以第二个例子可以通过类型检查。类似`i == 1`这样的比较,请参考[基本操作符](05_Control_Flow.html)。 `i == 1`的比较结果是`Bool`类型,所以第二个例子可以通过类型检查。类似`i == 1`这样的比较,请参考[基本操作符](./05_Control_Flow.html)。
和 Swift 中的其他类型安全的例子一样,这个方法可以避免错误并保证这块代码的意图总是清晰的。 和 Swift 中的其他类型安全的例子一样,这个方法可以避免错误并保证这块代码的意图总是清晰的。
<a name="tuples"></a> <a name="tuples"></a>
## 元组 ## 元组
_元组tuples_把多个值组合成一个复合值。元组内的值可以是任意类型,并不要求是相同类型。 元组tuples把多个值组合成一个复合值。元组内的值可以是任意类型并不要求是相同类型。
下面这个例子中,`(404, "Not Found")`是一个描述 _HTTP 状态码HTTP status code_的元组。HTTP 状态码是当你请求网页的时候 web 服务器返回的一个特殊值。如果你请求的网页不存在就会返回一个`404 Not Found`状态码。 下面这个例子中,`(404, "Not Found")`是一个描述 HTTP 状态码HTTP status code的元组。HTTP 状态码是当你请求网页的时候 web 服务器返回的一个特殊值。如果你请求的网页不存在就会返回一个`404 Not Found`状态码。
```swift ```swift
let http404Error = (404, "Not Found") let http404Error = (404, "Not Found")
@ -456,13 +490,13 @@ let http404Error = (404, "Not Found")
你可以把任意顺序的类型组合成一个元组,这个元组可以包含所有类型。只要你想,你可以创建一个类型为`(Int, Int, Int)`或者`(String, Bool)`或者其他任何你想要的组合的元组。 你可以把任意顺序的类型组合成一个元组,这个元组可以包含所有类型。只要你想,你可以创建一个类型为`(Int, Int, Int)`或者`(String, Bool)`或者其他任何你想要的组合的元组。
你可以将一个元组的内容_分解decompose_成单独的常量和变量,然后你就可以正常使用它们了: 你可以将一个元组的内容分解decompose成单独的常量和变量然后你就可以正常使用它们了
```swift ```swift
let (statusCode, statusMessage) = http404Error let (statusCode, statusMessage) = http404Error
println("The status code is \(statusCode)") print("The status code is \(statusCode)")
// 输出 "The status code is 404" // 输出 "The status code is 404"
println("The status message is \(statusMessage)") print("The status message is \(statusMessage)")
// 输出 "The status message is Not Found" // 输出 "The status message is Not Found"
``` ```
@ -470,16 +504,16 @@ println("The status message is \(statusMessage)")
```swift ```swift
let (justTheStatusCode, _) = http404Error let (justTheStatusCode, _) = http404Error
println("The status code is \(justTheStatusCode)") print("The status code is \(justTheStatusCode)")
// 输出 "The status code is 404" // 输出 "The status code is 404"
``` ```
此外,你还可以通过下标来访问元组中的单个元素,下标从零开始: 此外,你还可以通过下标来访问元组中的单个元素,下标从零开始:
```swift ```swift
println("The status code is \(http404Error.0)") print("The status code is \(http404Error.0)")
// 输出 "The status code is 404" // 输出 "The status code is 404"
println("The status message is \(http404Error.1)") print("The status message is \(http404Error.1)")
// 输出 "The status message is Not Found" // 输出 "The status message is Not Found"
``` ```
@ -492,27 +526,27 @@ let http200Status = (statusCode: 200, description: "OK")
给元组中的元素命名后,你可以通过名字来获取这些元素的值: 给元组中的元素命名后,你可以通过名字来获取这些元素的值:
```swift ```swift
println("The status code is \(http200Status.statusCode)") print("The status code is \(http200Status.statusCode)")
// 输出 "The status code is 200" // 输出 "The status code is 200"
println("The status message is \(http200Status.description)") print("The status message is \(http200Status.description)")
// 输出 "The status message is OK" // 输出 "The status message is OK"
``` ```
作为函数返回值时,元组非常有用。一个用来获取网页的函数可能会返回一个`(Int, String)`元组来描述是否获取成功。和只能返回一个类型的值比较起来,一个包含两个不同类型值的元组可以让函数的返回信息更有用。请参考[函数参数与返回值](06_Functions.html#Function_Parameters_and_Return_Values)。 作为函数返回值时,元组非常有用。一个用来获取网页的函数可能会返回一个`(Int, String)`元组来描述是否获取成功。和只能返回一个类型的值比较起来,一个包含两个不同类型值的元组可以让函数的返回信息更有用。请参考[函数参数与返回值](./06_Functions.html#Function_Parameters_and_Return_Values)。
> 注意: > 注意:
元组在临时组织值的时候很有用,但是并不适合创建复杂的数据结构。如果你的数据结构并不是临时使用,请使用类或者结构体而不是元组。请参考[类和结构体](09_Classes_and_Structures.html)。 元组在临时组织值的时候很有用,但是并不适合创建复杂的数据结构。如果你的数据结构并不是临时使用,请使用类或者结构体而不是元组。请参考[类和结构体](./09_Classes_and_Structures.html)。
<a name="optionals"></a> <a name="optionals"></a>
## 可选类型 ## 可选类型
使用_可选类型optionals_来处理值可能缺失的情况。可选类型表示: 使用可选类型optionals来处理值可能缺失的情况。可选类型表示
* _有_值,等于 x * 值,等于 x
或者 或者
* _没有_ * 没有值
> 注意: > 注意:
C 和 Objective-C 中并没有可选类型这个概念。最接近的是 Objective-C 中的一个特性,一个方法要不返回一个对象要不返回`nil``nil`表示“缺少一个合法的对象”。然而,这只对对象起作用——对于结构体,基本的 C 类型或者枚举类型不起作用。对于这些类型Objective-C 方法一般会返回一个特殊值(比如`NSNotFound`来暗示值缺失。这种方法假设方法的调用者知道并记得对特殊值进行判断。然而Swift 的可选类型可以让你暗示_任意类型_的值缺失并不需要一个特殊值。 C 和 Objective-C 中并没有可选类型这个概念。最接近的是 Objective-C 中的一个特性,一个方法要不返回一个对象要不返回`nil``nil`表示“缺少一个合法的对象”。然而,这只对对象起作用——对于结构体,基本的 C 类型或者枚举类型不起作用。对于这些类型Objective-C 方法一般会返回一个特殊值(比如`NSNotFound`来暗示值缺失。这种方法假设方法的调用者知道并记得对特殊值进行判断。然而Swift 的可选类型可以让你暗示_任意类型_的值缺失并不需要一个特殊值。
@ -527,60 +561,9 @@ let convertedNumber = possibleNumber.toInt()
// convertedNumber 被推测为类型 "Int?" 或者类型 "optional Int" // convertedNumber 被推测为类型 "Int?" 或者类型 "optional Int"
``` ```
因为`toInt`方法可能会失败所以它返回一个_可选类型optional_`Int`,而不是一个`Int`。一个可选的`Int`被写作`Int?`而不是`Int`。问号暗示包含的值是可选类型,也就是说可能包含`Int`值也可能不包含值。(不能包含其他任何值比如`Bool`值或者`String`值。只能是`Int`或者什么都没有。) 因为`toInt`方法可能会失败所以它返回一个_可选类型optional`Int`,而不是一个`Int`。一个可选的`Int`被写作`Int?`而不是`Int`。问号暗示包含的值是可选类型,也就是说可能包含`Int`值也可能不包含值。(不能包含其他任何值比如`Bool`值或者`String`值。只能是`Int`或者什么都没有。)
### if 语句以及强制解析
你可以使用`if`语句来判断一个可选是否包含值。如果可选类型有值,结果是`true`;如果没有值,结果是`false`。
当你确定可选类型_确实_包含值之后你可以在可选的名字后面加一个感叹号`!`来获取值。这个惊叹号表示“我知道这个可选有值请使用它。”这被称为可选值的_强制解析forced unwrapping_
```swift
if convertedNumber != nil {
println("\(possibleNumber) has an integer value of \(convertedNumber!)")
} else {
println("\(possibleNumber) could not be converted to an integer")
}
// 输出 "123 has an integer value of 123"
```
更多关于`if`语句的内容,请参考[控制流](05_Control_Flow.html)。
> 注意:
使用`!`来获取一个不存在的可选值会导致运行时错误。使用`!`来强制解析值之前,一定要确定可选包含一个非`nil`的值。
<a name="optional_binding"></a>
### 可选绑定
使用_可选绑定optional binding_来判断可选类型是否包含值如果包含就把值赋给一个临时常量或者变量。可选绑定可以用在`if`和`while`语句中来对可选类型的值进行判断并把值赋给一个常量或者变量。`if`和`while`语句,请参考[控制流](05_Control_Flow.html)。
像下面这样在`if`语句中写一个可选绑定:
```swift
if let constantName = someOptional {
statements
}
```
你可以像上面这样使用可选绑定来重写`possibleNumber`这个例子:
```swift
if let actualNumber = possibleNumber.toInt() {
println("\(possibleNumber) has an integer value of \(actualNumber)")
} else {
println("\(possibleNumber) could not be converted to an integer")
}
// 输出 "123 has an integer value of 123"
```
这段代码可以被理解为:
“如果`possibleNumber.toInt`返回的可选`Int`包含一个值,创建一个叫做`actualNumber`的新常量并将可选包含的值赋给它。”
如果转换成功,`actualNumber`常量可以在`if`语句的第一个分支中使用。它已经被可选类型_包含的_值初始化过所以不需要再使用`!`后缀来获取它的值。在这个例子中,`actualNumber`只被用来输出转换结果。
你可以在可选绑定中使用常量和变量。如果你想在`if`语句的第一个分支中操作`actualNumber`的值,你可以改成`if var actualNumber`,这样可选类型包含的值就会被赋给一个变量而非常量。
<a name="nil"></a>
### nil ### nil
你可以给可选变量赋值为`nil`来表示它没有值: 你可以给可选变量赋值为`nil`来表示它没有值:
@ -603,30 +586,94 @@ var surveyAnswer: String?
``` ```
> 注意: > 注意:
Swift 的`nil`和 Objective-C 中的`nil`并不一样。在 Objective-C 中,`nil`是一个指向不存在对象的指针。在 Swift 中,`nil`不是指针——它是一个确定的值,用来表示值缺失。_任何_类型的可选状态都可以被设置为`nil`,不只是对象类型。 Swift 的`nil`和 Objective-C 中的`nil`并不一样。在 Objective-C 中,`nil`是一个指向不存在对象的指针。在 Swift 中,`nil`不是指针——它是一个确定的值,用来表示值缺失。任何类型的可选状态都可以被设置为`nil`,不只是对象类型。
<a name="if"></a>
### if 语句以及强制解析
你可以使用`if`语句和nil比较来判断一个可选值是否包含值。你可以使用“相等”(==)或“不等”(!=)来执行比较。
如果可选类型有值它将不等于nil:
```swift
if convertedNumber != nil {
print("convertedNumber contains some integer value.")
}
// 输出 "convertedNumber contains some integer value."
```
当你确定可选类型确实包含值之后,你可以在可选的名字后面加一个感叹号(`!`来获取值。这个惊叹号表示“我知道这个可选有值请使用它。”这被称为可选值的_强制解析forced unwrapping
```swift
if convertedNumber != nil {
print("convertedNumber has an integer value of \(convertedNumber!).")
}
// 输出 "convertedNumber has an integer value of 123."
```
更多关于`if`语句的内容,请参考[控制流](05_Control_Flow.html)。
> 注意:
使用`!`来获取一个不存在的可选值会导致运行时错误。使用`!`来强制解析值之前,一定要确定可选包含一个非`nil`的值。
<a name="optional_binding"></a>
### 可选绑定
使用可选绑定optional binding来判断可选类型是否包含值如果包含就把值赋给一个临时常量或者变量。可选绑定可以用在`if`和`while`语句中来对可选类型的值进行判断并把值赋给一个常量或者变量。`if`和`while`语句,请参考[控制流](./05_Control_Flow.html)。
像下面这样在`if`语句中写一个可选绑定:
```swift
if let constantName = someOptional {
statements
}
```
你可以像上面这样使用可选绑定来重写`possibleNumber`这个例子:
```swift
if let actualNumber = Int(possibleNumber) {
print("\'\(possibleNumber)\' has an integer value of \(actualNumber)")
} else {
print("\'\(possibleNumber)\' could not be converted to an integer")
}
// 输出 "'123' has an integer value of 123"
```
这段代码可以被理解为:
“如果`possibleNumber.toInt`返回的可选`Int`包含一个值,创建一个叫做`actualNumber`的新常量并将可选包含的值赋给它。”
如果转换成功,`actualNumber`常量可以在`if`语句的第一个分支中使用。它已经被可选类型_包含的_值初始化过所以不需要再使用`!`后缀来获取它的值。在这个例子中,`actualNumber`只被用来输出转换结果。
你可以在可选绑定中使用常量和变量。如果你想在`if`语句的第一个分支中操作`actualNumber`的值,你可以改成`if var actualNumber`,这样可选类型包含的值就会被赋给一个变量而非常量。
多个可选绑定可以用逗号区分成一列表达式出现在一个`if`语句中。
```swift
if let constantName = someOptional, anotherConstantName = someOtherOptional {
statements
}
```
<a name="implicityly_unwrapped_optionals"></a>
### 隐式解析可选类型 ### 隐式解析可选类型
如上所述,可选类型暗示了常量或者变量可以“没有值”。可选可以通过`if`语句来判断是否有值,如果有值的话可以通过可选绑定来解析值。 如上所述,可选类型暗示了常量或者变量可以“没有值”。可选可以通过`if`语句来判断是否有值,如果有值的话可以通过可选绑定来解析值。
有时候在程序架构中第一次被赋值之后可以确定一个可选类型_总会_有值。在这种情况下每次都要判断和解析可选值是非常低效的因为可以确定它总会有值。 有时候在程序架构中第一次被赋值之后可以确定一个可选类型_总会_有值。在这种情况下每次都要判断和解析可选值是非常低效的因为可以确定它总会有值。
这种类型的可选状态被定义为_隐式解析可选类型implicitly unwrapped optionals_。把想要用作可选的类型的后面的问号(`String?`)改成感叹号(`String!`)来声明一个隐式解析可选类型。 这种类型的可选状态被定义为隐式解析可选类型implicitly unwrapped optionals。把想要用作可选的类型的后面的问号`String?`)改成感叹号(`String!`)来声明一个隐式解析可选类型。
当可选类型被第一次赋值之后就可以确定之后一直有值的时候,隐式解析可选类型非常有用。隐式解析可选类型主要被用在 Swift 中类的构造过程中,请参考[类实例之间的循环强引用](16_Automatic_Reference_Counting.html#strong_reference_cycles_between_class_instances)。 当可选类型被第一次赋值之后就可以确定之后一直有值的时候,隐式解析可选类型非常有用。隐式解析可选类型主要被用在 Swift 中类的构造过程中,请参考[无主引用以及隐式解析可选属性](./16_Automatic_Reference_Counting.html#unowned_references_and_implicitly_unwrapped_optional_properties)。
一个隐式解析可选类型其实就是一个普通的可选类型,但是可以被当做非可选类型来使用,并不需要每次都使用解析来获取可选值。下面的例子展示了可选类型`String`和隐式解析可选类型`String`之间的区别: 一个隐式解析可选类型其实就是一个普通的可选类型,但是可以被当做非可选类型来使用,并不需要每次都使用解析来获取可选值。下面的例子展示了可选类型`String`和隐式解析可选类型`String`之间的区别:
```swift ```swift
let possibleString: String? = "An optional string." let possibleString: String? = "An optional string."
println(possibleString!) // 需要惊叹号来获取值 let forcedString: String = possibleString! // 需要惊叹号来获取值
// 输出 "An optional string."
```
```swift
let assumedString: String! = "An implicitly unwrapped optional string." let assumedString: String! = "An implicitly unwrapped optional string."
println(assumedString) // 不需要感叹号 let implicitString: String = assumedString // 不需要感叹号
// 输出 "An implicitly unwrapped optional string."
``` ```
你可以把隐式解析可选类型当做一个可以自动解析的可选类型。你要做的只是声明的时候把感叹号放到类型的结尾,而不是每次取值的可选名字的结尾。 你可以把隐式解析可选类型当做一个可以自动解析的可选类型。你要做的只是声明的时候把感叹号放到类型的结尾,而不是每次取值的可选名字的结尾。
@ -637,8 +684,8 @@ println(assumedString) // 不需要感叹号
你仍然可以把隐式解析可选类型当做普通可选类型来判断它是否包含值: 你仍然可以把隐式解析可选类型当做普通可选类型来判断它是否包含值:
```swift ```swift
if assumedString { if assumedString != nil {
println(assumedString) print(assumedString)
} }
// 输出 "An implicitly unwrapped optional string." // 输出 "An implicitly unwrapped optional string."
``` ```
@ -647,7 +694,7 @@ if assumedString {
```swift ```swift
if let definiteString = assumedString { if let definiteString = assumedString {
println(definiteString) print(definiteString)
} }
// 输出 "An implicitly unwrapped optional string." // 输出 "An implicitly unwrapped optional string."
``` ```
@ -655,10 +702,60 @@ if let definiteString = assumedString {
> 注意: > 注意:
如果一个变量之后可能变成`nil`的话请不要使用隐式解析可选类型。如果你需要在变量的生命周期中判断是否是`nil`的话,请使用普通可选类型。 如果一个变量之后可能变成`nil`的话请不要使用隐式解析可选类型。如果你需要在变量的生命周期中判断是否是`nil`的话,请使用普通可选类型。
<a name="error_handling"></a>
## 错误处理
你可以使用错误处理(error handling)来应对程序执行中出错的条件。
相对于可选中运用值得存在与缺失来表达函数的成功与失败,错误处理可以推断失败的原因,并传送至程序的其他部分。
当一个函数遇到错误条件,它能报错。调用函数的地方能抛出错误消息并合理处理。
```swift
func canThrowAnErrow() throws{
//this function may or may not throw an error
}
```
一个函数可以通过在声明中添加`throws`关键词来抛出错误消息。当你的函数能抛出错误消息时, 你应该在表达式中前置`try`关键词。
```swift
do {
try canThrowAnErrow()
// 没有错误消息抛出
} catch {
// 有一个错误消息抛出
}
```
一个`do`的声明创建了一个新的包含作用域,使得错误能被传播到一个或更多`catch`从句。
这里有一个错误处理用来应对不同错误条件的例子。
```swift
func makeASandwich() throws {
// ...
}
do {
try makeASandwich()
eatASandwich()
} catch Error.OutOfCleanDishes{
washDishes()
} catch Error.MissingIngredients(let ingredients) {
buyGroceries(ingredients)
}
```
在此例中,`makeASandwich()`(做一个三明治)函数会抛出一个错误消息如果没有干净的盘子或者某个原料缺失。因为`makeASandwich()`抛出错误,调用函数会被包裹在`try`表达式中。将函数包裹在一个`do`声明中,任何被抛出的错误会被传送到提供的`catch`从句中。
如果没有错误被抛出, `eatASandwich()`函数会被调用。如果一个符合`Error.OutOfCleanDishes`的错误被抛出,`washDishes`函数会被调用。如果一个符合`Error.MissingIngredients`的错误被抛出,`buyGroceries(_:)`函数会被调用并传递相关被`catch`所捕捉到的`[String]`值。
抛出,捕捉,传递错误会在[错误处理](./18_Error_Handling.html)章节详细说明。
<a name="assertions"></a> <a name="assertions"></a>
## 断言 ## 断言
可选类型可以让你判断值是否存在,你可以在代码中优雅地处理值缺失的情况。然而,在某些情况下,如果值缺失或者值并不满足特定的条件,你的代码可能没办法继续执行。这时,你可以在你的代码中触发一个_断言assertion_来结束代码运行并通过调试来找到值缺失的原因。 可选类型可以让你判断值是否存在你可以在代码中优雅地处理值缺失的情况。然而在某些情况下如果值缺失或者值并不满足特定的条件你的代码可能没办法继续执行。这时你可以在你的代码中触发一个断言assertion来结束代码运行并通过调试来找到值缺失的原因。
### 使用断言进行调试 ### 使用断言进行调试
@ -690,7 +787,7 @@ assert(age >= 0)
* 需要给函数传入一个值,但是非法的值可能导致函数不能正常执行。 * 需要给函数传入一个值,但是非法的值可能导致函数不能正常执行。
* 一个可选值现在是`nil`,但是后面的代码运行需要一个非`nil`值。 * 一个可选值现在是`nil`,但是后面的代码运行需要一个非`nil`值。
请参考[下标脚本](12_Subscripts.html)和[函数](06_Functions.html)。 请参考[下标脚本](./12_Subscripts.html)和[函数](./06_Functions.html)。
> 注意: > 注意:
断言可能导致你的应用终止运行,所以你应当仔细设计你的代码来让非法条件不会出现。然而,在你的应用发布之前,有时候非法条件可能出现,这时使用断言可以快速发现问题。 断言可能导致你的应用终止运行,所以你应当仔细设计你的代码来让非法条件不会出现。然而,在你的应用发布之前,有时候非法条件可能出现,这时使用断言可以快速发现问题。

View File

@ -1,11 +1,13 @@
# 基本运算符Basic Operators
> 翻译:[XieLingWang](https://github.com/xielingwang)
> 校对:[EvilCome](https://github.com/Evilcome)
> 最终校对:[老码团队翻译组-Tyrion](http://weibo.com/u/5241713117)
# 基本运算符
----------------- -----------------
> 1.0
> 翻译:[XieLingWang](https://github.com/xielingwang)
> 校对:[EvilCome](https://github.com/Evilcome)
> 2.0
> 翻译+校对:[JackAlan](https://github.com/AlanMelody)
本页包含内容: 本页包含内容:
- [术语](#terminology) - [术语](#terminology)
@ -18,20 +20,20 @@
- [区间运算符](#range_operators) - [区间运算符](#range_operators)
- [逻辑运算符](#logical_operators) - [逻辑运算符](#logical_operators)
运算符是检查、改变、合并值的特殊符号或短语。例如,加号`+`将两个数相加(如`let i = 1 + 2`)。复杂的运算例逻辑与运算符`&&`(如`if enteredDoorCode && passedRetinaScan`),或让 i 值加1的便捷自增运算符`++i`等。 运算符是检查、改变、合并值的特殊符号或短语。例如,加号`+`将两个数相加(如`let i = 1 + 2`)。复杂的运算例子包括逻辑与运算符`&&`(如`if enteredDoorCode && passedRetinaScan`),或让 i 值加1的便捷自增运算符`++i`等。
Swift 支持大部分标准 C 语言的运算符,且改进许多特性来减少常规编码错误。如:赋值符(`=`)不返回值,以防止把想要判断相等运算符(`==`)的地方写成赋值符导致的错误。算术运算符(`+``-``*``/``%`等)会检测并不允许值溢出,以此来避免保存变量时由于变量大于或小于其类型所能承载的范围时导致的异常结果。当然允许你使用 Swift 的溢出运算符来实现溢出。详情参见[溢出运算符](24_Advanced_Operators.html#overflow_operators)。 Swift 支持大部分标准 C 语言的运算符,且改进许多特性来减少常规编码错误。如:赋值符(`=`)不返回值,以防止把想要判断相等运算符(`==`)的地方写成赋值符导致的错误。算术运算符(`+``-``*``/``%`等)会检测并不允许值溢出,以此来避免保存变量时由于变量大于或小于其类型所能承载的范围时导致的异常结果。当然允许你使用 Swift 的溢出运算符来实现溢出。详情参见[溢出运算符](./24_Advanced_Operators.html#overflow_operators)。
区别于 C 语言,在 Swift 中你可以对浮点数进行取余运算(`%`Swift 还提供了 C 语言没有的表达两数之间的值的区间运算符(`a..<b``a...b`),这方便我们表达一个区间内的数值。 区别于 C 语言,在 Swift 中你可以对浮点数进行取余运算(`%`Swift 还提供了 C 语言没有的表达两数之间的值的区间运算符(`a..<b``a...b`),这方便我们表达一个区间内的数值。
本章节只描述了 Swift 中的基本运算符,[高级运算符](24_Advanced_Operators.html)包含了高级运算符,及如何自定义运算符,及如何进行自定义类型的运算符重载。 本章节只描述了 Swift 中的基本运算符,[高级运算符](./24_Advanced_Operators.html)包含了高级运算符,及如何自定义运算符,及如何进行自定义类型的运算符重载。
<a name="terminology"></a> <a name="terminology"></a>
## 术语 ## 术语
运算符有一元、二元和三元运算符。 运算符有一元、二元和三元运算符。
- 一元运算符对单一操作对象操作(如`-a`)。一元运算符分前置运算符和后置运算符,前置运算符需紧操作对象之前(如`!b`),后置运算符需紧跟操作对象之后(如`i++`)。 - 一元运算符对单一操作对象操作(如`-a`)。一元运算符分前置运算符和后置运算符,前置运算符需紧跟在操作对象之前(如`!b`),后置运算符需紧跟操作对象之后(如`i++`)。
- 二元运算符操作两个操作对象(如`2 + 3`),是中置的,因为它们出现在两个操作对象之间。 - 二元运算符操作两个操作对象(如`2 + 3`),是中置的,因为它们出现在两个操作对象之间。
- 三元运算符操作三个操作对象,和 C 语言一样Swift 只有一个三元运算符,就是三目运算符(`a ? b : c`)。 - 三元运算符操作三个操作对象,和 C 语言一样Swift 只有一个三元运算符,就是三目运算符(`a ? b : c`)。
@ -47,7 +49,7 @@ Swift 支持大部分标准 C 语言的运算符,且改进许多特性来减
a = b a = b
// a 现在等于 10 // a 现在等于 10
如果赋值的右边是一个多元组,它的元素可以马上被分解多个常量或变量: 如果赋值的右边是一个多元组,它的元素可以马上被分解多个常量或变量:
let (x, y) = (1, 2) let (x, y) = (1, 2)
// 现在 x 等于 1, y 等于 2 // 现在 x 等于 1, y 等于 2
@ -70,15 +72,15 @@ Swift 中所有数值类型都支持了基本的四则算术运算:
- 减法(`-` - 减法(`-`
- 乘法(`*` - 乘法(`*`
- 除法(`/` - 除法(`/`
1 + 2 // 等于 3 1 + 2 // 等于 3
5 - 3 // 等于 2 5 - 3 // 等于 2
2 * 3 // 等于 6 2 * 3 // 等于 6
10.0 / 2.5 // 等于 4.0 10.0 / 2.5 // 等于 4.0
与 C 语言和 Objective-C 不同的是Swift 默认情况下不允许在数值运算中出现溢出情况。但是你可以使用 Swift 的溢出运算符来实现溢出运算(如`a &+ b`)。详情参见[溢出运算符](24_Advanced_Operators.html#overflow_operators)。 与 C 语言和 Objective-C 不同的是Swift 默认情况下不允许在数值运算中出现溢出情况。但是你可以使用 Swift 的溢出运算符来实现溢出运算(如`a &+ b`)。详情参见[溢出运算符](./24_Advanced_Operators.html#overflow_operators)。
加法运算符也可用于`String`的拼接: 加法运算符也可用于`String`的拼接:
@ -190,7 +192,7 @@ Swift 中所有数值类型都支持了基本的四则算术运算:
<a name="compound_assignment_operators"></a> <a name="compound_assignment_operators"></a>
## 复合赋值Compound Assignment Operators ## 复合赋值Compound Assignment Operators
如同强大的 C 语言Swift 也提供把其他运算符和赋值运算(`=`)组合的复合赋值运算符,组合加运算(`+=`)是其中一个例子: 如同 C 语言Swift 也提供把其他运算符和赋值运算(`=`)组合的复合赋值运算符,组合加运算(`+=`)是其中一个例子:
var a = 1 var a = 1
a += 2 // a 现在是 3 a += 2 // a 现在是 3
@ -216,7 +218,7 @@ Swift 中所有数值类型都支持了基本的四则算术运算:
- 小于等于(`a <= b` - 小于等于(`a <= b`
> 注意: > 注意:
Swift 也提供恒等`===`和不恒等`!==`这两个比较符来判断两个对象是否引用同一个对象实例。更多细节在[类与结构](09_Classes_and_Structures.html)。 Swift 也提供恒等`===`和不恒等`!==`这两个比较符来判断两个对象是否引用同一个对象实例。更多细节在[类与结构](./09_Classes_and_Structures.html)。
每个比较运算都返回了一个标识表达式是否成立的布尔值: 每个比较运算都返回了一个标识表达式是否成立的布尔值:
@ -231,17 +233,17 @@ Swift 也提供恒等`===`和不恒等`!==`这两个比较符来判断两个对
比较运算多用于条件语句,如`if`条件: 比较运算多用于条件语句,如`if`条件:
let name = "world" let name = "world"
if name == "world" { if name == "world" {
println("hello, world") print("hello, world")
} else { } else {
println("I'm sorry \(name), but I don't recognize you") print("I'm sorry \(name), but I don't recognize you")
} }
// 输出 "hello, world", 因为 `name` 就是等于 "world" // 输出 "hello, world", 因为 `name` 就是等于 "world"
关于`if`语句,请看[控制流](05_Control_Flow.html)。 关于`if`语句,请看[控制流](./05_Control_Flow.html)。
<a name="ternary_conditional_operator"></a> <a name="ternary_conditional_operator"></a>
## 三目运算符(Ternary Conditional Operator) ## 三目运算符(Ternary Conditional Operator)
@ -257,7 +259,7 @@ Swift 也提供恒等`===`和不恒等`!==`这两个比较符来判断两个对
} }
这里有个计算表格行高的例子。如果有表头那行高应比内容高度要高出50像素; 如果没有表头只需高出20像素。 这里有个计算表格行高的例子。如果有表头那行高应比内容高度要高出50像素; 如果没有表头只需高出20像素。
let contentHeight = 40 let contentHeight = 40
let hasHeader = true let hasHeader = true
let rowHeight = contentHeight + (hasHeader ? 50 : 20) let rowHeight = contentHeight + (hasHeader ? 50 : 20)
@ -307,8 +309,8 @@ Swift 也提供恒等`===`和不恒等`!==`这两个比较符来判断两个对
//userDefinedColorName的值为空 所以colorNameToUse的值为`red` //userDefinedColorName的值为空 所以colorNameToUse的值为`red`
`userDefinedColorName`变量被定义为一个可选字符串类型默认值为nil。由于`userDefinedColorName`是一个可选类型,我们可以使用空合运算符去判断其值。在上一个例子中,通过空合运算符为一个名为`colorNameToUse`的变量赋予一个字符串类型初始值。 `userDefinedColorName`变量被定义为一个可选`String`类型,默认值为`nil`。由于`userDefinedColorName`是一个可选类型,我们可以使用空合运算符去判断其值。在上一个例子中,通过空合运算符为一个名为`colorNameToUse`的变量赋予一个字符串类型初始值。
由于`userDefinedColorName`值为空,因此表达式` userDefinedColorName ?? defaultColorName `返回默认值,即`red` 由于`userDefinedColorName`值为空,因此表达式` userDefinedColorName ?? defaultColorName `返回`defaultColorName`值,即`red`
另一种情况,分配一个非空值(`non-nil`)给 `userDefinedColorName`,再次执行空合运算,运算结果为封包在`userDefaultColorName`中的值,而非默认值。 另一种情况,分配一个非空值(`non-nil`)给 `userDefinedColorName`,再次执行空合运算,运算结果为封包在`userDefaultColorName`中的值,而非默认值。
@ -322,12 +324,12 @@ Swift 也提供恒等`===`和不恒等`!==`这两个比较符来判断两个对
Swift 提供了两个方便表达一个区间的值的运算符。 Swift 提供了两个方便表达一个区间的值的运算符。
### 闭区间运算符 ### 闭区间运算符
闭区间运算符(`a...b`)定义一个包含从`a``b`(包括`a``b`)的所有值的区间,`b`必须大于`a` 闭区间运算符(`a...b`)定义一个包含从`a``b`(包括`a``b`)的所有值的区间,`b`必须大于等于`a`
闭区间运算符在迭代一个区间的所有值时是非常有用的,如在`for-in`循环中: 闭区间运算符在迭代一个区间的所有值时是非常有用的,如在`for-in`循环中:
for index in 1...5 { for index in 1...5 {
println("\(index) * 5 = \(index * 5)") print("\(index) * 5 = \(index * 5)")
} }
// 1 * 5 = 5 // 1 * 5 = 5
// 2 * 5 = 10 // 2 * 5 = 10
@ -336,7 +338,7 @@ Swift 提供了两个方便表达一个区间的值的运算符。
// 5 * 5 = 25 // 5 * 5 = 25
关于`for-in`,请看[控制流](05_Control_Flow.html)。 关于`for-in`,请看[控制流](./05_Control_Flow.html)。
### 半开区间运算符 ### 半开区间运算符
@ -349,7 +351,7 @@ Swift 提供了两个方便表达一个区间的值的运算符。
let names = ["Anna", "Alex", "Brian", "Jack"] let names = ["Anna", "Alex", "Brian", "Jack"]
let count = names.count let count = names.count
for i in 0..<count { for i in 0..<count {
println(" \(i + 1) 个人叫 \(names[i])") print(" \(i + 1) 个人叫 \(names[i])")
} }
// 1 个人叫 Anna // 1 个人叫 Anna
// 2 个人叫 Alex // 2 个人叫 Alex
@ -357,7 +359,7 @@ Swift 提供了两个方便表达一个区间的值的运算符。
// 4 个人叫 Jack // 4 个人叫 Jack
数组有4个元素`0..<count`只数到3(最后一个元素的下标)因为它是半开区间关于数组请查阅[数组](04_Collection_Types.html#arrays) 数组有4个元素`0..<count`只数到3(最后一个元素的下标)因为它是半开区间关于数组请查阅[数组](./04_Collection_Types.html#arrays)
<a name="logical_operators"></a> <a name="logical_operators"></a>
## 逻辑运算 ## 逻辑运算
@ -372,11 +374,11 @@ Swift 提供了两个方便表达一个区间的值的运算符。
逻辑非运算(`!a`)对一个布尔值取反,使得`true``false``false``true` 逻辑非运算(`!a`)对一个布尔值取反,使得`true``false``false``true`
它是一个前置运算符,需出现在操作数之前,且不加空格。读作`非 a`,例子如下: 它是一个前置运算符,需紧跟在操作数之前,且不加空格。读作`非 a`,例子如下:
let allowedEntry = false let allowedEntry = false
if !allowedEntry { if !allowedEntry {
println("ACCESS DENIED") print("ACCESS DENIED")
} }
// 输出 "ACCESS DENIED" // 输出 "ACCESS DENIED"
@ -396,9 +398,9 @@ Swift 提供了两个方便表达一个区间的值的运算符。
let enteredDoorCode = true let enteredDoorCode = true
let passedRetinaScan = false let passedRetinaScan = false
if enteredDoorCode && passedRetinaScan { if enteredDoorCode && passedRetinaScan {
println("Welcome!") print("Welcome!")
} else { } else {
println("ACCESS DENIED") print("ACCESS DENIED")
} }
// 输出 "ACCESS DENIED" // 输出 "ACCESS DENIED"
@ -414,9 +416,9 @@ Swift 提供了两个方便表达一个区间的值的运算符。
let hasDoorKey = false let hasDoorKey = false
let knowsOverridePassword = true let knowsOverridePassword = true
if hasDoorKey || knowsOverridePassword { if hasDoorKey || knowsOverridePassword {
println("Welcome!") print("Welcome!")
} else { } else {
println("ACCESS DENIED") print("ACCESS DENIED")
} }
// 输出 "Welcome!" // 输出 "Welcome!"
@ -425,9 +427,9 @@ Swift 提供了两个方便表达一个区间的值的运算符。
我们可以组合多个逻辑运算来表达一个复合逻辑: 我们可以组合多个逻辑运算来表达一个复合逻辑:
if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword { if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
println("Welcome!") print("Welcome!")
} else { } else {
println("ACCESS DENIED") print("ACCESS DENIED")
} }
// 输出 "Welcome!" // 输出 "Welcome!"
@ -437,15 +439,18 @@ Swift 提供了两个方便表达一个区间的值的运算符。
前两种情况,我们都不满足,所以前两个简单逻辑的结果是`false`,但是我们是知道紧急情况下重置的密码的,所以整个复杂表达式的值还是`true` 前两种情况,我们都不满足,所以前两个简单逻辑的结果是`false`,但是我们是知道紧急情况下重置的密码的,所以整个复杂表达式的值还是`true`
>注意:
Swift 逻辑操作符`&&``||`是左结合的,这意味着拥有多元逻辑操作符的复合表达式优先计算最左边的子表达式。
### 使用括号来明确优先级 ### 使用括号来明确优先级
为了一个复杂表达式更容易读懂,在合适的地方使用括号来明确优先级是很有效的,虽然它并非必要的。在上个关于门的权限的例子中,我们给第一个部分加个括号,使用它看起来逻辑更明确: 为了一个复杂表达式更容易读懂,在合适的地方使用括号来明确优先级是很有效的,虽然它并非必要的。在上个关于门的权限的例子中,我们给第一个部分加个括号,使用它看起来逻辑更明确:
if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword { if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword {
println("Welcome!") print("Welcome!")
} else { } else {
println("ACCESS DENIED") print("ACCESS DENIED")
} }
// 输出 "Welcome!" // 输出 "Welcome!"

File diff suppressed because it is too large Load Diff

View File

@ -1,42 +1,103 @@
> 翻译:[zqp](https://github.com/zqp)
> 校对:[shinyzhu](https://github.com/shinyzhu), [stanzhai](https://github.com/stanzhai), [feiin](https://github.com/feiin)
# 集合类型 (Collection Types) # 集合类型 (Collection Types)
----------------- -----------------
> 1.0
> 翻译:[zqp](https://github.com/zqp)
> 校对:[shinyzhu](https://github.com/shinyzhu), [stanzhai](https://github.com/stanzhai), [feiin](https://github.com/feiin)
> 2.0
> 翻译+校对:[JackAlan](https://github.com/AlanMelody)
本页包含内容: 本页包含内容:
- [数组Arrays](#arrays)
- [集合(Sets)](#sets)
- [字典Dictionaries](#dictionaries)
- [集合的可变性Mutability of Collections](#mutability_of_collections) - [集合的可变性Mutability of Collections](#mutability_of_collections)
- [数组Arrays](#arrays)
- [集合Sets](#sets)
- [字典Dictionaries](#dictionaries)
Swift 语言提供经典的数组和字典两种集合类型来存储集合数据。数组用来按顺序存储相同类型的数据。字典虽然无序存储相同类型数据值但是需要由独有的标识符引用和寻址(就是键值对 Swift 语言提供`Arrays``Sets``Dictionaries`三种基本的集合类型来存储集合数据。数组是有序数据的集。集合是无序无重复数据的集。字典是无序的键值对的集
Swift 语言里的数组和字典中存储的数据值类型必须明确。 这意味着我们不能把不正确的数据类型插入其中。 同时这也说明我们完全可以对获取出的值类型非常自信。 Swift 对显式类型集合的使用确保了我们的代码对工作所需要的类型非常清楚,也让我们在开发中可以早早地找到任何的类型不匹配错误。 ![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/CollectionTypes_intro_2x.png)
Swift 语言中的`Arrays``Sets``Dictionaries`中存储的数据值类型必须明确。这意味着我们不能把不正确的数据类型插入其中。同时这也说明我们完全可以对取回值的类型非常自信。
> 注意: > 注意:
Swift 的数组结构在被声明成常量和变量或者被传入函数与方法中时会相对于其他类型展现出不同的特性。 获取更多信息请参见[集合的可变性](#mutability_of_collections)与[集合在赋值和复制中的行为](09_Classes_and_Structures.html#assignment_and_copy_behavior_for_collection_types)章节。 Swift 的`Arrays``Sets``Dictionaries`类型被实现为泛型集合。更多关于泛型类型和集合,参见 [泛型](./23_Generics.html)章节。
<a name="mutability_of_collections"></a>
## 集合的可变性
如果创建一个`Arrays``Sets``Dictionaries`并且把它分配成一个变量,这个集合将会是可变的。这意味着我们可以在创建之后添加更多或移除已存在的数据项来改变这个集合的大小。如果我们把`Arrays``Sets``Dictionaries`分配成常量,那么它就是不可变的,它的大小不能被改变。
> 注意:
在我们不需要改变集合大小的时候创建不可变集合是很好的习惯。如此 Swift 编译器可以优化我们创建的集合。
<a name="arrays"></a> <a name="arrays"></a>
## 数组 ## 数组(Arrays)
数组使用有序列表存储同一类型的多个值。相同的值可以多次出现在一个数组的不同位置中。 数组使用有序列表存储同一类型的多个值。相同的值可以多次出现在一个数组的不同位置中。
Swift 数组特定于它所存储元素的类型。这与 Objective-C 的 NSArray 和 NSMutableArray 不同,这两个类可以存储任意类型的对象,并且不提供所返回对象的任何特别信息。在 Swift 中,数据值在被存储进入某个数组之前类型必须明确,方法是通过显式的类型标注或类型推断,而且不是必须是`class`类型。例如: 如果我们创建了一个`Int`值类型的数组,我们不能往其中插入任何不是`Int`类型的数据。 Swift 中的数组是类型安全的,并且它们中包含的类型必须明确。 > 注意:
Swift 的`Array`类型被桥接到`Foundation`中的`NSArray`类。
更多关于在`Foundation``Cocoa`中使用`Array`的信息,参见 [*Using Swift with Cocoa and Obejective-C*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 一书。
<a name="array_type_shorthand_syntax"></a> <a name="array_type_shorthand_syntax"></a>
### 数组的简单语法 ### 数组的简单语法
写 Swift 数组应该遵循像`Array<SomeType>`这样的形式,其中`SomeType`是这个数组中唯一允许存在的数据类型。 我们也可以使用像`[SomeType]`这样的简单语法。 尽管两种形式在功能上是一样的,但是推荐较短的那种,而且在本文中都会使用这种形式来使用数组。 写 Swift 数组应该遵循像`Array<T>`这样的形式,其中`T`是这个数组中唯一允许存在的数据类型。我们也可以使用像`[T]`这样的简单语法。尽管两种形式在功能上是一样的,但是推荐较短的那种,而且在本文中都会使用这种形式来使用数组。
<a name="array_literals"></a> <a name="creating_an_empty_array"></a>
### 数组构造语句 ###创建一个空数组
我们可以使用构造语法来创建一个由特定数据类型构成的空数组:
```swift
var someInts = [Int]()
print("someInts is of type [Int] with \(someInts.count) items。")
// 打印 "someInts is of type [Int] with 0 items。"
```
注意 通过构造函数的类型,`someInts`的值类型被推断为`[Int]`
或者,如果代码上下文中已经提供了类型信息,例如一个函数参数或者一个已经定义好类型的常量或者变量,我们可以使用空数组语句创建一个空数组,它的写法很简单:`[]`(一对空方括号):
```swift
someInts.append(3)
// someInts 现在包含一个Int值
someInts = []
// someInts 现在是空数组,但是仍然是[Int]类型的。
```
<a name="creating_an_array_with_a_default_value"></a>
###创建一个带有默认值的数组
Swift 中的`Array`类型还提供一个可以创建特定大小并且所有数据都被默认的构造方法。我们可以把准备加入新数组的数据项数量(`count`)和适当类型的初始值(`repeatedValue`)传入数组构造函数:
```swift
var threeDoubles = [Double](count: 3, repeatedValue:0.0)
// threeDoubles 是一种 [Double]数组, 等于 [0.0, 0.0, 0.0]
```
<a name="creating_an_array_by_adding_two_arrays_together"></a>
###通过两个数组相加创建一个数组
我们可以使用加法操作符(`+`)来组合两种已存在的相同类型数组。新数组的数据类型会被从两个数组的数据类型中推断出来:
```swift
var anotherThreeDoubles = Array(count: 3, repeatedValue: 2.5)
// anotherThreeDoubles is inferred as [Double], and equals [2.5, 2.5, 2.5]
var sixDoubles = threeDoubles + anotherThreeDoubles
// sixDoubles 被推断为 [Double], 等于 [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]
```
<a name="creating_an_array_with_an_array_literals"></a>
### 用字面量构造数组
我们可以使用字面量来进行数组构造,这是一种用一个或者多个数值构造数组的简单方法。字面量是一系列由逗号分割并由方括号包含的数值。 我们可以使用字面量来进行数组构造,这是一种用一个或者多个数值构造数组的简单方法。字面量是一系列由逗号分割并由方括号包含的数值。
`[value 1, value 2, value 3]` `[value 1, value 2, value 3]`
下面这个例子创建了一个叫做`shoppingList`并且存储字符串的数组: 下面这个例子创建了一个叫做`shoppingList`并且存储`String`的数组:
```swift ```swift
var shoppingList: [String] = ["Eggs", "Milk"] var shoppingList: [String] = ["Eggs", "Milk"]
@ -65,7 +126,7 @@ var shoppingList = ["Eggs", "Milk"]
还可以使用数组的只读属性`count`来获取数组中的数据项数量。 还可以使用数组的只读属性`count`来获取数组中的数据项数量。
```swift ```swift
println("The shopping list contains \(shoppingList.count) items.") print("The shopping list contains \(shoppingList.count) items.")
// 输出"The shopping list contains 2 items."这个数组有2个项 // 输出"The shopping list contains 2 items."这个数组有2个项
``` ```
@ -73,9 +134,9 @@ println("The shopping list contains \(shoppingList.count) items.")
```swift ```swift
if shoppingList.isEmpty { if shoppingList.isEmpty {
println("The shopping list is empty.") print("The shopping list is empty.")
} else { } else {
println("The shopping list is not empty.") print("The shopping list is not empty.")
} }
// 打印 "The shopping list is not empty."shoppinglist不是空的 // 打印 "The shopping list is not empty."shoppinglist不是空的
``` ```
@ -120,9 +181,10 @@ shoppingList[4...6] = ["Bananas", "Apples"]
``` ```
> 注意: > 注意:
>我们不能使用下标语法在数组尾部添加新项。如果我们试着用这种方法对索引越界的数据进行检索或者设置新值的操作,我们会引发一个运行期错误。我们可以使用索引值和数组的`count`属性进行比较来在使用某个索引之前先检验是否有效。除了当`count`等于 0 时(说明这是个空数组),最大索引值一直是`count - 1`,因为数组都是零起索引 > 不可以用下标访问的形式去在数组尾部添加新项
调用数组的`insert(atIndex:)`方法来在某个具体索引值之前添加数据项:
调用数组的`insert(_:atIndex:)`方法来在某个具体索引值之前添加数据项:
```swift ```swift
shoppingList.insert("Maple Syrup", atIndex: 0) shoppingList.insert("Maple Syrup", atIndex: 0)
@ -140,6 +202,8 @@ let mapleSyrup = shoppingList.removeAtIndex(0)
// shoppingList 现在只有6项而且不包括Maple Syrup // shoppingList 现在只有6项而且不包括Maple Syrup
// mapleSyrup常量的值等于被移除数据项的值 "Maple Syrup" // mapleSyrup常量的值等于被移除数据项的值 "Maple Syrup"
``` ```
> 注意:
> 如果我们试着对索引越界的数据进行检索或者设置新值的操作,会引发一个运行期错误。我们可以使用索引值和数组的`count`属性进行比较来在使用某个索引之前先检验是否有效。除了当`count`等于 0 时(说明这是个空数组),最大索引值一直是`count - 1`,因为数组都是零起索引。
数据项被移除后数组中的空出项会被自动填补,所以现在索引值为`0`的数据项的值再次等于`"Six eggs"`: 数据项被移除后数组中的空出项会被自动填补,所以现在索引值为`0`的数据项的值再次等于`"Six eggs"`:
@ -148,7 +212,7 @@ firstItem = shoppingList[0]
// firstItem 现在等于 "Six eggs" // firstItem 现在等于 "Six eggs"
``` ```
如果我们只想把数组中的最后一项移除,可以使用`removeLast`方法而不是`removeAtIndex`方法来避免我们需要获取数组的`count`属性。就像后者一样,前者也会返回被移除的数据项: 如果我们只想把数组中的最后一项移除,可以使用`removeLast`方法而不是`removeAtIndex(_:)`方法来避免我们需要获取数组的`count`属性。就像后者一样,前者也会返回被移除的数据项:
```swift ```swift
let apples = shoppingList.removeLast() let apples = shoppingList.removeLast()
@ -164,7 +228,7 @@ let apples = shoppingList.removeLast()
```swift ```swift
for item in shoppingList { for item in shoppingList {
println(item) print(item)
} }
// Six eggs // Six eggs
// Milk // Milk
@ -173,11 +237,11 @@ for item in shoppingList {
// Bananas // Bananas
``` ```
如果我们同时需要每个数据项的值和索引值,可以使用全局`enumerate`函数来进行数组遍历。`enumerate`返回一个由每一个数据项索引值和数据值组成的元组。我们可以把这个元组分解成临时常量或者变量来进行遍历: 如果我们同时需要每个数据项的值和索引值,可以使用`enumerate()`方法来进行数组遍历。`enumerate()`返回一个由每一个数据项索引值和数据值组成的元组。我们可以把这个元组分解成临时常量或者变量来进行遍历:
```swift ```swift
for (index, value) in enumerate(shoppingList) { for (index, value) in shoppingList.enumerate() {
println("Item \(String(index + 1)): \(value)") print("Item \(String(index + 1)): \(value)")
} }
// Item 1: Six eggs // Item 1: Six eggs
// Item 2: Milk // Item 2: Milk
@ -188,146 +252,122 @@ for (index, value) in enumerate(shoppingList) {
更多关于`for-in`循环的介绍请参见[for 循环](05_Control_Flow.html#for_loops)。 更多关于`for-in`循环的介绍请参见[for 循环](05_Control_Flow.html#for_loops)。
<a name="creating_and_initializing_an_array"></a>
### 创建并且构造一个数组
我们可以使用构造语法来创建一个由特定数据类型构成的空数组:
```swift
var someInts = [Int]()
println("someInts is of type [Int] with \(someInts.count) items。")
// 打印 "someInts is of type [Int] with 0 items。"someInts是0数据项的Int[]数组)
```
注意`someInts`被设置为一个`[Int]`构造函数的输出所以它的变量类型被定义为`[Int]`
除此之外,如果代码上下文中提供了类型信息, 例如一个函数参数或者一个已经定义好类型的常量或者变量,我们可以使用空数组语句创建一个空数组,它的写法很简单:`[]`(一对空方括号):
```swift
someInts.append(3)
// someInts 现在包含一个INT值
someInts = []
// someInts 现在是空数组,但是仍然是[Int]类型的。
```
Swift 中的`Array`类型还提供一个可以创建特定大小并且所有数据都被默认的构造方法。我们可以把准备加入新数组的数据项数量(`count`)和适当类型的初始值(`repeatedValue`)传入数组构造函数:
```swift
var threeDoubles = [Double](count: 3, repeatedValue:0.0)
// threeDoubles 是一种 [Double]数组, 等于 [0.0, 0.0, 0.0]
```
因为类型推断的存在,我们使用这种构造方法的时候不需要特别指定数组中存储的数据类型,因为类型可以从默认值推断出来:
```swift
var anotherThreeDoubles = Array(count: 3, repeatedValue: 2.5)
// anotherThreeDoubles is inferred as [Double], and equals [2.5, 2.5, 2.5]
```
最后,我们可以使用加法操作符(`+`)来组合两种已存在的相同类型数组。新数组的数据类型会被从两个数组的数据类型中推断出来:
```swift
var sixDoubles = threeDoubles + anotherThreeDoubles
// sixDoubles 被推断为 [Double], 等于 [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]
```
<a name="sets"></a> <a name="sets"></a>
## 集合 ## 集合
集合用来存储相同类型并且没有确定顺序的值。当集合元素顺序不重要时或者希望确保每个元素只出现一次时可以把集合当做是数组另一形式。 集合(Set)用来存储相同类型并且没有确定顺序的值。当集合元素顺序不重要时或者希望确保每个元素只出现一次时可以把集合当做是数组另一形式。
> 注意: > 注意:
> Swift的<code>Set</code>类型被桥接到<code>Fundation</code>中的<code>NSSet</code>类 > Swift的`Set`类型被桥接到`Fundation`中的`NSSet`类。
> 关于使用<code>Fundation</code>和<code>Cocoa</code>中集合的知识,请看<a link="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216">Swift与Cocoa和Objective-C使用</a> > 关于使用`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)。
<a name="hash_values_for_set_types"></a>
#### Set类型的哈希值
为了存储在集合中,该类型必须是可哈希化的-也就是说,该类型必须提供一个方法来计算它的哈希值。一个哈希值是```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)
<a name="set_type_syntax"></a> <a name="set_type_syntax"></a>
### Set类型语法 ### Set类型语法
Swift中的<code>Set</code>类型被写为```Set<SomeType>```,这里的```SomeType```表示```Set```中允许存储的类型,和数组不同的是,集合没有等价的简化形式。 Swift中的`Set`类型被写为`Set<T>`, 这里的`T`表示`Set`中允许存储的类型,和数组不同的是,集合没有等价的简化形式。
<a name="create_and_initializing_a_set"></a> <a name="creating_and_initalizing_an_empty_set"></a>
### 创建和构造一个Set ### 创建和构造一个空的Set
你可以通过构造器语法创建一个特定类型的空集合: 你可以通过构造器语法创建一个特定类型的空集合:
```swift ```swift
var letters = Set<Character>() var letters = Set<Character>()
println("letters is of type Set<Character> with \(letters.count) items.") print("letters is of type Set<Character> with \(letters.count) items.")
// 打印 "letters is of type Set<Character> with 0 items." // 打印 "letters is of type Set<Character> with 0 items."
``` ```
注意这里的```letters```变量的类型来自于构造器的类型,其为```Set<Character>```。 > 注意:
> 通过构造器,这里的`letters`变量的类型被推断为`Set<Character>`。
外,如果上下文提供了类型信息,比如作为函数的参数或者已知类型的变量或常量,可以通过一个空的数组字面量创建一个空的```Set``` 外,如果上下文提供了类型信息,比如作为函数的参数或者已知类型的变量或常量,我们可以通过一个空的数组字面量创建一个空的`Set`
```swift ```swift
letters.insert("a") letters.insert("a")
// letters现在含有1个Character类型的值 // letters 现在含有1个Character类型的值
letters = [] letters = []
// letters现在是一个空的Set, 但是它依然是Set<Character>类型 // letters 现在是一个空的Set, 但是它依然是 Set<Character> 类型
``` ```
<a name="sets_with_arrays_literals"></a> <a name="creating_a_set_with_an_array_literal"></a>
### 集合与数组字面量 ### 数组字面量创建集合
你可以使用一个数组字面量来构造一个集合,并且可以使用简化形式写一个或者多个值作为集合元素。 你可以使用数组字面量来构造集合,并且可以使用简化形式写一个或者多个值作为集合元素。
下面的例子创建一个称之为```favoriteGenres```的集合来存储```String```类型的值: 下面的例子创建一个称之为`favoriteGenres`的集合来存储`String`类型的值:
```swift ```swift
var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"] var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
// favoriteGenres被构造成含有三个初始值的集合 // favoriteGenres被构造成含有三个初始值的集合
``` ```
这个```favoriteGenres```变量被声明为“一个```String```值的集合”,写为```Set<String>```。由于这个特定的集合含有指定```String```类型的值,所以它只允许存储```String```类型值。这里的```favoriteGenres```变量有三个```String```类型的初始值("```Rock```","```Classical```"和"```Hip hop```"),并以数组字面量的方式出现。 这个`favoriteGenres`变量被声明为“一个`String`值的集合”,写为`Set<String>`。由于这个特定的集合含有指定`String`类型的值,所以它只允许存储`String`类型值。这里的`favoriteGenres`变量有三个`String`类型的初始值("`Rock`","`Classical`"和"`Hip hop`")并以数组字面量的方式出现。
> 注意: > 注意:
> ```favoriteGenres```被声明为一个变量(拥有```var```标示符)而不是一个常量(拥有```let```标示符),因为它里面的元素将会在下面的例子中被增加或者移除。 > `favoriteGenres`被声明为一个变量(拥有`var`标示符)而不是一个常量(拥有`let`标示符),因为它里面的元素将会在下面的例子中被增加或者移除。
一个```Set```类型不能从数组中字面量中独立地被推断出来,因此```Set```类型必须显式声明。然而由于Swift的类型推导功能如果你想使用一个数组字面量构造一个Set并且该数组字面量中的所有元素类型相同那么你无须写出```Set```的具体类型。```favoriteGenres```的构造形式可以采用简化的方式代替: 一个`Set`类型不能从数组中字面量中独立地被推断出来,因此`Set`类型必须显式声明。然而由于Swift的类型推导功能如果你想使用一个数组字面量构造一个Set并且该数组字面量中的所有元素类型相同那么你无须写出`Set`的具体类型。`favoriteGenres`的构造形式可以采用简化的方式代替:
```swift ```swift
var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"] var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]
``` ```
由于数组字面量中的所有元素类型相同Swift可以推断出```Set<String>```作为```favoriteGenres```变量的正确类型。 由于数组字面量中的所有元素类型相同Swift可以推断出`Set<String>`作为`favoriteGenres`变量的正确类型。
<a name="accesing_and_modifying_a_set"></a> <a name="accesing_and_modifying_a_set"></a>
### 访问和修改一个Set ### 访问和修改一个Set
你可以通过```Set```的属性和方法来访问和修改一个```Set```. 你可以通过`Set`的属性和方法来访问和修改一个`Set`.
为了找出一个```Set```中元素的数量,可以使用其只读属性```count```: 为了找出一个`Set`中元素的数量,可以使用其只读属性`count`:
```swift ```swift
println("I have \(favoriteGenres.count) favorite music genres.") print("I have \(favoriteGenres.count) favorite music genres.")
// 打印 ""I have 3 favorite music genres."" // 打印 "I have 3 favorite music genres."
``` ```
使用布尔属性```isEmpty```作为一个缩写形式去检查```count```属性是否为```0```: 使用布尔属性`isEmpty`作为一个缩写形式去检查`count`属性是否为`0`:
```swift ```swift
if favoriteGenres.isEmpty { if favoriteGenres.isEmpty {
println("As far as music goes, I'm not picky.") print("As far as music goes, I'm not picky.")
} else { } else {
println("I have particular music preferences.") print("I have particular music preferences.")
} }
// 打印 "I have particular music preferences." // 打印 "I have particular music preferences."
``` ```
你可以通过调用```Set```的``` insert(_:) ```方法添加一个新元素: 你可以通过调用`Set`的` insert(_:) `方法添加一个新元素:
```swift ```swift
favoriteGenres.insert("Jazz") favoriteGenres.insert("Jazz")
// favoriteGenres 现在包含4个元素 // favoriteGenres 现在包含4个元素
``` ```
你可以通过调用```Set```的```remove(_:)```方法去删除一个元素,如果该值是该```Set```的一个元素则删除该元素并且返回被删除的元素值,否认如果该```Set```不包含该值,则返回```nil```。另外,```Set```中的所有元素可以通过它的```removeAll()```方法删除。 你可以通过调用`Set`的`remove(_:)`方法去删除一个元素,如果该值是该`Set`的一个元素则删除该元素并且返回被删除的元素值,否认如果该`Set`不包含该值,则返回`nil`。另外,`Set`中的所有元素可以通过它的`removeAll()`方法删除。
```swift ```swift
if let removedGenre = favoriteGenres.remove("Rock") { if let removedGenre = favoriteGenres.remove("Rock") {
println("\(removedValue)? I'm over it.") print("\(removedGenre)? I'm over it.")
} else { } else {
println("I never much cared for that.") print("I never much cared for that.")
} }
// 打印 "Rock? I'm over it." // 打印 "Rock? I'm over it."
``` ```
@ -336,9 +376,9 @@ if let removedGenre = favoriteGenres.remove("Rock") {
```swift ```swift
if favoriteGenres.contains("Funk") { if favoriteGenres.contains("Funk") {
println("I get up on the good foot.") print("I get up on the good foot.")
} else { } else {
println("It's too funky in here.") print("It's too funky in here.")
} }
// 打印 "It's too funky in here." // 打印 "It's too funky in here."
``` ```
@ -350,20 +390,20 @@ if favoriteGenres.contains("Funk") {
```swift ```swift
for genre in favoriteGenres { for genre in favoriteGenres {
println("\(genre)") print("\(genre)")
} }
// Classical // Classical
// Jazz // Jazz
// Hip hop // Hip hop
``` ```
更多关于```for-in```循环信息,请看<a link="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ControlFlow.html#//apple_ref/doc/uid/TP40014097-CH9-ID121">For循环</a> 更多关于`for-in`循环信息,参见[For循环](./05_Control_Flow.html#for_loops)。
Swift的```Set```类型没有确定的顺序,为了按照特定顺序来遍历一个```Set```中值可以使用全局```sorted```函数,它将根据提供的序列返回一个排序的集合. Swift 的`Set`类型没有确定的顺序,为了按照特定顺序来遍历一个`Set`中值可以使用`sort()`方法,它将根据提供的序列返回一个排序的集合.
```swift ```swift
-> for genre in sorted(favoriteGenres) { for genre in favoriteGenres.sort() {
println("\(genre)") print("\(genre)")
} }
// prints "Classical" // prints "Classical"
// prints "Hip hop" // prints "Hip hop"
@ -373,46 +413,46 @@ Swift的```Set```类型没有确定的顺序,为了按照特定顺序来遍历
<a name="performing_set_operations"></a> <a name="performing_set_operations"></a>
### 完成集合操作 ### 完成集合操作
你可以高效的完成```Set```的一些基本操作,比如把两个集合组合到一起,判断两个集合共有元素,或者判断两个集合是否全包含,部分包含或者不相交。 你可以高效的完成`Set`的一些基本操作,比如把两个集合组合到一起,判断两个集合共有元素,或者判断两个集合是否全包含,部分包含或者不相交。
<a name="constructing_sets"></a> <a name="fundamental_set_operations"></a>
#### 构造集合 #### 基本集合操作
下面的插图描述了两个集合-```a```和```b```-以及通过阴影部分的区域显示集合各种操作的结果。 下面的插图描述了两个集合-`a`和`b`-以及通过阴影部分的区域显示集合各种操作的结果。
![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/setVennDiagram_2x.png) ![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/setVennDiagram_2x.png)
* 使用```union(_:)```方法根据两个集合的值创建一个新的集合。 * 使用`intersect(_:)`方法根据两个集合中都包含的值创建一个新的集合。
* 使用```subtract(_:)```方法根据不在该集合中的值创建一个新的集合。 * 使用`exclusiveOr(_:)`方法根据值在一个集合中但不在两个集合中的值创建一个新的集合。
* 使用```intersect(_:)```方法根据两个集合中都包含的值创建一个新的集合。 * 使用`union(_:)`方法根据两个集合的值创建一个新的集合。
* 使用```exclusiveOr(_:)```方法根据值在一个集合中但不在两个集合中的值创建一个新的集合。 * 使用`subtract(_:)`方法根据不在该集合中的值创建一个新的集合。
```swift ```swift
let oddDigits: Set = [1, 3, 5, 7, 9] let oddDigits: Set = [1, 3, 5, 7, 9]
let evenDigits: Set = [0, 2, 4, 6, 8] let evenDigits: Set = [0, 2, 4, 6, 8]
let singleDigitPrimeNumbers: Set = [2, 3, 5, 7] let singleDigitPrimeNumbers: Set = [2, 3, 5, 7]
sorted(oddDigits.union(evenDigits)) oddDigits.union(evenDigits).sort()
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
sorted(oddDigits.intersect(evenDigits)) oddDigits.intersect(evenDigits).sort()
// [] // []
sorted(oddDigits.subtract(singleDigitPrimeNumbers)) oddDigits.subtract(singleDigitPrimeNumbers).sort()
// [1, 9] // [1, 9]
sorted(oddDigits.exclusiveOr(singleDigitPrimeNumbers)) oddDigits.exclusiveOr(singleDigitPrimeNumbers).sort()
// [1, 2, 9] // [1, 2, 9]
``` ```
<a name="comparing_sets"></a> <a name="set_membership_and_equality"></a>
#### 集合比较 #### 集合成员关系和相等
下面的插图描述了三个集合-```a```,```b```和```c```,以及通过悬浮区域表述集合间共享的元素。Set ```a```是Set ```b```的父集合,因为```a```包含了```b```中所有的元素相反的Set ```b```是```a```的子集合,因为属于```b```的元素也被```a```包含。Set ```b```和Set ```c```彼此不关联,因为它们之间没有共同的元素。 下面的插图描述了三个集合-`a`,`b`和`c`,以及通过悬浮区域表述集合间共享的元素。Set `a`是Set`b`的父集合,因为`a`包含了`b`中所有的元素相反的Set `b`是`a`的子集合,因为属于`b`的元素也被`a`包含。Set `b`和Set `c`彼此不关联,因为它们之间没有共同的元素。
![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/setEulerDiagram_2x.png) ![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/setEulerDiagram_2x.png)
* 使用“是否等”运算符(```==```)来判断两个集合是否包含相同的值。 * 使用“是否等”运算符(`==`)来判断两个集合是否包含全部相同的值。
* 使用```isSubsetOf(_:)```方法来判断一个集合中的值是否也被包含在另外一个集合中。 * 使用`isSubsetOf(_:)`方法来判断一个集合中的值是否也被包含在另外一个集合中。
* 使用```isSupersetOf(_:)```方法来判断一个集合中包含的值是另一个集合中所有的值。 * 使用`isSupersetOf(_:)`方法来判断一个集合中包含的值是另一个集合中所有的值。
* 使用```isStrictSubsetOf(_:)```或者```isStrictSupersetOf(_:)```方法来判断一个集合是否是另外一个集合的子集合或者父集合并且和特定集合不相等。 * 使用`isStrictSubsetOf(_:)`或者`isStrictSupersetOf(_:)`方法来判断一个集合是否是另外一个集合的子集合或者父集合并且和特定集合不相等。
* 使用```isDisjointWith(_:)```方法来判断两个结合是否不含有相同的值。 * 使用`isDisjointWith(_:)`方法来判断两个结合是否不含有相同的值。
```swift ```swift
let houseAnimals: Set = ["🐶", "🐱"] let houseAnimals: Set = ["🐶", "🐱"]
@ -420,28 +460,12 @@ let farmAnimals: Set = ["🐮", "🐔", "🐑", "🐶", "🐱"]
let cityAnimals: Set = ["🐦", "🐭"] let cityAnimals: Set = ["🐦", "🐭"]
houseAnimals.isSubsetOf(farmAnimals) houseAnimals.isSubsetOf(farmAnimals)
// true // true
farmAnimals.isSuperSetOf(houseAnimals) farmAnimals.isSupersetOf(houseAnimals)
// true // true
farmAnimals.isDisjointWith(cityAnimals) farmAnimals.isDisjointWith(cityAnimals)
// true // true
``` ```
<a name="hash_values_for_set_types"></a>
#### Set类型的哈希值
为了存储在集合中,该类型必须是可哈希化的-也就是说,该类型必须提供一个方法来计算它的哈希值。一个哈希值是```Int```类型的,它和其他的对象相同,其被用来比较相等与否,比如```a==b```,它遵循的是``` a.hashValue == b.hashValue```。
Swift的所有基本类型(比如```String```,```Int```,```Double```和```Bool```)默认都是可哈希化的,它可以作为集合的值或者字典的键值类型。没有关联值的枚举成员值(在[枚举部分](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Enumerations.html#//apple_ref/doc/uid/TP40014097-CH12-ID145)有讲述)默认也是可哈希化的。
>注意
>你可以使用你自定义的类型作为集合的值或者是字典的键值类型但你需要使你的自定义类型服从Swift标准库中的```Hashable```协议。服从```Hashable```协议的类型需要提供一个类型为```Int```的取值访问器属性```hashValue```。这个由类型的```hashValue```返回的值不需要在同一程序的不同执行周期或者不同程序之间保持相同。
>因为```hashable```协议服从于```Equatable```协议,所以遵循该协议的类型也必须提供一个"是否等"运算符(```==```)的实现。这个```Equatable```协议需要任何遵循的```==```的实现都是一种相等的关系。也就是说,对于```a,b,c```三个值来说,```==```的实现必须满足下面三种情况:
* ```a==a```(自反性)
* ```a==b```意味着```b==a```(对称性)
* ```a==b&&b==c```意味着```a==c```(传递性)
关于协议遵循的更多信息,请看[协议](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-ID267)
<a name="dictionaries"></a> <a name="dictionaries"></a>
@ -449,18 +473,54 @@ Swift的所有基本类型(比如```String```,```Int```,```Double```和```Bool``
字典是一种存储多个相同类型的值的容器。每个值value都关联唯一的键key键作为字典中的这个值数据的标识符。和数组中的数据项不同字典中的数据项并没有具体顺序。我们在需要通过标识符访问数据的时候使用字典这种方法很大程度上和我们在现实世界中使用字典查字义的方法一样。 字典是一种存储多个相同类型的值的容器。每个值value都关联唯一的键key键作为字典中的这个值数据的标识符。和数组中的数据项不同字典中的数据项并没有具体顺序。我们在需要通过标识符访问数据的时候使用字典这种方法很大程度上和我们在现实世界中使用字典查字义的方法一样。
> 注意:
> Swiftly 的`Dictionary` 类型被桥接到Foundation的`NSDictionary`类。
> 更多关于在`Foundation`和`Cocoa`中使用`Dictionary`类型的信息,参见 [*Using Swift with Cocoa and Obejective-C*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 一书。
<a name="dictionary_type_shorthand_syntax"></a>
## 字典类型快捷语法
<!--more
Swift 的字典使用时需要具体规定可以存储键和值类型。不同于 Objective-C 的`NSDictionary`和`NSMutableDictionary` 类可以使用任何类型的对象来作键和值并且不提供任何关于这些对象的本质信息。在 Swift 中,在某个特定字典中可以存储的键和值必须提前定义清楚,方法是通过显性类型标注或者类型推断。 Swift 的字典使用时需要具体规定可以存储键和值类型。不同于 Objective-C 的`NSDictionary`和`NSMutableDictionary` 类可以使用任何类型的对象来作键和值并且不提供任何关于这些对象的本质信息。在 Swift 中,在某个特定字典中可以存储的键和值必须提前定义清楚,方法是通过显性类型标注或者类型推断。
-->
Swift 的字典使用`Dictionary<Key, Value>`定义,其中`Key`是字典中键的数据类型,`Value`是字典中对应于这些键所存储值的数据类型。
Swift 的字典使用`Dictionary<KeyType, ValueType>`定义,其中`KeyType`是字典中键的数据类型,`ValueType`是字典中对应于这些键所存储值的数据类型。 > 注意:
> 一个字典的`Key`类型必须遵循`Hashable`协议,就像`Set`的值类型。
我们也可以用`[Key: Value]`这样快捷的形式去创建一个字典类型。虽然这俩种形式功能上相同,但是后者是首选,并且这本指导书涉及到字典类型时通篇采用后者。
<!--more
`KeyType`的唯一限制就是可哈希的,这样可以保证它是独一无二的,所有的 Swift 基本类型(例如`String``Int` `Double`和`Bool`)都是默认可哈希的,并且所有这些类型都可以在字典中当做键使用。未关联值的枚举成员(参见[枚举](08_Enumerations.html))也是默认可哈希的。 `KeyType`的唯一限制就是可哈希的,这样可以保证它是独一无二的,所有的 Swift 基本类型(例如`String``Int` `Double`和`Bool`)都是默认可哈希的,并且所有这些类型都可以在字典中当做键使用。未关联值的枚举成员(参见[枚举](08_Enumerations.html))也是默认可哈希的。
-->
<a name="creating_an_empty_dictionary"></a>
### 创建一个空字典
我们可以像数组一样使用构造语法创建一个拥有确定类型的空字典:
```swift
var namesOfIntegers = [Int: String]()
// namesOfIntegers 是一个空的 [Int: String] 字典
```
这个例子创建了一个`[Int: String]`类型的空字典来储存英语对整数的命名。它的键是`Int`型,值是`String`型。
如果上下文已经提供了信息类型,我们可以使用空字典字面量来创建一个空字典,记作`[:]`(中括号中放一个冒号):
```swift
namesOfIntegers[16] = "sixteen"
// namesOfIntegers 现在包含一个键值对
namesOfIntegers = [:]
// namesOfIntegers 又成为了一个 Int, String类型的空字典
```
<a name="creating_a_dictionary_with_a_dictionary_literal"></a>
<a name="dictionary_literals"></a>
## 字典字面量 ## 字典字面量
我们可以使用字典字面量来构造字典,它们和我们刚才介绍过的数组字面量拥有相似语法。一个字典字面量是一个定义拥有一个或多个键值对的字典集合的简单语句 我们可以使用字典字面量来构造字典,和我们刚才介绍过的数组字面量拥有相似语法。字典字面量是一种将写一个或多个键值对作`Dictionary`集合的快捷途径
一个键值对是一个`key`和一个`value`的结合体。在字典字面量中,每一个键值对的键和值都由冒号分割。这些键值对构成一个列表,其中这些键值对由方括号包含并且由逗号分割: 一个键值对是一个`key`和一个`value`的结合体。在字典字面量中,每一个键值对的键和值都由冒号分割。这些键值对构成一个列表,其中这些键值对由方括号包含由逗号分割:
```swift ```swift
[key 1: value 1, key 2: value 2, key 3: value 3] [key 1: value 1, key 2: value 2, key 3: value 3]
@ -469,48 +529,51 @@ Swift 的字典使用`Dictionary<KeyType, ValueType>`定义,其中`KeyType`是
下面的例子创建了一个存储国际机场名称的字典。在这个字典中键是三个字母的国际航空运输相关代码,值是机场名称: 下面的例子创建了一个存储国际机场名称的字典。在这个字典中键是三个字母的国际航空运输相关代码,值是机场名称:
```swift ```swift
var airports: [String:String] = ["TYO": "Tokyo", "DUB": "Dublin"] var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
``` ```
`airports`字典被定义为一种 `[String: String]`,它意味着这个字典的键和值都是`String`类型。 `airports`字典被声明为一种 `[String: String]`类型,这意味着这个字典的键和值都是`String`类型。
> 注意: > 注意:
> `airports`字典被声明为变量(用`var`关键字)而不是常量(`let`关键字)因为后来更多的机场信息会被添加到这个示例字典中。 > `airports`字典被声明为变量(用`var`关键字)而不是常量(`let`关键字)因为后来更多的机场信息会被添加到这个示例字典中。
`airports`字典使用字典字面量初始化,包含两个键值对。第一对的键是`TYO`,值是`Tokyo`。第二对的键是`DUB`,值是`Dublin`。 `airports`字典使用字典字面量初始化,包含两个键值对。第一对的键是`YYZ`,值是`Toronto Pearson`。第二对的键是`DUB`,值是`Dublin`。
这个字典语句包含了两个`String: String`类型的键值对。它们对应`airports`变量声明的类型(一个只有`String`键和`String`值的字典)所以这个字典字面量是构造两个初始数据项的`airport`字典。 这个字典语句包含了两个`String: String`类型的键值对。它们对应`airports`变量声明的类型(一个只有`String`键和`String`值的字典)所以这个字典字面量的任务是构造拥有两个初始数据项的`airport`字典。
和数组一样,如果我们使用字面量构造字典就不用把类型定义清楚。`airports`的也可以用这种方法简短定义: 和数组一样,我们用字典字面量构造字典时,如果它的键和值都有各自一致的类型,那么就不必写出字典的类型。
`airports`的也可以用这种方法简短定义:
```swift ```swift
var airports = ["TYO": "Tokyo", "DUB": "Dublin"] var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
``` ```
因为这个语句中所有的键和值都分别是相同的数据类型Swift 可以推断出`Dictionary<String, String>`是`airports`字典的正确类型。 因为这个语句中所有的键和值都各自拥有相同的数据类型Swift 可以推断出`Dictionary<String, String>`是`airports`字典的正确类型。
<a name="accessing_and_modifying_a_dictionary"></a> <a name="accessing_and_modifying_a_dictionary"></a>
### 读取和修改字典 ### 读取和修改字典
我们可以通过字典的方法和属性来读取和修改字典,或者使用下标语法。和数组一样,我们可以通过字典的只读属性`count`来获取某个字典的数据项数量: 我们可以通过字典的方法和属性来读取和修改字典,或者通过使用下标语法。
和数组一样,我们可以通过字典的只读属性`count`来获取某个字典的数据项数量:
```swift ```swift
println("The dictionary of airports contains \(airports.count) items.") print("The dictionary of airports contains \(airports.count) items.")
// 打印 "The dictionary of airports contains 2 items."(这个字典有两个数据项) // 打印 "The dictionary of airports contains 2 items."(这个字典有两个数据项)
``` ```
可以使用布尔属性`isEmpty`来快捷的检查字典的`count`属性是否等于0。 使用布尔属性`isEmpty`来快捷的检查字典的`count`属性是否等于0。
```swift ```swift
if airports.isEmpty { if airports.isEmpty {
println("The airports dictionary is empty.") print("The airports dictionary is empty.")
} else { } else {
println("The airports dictionary is not empty.") print("The airports dictionary is not empty.")
} }
// 打印 "The airports dictionary is not empty.(这个字典不为空)" // 打印 "The airports dictionary is not empty."
``` ```
我们也可以在字典中使用下标语法来添加新的数据项。可以使用一个合适类型的 key 作为下标索引,并且分配新的合适类型的值: 我们也可以在字典中使用下标语法来添加新的数据项。可以使用一个合适类型的键值作为下标索引,并且分配新的合适类型的值:
```swift ```swift
airports["LHR"] = "London" airports["LHR"] = "London"
@ -524,26 +587,28 @@ airports["LHR"] = "London Heathrow"
// "LHR"对应的值 被改为 "London Heathrow // "LHR"对应的值 被改为 "London Heathrow
``` ```
作为另一种下标方法,字典的`updateValue(forKey:)`方法可以设置或者更新特定键对应的值。就像上面所示的示例,`updateValue(forKey:)`方法在这个键不存在对应值的时候设置值或者在存在时更新已存在的值。和上面的下标方法不一样,这个方法返回更新值之前的原值。这样方便我们检查更新是否成功。 作为另一种下标方法,字典的`updateValue(_:forKey:)`方法可以设置或者更新特定键对应的值。就像上面所示的下标示例,`updateValue(_:forKey:)`方法在这个键不存在对应值的时候设置值或者在存在时更新已存在的值。和上面的下标方法不同的,`updateValue(_:forKey:)`这个方法返回更新值之前的原值。这样使得我们可以检查更新是否成功。
`updateValue(forKey:)`函数会返回包含一个字典值类型的可选值。举例来说:对于存储`String`值的字典,这个函数会返回一个`String?`或者“可选 `String`”类型的值。如果值存在,则这个可选值值等于被替换的值,否则将会是`nil`。 `updateValue(_:forKey:)`函数会返回包含一个字典值类型的可选值。举例来说:对于存储`String`值的字典,这个函数会返回一个`String?`或者“可选 `String`”类型的值。
如果有值存在于更新前,则这个可选值包含了旧值,否则它将会是`nil`。
```swift ```swift
if let oldValue = airports.updateValue("Dublin Internation", forKey: "DUB") { if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") {
println("The old value for DUB was \(oldValue).") print("The old value for DUB was \(oldValue).")
} }
// 输出 "The old value for DUB was Dublin."DUB原值是dublin // 输出 "The old value for DUB was Dublin."
``` ```
我们也可以使用下标语法来在字典中检索特定键对应的值。由于使用一个没有值的键这种情况是有可能发生的,可选类型返回这个存在的相关值,否则返回`nil` 我们也可以使用下标语法来在字典中检索特定键对应的值。因为有可能请求的键没有对应的值存在,字典的下标访问会返回一个字典值类型的可选值。如果这个字典包含请求键所对应的值,下标会返回一个包含这个存在值的可选值,否则返回`nil`
```swift ```swift
if let airportName = airports["DUB"] { if let airportName = airports["DUB"] {
println("The name of the airport is \(airportName).") print("The name of the airport is \(airportName).")
} else { } else {
println("That airport is not in the airports dictionary.") print("That airport is not in the airports dictionary.")
} }
// 打印 "The name of the airport is Dublin Internation."(机场的名字是都柏林国际) // 打印 "The name of the airport is Dublin Airport."
``` ```
我们还可以使用下标语法来通过给某个键的对应值赋值为`nil`来从字典里移除一个键值对: 我们还可以使用下标语法来通过给某个键的对应值赋值为`nil`来从字典里移除一个键值对:
@ -555,45 +620,45 @@ airports["APL"] = nil
// APL现在被移除了 // APL现在被移除了
``` ```
外,`removeValueForKey`方法也可以用来在字典中移除键值对。这个方法在键值对存在的情况下会移除该键值对并且返回被移除的value或者在没有值的情况下返回`nil` 外,`removeValueForKey(_:)`方法也可以用来在字典中移除键值对。这个方法在键值对存在的情况下会移除该键值对并且返回被移除的或者在没有值的情况下返回`nil`
```swift ```swift
if let removedValue = airports.removeValueForKey("DUB") { if let removedValue = airports.removeValueForKey("DUB") {
println("The removed airport's name is \(removedValue).") print("The removed airport's name is \(removedValue).")
} else { } else {
println("The airports dictionary does not contain a value for DUB.") print("The airports dictionary does not contain a value for DUB.")
} }
// prints "The removed airport's name is Dublin International." // prints "The removed airport's name is Dublin Airport."
``` ```
<a name="iterating_over_a_dictionary"></a> <a name="iterating_over_a_dictionary"></a>
### 字典遍历 ### 字典遍历
我们可以使用`for-in`循环来遍历某个字典中的键值对。每一个字典中的数据项都`(key, value)`元组形式返回,并且我们可以使用临时常量或者变量来分解这些元组: 我们可以使用`for-in`循环来遍历某个字典中的键值对。每一个字典中的数据项都`(key, value)`元组形式返回,并且我们可以使用临时常量或者变量来分解这些元组:
```swift ```swift
for (airportCode, airportName) in airports { for (airportCode, airportName) in airports {
println("\(airportCode): \(airportName)") print("\(airportCode): \(airportName)")
} }
// TYO: Tokyo // YYZ: Toronto Pearson
// LHR: London Heathrow // LHR: London Heathrow
``` ```
`for-in`循环参见[For 循环](05_Control_Flow.html#for_loops)。 更多关于`for-in`循环的信息,参见[For 循环](./05_Control_Flow.html#for_loops)。
我们也可以通过访问它的`keys`或者`values`属性(都是可遍历集合)检索一个字典的键或者值 通过访问`keys`或者`values`属性,我们也可以遍历字典的键或者值
```swift ```swift
for airportCode in airports.keys { for airportCode in airports.keys {
println("Airport code: \(airportCode)") print("Airport code: \(airportCode)")
} }
// Airport code: TYO // Airport code: YYZ
// Airport code: LHR // Airport code: LHR
for airportName in airports.values { for airportName in airports.values {
println("Airport name: \(airportName)") print("Airport name: \(airportName)")
} }
// Airport name: Tokyo // Airport name: Toronto Pearson
// Airport name: London Heathrow // Airport name: London Heathrow
``` ```
@ -601,47 +666,10 @@ for airportName in airports.values {
```swift ```swift
let airportCodes = Array(airports.keys) let airportCodes = Array(airports.keys)
// airportCodes is ["TYO", "LHR"] // airportCodes is ["YYZ", "LHR"]
let airportNames = Array(airports.values) let airportNames = Array(airports.values)
// airportNames is ["Tokyo", "London Heathrow"] // airportNames is ["Toronto Pearson", "London Heathrow"]
``` ```
> 注意: Swift 的字典类型是无序集合类型。为了以特定的顺序遍历字典的键或值,可以对字典的`keys`或`values`属性使用`sort()`方法。
> Swift 的字典类型是无序集合类型。其中字典键,值,键值对在遍历的时候会重新排列,而且其中顺序是不固定的。
<a name="creating_an_empty_dictionary"></a>
### 创建一个空字典
我们可以像数组一样使用构造语法创建一个空字典:
```swift
var namesOfIntegers = Dictionary<Int, String>()
// namesOfIntegers 是一个空的 Dictionary<Int, String>
```
这个例子创建了一个`Int, String`类型的空字典来储存英语对整数的命名。它的键是`Int`型,值是`String`型。
如果上下文已经提供了信息类型,我们可以使用空字典字面量来创建一个空字典,记作`[:]`(中括号中放一个冒号):
```swift
namesOfIntegers[16] = "sixteen"
// namesOfIntegers 现在包含一个键值对
namesOfIntegers = [:]
// namesOfIntegers 又成为了一个 Int, String类型的空字典
```
> 注意:
> 在后台Swift 的数组和字典都是由泛型集合来实现的,想了解更多泛型和集合信息请参见[泛型](22_Generics.html)。
<a name="mutability_of_collections"></a>
## 集合的可变性
数组和字典都是在单个集合中存储可变值。如果我们创建一个数组或者字典并且把它分配成一个变量,这个集合将会是可变的。这意味着我们可以在创建之后添加更多或移除已存在的数据项来改变这个集合的大小。与此相反,如果我们把数组或字典分配成常量,那么它就是不可变的,它的大小不能被改变。
相反,如果你给常量赋值一个数组、集合或者字典,那它就是不可变的,大小和内容都不能修改。
Swift 数组的可变性行为同时影响了数组实例如何被分配和修改,想获取更多信息,请参见[集合在赋值和复制中的行为](09_Classes_and_Structures.html#assignment_and_copy_behavior_for_collection_types)。
> 注意:
> 在我们不需要改变数组大小的时候创建不可变数组是很好的习惯。如此 Swift 编译器可以优化我们创建的集合。

View File

@ -1,8 +1,12 @@
# 控制流Control Flow
-----------------
> 1.0
> 翻译:[vclwei](https://github.com/vclwei), [coverxit](https://github.com/coverxit), [NicePiao](https://github.com/NicePiao) > 翻译:[vclwei](https://github.com/vclwei), [coverxit](https://github.com/coverxit), [NicePiao](https://github.com/NicePiao)
> 校对:[coverxit](https://github.com/coverxit), [stanzhai](https://github.com/stanzhai) > 校对:[coverxit](https://github.com/coverxit), [stanzhai](https://github.com/stanzhai)
# 控制流 > 2.0
----------------- > 翻译+校对:[JackAlan](https://github.com/AlanMelody)
本页包含内容: 本页包含内容:
@ -11,19 +15,19 @@
- [条件语句](#conditional_statement) - [条件语句](#conditional_statement)
- [控制转移语句Control Transfer Statements](#control_transfer_statements) - [控制转移语句Control Transfer Statements](#control_transfer_statements)
Swift提供了类似 C 语言的流程控制结构,包括可以多次执行任务的`for``while`循环,基于特定条件选择执行不同代码分支的`if``switch`语句,还有控制流程跳转到其他代码的`break``continue`语句。 Swift提供了类似 C 语言的流程控制结构,包括可以多次执行任务的`for``while`循环,基于特定条件选择执行不同代码分支的`if``guard``switch`语句,还有控制流程跳转到其他代码的`break``continue`语句。
除了 C 语言里面传统的 for 条件递增(`for-condition-increment`循环Swift 还增加了`for-in`循环用来更简单地遍历数组array字典dictionary区间range字符串string和其他序列类型。 除了 C 语言里面传统的 for 循环Swift 还增加了`for-in`循环用来更简单地遍历数组array字典dictionary区间range字符串string和其他序列类型。
Swift 的`switch`语句比 C 语言中更加强大。在 C 语言中,如果某个 case 不小心漏写了`break`,这个 case 就会贯穿fallthrough至下一个 caseSwift 无需写`break`,所以不会发生这种贯穿fallthrough的情况。case 还可以匹配更多的类型模式包括区间匹配range matching元组tuple和特定类型的描述。`switch`的 case 语句中匹配的值可以是由 case 体内部临时的常量或者变量决定,也可以由`where`分句描述更复杂的匹配条件。 Swift 的`switch`语句比 C 语言中更加强大。在 C 语言中,如果某个 case 不小心漏写了`break`,这个 case 就会贯穿至下一个 caseSwift 无需写`break`所以不会发生这种贯穿的情况。case 还可以匹配更多的类型模式包括区间匹配range matching元组tuple和特定类型的描述。`switch`的 case 语句中匹配的值可以是由 case 体内部临时的常量或者变量决定,也可以由`where`分句描述更复杂的匹配条件。
<a name="for_loops"></a> <a name="for_loops"></a>
## For 循环 ## For 循环
`for`循环来按照指定的次数多次执行一系列语句。Swift 提供两种`for`循环形式 Swift 提供两种`for`循环形式以来按照指定的次数多次执行一系列语句:
* `for-in`用来遍历一个区间range序列sequence集合collection系列progression里面所有的元素执行一系列语句。 * `for-in`循环对一个集合里面的每个元素执行一系列语句。
* for条件递增(`for-condition-increment`)语句,用来重复执行一系列语句直到达成特定条件达成,一般通过在每次循环完成后增加计数器的值来实现。 * for循环,用来重复执行一系列语句直到达成特定条件达成,一般通过在每次循环完成后增加计数器的值来实现。
<a name="for_in"></a> <a name="for_in"></a>
### For-In ### For-In
@ -34,7 +38,7 @@ Swift 的`switch`语句比 C 语言中更加强大。在 C 语言中,如果某
```swift ```swift
for index in 1...5 { for index in 1...5 {
println("\(index) times 5 is \(index * 5)") print("\(index) times 5 is \(index * 5)")
} }
// 1 times 5 is 5 // 1 times 5 is 5
// 2 times 5 is 10 // 2 times 5 is 10
@ -43,13 +47,10 @@ for index in 1...5 {
// 5 times 5 is 25 // 5 times 5 is 25
``` ```
例子中用来进行遍历的元素是一组使用闭区间操作符(`...`)表示的从`1``5`的数字。`index`被赋值为闭区间中的第一个数字(`1`),然后循环中的语句被执行一次。在本例中,这个循环只包含一个语句,用来输出当前`index`值所对应的乘 5 乘法表结果。该语句执行后,`index`的值被更新为闭区间中的第二个数字(`2`),之后`println`方法会再执行一次。整个过程会进行到闭区间结尾为止。 例子中用来进行遍历的元素是一组使用闭区间操作符(`...`)表示的从`1``5`的数字。`index`被赋值为闭区间中的第一个数字(`1`),然后循环中的语句被执行一次。在本例中,这个循环只包含一个语句,用来输出当前`index`值所对应的乘 5 乘法表结果。该语句执行后,`index`的值被更新为闭区间中的第二个数字(`2`),之后`print(_:)`函数会再执行一次。整个过程会进行到闭区间结尾为止。
上面的例子中,`index`是一个每次循环遍历开始时被自动赋值的常量。这种情况下,`index`在使用前不需要声明,只需要将它包含在循环的声明中,就可以对其进行隐式声明,而无需使用`let`关键字声明。 上面的例子中,`index`是一个每次循环遍历开始时被自动赋值的常量。这种情况下,`index`在使用前不需要声明,只需要将它包含在循环的声明中,就可以对其进行隐式声明,而无需使用`let`关键字声明。
>注意:
`index`常量只存在于循环的生命周期里。如果你想在循环完成后访问`index`的值,又或者想让`index`成为一个变量而不是常量,你必须在循环之前自己进行声明。
如果你不需要知道区间内每一项的值,你可以使用下划线(`_`)替代变量名来忽略对值的访问: 如果你不需要知道区间内每一项的值,你可以使用下划线(`_`)替代变量名来忽略对值的访问:
```swift ```swift
@ -59,7 +60,7 @@ var answer = 1
for _ in 1...power { for _ in 1...power {
answer *= base answer *= base
} }
println("\(base) to the power of \(power) is \(answer)") print("\(base) to the power of \(power) is \(answer)")
// 输出 "3 to the power of 10 is 59049" // 输出 "3 to the power of 10 is 59049"
``` ```
@ -70,7 +71,7 @@ println("\(base) to the power of \(power) is \(answer)")
```swift ```swift
let names = ["Anna", "Alex", "Brian", "Jack"] let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names { for name in names {
println("Hello, \(name)!") print("Hello, \(name)!")
} }
// Hello, Anna! // Hello, Anna!
// Hello, Alex! // Hello, Alex!
@ -78,41 +79,27 @@ for name in names {
// Hello, Jack! // Hello, Jack!
``` ```
你也可以通过遍历一个字典来访问它的键值对key-value pairs。遍历字典时,字典的每项元素会以`(key, value)`元组的形式返回,你可以在`for-in`循环中使用显式的常量名称来解读`(key, value)`元组。下面的例子中字典的键key解读为常量`animalName`,字典的值会被解读为常量`legCount` 你也可以通过遍历一个字典来访问它的键值对。遍历字典时,字典的每项元素会以`(key, value)`元组的形式返回,你可以在`for-in`循环中使用显式的常量名称来解读`(key, value)`元组。下面的例子中字典的键key解读为常量`animalName`,字典的值会被解读为常量`legCount`
```swift ```swift
let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4] let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
for (animalName, legCount) in numberOfLegs { for (animalName, legCount) in numberOfLegs {
println("\(animalName)s have \(legCount) legs") print("\(animalName)s have \(legCount) legs")
} }
// spiders have 8 legs
// ants have 6 legs // ants have 6 legs
// cats have 4 legs // cats have 4 legs
// spiders have 8 legs
``` ```
字典元素的遍历顺序和插入顺序可能不同,字典的内容在内部是无序的,所以遍历元素时不能保证顺序。关于数组和字典,详情参见[集合类型](../chapter2/04_Collection_Types.html)。 字典元素的遍历顺序和插入顺序可能不同,字典的内容在内部是无序的,所以遍历元素时不能保证顺序。关于数组和字典,详情参见[集合类型](./04_Collection_Types.html)。
除了数组和字典,你也可以使用`for-in`循环来遍历字符串中的字符(`Character` <a name="for"></a>
```swift
for character in "Hello" {
println(character)
}
// H
// e
// l
// l
// o
```
<a name="for_condition_increment"></a>
### For条件递增for-condition-increment
除了`for-in`循环Swift 提供使用条件判断和递增方法的标准 C 样式`for`循环: 除了`for-in`循环Swift 提供使用条件判断和递增方法的标准 C 样式`for`循环:
```swift ```swift
for var index = 0; index < 3; ++index { for var index = 0; index < 3; ++index {
println("index is \(index)") print("index is \(index)")
} }
// index is 0 // index is 0
// index is 1 // index is 1
@ -129,30 +116,23 @@ for var index = 0; index < 3; ++index {
这个循环执行流程如下: 这个循环执行流程如下:
1. 循环首次启动时,初始化表达式_initialization expression_被调用一次,用来初始化循环所需的所有常量和变量。 1. 循环首次启动时,初始化表达式被调用一次,用来初始化循环所需的所有常量和变量。
2. 条件表达式_condition expression_被调用,如果表达式调用结果为`false`,循环结束,继续执行`for`循环关闭大括号 2. 条件表达式被调用,如果表达式调用结果为`false`,循环结束,继续执行`for`循环关闭大括号
`}`)之后的代码。如果表达式调用结果为`true`,则会执行大括号内部的代码_statements_ `}`)之后的代码。如果表达式调用结果为`true`,则会执行大括号内部的代码。
3. 执行所有语句_statements_之后执行递增表达式_increment expression_。通常会增加或减少计数器的值,或者根据语句_statements_输出来修改某一个初始化的变量。当递增表达式运行完成后,重复执行第 2 步,条件表达式会再次执行。 3. 执行所有语句之后,执行递增表达式。通常会增加或减少计数器的值,或者根据语句输出来修改某一个初始化的变量。当递增表达式运行完成后,重复执行第 2 步,条件表达式会再次执行。
上述描述和循环格式等同于:
> `initialization`
> while `condition` {
> `statements`
> `increment`
> }
在初始化表达式中声明的常量和变量(比如`var index = 0`)只在`for`循环的生命周期里有效。如果想在循环结束后访问`index`的值,你必须要在循环生命周期开始前声明`index` 在初始化表达式中声明的常量和变量(比如`var index = 0`)只在`for`循环的生命周期里有效。如果想在循环结束后访问`index`的值,你必须要在循环生命周期开始前声明`index`
```swift ```swift
var index: Int var index: Int
for index = 0; index < 3; ++index { for index = 0; index < 3; ++index {
println("index is \(index)") print("index is \(index)")
} }
// index is 0 // index is 0
// index is 1 // index is 1
// index is 2 // index is 2
println("The loop statements were executed \(index) times") print("The loop statements were executed \(index) times")
// 输出 "The loop statements were executed 3 times // 输出 "The loop statements were executed 3 times
``` ```
@ -164,7 +144,7 @@ println("The loop statements were executed \(index) times")
`while`循环运行一系列语句直到条件变成`false`。这类循环适合使用在第一次迭代前迭代次数未知的情况下。Swift 提供两种`while`循环形式: `while`循环运行一系列语句直到条件变成`false`。这类循环适合使用在第一次迭代前迭代次数未知的情况下。Swift 提供两种`while`循环形式:
* `while`循环,每次在循环开始时计算条件是否符合; * `while`循环,每次在循环开始时计算条件是否符合;
* `do-while`循环,每次在循环结束时计算条件是否符合。 * `repeat-while`循环,每次在循环结束时计算条件是否符合。
<a name="while"></a> <a name="while"></a>
###While ###While
@ -177,7 +157,7 @@ println("The loop statements were executed \(index) times")
> `statements` > `statements`
> } > }
下面的例子来玩一个叫做_蛇和梯子Snakes and Ladders_的小游戏,也叫做_滑道和梯子Chutes and Ladders_ 下面的例子来玩一个叫做蛇和梯子的小游戏,也叫做滑道和梯子:
![image](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/snakesAndLadders_2x.png) ![image](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/snakesAndLadders_2x.png)
@ -219,7 +199,7 @@ while square < finalSquare {
square += board[square] square += board[square]
} }
} }
println("Game over!") print("Game over!")
``` ```
本例中使用了最简单的方法来模拟掷骰子。 `diceRoll`的值并不是一个随机数,而是以`0`为初始值,之后每一次`while`循环,`diceRoll`的值使用前置自增操作符(`++i`)来自增 1 ,然后检测是否超出了最大值。`++diceRoll`调用完成_后_返回值等于`diceRoll`自增后的值。任何时候如果`diceRoll`的值等于7时就超过了骰子的最大值会被重置为`1`。所以`diceRoll`的取值顺序会一直是`1``2``3``4``5``6``1``2` 本例中使用了最简单的方法来模拟掷骰子。 `diceRoll`的值并不是一个随机数,而是以`0`为初始值,之后每一次`while`循环,`diceRoll`的值使用前置自增操作符(`++i`)来自增 1 ,然后检测是否超出了最大值。`++diceRoll`调用完成_后_返回值等于`diceRoll`自增后的值。任何时候如果`diceRoll`的值等于7时就超过了骰子的最大值会被重置为`1`。所以`diceRoll`的取值顺序会一直是`1``2``3``4``5``6``1``2`
@ -233,18 +213,21 @@ println("Game over!")
`while` 循环比较适合本例中的这种情况,因为在 `while` 循环开始时,我们并不知道游戏的长度或者循环的次数,只有在达成指定条件时循环才会结束。 `while` 循环比较适合本例中的这种情况,因为在 `while` 循环开始时,我们并不知道游戏的长度或者循环的次数,只有在达成指定条件时循环才会结束。
<a name="do_while"></a> <a name="repeat_while"></a>
###Do-While ###Repeat-While
`while`循环的另外一种形式是`do-while`,它和`while`的区别是在判断循环条件之前,先执行一次循环的代码块,然后重复循环直到条件为`false` `while`循环的另外一种形式是`repeat-while`,它和`while`的区别是在判断循环条件之前,先执行一次循环的代码块,然后重复循环直到条件为`false`
下面是一般情况下 `do-while`循环的格式 > 注意
> Swift语言的`repeat-while`循环合其他语言中的`do-while`循环是类似的。
> do { 下面是一般情况下 `repeat-while`循环的格式:
> repeat {
> `statements` > `statements`
> } while `condition` > } while `condition`
还是蛇和梯子的游戏,使用`do-while`循环来替代`while`循环。`finalSquare``board``square``diceRoll`的值初始化同`while`循环一样: 还是蛇和梯子的游戏,使用`repeat-while`循环来替代`while`循环。`finalSquare``board``square``diceRoll`的值初始化同`while`循环一样:
``` swift ``` swift
let finalSquare = 25 let finalSquare = 25
@ -255,12 +238,12 @@ var square = 0
var diceRoll = 0 var diceRoll = 0
``` ```
`do-while`的循环版本循环中_第一步_就需要去检测是否在梯子或者蛇的方块上。没有梯子会让玩家直接上到第 25 个方格,所以玩家不会通过梯子直接赢得游戏。这样在循环开始时先检测是否踩在梯子或者蛇上是安全的。 `repeat-while`的循环版本循环中_第一步_就需要去检测是否在梯子或者蛇的方块上。没有梯子会让玩家直接上到第 25 个方格,所以玩家不会通过梯子直接赢得游戏。这样在循环开始时先检测是否踩在梯子或者蛇上是安全的。
游戏开始时,玩家在第 0 个方格上,`board[0]`一直等于 0 不会有什么影响: 游戏开始时,玩家在第 0 个方格上,`board[0]`一直等于 0 不会有什么影响:
```swift ```swift
do { repeat {
// 顺着梯子爬上去或者顺着蛇滑下去 // 顺着梯子爬上去或者顺着蛇滑下去
square += board[square] square += board[square]
// 掷骰子 // 掷骰子
@ -268,12 +251,12 @@ do {
// 根据点数移动 // 根据点数移动
square += diceRoll square += diceRoll
} while square < finalSquare } while square < finalSquare
println("Game over!") print("Game over!")
``` ```
检测完玩家是否踩在梯子或者蛇上之后,开始掷骰子,然后玩家向前移动`diceRoll`个方格,本轮循环结束。 检测完玩家是否踩在梯子或者蛇上之后,开始掷骰子,然后玩家向前移动`diceRoll`个方格,本轮循环结束。
循环条件(`while square < finalSquare`)和`while`方式相同,但是只会在循环结束后进行计算。在这个游戏中,`do-while`表现得比`while`循环更好。`do-while`方式会在条件判断`square`没有超出后直接运行`square += board[square]`,这种方式可以去掉`while`版本中的数组越界判断。 循环条件(`while square < finalSquare`)和`while`方式相同,但是只会在循环结束后进行计算。在这个游戏中,`repeat-while`表现得比`while`循环更好。`repeat-while`方式会在条件判断`square`没有超出后直接运行`square += board[square]`,这种方式可以去掉`while`版本中的数组越界判断。
<a name="conditional_statement"></a> <a name="conditional_statement"></a>
## 条件语句 ## 条件语句
@ -290,7 +273,7 @@ Swift 提供两种类型的条件语句:`if`语句和`switch`语句。通常
```swift ```swift
var temperatureInFahrenheit = 30 var temperatureInFahrenheit = 30
if temperatureInFahrenheit <= 32 { if temperatureInFahrenheit <= 32 {
println("It's very cold. Consider wearing a scarf.") print("It's very cold. Consider wearing a scarf.")
} }
// 输出 "It's very cold. Consider wearing a scarf." // 输出 "It's very cold. Consider wearing a scarf."
``` ```
@ -302,9 +285,9 @@ if temperatureInFahrenheit <= 32 {
```swift ```swift
temperatureInFahrenheit = 40 temperatureInFahrenheit = 40
if temperatureInFahrenheit <= 32 { if temperatureInFahrenheit <= 32 {
println("It's very cold. Consider wearing a scarf.") print("It's very cold. Consider wearing a scarf.")
} else { } else {
println("It's not that cold. Wear a t-shirt.") print("It's not that cold. Wear a t-shirt.")
} }
// 输出 "It's not that cold. Wear a t-shirt." // 输出 "It's not that cold. Wear a t-shirt."
``` ```
@ -316,11 +299,11 @@ if temperatureInFahrenheit <= 32 {
```swift ```swift
temperatureInFahrenheit = 90 temperatureInFahrenheit = 90
if temperatureInFahrenheit <= 32 { if temperatureInFahrenheit <= 32 {
println("It's very cold. Consider wearing a scarf.") print("It's very cold. Consider wearing a scarf.")
} else if temperatureInFahrenheit >= 86 { } else if temperatureInFahrenheit >= 86 {
println("It's really warm. Don't forget to wear sunscreen.") print("It's really warm. Don't forget to wear sunscreen.")
} else { } else {
println("It's not that cold. Wear a t-shirt.") print("It's not that cold. Wear a t-shirt.")
} }
// 输出 "It's really warm. Don't forget to wear sunscreen." // 输出 "It's really warm. Don't forget to wear sunscreen."
``` ```
@ -330,11 +313,11 @@ if temperatureInFahrenheit <= 32 {
实际上,最后的`else`语句是可选的: 实际上,最后的`else`语句是可选的:
```swift ```swift
temperatureInFahrenheit = 72 temperatureInFahrenheit = 90
if temperatureInFahrenheit <= 32 { if temperatureInFahrenheit <= 32 {
println("It's very cold. Consider wearing a scarf.") print("It's very cold. Consider wearing a scarf.")
} else if temperatureInFahrenheit >= 86 { } else if temperatureInFahrenheit >= 86 {
println("It's really warm. Don't forget to wear sunscreen.") print("It's really warm. Don't forget to wear sunscreen.")
} }
``` ```
@ -361,7 +344,7 @@ if temperatureInFahrenheit <= 32 {
每一个 case 都是代码执行的一条分支,这与`if`语句类似。与之不同的是,`switch`语句会决定哪一条分支应该被执行。 每一个 case 都是代码执行的一条分支,这与`if`语句类似。与之不同的是,`switch`语句会决定哪一条分支应该被执行。
`switch`语句必须是_完备的_。这就是说,每一个可能的值都必须至少有一个 case 分支与之对应。在某些不可能涵盖所有值的情况下,你可以使用默认(`default`)分支满足该要求,这个默认分支必须在`switch`语句的最后面。 `switch`语句必须是完备的。这就是说,每一个可能的值都必须至少有一个 case 分支与之对应。在某些不可能涵盖所有值的情况下,你可以使用默认(`default`)分支满足该要求,这个默认分支必须在`switch`语句的最后面。
下面的例子使用`switch`语句来匹配一个名为`someCharacter`的小写字符: 下面的例子使用`switch`语句来匹配一个名为`someCharacter`的小写字符:
@ -369,12 +352,12 @@ if temperatureInFahrenheit <= 32 {
let someCharacter: Character = "e" let someCharacter: Character = "e"
switch someCharacter { switch someCharacter {
case "a", "e", "i", "o", "u": case "a", "e", "i", "o", "u":
println("\(someCharacter) is a vowel") print("\(someCharacter) is a vowel")
case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
"n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z": "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
println("\(someCharacter) is a consonant") print("\(someCharacter) is a consonant")
default: default:
println("\(someCharacter) is not a vowel or a consonant") print("\(someCharacter) is not a vowel or a consonant")
} }
// 输出 "e is a vowel" // 输出 "e is a vowel"
``` ```
@ -389,7 +372,7 @@ default:
与 C 语言和 Objective-C 中的`switch`语句不同,在 Swift 中,当匹配的 case 分支中的代码执行完毕后,程序会终止`switch`语句,而不会继续执行下一个 case 分支。这也就是说,不需要在 case 分支中显式地使用`break`语句。这使得`switch`语句更安全、更易用,也避免了因忘记写`break`语句而产生的错误。 与 C 语言和 Objective-C 中的`switch`语句不同,在 Swift 中,当匹配的 case 分支中的代码执行完毕后,程序会终止`switch`语句,而不会继续执行下一个 case 分支。这也就是说,不需要在 case 分支中显式地使用`break`语句。这使得`switch`语句更安全、更易用,也避免了因忘记写`break`语句而产生的错误。
> 注意: > 注意:
你依然可以在 case 分支中的代码执行完毕前跳出,详情请参[Switch 语句中的 break](#break_in_a_switch_statement)。 虽然在Swift中`break`不是必须的,但你依然可以在 case 分支中的代码执行完毕前使用`break`跳出,详情请参[Switch 语句中的 break](#break_in_a_switch_statement)。
每一个 case 分支都*必须*包含至少一条语句。像下面这样书写代码是无效的,因为第一个 case 分支是空的: 每一个 case 分支都*必须*包含至少一条语句。像下面这样书写代码是无效的,因为第一个 case 分支是空的:
@ -398,9 +381,9 @@ let anotherCharacter: Character = "a"
switch anotherCharacter { switch anotherCharacter {
case "a": case "a":
case "A": case "A":
println("The letter A") print("The letter A")
default: default:
println("Not the letter A") print("Not the letter A")
} }
// this will report a compile-time error // this will report a compile-time error
``` ```
@ -418,39 +401,42 @@ default:
> 注意: > 注意:
如果想要贯穿至特定的 case 分支中,请使用`fallthrough`语句,详情请参考[贯穿Fallthrough](#fallthrough)。 如果想要贯穿至特定的 case 分支中,请使用`fallthrough`语句,详情请参考[贯穿Fallthrough](#fallthrough)。
<a name="range_matching"></a> <a name="interval_matching"></a>
#### 区间匹配Range Matching #### 区间匹配
case 分支的模式也可以是一个值的区间。下面的例子展示了如何使用区间匹配来输出任意数字对应的自然语言格式: case 分支的模式也可以是一个值的区间。下面的例子展示了如何使用区间匹配来输出任意数字对应的自然语言格式:
```swift ```
let count = 3_000_000_000_000 let approximateCount = 62
let countedThings = "stars in the Milky Way" let countedThings = "moons orbiting Saturn"
var naturalCount: String var naturalCount: String
switch count { switch approximateCount {
case 0: case 0:
naturalCount = "no" naturalCount = "no"
case 1...3: case 1..<5:
naturalCount = "a few" naturalCount = "a few"
case 4...9: case 5..<12:
naturalCount = "several" naturalCount = "several"
case 10...99: case 12..<100:
naturalCount = "tens of" naturalCount = "dozens of"
case 100...999: case 100..<1000:
naturalCount = "hundreds of" naturalCount = "hundreds of"
case 1000...999_999:
naturalCount = "thousands of"
default: default:
naturalCount = "millions and millions of" naturalCount = "many"
} }
println("There are \(naturalCount) \(countedThings).") print("There are \(naturalCount) \(countedThings).")
// 输出 "There are millions and millions of stars in the Milky Way." // 输出 "There are dozens of moons orbiting Saturn."
``` ```
在上例中,`approximateCount`在一个`switch`声明中被估值。每一个`case`都与之进行比较。因为`approximateCount`落在了12到100的区间所以`naturalCount`等于`"dozens of"`值,并且此后这段执行跳出了`switch`声明。
> 注意:
> 闭区间操作符(`...`)以及半开区间操作符(`..<`)功能被重载去返回`IntervalType`或`Range`。一个区间可以决定他是否包含特定的元素,就像当匹配一个`switch`声明的`case`一样。区间是一个连续值的集合,可以用`for-in`语句遍历它。
<a name="tuples"></a> <a name="tuples"></a>
#### 元组Tuple #### 元组Tuple
可以使用元组在同一个`switch`语句中测试多个值。元组中的元素可以是值,也可以是区间。另外,使用下划线(`_`)来匹配所有可能的值。 我们可以使用元组在同一个`switch`语句中测试多个值。元组中的元素可以是值,也可以是区间。另外,使用下划线(`_`)来匹配所有可能的值。
下面的例子展示了如何使用一个`(Int, Int)`类型的元组来分类下图中的点(x, y) 下面的例子展示了如何使用一个`(Int, Int)`类型的元组来分类下图中的点(x, y)
@ -458,15 +444,15 @@ println("There are \(naturalCount) \(countedThings).")
let somePoint = (1, 1) let somePoint = (1, 1)
switch somePoint { switch somePoint {
case (0, 0): case (0, 0):
println("(0, 0) is at the origin") print("(0, 0) is at the origin")
case (_, 0): case (_, 0):
println("(\(somePoint.0), 0) is on the x-axis") print("(\(somePoint.0), 0) is on the x-axis")
case (0, _): case (0, _):
println("(0, \(somePoint.1)) is on the y-axis") print("(0, \(somePoint.1)) is on the y-axis")
case (-2...2, -2...2): case (-2...2, -2...2):
println("(\(somePoint.0), \(somePoint.1)) is inside the box") print("(\(somePoint.0), \(somePoint.1)) is inside the box")
default: default:
println("(\(somePoint.0), \(somePoint.1)) is outside of the box") print("(\(somePoint.0), \(somePoint.1)) is outside of the box")
} }
// 输出 "(1, 1) is inside the box" // 输出 "(1, 1) is inside the box"
``` ```
@ -489,11 +475,11 @@ case 分支的模式允许将匹配的值绑定到一个临时的常量或变量
let anotherPoint = (2, 0) let anotherPoint = (2, 0)
switch anotherPoint { switch anotherPoint {
case (let x, 0): case (let x, 0):
println("on the x-axis with an x value of \(x)") print("on the x-axis with an x value of \(x)")
case (0, let y): case (0, let y):
println("on the y-axis with a y value of \(y)") print("on the y-axis with a y value of \(y)")
case let (x, y): case let (x, y):
println("somewhere else at (\(x), \(y))") print("somewhere else at (\(x), \(y))")
} }
// 输出 "on the x-axis with an x value of 2" // 输出 "on the x-axis with an x value of 2"
``` ```
@ -504,7 +490,7 @@ case let (x, y):
这三个 case 都声明了常量`x`和`y`的占位符,用于临时获取元组`anotherPoint`的一个或两个值。第一个 case ——`case (let x, 0)`将匹配一个纵坐标为`0`的点,并把这个点的横坐标赋给临时的常量`x`。类似的,第二个 case ——`case (0, let y)`将匹配一个横坐标为`0`的点,并把这个点的纵坐标赋给临时的常量`y`。 这三个 case 都声明了常量`x`和`y`的占位符,用于临时获取元组`anotherPoint`的一个或两个值。第一个 case ——`case (let x, 0)`将匹配一个纵坐标为`0`的点,并把这个点的横坐标赋给临时的常量`x`。类似的,第二个 case ——`case (0, let y)`将匹配一个横坐标为`0`的点,并把这个点的纵坐标赋给临时的常量`y`。
一旦声明了这些临时的常量,它们就可以在其对应的 case 分支里引用。在这个例子中,它们用于简化`println`的书写。 一旦声明了这些临时的常量,它们就可以在其对应的 case 分支里引用。在这个例子中,它们用于简化`print(_:)`的书写。
请注意,这个`switch`语句不包含默认分支。这是因为最后一个 case ——`case let(x, y)`声明了一个可以匹配余下所有值的元组。这使得`switch`语句已经完备了,因此不需要再书写默认分支。 请注意,这个`switch`语句不包含默认分支。这是因为最后一个 case ——`case let(x, y)`声明了一个可以匹配余下所有值的元组。这使得`switch`语句已经完备了,因此不需要再书写默认分支。
@ -521,11 +507,11 @@ case 分支的模式可以使用`where`语句来判断额外的条件。
let yetAnotherPoint = (1, -1) let yetAnotherPoint = (1, -1)
switch yetAnotherPoint { switch yetAnotherPoint {
case let (x, y) where x == y: case let (x, y) where x == y:
println("(\(x), \(y)) is on the line x == y") print("(\(x), \(y)) is on the line x == y")
case let (x, y) where x == -y: case let (x, y) where x == -y:
println("(\(x), \(y)) is on the line x == -y") print("(\(x), \(y)) is on the line x == -y")
case let (x, y): case let (x, y):
println("(\(x), \(y)) is just some arbitrary point") print("(\(x), \(y)) is just some arbitrary point")
} }
// 输出 "(1, -1) is on the line x == -y" // 输出 "(1, -1) is on the line x == -y"
``` ```
@ -543,20 +529,21 @@ case let (x, y):
控制转移语句改变你代码的执行顺序通过它你可以实现代码的跳转。Swift有四种控制转移语句。 控制转移语句改变你代码的执行顺序通过它你可以实现代码的跳转。Swift有四种控制转移语句。
- continue - `continue`
- break - `break`
- fallthrough - `fallthrough`
- return - `return`
- `throw`
我们将会在下面讨论`continue`、`break`和`fallthrough`语句。`return`语句将会在[函数](../chapter2/06_Functions.html)章节讨论 我们将会在下面讨论`continue`、`break`和`fallthrough`语句。`return`语句将会在[函数](./06_Functions.html)章节讨论`throw`语句会在[错误抛出](./18_Error_Handling.html#throwing_errors)
<a name="continue"></a> <a name="continue"></a>
### Continue ### Continue
`continue`语句告诉一个循环体立刻停止本次循环迭代,重新开始下次循环迭代。就好像在说“本次循环迭代我已经执行完了”,但是并不会离开整个循环体。 `continue`语句告诉一个循环体立刻停止本次循环迭代,重新开始下次循环迭代。就好像在说“本次循环迭代我已经执行完了”,但是并不会离开整个循环体。
>注意: > 注意:
在一个for条件递增`for-condition-increment`循环体中,调用`continue`语句后,迭代增量仍然会被计算求值。循环体继续像往常一样工作,仅仅只是循环体中的执行代码会被跳过。 > 在一个带有条件递增的for循环体中,调用`continue`语句后,迭代增量仍然会被计算求值。循环体继续像往常一样工作,仅仅只是循环体中的执行代码会被跳过。
下面的例子把一个小写字符串中的元音字母和空格字符移除,生成了一个含义模糊的短句: 下面的例子把一个小写字符串中的元音字母和空格字符移除,生成了一个含义模糊的短句:
@ -571,7 +558,7 @@ for character in puzzleInput {
puzzleOutput.append(character) puzzleOutput.append(character)
} }
} }
println(puzzleOutput) print(puzzleOutput)
// 输出 "grtmndsthnklk" // 输出 "grtmndsthnklk"
``` ```
@ -615,9 +602,9 @@ default:
break break
} }
if let integerValue = possibleIntegerValue { if let integerValue = possibleIntegerValue {
println("The integer value of \(numberSymbol) is \(integerValue).") print("The integer value of \(numberSymbol) is \(integerValue).")
} else { } else {
println("An integer value could not be found for \(numberSymbol).") print("An integer value could not be found for \(numberSymbol).")
} }
// 输出 "The integer value of 三 is 3." // 输出 "The integer value of 三 is 3."
``` ```
@ -633,7 +620,7 @@ if let integerValue = possibleIntegerValue {
Swift 中的`switch`不会从上一个 case 分支落入到下一个 case 分支中。相反,只要第一个匹配到的 case 分支完成了它需要执行的语句,整个`switch`代码块完成了它的执行。相比之下C 语言要求你显示的插入`break`语句到每个`switch`分支的末尾来阻止自动落入到下一个 case 分支中。Swift 的这种避免默认落入到下一个分支中的特性意味着它的`switch` 功能要比 C 语言的更加清晰和可预测,可以避免无意识地执行多个 case 分支从而引发的错误。 Swift 中的`switch`不会从上一个 case 分支落入到下一个 case 分支中。相反,只要第一个匹配到的 case 分支完成了它需要执行的语句,整个`switch`代码块完成了它的执行。相比之下C 语言要求你显示的插入`break`语句到每个`switch`分支的末尾来阻止自动落入到下一个 case 分支中。Swift 的这种避免默认落入到下一个分支中的特性意味着它的`switch` 功能要比 C 语言的更加清晰和可预测,可以避免无意识地执行多个 case 分支从而引发的错误。
如果你确实需要 C 风格的贯穿fallthrough的特性,你可以在每个需要该特性的 case 分支中使用`fallthrough`关键字。下面的例子使用`fallthrough`来创建一个数字的描述语句。 如果你确实需要 C 风格的贯穿的特性,你可以在每个需要该特性的 case 分支中使用`fallthrough`关键字。下面的例子使用`fallthrough`来创建一个数字的描述语句。
```swift ```swift
let integerToDescribe = 5 let integerToDescribe = 5
@ -645,7 +632,7 @@ case 2, 3, 5, 7, 11, 13, 17, 19:
default: default:
description += " an integer." description += " an integer."
} }
println(description) print(description)
// 输出 "The number 5 is a prime number, and also an integer." // 输出 "The number 5 is a prime number, and also an integer."
``` ```
@ -653,13 +640,13 @@ println(description)
如果`integerToDescribe`的值不属于列表中的任何质数,那么它不会匹配到第一个`switch`分支。而这里没有其他特别的分支情况,所以`integerToDescribe`匹配到包含所有的`default`分支中。 如果`integerToDescribe`的值不属于列表中的任何质数,那么它不会匹配到第一个`switch`分支。而这里没有其他特别的分支情况,所以`integerToDescribe`匹配到包含所有的`default`分支中。
当`switch`代码块执行完后,使用`println`函数打印该数字的描述。在这个例子中,数字`5`被准确的识别为了一个质数。 当`switch`代码块执行完后,使用`print`函数打印该数字的描述。在这个例子中,数字`5`被准确的识别为了一个质数。
>注意: > 注意:
`fallthrough`关键字不会检查它下一个将会落入执行的 case 中的匹配条件。`fallthrough`简单地使代码执行继续连接到下一个 case 中的执行代码,这和 C 语言标准中的`switch`语句特性是一样的。 > `fallthrough`关键字不会检查它下一个将会落入执行的 case 中的匹配条件。`fallthrough`简单地使代码执行继续连接到下一个 case 中的执行代码,这和 C 语言标准中的`switch`语句特性是一样的。
<a name="labeled_statements"></a> <a name="labeled_statements"></a>
### 带标签的语句Labeled Statements ### 带标签的语句
在 Swift 中,你可以在循环体和`switch`代码块中嵌套循环体和`switch`代码块来创造复杂的控制流结构。然而,循环体和`switch`代码块两者都可以使用`break`语句来提前结束整个方法体。因此,显示地指明`break`语句想要终止的是哪个循环体或者`switch`代码块,会很有用。类似地,如果你有许多嵌套的循环体,显示指明`continue`语句想要影响哪一个循环体也会非常有用。 在 Swift 中,你可以在循环体和`switch`代码块中嵌套循环体和`switch`代码块来创造复杂的控制流结构。然而,循环体和`switch`代码块两者都可以使用`break`语句来提前结束整个方法体。因此,显示地指明`break`语句想要终止的是哪个循环体或者`switch`代码块,会很有用。类似地,如果你有许多嵌套的循环体,显示指明`continue`语句想要影响哪一个循环体也会非常有用。
@ -671,9 +658,9 @@ println(description)
> `statements` > `statements`
> } > }
下面的例子是在一个带有标签的`while`循环体中调用`break`和`continue`语句,该循环体是前面章节中_蛇和梯子_的改编版本。这次,游戏增加了一条额外的规则: 下面的例子是在一个带有标签的`while`循环体中调用`break`和`continue`语句,该循环体是前面章节中*蛇和梯子*的改编版本。这次,游戏增加了一条额外的规则:
- 为了获胜,你必须_刚好_落在第 25 个方块中。 - 为了获胜,你必须*刚好*落在第 25 个方块中。
如果某次掷骰子使你的移动超出第 25 个方块,你必须重新掷骰子,直到你掷出的骰子数刚好使你能落在第 25 个方块中。 如果某次掷骰子使你的移动超出第 25 个方块,你必须重新掷骰子,直到你掷出的骰子数刚好使你能落在第 25 个方块中。
@ -681,7 +668,7 @@ println(description)
![image](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/snakesAndLadders_2x.png) ![image](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/snakesAndLadders_2x.png)
`finalSquare`、`board`、`square`和`diceRoll`的初始化也和之前一样 `finalSquare`、`board`、`square`和`diceRoll`值被和之前一样的方式初始化
```swift ```swift
let finalSquare = 25 let finalSquare = 25
@ -712,7 +699,7 @@ gameLoop: while square != finalSquare {
square += board[square] square += board[square]
} }
} }
println("Game over!") print("Game over!")
``` ```
每次循环迭代开始时掷骰子。与之前玩家掷完骰子就立即移动不同,这里使用了`switch`来考虑每次移动可能产生的结果,从而决定玩家本次是否能够移动。 每次循环迭代开始时掷骰子。与之前玩家掷完骰子就立即移动不同,这里使用了`switch`来考虑每次移动可能产生的结果,从而决定玩家本次是否能够移动。
@ -724,3 +711,65 @@ println("Game over!")
>注意: >注意:
如果上述的`break`语句没有使用`gameLoop`标签,那么它将会中断`switch`代码块而不是`while`循环体。使用`gameLoop`标签清晰的表明了`break`想要中断的是哪个代码块。 如果上述的`break`语句没有使用`gameLoop`标签,那么它将会中断`switch`代码块而不是`while`循环体。使用`gameLoop`标签清晰的表明了`break`想要中断的是哪个代码块。
同时请注意,当调用`continue gameLoop`去跳转到下一次循环迭代时,这里使用`gameLoop`标签并不是严格必须的。因为在这个游戏中,只有一个循环体,所以`continue`语句会影响到哪个循环体是没有歧义的。然而,`continue`语句使用`gameLoop`标签也是没有危害的。这样做符合标签的使用规则,同时参照旁边的`break gameLoop`,能够使游戏的逻辑更加清晰和易于理解。 同时请注意,当调用`continue gameLoop`去跳转到下一次循环迭代时,这里使用`gameLoop`标签并不是严格必须的。因为在这个游戏中,只有一个循环体,所以`continue`语句会影响到哪个循环体是没有歧义的。然而,`continue`语句使用`gameLoop`标签也是没有危害的。这样做符合标签的使用规则,同时参照旁边的`break gameLoop`,能够使游戏的逻辑更加清晰和易于理解。
<a name="early_exit"></a>
### 提前退出
像`if`语句一样,`guard`的执行取决于一个表达式的布尔值。我们可以使用`guard`语句来要求条件必须为真时,以执行`guard`语句后的代码。不同于`if`语句,一个`guard`语句总是有一个`else`分句,如果条件不为真则执行`else`分局中的代码。
```swift
func greet(person: [String: String]) {
guard let name = person["name"] else {
return
}
print("Hello \(name)")
guard let location = person["location"] else {
print("I hope the weather is nice near you.")
return
}
print("I hope the weather is nice in \(location).")
}
greet(["name": "John"])
// prints "Hello John!"
// prints "I hope the weather is nice near you."
greet(["name": "Jane", "location": "Cupertino"])
// prints "Hello Jane!"
// prints "I hope the weather is nice in Cupertino."
```
如果`guard`语句的条件被满足,则在保护语句的封闭大括号结束后继续执行代码。任何使用了可选绑定作为条件的一部分并被分配了值的变量或常量对于剩下的保护语句出现的代码段是可用的。
如果条件不被满足,在`else`分支上的代码就会被执行。这个分支必须转移控制以退出`guard`语句出现的代码段。它可以用控制转移语句如`return`,`break`或`continue`做这件事,或者它调用了一个不返回的方法或函数,例如`fatalError()`。
相比于可以实现同样功能的`if`语句,按需使用`guard`语句会提升我们代码的可靠性。
它可以使你的代码连贯的被执行而不需要将它包在`else`块中,它可以使你处理违反要求的代码接近要求。
<a name="checking_api_availability"></a>
### 检测API是否可用
Swift 有内置支持去检查接口的可用性的这可以确保我们不会不小心地使用对于当前部署目标不可用的API。
编译器使用SDK中的可用信息来验证在我们在可用部署目标指定项目的代码中所有的API调用。如果我们尝试使用一个不可用的APISwift会在编译期报错。
我们使用一个可用性条件在一个`if`或`guard`语句中去有条件的执行一段代码这取决于我们想要使用的API是否在运行时是可用的。编译器使用从可用性条件语句中获取的信息这时它会去验证在代码块中调用的API是否都可用。
```swift
if #available(iOS 9, OSX 10.10, *) {
// 在 iOS 使用 iOS 9 APIs , 并且在 OS X 使用 OS X v10.10 APIs
} else {
// 回滚至早前 iOS and OS X 的API
}
```
以上可用性条件指定在iOS`if`段的代码仅仅在iOS9及更高可运行在OS X仅在OS X v10.10及更高可运行。最后一个参数,`*`,是必须的并且指定在任何其他平台上,`if`段的代码在最小可用部署目标指定项目中执行。
在它普遍的形式中,可用性条件获取了平台名字和版本的清单。平台名字可以是`iOS``OSX`或`watchOS`。除了特定的主板本号像iOS8我们可以指定较小的版本号像iOS8.3以及 OS X v10.10.3。
```swift
if #available(`platform name` `version`, `...`, *) {
`statements to execute if the APIs are available`
} else {
`fallback statements to execute if the APIs are unavailable`
}
```

View File

@ -1,8 +1,12 @@
# 函数Functions
-----------------
> 1.0
> 翻译:[honghaoz](https://github.com/honghaoz) > 翻译:[honghaoz](https://github.com/honghaoz)
> 校对:[LunaticM](https://github.com/LunaticM) > 校对:[LunaticM](https://github.com/LunaticM)
# 函数Functions > 2.0
----------------- > 翻译+校对:[dreamkidd](https://github.com/dreamkidd)
本页包含内容: 本页包含内容:
@ -25,7 +29,7 @@ Swift 统一的函数语法足够灵活,可以用来表示任何函数,包
每个函数有个函数名用来描述函数执行的任务。要使用一个函数时你用函数名“调用”并传给它匹配的输入值称作实参arguments。一个函数的实参必须与函数参数表里参数的顺序一致。 每个函数有个函数名用来描述函数执行的任务。要使用一个函数时你用函数名“调用”并传给它匹配的输入值称作实参arguments。一个函数的实参必须与函数参数表里参数的顺序一致。
在下面例子中的函数叫做`"greetingForPerson"`,之所以叫这个名字是因为这个函数用一个人的名字当做输入,并返回给这个人的问候语。为了完成这个任务,你定义一个输入参数-一个叫做 `personName``String` 值,和一个包含给这个人问候语的 `String` 类型的返回值: 在下面例子中的函数叫做`"sayHello(_:)"`,之所以叫这个名字,是因为这个函数用一个人的名字当做输入,并返回给这个人的问候语。为了完成这个任务,你定义一个输入参数-一个叫做 `personName``String` 值,和一个包含给这个人问候语的 `String` 类型的返回值:
```swift ```swift
func sayHello(personName: String) -> String { func sayHello(personName: String) -> String {
@ -39,17 +43,17 @@ func sayHello(personName: String) -> String {
该定义描述了函数做什么,它期望接收什么和执行结束时它返回的结果是什么。这样的定义使得函数可以在别的地方以一种清晰的方式被调用: 该定义描述了函数做什么,它期望接收什么和执行结束时它返回的结果是什么。这样的定义使得函数可以在别的地方以一种清晰的方式被调用:
```swift ```swift
println(sayHello("Anna")) print(sayHello("Anna"))
// prints "Hello, Anna!" // prints "Hello, Anna!"
println(sayHello("Brian")) print(sayHello("Brian"))
// prints "Hello, Brian!" // prints "Hello, Brian!"
``` ```
调用 `sayHello` 函数时,在圆括号中传给它一个 `String` 类型的实参。因为这个函数返回一个 `String` 类型的值,`sayHello` 可以被包含在 `println` 的调用中,用来输出这个函数的返回值,正如上面所示。 调用 `sayHello(_:)` 函数时,在圆括号中传给它一个 `String` 类型的实参。因为这个函数返回一个 `String` 类型的值,`sayHello` 可以被包含在 `print` 的调用中,用来输出这个函数的返回值,正如上面所示。
`sayHello` 的函数体中,先定义了一个新的名为 `greeting``String` 常量,同时赋值了给 `personName` 的一个简单问候消息。然后用 `return` 关键字把这个问候返回出去。一旦 `return greeting` 被调用,该函数结束它的执行并返回 `greeting` 的当前值。 `sayHello(_:)` 的函数体中,先定义了一个新的名为 `greeting``String` 常量,同时赋值了给 `personName` 的一个简单问候消息。然后用 `return` 关键字把这个问候返回出去。一旦 `return greeting` 被调用,该函数结束它的执行并返回 `greeting` 的当前值。
你可以用不同的输入值多次调用 `sayHello`。上面的例子展示的是用`"Anna"``"Brian"`调用的结果,该函数分别返回了不同的结果。 你可以用不同的输入值多次调用 `sayHello(_:)`。上面的例子展示的是用`"Anna"``"Brian"`调用的结果,该函数分别返回了不同的结果。
为了简化这个函数的定义,可以将问候消息的创建和返回写成一句: 为了简化这个函数的定义,可以将问候消息的创建和返回写成一句:
@ -57,7 +61,7 @@ println(sayHello("Brian"))
func sayHelloAgain(personName: String) -> String { func sayHelloAgain(personName: String) -> String {
return "Hello again, " + personName + "!" return "Hello again, " + personName + "!"
} }
println(sayHelloAgain("Anna")) print(sayHelloAgain("Anna"))
// prints "Hello again, Anna!" // prints "Hello again, Anna!"
``` ```
@ -76,7 +80,7 @@ println(sayHelloAgain("Anna"))
func halfOpenRangeLength(start: Int, end: Int) -> Int { func halfOpenRangeLength(start: Int, end: Int) -> Int {
return end - start return end - start
} }
println(halfOpenRangeLength(1, 10)) print(halfOpenRangeLength(1, 10))
// prints "9" // prints "9"
``` ```
@ -88,19 +92,42 @@ println(halfOpenRangeLength(1, 10))
func sayHelloWorld() -> String { func sayHelloWorld() -> String {
return "hello, world" return "hello, world"
} }
println(sayHelloWorld()) print(sayHelloWorld())
// prints "hello, world" // prints "hello, world"
``` ```
尽管这个函数没有参数,但是定义中在函数名后还是需要一对圆括号。当被调用时,也需要在函数名后写一对圆括号。 尽管这个函数没有参数,但是定义中在函数名后还是需要一对圆括号。当被调用时,也需要在函数名后写一对圆括号。
### 多参量函数 (Functions With Multiple Parameters)
函数可以有多种输入参数,这写参数被包含在函数的括号之中,以逗号分隔.
这个函数取得一个人的名字和是否被招呼作为输入,并对那个人返回适当地问候语:
```swift
func sayHello(personName: String, alreadyGreeted: Bool) -> String {
if alreadyGreeted {
return sayHelloAgain(personName)
} else {
return sayHello(personName)
}
}
print(sayHello("Tim", alreadyGreeted: true))
// prints "Hello again, Tim!"
```
你通过在括号内传递一个`String`参数值和一个标识为`alreadyGreeted``Bool`值,使用逗号分隔来调用`sayHello(_:alreadyGreeted:)`函数.
当调用超过一个参数的函数时,第一个参数后的参数根据其对应的参数名称标记,函数参数命名在[函数参数名称Function Parameter Names](#Function_Parameter_Names)有更详细的描述.
<a name="functions_without_return_values"></a>
### 无返回值函数Functions Without Return Values ### 无返回值函数Functions Without Return Values
函数可以没有返回值。下面是 `sayHello` 函数的另一个版本,叫 `waveGoodbye`,这个函数直接输出 `String` 值,而不是返回它: 函数可以没有返回值。下面是 `sayHello(_:)` 函数的另一个版本,叫 `sayGoodbye(_:)`,这个函数直接输出 `String` 值,而不是返回它:
```swift ```swift
func sayGoodbye(personName: String) { func sayGoodbye(personName: String) {
println("Goodbye, \(personName)!") print("Goodbye, \(personName)!")
} }
sayGoodbye("Dave") sayGoodbye("Dave")
// prints "Goodbye, Dave!" // prints "Goodbye, Dave!"
@ -109,14 +136,14 @@ sayGoodbye("Dave")
因为这个函数不需要返回值,所以这个函数的定义中没有返回箭头(->)和返回类型。 因为这个函数不需要返回值,所以这个函数的定义中没有返回箭头(->)和返回类型。
> 注意: > 注意:
> 严格上来说,虽然没有返回值被定义,`sayGoodbye` 函数依然返回了值。没有定义返回类型的函数会返回特殊的值,叫 `Void`。它其实是一个空的元组tuple没有任何元素可以写成`()`。 > 严格上来说,虽然没有返回值被定义,`sayGoodbye(_:)` 函数依然返回了值。没有定义返回类型的函数会返回特殊的值,叫 `Void`。它其实是一个空的元组tuple没有任何元素可以写成`()`。
被调用时,一个函数的返回值可以被忽略: 被调用时,一个函数的返回值可以被忽略:
```swift ```swift
func printAndCount(stringToPrint: String) -> Int { func printAndCount(stringToPrint: String) -> Int {
println(stringToPrint) print(stringToPrint)
return count(stringToPrint) return stringToPrint.characters.count
} }
func printWithoutCounting(stringToPrint: String) { func printWithoutCounting(stringToPrint: String) {
printAndCount(stringToPrint) printAndCount(stringToPrint)
@ -128,64 +155,102 @@ printWithoutCounting("hello, world")
``` ```
第一个函数 `printAndCount`,输出一个字符串并返回 `Int` 类型的字符数。第二个函数 `printWithoutCounting`调用了第一个函数,但是忽略了它的返回值。当第二个函数被调用时,消息依然会由第一个函数输出,但是返回值不会被用到。 第一个函数 `printAndCount(_:)`,输出一个字符串并返回 `Int` 类型的字符数。第二个函数 `printWithoutCounting`调用了第一个函数,但是忽略了它的返回值。当第二个函数被调用时,消息依然会由第一个函数输出,但是返回值不会被用到。
> 注意: > 注意:
> 返回值可以被忽略但定义了有返回值的函数必须返回一个值如果在函数定义底部没有返回任何值这将导致编译错误compile-time error > 返回值可以被忽略,但定义了有返回值的函数必须返回一个值,如果在函数定义底部没有返回任何值,并且试图这样做,这将导致编译错误compile-time error
### 多重返回值函数Functions with Multiple Return Values ### 多重返回值函数Functions with Multiple Return Values
你可以用元组tuple类型让多个值作为一个复合值从函数中返回。 你可以用元组tuple类型让多个值作为一个复合值从函数中返回。
下面的这个例子中,`count` 函数用来计算一个字符串中元音,辅音和其他字母的个数(基于美式英语的标准)。 下面的这个例子中,定义了一个名为`minMax(_:)`函数,作用是在一个`Int`数组中找出最小值与最大值.
```swift ```swift
func count(string: String) -> (vowels: Int, consonants: Int, others: Int) { func minMax(array: [Int]) -> (min: Int, max: Int) {
var vowels = 0, consonants = 0, others = 0 var currentMin = array[0]
for character in string { var currentMax = array[0]
switch String(character).lowercaseString { for value in array[1..<array.count] {
case "a", "e", "i", "o", "u": if value < currentMin {
++vowels currentMin = value
case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", } else if value > currentMax {
"n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z": currentMax = value
++consonants
default:
++others
} }
} }
return (vowels, consonants, others) return (currentMin, currentMax)
} }
``` ```
你可以用 `count` 函数来处理任何一个字符串,返回的值将是一个包含三个 `Int`值的元组tuple `minMax(_:)`函数返回一个包含两个`Int`值的元组,这些值被标记为`min``max`,一遍查询函数的返回值时他们可以被访问.
`minMax(_:)`的函数体中,在开始的时候设置两个工作变量`currentMin``currentMax`作为数组中的第一个`Int`值.然后函数会遍历数组中剩余的值并检查该值是否比`currentMin``currentMax`更小或更大.最后数组中的最小值与最大值返回两个`Int`值最为一个元组.
因为元组的成员值被命名为函数的返回类型的一部分​​,可以通过点语法来访问与取回发现的最小值与最小值:
```swift ```swift
let total = count("some arbitrary string!") let bounds = minMax([8, -6, 2, 109, 3, 71])
println("\(total.vowels) vowels and \(total.consonants) consonants") print("min is \(bounds.min) and max is \(bounds.max)")
// prints "6 vowels and 13 consonants" // prints "min is -6 and max is 109"
``` ```
需要注意的是,元组的成员不需要在函数中返回时命名,因为它们的名字已经在函数返回类型中有了定义。 需要注意的是,元组的成员不需要在函数中返回时命名,因为它们的名字已经在函数返回类型中有了定义。
<a name="Function_Parameter_Names"></a> 可选元组返回类型(Optional Tuple Return Types)
## 函数参数名称Function Parameter Names
以上所有的函数都给它们的参数定义了`参数名parameter name` 如果函数返回的元组类型中有可能在整个元组中含有“没有值”,你可以使用*可选的(Optional)* 元组返回类型反映整个元组可以是`nil`的事实.你可以通过在元组类型的右括号后放置一个问号来定义一个可选元组,例如`(Int,Int)?``(String,Int,Bool)?`
> 注意:
> 可选元组类型如`(Int,Int)?`与元组包含可选属性如`(Int?,Int?)`是不同的.可选的元组类型,整个数组是可选的,而不只是元组中的每个元素值.
前面的`minMax(_:)`函数返回了一个包含两个`Int`值的元组.但是函数不会在数组中执行任何安全检查,如果`array`参数有一个空数组,如上定义的`minMax(_:)`在试图访问`array[0]`时会触发一个运行时错误.
为了安全的处理这个"空数组"问题,写一个`minMax(_:)`函数使用可选元组返回类型,并且当数组为空时返回`nil`:
```swift ```swift
func someFunction(parameterName: Int) { func minMax(array: [Int]) -> (min: Int, max: Int)? {
// function body goes here, and can use parameterName if array.isEmpty { return nil }
// to refer to the argument value for that parameter var currentMin = array[0]
var currentMax = array[0]
for value in array[1..<array.count] {
if value < currentMin {
currentMin = value
} else if value > currentMax {
currentMax = value
}
}
return (currentMin, currentMax)
} }
``` ```
但是,这些参数名仅在函数体中使用,不能在函数调用时使用。这种类型的参数名被称作`局部参数名local parameter name`,因为它们只能在函数体中使用。 你可以选择性的绑定当`minMax(_:)`函数返回的是一个实际的元组值还是`nil`
### 外部参数名External Parameter Names ```swift
if let bounds = minMax([8, -6, 2, 109, 3, 71]) {
print("min is \(bounds.min) and max is \(bounds.max)")
}
// prints "min is -6 and max is 109"
```
有时候,调用函数时,给每个参数命名是非常有用的,因为这些参数名可以指出各个实参的用途是什么。 <a name="Function_Parameter_Names"></a>
## 函数参数名称Function Parameter Names
如果你希望函数的使用者在调用函数时提供参数名字,那就需要给每个参数除了局部参数名外再定义一个`外部参数名`。外部参数名写在局部参数名之前,用空格分隔。 函数参数都有一个*外部参数名(external parameter name)*和一个*本地参数名(local parameter name)*.外部参数名用来标记传递给函数调用的参数,本地参数名在实现函数的时候使用.
```swift
func someFunction(firstParameterName: Int, secondParameterName: Int) {
// function body goes here
// firstParameterName and secondParameterName refer to
// the argument values for the first and second parameters
}
someFunction(1, secondParameterName: 2)
```
一般情况下,第一个参数省略其外部参数名,第二个以后的参数使用其本地参数名作为自己的外部参数名.所有参数需要有不同的本地参数名,但可以共享相同的外部参数名.
<a name="specifying_external_parameter_names"></a>
### 指定外部参数名(Specifying External Parameter Names)
你可以在本地参数名前指定外部参数名,中间以空格分隔.
```swift ```swift
func someFunction(externalParameterName localParameterName: Int) { func someFunction(externalParameterName localParameterName: Int) {
@ -194,130 +259,64 @@ func someFunction(externalParameterName localParameterName: Int) {
} }
``` ```
> 注意 > 注意:
> 如果你提供了外部参数名,那么函数在被调用时,必须使用外部参数名。 > 如果你提供了外部参数名,那么函数在被调用时,必须使用外部参数名。
以下是个例子,这个函数使用一个`结合者joiner`把两个字符串联在一起: 这个版本的`sayHello(_:)`函数,得到了两个人的名字,会同时返回对他俩的问候:
```swift ```swift
func join(s1: String, s2: String, joiner: String) -> String { func sayHello(to person: String, and anotherPerson: String) -> String {
return s1 + joiner + s2 return "Hello \(person) and \(anotherPerson)!"
} }
print(sayHello(to: "Bill", and: "Ted"))
// prints "Hello Bill and Ted!"
``` ```
当你调用这个函数时,这三个字符串的用途是不清楚的: 为每个参数指定外部参数名,在你调用函数`sayHello(to:and:)`函数时时两个参数都必须被标记出来.
使用外部函数名可以使得函数可以用一句话表达清楚,并且使得函数体内部可读,能表达出函数的明确意图.
### 忽略外部参数名(Omitting External Parameter Names)
如果你不想为第二个及后续的参数设置参数名,用一个下划线(_)代替一个明确地参数名.
```swift ```swift
join("hello", "world", ", ") func someFunction(firstParameterName: Int, _ secondParameterName: Int) {
// returns "hello, world" // function body goes here
``` // firstParameterName and secondParameterName refer to
// the argument values for the first and second parameters
为了让这些字符串的用途更为明显,我们为 `join` 函数添加外部参数名:
```swift
func join(string s1: String, toString s2: String, withJoiner joiner: String) -> String {
return s1 + joiner + s2
} }
someFunction(1, 2)
``` ```
在这个版本的 `join` 函数中,第一个参数有一个叫 `string` 的外部参数名和 `s1` 的局部参数名,第二个参数有一个叫 `toString` 的外部参数名和 `s2` 的局部参数名,第三个参数有一个叫 `withJoiner` 的外部参数名和 `joiner` 的局部参数名。 > 注意:
> 因为第一个参数默认忽略其外部参数名称,明确写下划线是多余的。
现在,你可以使用这些外部参数名以一种清晰地方式来调用函数了:
```swift
join(string: "hello", toString: "world", withJoiner: ", ")
// returns "hello, world"
```
使用外部参数名让第二个版本的 `join` 函数的调用更为有表现力,更为通顺,同时还保持了函数体是可读的和有明确意图的。
> 注意:
> 当其他人在第一次读你的代码,函数参数的意图显得不明显时,考虑使用外部参数名。如果函数参数名的意图是很明显的,那就不需要定义外部参数名了。
### 简写外部参数名Shorthand External Parameter Names
如果你需要提供外部参数名,但是局部参数名已经定义好了,那么你不需要写两次参数名。相反,只写一次参数名,并用`井号(#`作为前缀就可以了。这告诉 Swift 使用这个参数名作为局部和外部参数名。
下面这个例子定义了一个叫 `containsCharacter` 的函数,使用`井号(#`的方式定义了外部参数名:
```swift
func containsCharacter(#string: String, #characterToFind: Character) -> Bool {
for character in string {
if character == characterToFind {
return true
}
}
return false
}
```
这样定义参数名,使得函数体更为可读,清晰,同时也可以以一个不含糊的方式被调用:
```swift
let containsAVee = containsCharacter(string: "aardvark", characterToFind: "v")
// containsAVee equals true, because "aardvark" contains a "v”
```
### 默认参数值Default Parameter Values ### 默认参数值Default Parameter Values
你可以在函数体中为每个参数定义`默认值`。当默认值被定义后,调用这个函数时可以忽略这个参数。 你可以在函数体中为每个参数定义`默认值(Deafult Values)`。当默认值被定义后,调用这个函数时可以忽略这个参数。
```swift
func someFunction(parameterWithDefault: Int = 12) {
// function body goes here
// if no arguments are passed to the function call,
// value of parameterWithDefault is 42
}
someFunction(6) // parameterWithDefault is 6
someFunction() // parameterWithDefault is 12
```
> 注意: > 注意:
> 将带有默认值的参数放在函数参数列表的最后。这样可以保证在函数调用时,非默认参数的顺序是一致的,同时使得相同的函数在不同情况下调用时显得更为清晰。 > 将带有默认值的参数放在函数参数列表的最后。这样可以保证在函数调用时,非默认参数的顺序是一致的,同时使得相同的函数在不同情况下调用时显得更为清晰。
以下是另一个版本的`join`函数,其中`joiner`有了默认参数值:
```swift
func join(string s1: String, toString s2: String, withJoiner joiner: String = " ") -> String {
return s1 + joiner + s2
}
```
像第一个版本的 `join` 函数一样,如果 `joiner` 被赋值时,函数将使用这个字符串值来连接两个字符串:
```swift
join(string: "hello", toString: "world", withJoiner: "-")
// returns "hello-world"
```
当这个函数被调用时,如果 `joiner` 的值没有被指定,函数会使用默认值(" "
```swift
join(string: "hello", toString:"world")
// returns "hello world"
```
### 默认值参数的外部参数名External Names for Parameters with Default Values
在大多数情况下,给带默认值的参数起一个外部参数名是很有用的。这样可以保证当函数被调用且带默认值的参数被提供值时,实参的意图是明显的。
为了使定义外部参数名更加简单当你未给带默认值的参数提供外部参数名时Swift 会自动提供外部名字。此时外部参数名与局部名字是一样的,就像你已经在局部参数名前写了`井号(#`一样。
下面是 `join` 函数的另一个版本,这个版本中并没有为它的参数提供外部参数名,但是 `joiner` 参数依然有外部参数名:
```swift
func join(s1: String, s2: String, joiner: String = " ") -> String {
return s1 + joiner + s2
}
```
在这个例子中Swift 自动为 `joiner` 提供了外部参数名。因此,当函数调用时,外部参数名必须使用,这样使得参数的用途变得清晰。
```swift
join("hello", "world", joiner: "-")
// returns "hello-world"
```
> 注意:
> 你可以使用`下划线_`作为默认值参数的外部参数名,这样可以在调用时不用提供外部参数名。但是给带默认值的参数命名总是更加合适的。
### 可变参数Variadic Parameters ### 可变参数Variadic Parameters
一个`可变参数variadic parameter`可以接受零个或多个值。函数调用时,你可以用可变参数来传入不确定数量的输入参数。通过在变量类型名后面加入`...`的方式来定义可变参数。 一个`可变参数variadic parameter`可以接受零个或多个值。函数调用时,你可以用可变参数来传入不确定数量的输入参数。通过在变量类型名后面加入`...`的方式来定义可变参数。
传入可变参数的值在函数体内当做这个类型的一个数组。例如,一个叫做 `numbers``Double...` 型可变参数,在函数体内可以当做一个叫 `numbers``Double[]` 型的数组常量。 传入可变参数的值在函数体内当做这个类型的一个数组。例如,一个叫做 `numbers``Double...` 型可变参数,在函数体内可以当做一个叫 `numbers``Double[]` 型的数组常量。
下面的这个函数用来计算一组任意长度数字的算术平均数: 下面的这个函数用来计算一组任意长度数字的`算术平均数(arithmetic mean)`
```swift ```swift
func arithmeticMean(numbers: Double...) -> Double { func arithmeticMean(numbers: Double...) -> Double {
@ -329,12 +328,13 @@ func arithmeticMean(numbers: Double...) -> Double {
} }
arithmeticMean(1, 2, 3, 4, 5) arithmeticMean(1, 2, 3, 4, 5)
// returns 3.0, which is the arithmetic mean of these five numbers // returns 3.0, which is the arithmetic mean of these five numbers
arithmeticMean(3, 8, 19) arithmeticMean(3, 8.25, 18.75)
// returns 10.0, which is the arithmetic mean of these three numbers // returns 10.0, which is the arithmetic mean of these three numbers
``` ```
> 注意: > 注意:
> 一个函数至多能有一个可变参数,而且它必须是参数表中最后的一个。这样做是为了避免函数调用时出现歧义 > 最多可以有一个可变参数函数,和它必须出现在参数表中,为了避免歧义在调用函数有多个参数
> 如果你的函数有一个或多个参数有默认值,还有一个可变的参数,将可变参写在参数列表的最后。
如果函数有一个或多个带默认值的参数,而且还有一个可变参数,那么把可变参数放在参数表的最后。 如果函数有一个或多个带默认值的参数,而且还有一个可变参数,那么把可变参数放在参数表的最后。
@ -348,7 +348,7 @@ arithmeticMean(3, 8, 19)
```swift ```swift
func alignRight(var string: String, totalLength: Int, pad: Character) -> String { func alignRight(var string: String, totalLength: Int, pad: Character) -> String {
let amountToPad = totalLength - count(string) let amountToPad = totalLength - string.characters.count
if amountToPad < 1 { if amountToPad < 1 {
return string return string
} }
@ -359,20 +359,27 @@ func alignRight(var string: String, totalLength: Int, pad: Character) -> String
return string return string
} }
let originalString = "hello" let originalString = "hello"
let paddedString = alignRight(originalString, 10, "-") let paddedString = alignRight(originalString, totalLength: 10, pad: "-")
// paddedString is equal to "-----hello" // paddedString is equal to "-----hello"
// originalString is still equal to "hello" // originalString is still equal to "hello"
``` ```
这个例子中定义了一个新的叫做 `alignRight` 的函数,用来右对齐输入的字符串到一个长的输出字符串中。左侧空余的地方用指定的填充字符填充。这个例子中,字符串`"hello"`被转换成了`"-----hello"` 这个例子中定义了一个新的叫做 `alignRight(_:totalLength:pad:)` 的函数,用来右对齐输入的字符串到一个长的输出字符串中。左侧空余的地方用指定的填充字符填充。这个例子中,字符串`"hello"`被转换成了`"-----hello"`
`alignRight` 函数将参数 `string` 定义为变量参数。这意味着 `string` 现在可以作为一个局部变量,用传入的字符串值初始化,并且可以在函数体中进行操作。 `alignRight(_:totalLength:pad:)` 函数将参数 `string` 定义为变量参数。这意味着 `string` 现在可以作为一个局部变量,用传入的字符串值初始化,并且可以在函数体中进行操作。
函数首先计算出多少字符需要被添加到 `string` 的左边,以右对齐到总的字符串。这个值存在局部常量 `amountToPad` 中。这个函数然后将 `amountToPad` 多的填充pad字符填充到 `string` 左边,并返回结果。它使用了 `string` 这个变量参数来进行所有字符串操作  函数首先找出有多少字符需要被添加到左边的字符串以右对齐在整个字符串。这个值是存储在一个本地常数称为amountToPad。如果不需要填充(也就是说,如果amountToPad小于1),该函数返回字符串没有填充的输入值
  
  否则,该函数创建一个新的临时字符串常量称为padString,初始化填充字符,并将amountToPad padString副本添加到现有的左边的字符串。(一个字符串值不能被添加到一个字符值,所以padString常数用于确保双方+操作符的字符串值)。
该函数首先计算出多少个字符需要被添加到 `string` 的左边,以右对齐到总的字符串中。这个值存在局部常量 `amountToPad` 中。如果不需要填充(即,如果`amountToPad`小于`1`),该函数返回没有填充输入的`string`.
否则,该函数会建立一个临时的String常量称为`padString`,初始化`pad`字符,并将`amountToPad`作为`padString`的副本添加到现有的字符串左边.(一个`Character`后不能直接添加一个`String`值,所以`padString`经常用于确保`+`号两边都是`String`值.)
> 注意: > 注意:
> 对变量参数所进行的修改在函数调用结束后便消失了,并且对于函数体外是不可见的。变量参数仅仅存在于函数调用的生命周期中。 > 对变量参数所进行的修改在函数调用结束后便消失了,并且对于函数体外是不可见的。变量参数仅仅存在于函数调用的生命周期中。
<a name="in_out_parameters"></a>
### 输入输出参数In-Out Parameters ### 输入输出参数In-Out Parameters
变量参数正如上面所述仅仅能在函数体内被更改。如果你想要一个函数可以修改参数的值并且想要在这些修改在函数调用结束后仍然存在那么就应该把这个参数定义为输入输出参数In-Out Parameters 变量参数正如上面所述仅仅能在函数体内被更改。如果你想要一个函数可以修改参数的值并且想要在这些修改在函数调用结束后仍然存在那么就应该把这个参数定义为输入输出参数In-Out Parameters
@ -384,29 +391,29 @@ let paddedString = alignRight(originalString, 10, "-")
> 注意: > 注意:
> 输入输出参数不能有默认值,而且可变参数不能用 `inout` 标记。如果你用 `inout` 标记一个参数,这个参数不能被 `var` 或者 `let` 标记。 > 输入输出参数不能有默认值,而且可变参数不能用 `inout` 标记。如果你用 `inout` 标记一个参数,这个参数不能被 `var` 或者 `let` 标记。
下面是例子,`swapTwoInts` 函数,有两个分别叫做 `a``b` 的输入输出参数: 下面是例子,`swapTwoInts(_:_:)` 函数,有两个分别叫做 `a``b` 的输入输出参数:
```swift ```swift
func swapTwoInts(inout a: Int, inout b: Int) { func swapTwoInts(inout a: Int, inout _ b: Int) {
let temporaryA = a let temporaryA = a
a = b a = b
b = temporaryA b = temporaryA
} }
``` ```
这个 `swapTwoInts` 函数仅仅交换 `a``b` 的值。该函数先将 `a` 的值存到一个暂时常量 `temporaryA` 中,然后将 `b` 的值赋给 `a`,最后将 `temporaryA` 幅值给 `b` 这个 `swapTwoInts(_:_:)` 函数仅仅交换 `a``b` 的值。该函数先将 `a` 的值存到一个暂时常量 `temporaryA` 中,然后将 `b` 的值赋给 `a`,最后将 `temporaryA` 幅值给 `b`
你可以用两个 `Int` 型的变量来调用 `swapTwoInts`。需要注意的是,`someInt``anotherInt` 在传入 `swapTwoInts` 函数前,都加了 `&` 的前缀: 你可以用两个 `Int` 型的变量来调用 `swapTwoInts(_:_:)`。需要注意的是,`someInt``anotherInt` 在传入 `swapTwoInts(_:_:)` 函数前,都加了 `&` 的前缀:
```swift ```swift
var someInt = 3 var someInt = 3
var anotherInt = 107 var anotherInt = 107
swapTwoInts(&someInt, &anotherInt) swapTwoInts(&someInt, &anotherInt)
println("someInt is now \(someInt), and anotherInt is now \(anotherInt)") print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// prints "someInt is now 107, and anotherInt is now 3 // prints "someInt is now 107, and anotherInt is now 3"
``` ```
从上面这个例子中,我们可以看到 `someInt``anotherInt` 的原始值在 `swapTwoInts` 函数中被修改,尽管它们的定义在函数体外。 从上面这个例子中,我们可以看到 `someInt``anotherInt` 的原始值在 `swapTwoInts(_:_:)` 函数中被修改,尽管它们的定义在函数体外。
> 注意: > 注意:
> 输入输出参数和返回值是不一样的。上面的 `swapTwoInts` 函数并没有定义任何返回值,但仍然修改了 `someInt` 和 `anotherInt` 的值。输入输出参数是函数对函数体外产生影响的另一种方式。 > 输入输出参数和返回值是不一样的。上面的 `swapTwoInts` 函数并没有定义任何返回值,但仍然修改了 `someInt` 和 `anotherInt` 的值。输入输出参数是函数对函数体外产生影响的另一种方式。
@ -419,10 +426,10 @@ println("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
例如: 例如:
```swift ```swift
func addTwoInts(a: Int, b: Int) -> Int { func addTwoInts(a: Int, _ b: Int) -> Int {
return a + b return a + b
} }
func multiplyTwoInts(a: Int, b: Int) -> Int { func multiplyTwoInts(a: Int, _ b: Int) -> Int {
return a * b return a * b
} }
``` ```
@ -435,11 +442,11 @@ func multiplyTwoInts(a: Int, b: Int) -> Int {
```swift ```swift
func printHelloWorld() { func printHelloWorld() {
println("hello, world") print("hello, world")
} }
``` ```
这个函数的类型是:`() -> ()`,或者叫“没有参数,并返回 `Void` 类型的函数”。没有指定返回类型的函数总返回 `Void`。在Swift中`Void` 与空的元组是一样的。 这个函数的类型是:`() -> void`,或者叫“没有参数,并返回 `Void` 类型的函数”。
### 使用函数类型Using Function Types ### 使用函数类型Using Function Types
@ -458,7 +465,7 @@ var mathFunction: (Int, Int) -> Int = addTwoInts
现在,你可以用 `mathFunction` 来调用被赋值的函数了: 现在,你可以用 `mathFunction` 来调用被赋值的函数了:
```swift ```swift
println("Result: \(mathFunction(2, 3))") print("Result: \(mathFunction(2, 3))")
// prints "Result: 5" // prints "Result: 5"
``` ```
@ -466,7 +473,7 @@ println("Result: \(mathFunction(2, 3))")
```swift ```swift
mathFunction = multiplyTwoInts mathFunction = multiplyTwoInts
println("Result: \(mathFunction(2, 3))") print("Result: \(mathFunction(2, 3))")
// prints "Result: 6" // prints "Result: 6"
``` ```
@ -484,18 +491,18 @@ let anotherMathFunction = addTwoInts
下面是另一个例子,正如上面的函数一样,同样是输出某种数学运算结果: 下面是另一个例子,正如上面的函数一样,同样是输出某种数学运算结果:
```swift ```swift
func printMathResult(mathFunction: (Int, Int) -> Int, a: Int, b: Int) { func printMathResult(mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
println("Result: \(mathFunction(a, b))") print("Result: \(mathFunction(a, b))")
} }
printMathResult(addTwoInts, 3, 5) printMathResult(addTwoInts, 3, 5)
// prints "Result: 8 // prints "Result: 8"
``` ```
这个例子定义了 `printMathResult` 函数,它有三个参数:第一个参数叫 `mathFunction`,类型是`(Int, Int) -> Int`,你可以传入任何这种类型的函数;第二个和第三个参数叫 `a``b`,它们的类型都是 `Int`,这两个值作为已给的函数的输入值。 这个例子定义了 `printMathResult(_:_:_:)` 函数,它有三个参数:第一个参数叫 `mathFunction`,类型是`(Int, Int) -> Int`,你可以传入任何这种类型的函数;第二个和第三个参数叫 `a``b`,它们的类型都是 `Int`,这两个值作为已给的函数的输入值。
`printMathResult` 被调用时,它被传入 `addTwoInts` 函数和整数`3``5`。它用传入`3``5`调用 `addTwoInts`,并输出结果:`8` `printMathResult(_:_:_:)` 被调用时,它被传入 `addTwoInts` 函数和整数`3``5`。它用传入`3``5`调用 `addTwoInts`,并输出结果:`8`
`printMathResult` 函数的作用就是输出另一个合适类型的数学函数的调用结果。它不关心传入函数是如何实现的,它只关心这个传入的函数类型是正确的。这使得 `printMathResult` 可以以一种类型安全type-safe的方式来保证传入函数的调用是正确的。 `printMathResult(_:_:_:)` 函数的作用就是输出另一个合适类型的数学函数的调用结果。它不关心传入函数是如何实现的,它只关心这个传入的函数类型是正确的。这使得 `printMathResult(_:_:_:)` 可以以一种类型安全type-safe的方式来保证传入函数的调用是正确的。
### 函数类型作为返回类型Function Type as Return Types ### 函数类型作为返回类型Function Type as Return Types
@ -512,7 +519,7 @@ func stepBackward(input: Int) -> Int {
} }
``` ```
下面这个叫做 `chooseStepFunction` 的函数,它的返回类型是 `(Int) -> Int` 的函数。`chooseStepFunction` 根据布尔值 `backwards` 来返回 `stepForward` 函数或 `stepBackward` 函数: 下面这个叫做 `chooseStepFunction(_:)` 的函数,它的返回类型是 `(Int) -> Int` 的函数。`chooseStepFunction(_:)` 根据布尔值 `backwards` 来返回 `stepForward(_:)` 函数或 `stepBackward(_:)` 函数:
```swift ```swift
func chooseStepFunction(backwards: Bool) -> (Int) -> Int { func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
@ -520,7 +527,7 @@ func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
} }
``` ```
你现在可以用 `chooseStepFunction` 来获得一个函数,不管是那个方向: 你现在可以用 `chooseStepFunction(_:)` 来获得一个函数,不管是那个方向:
```swift ```swift
var currentValue = 3 var currentValue = 3
@ -528,22 +535,23 @@ let moveNearerToZero = chooseStepFunction(currentValue > 0)
// moveNearerToZero now refers to the stepBackward() function // moveNearerToZero now refers to the stepBackward() function
``` ```
上面这个例子中计算出从 `currentValue` 逐渐接近到`0`是需要向正数走还是向负数走。`currentValue` 的初始值是`3`,这意味着 `currentValue > 0` 是真的(`true`),这将使得 `chooseStepFunction` 返回 `stepBackward` 函数。一个指向返回的函数的引用保存在了 `moveNearerToZero` 常量中。 上面这个例子中计算出从 `currentValue` 逐渐接近到`0`是需要向正数走还是向负数走。`currentValue` 的初始值是`3`,这意味着 `currentValue > 0` 是真的(`true`),这将使得 `chooseStepFunction(_:)` 返回 `stepBackward(_:)` 函数。一个指向返回的函数的引用保存在了 `moveNearerToZero` 常量中。
现在,`moveNearerToZero` 指向了正确的函数,它可以被用来数到`0` 现在,`moveNearerToZero` 指向了正确的函数,它可以被用来数到`0`
```swift ```swift
println("Counting to zero:") print("Counting to zero:")
// Counting to zero: // Counting to zero:
while currentValue != 0 { while currentValue != 0 {
println("\(currentValue)... ") print("\(currentValue)... ")
currentValue = moveNearerToZero(currentValue) currentValue = moveNearerToZero(currentValue)
} }
println("zero!") print("zero!")
// 3... // 3...
// 2... // 2...
// 1... // 1...
// zero! // zero!
``` ```
<a name="Nested_Functions"></a> <a name="Nested_Functions"></a>
@ -553,7 +561,7 @@ println("zero!")
默认情况下嵌套函数是对外界不可见的但是可以被他们封闭函数enclosing function来调用。一个封闭函数也可以返回它的某一个嵌套函数使得这个函数可以在其他域中被使用。 默认情况下嵌套函数是对外界不可见的但是可以被他们封闭函数enclosing function来调用。一个封闭函数也可以返回它的某一个嵌套函数使得这个函数可以在其他域中被使用。
你可以用返回嵌套函数的方式重写 `chooseStepFunction` 函数: 你可以用返回嵌套函数的方式重写 `chooseStepFunction(_:)` 函数:
```swift ```swift
func chooseStepFunction(backwards: Bool) -> (Int) -> Int { func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
@ -565,10 +573,10 @@ var currentValue = -4
let moveNearerToZero = chooseStepFunction(currentValue > 0) let moveNearerToZero = chooseStepFunction(currentValue > 0)
// moveNearerToZero now refers to the nested stepForward() function // moveNearerToZero now refers to the nested stepForward() function
while currentValue != 0 { while currentValue != 0 {
println("\(currentValue)... ") print("\(currentValue)... ")
currentValue = moveNearerToZero(currentValue) currentValue = moveNearerToZero(currentValue)
} }
println("zero!") print("zero!")
// -4... // -4...
// -3... // -3...
// -2... // -2...

View File

@ -1,8 +1,12 @@
# 闭包Closures
-----------------
> 1.0
> 翻译:[wh1100717](https://github.com/wh1100717) > 翻译:[wh1100717](https://github.com/wh1100717)
> 校对:[lyuka](https://github.com/lyuka) > 校对:[lyuka](https://github.com/lyuka)
# 闭包Closures > 2.0
----------------- > 翻译+校对:[100mango](https://github.com/100mango)
本页包含内容: 本页包含内容:
@ -20,7 +24,7 @@ Swift 中的闭包与 C 和 Objective-C 中的代码块blocks以及其他
> 注意: > 注意:
> 如果您不熟悉捕获capturing这个概念也不用担心您可以在 [值捕获](#capturing_values) 章节对其进行详细了解。 > 如果您不熟悉捕获capturing这个概念也不用担心您可以在 [值捕获](#capturing_values) 章节对其进行详细了解。
在[函数](../chapter2/06_Functions.html) 章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之一: 在[函数](./06_Functions.html) 章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之一:
* 全局函数是一个有名字但不会捕获任何值的闭包 * 全局函数是一个有名字但不会捕获任何值的闭包
* 嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包 * 嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包
@ -37,7 +41,7 @@ Swift 的闭包表达式拥有简洁的风格,并鼓励在常见场景中进
## 闭包表达式Closure Expressions ## 闭包表达式Closure Expressions
[嵌套函数](../chapter2/06_Functions.html#nested_function) 是一个在较复杂函数中方便进行命名和定义自包含代码模块的方式。当然,有时候撰写小巧的没有完整定义和命名的类函数结构也是很有用处的,尤其是在您处理一些函数并需要将另外一些函数作为该函数的参数时。 [嵌套函数](./06_Functions.html#nested_function) 是一个在较复杂函数中方便进行命名和定义自包含代码模块的方式。当然,有时候撰写小巧的没有完整定义和命名的类函数结构也是很有用处的,尤其是在您处理一些函数并需要将另外一些函数作为该函数的参数时。
闭包表达式是一种利用简洁语法构建内联闭包的方式。 闭包表达式是一种利用简洁语法构建内联闭包的方式。
闭包表达式提供了一些语法优化,使得撰写闭包变得简单明了。 闭包表达式提供了一些语法优化,使得撰写闭包变得简单明了。
@ -172,7 +176,7 @@ Swift 的`String`类型定义了关于大于号 (`>`) 的字符串实现,其
reversed = names.sort(>) reversed = names.sort(>)
``` ```
更多关于运算符表达式的内容请查看 [运算符函数](../chapter2/24_Advanced_Operators.html#operator_functions)。 更多关于运算符表达式的内容请查看 [运算符函数](./24_Advanced_Operators.html#operator_functions)。
<a name="trailing_closures"></a> <a name="trailing_closures"></a>
## 尾随闭包Trailing Closures ## 尾随闭包Trailing Closures
@ -246,7 +250,7 @@ let strings = numbers.map {
`map`在数组中为每一个元素调用了闭包表达式。 `map`在数组中为每一个元素调用了闭包表达式。
您不需要指定闭包的输入参数`number`的类型,因为可以通过要映射的数组类型进行推断。 您不需要指定闭包的输入参数`number`的类型,因为可以通过要映射的数组类型进行推断。
闭包`number`参数被声明为一个变量参数(变量的具体描述请参看[常量参数和变量参数](../chapter2/06_Functions.html#constant_and_variable_parameters)),因此可以在闭包函数体内对其进行修改。闭包表达式制定了返回类型为`String`,以表明存储映射值的新数组类型为`String` 闭包`number`参数被声明为一个变量参数(变量的具体描述请参看[常量参数和变量参数](./06_Functions.html#constant_and_variable_parameters)),因此可以在闭包函数体内对其进行修改。闭包表达式制定了返回类型为`String`,以表明存储映射值的新数组类型为`String`
闭包表达式在每次被调用的时候创建了一个字符串并返回。 闭包表达式在每次被调用的时候创建了一个字符串并返回。
其使用求余运算符 (number % 10) 计算最后一位数字并利用`digitNames`字典获取所映射的字符串。 其使用求余运算符 (number % 10) 计算最后一位数字并利用`digitNames`字典获取所映射的字符串。
@ -283,7 +287,7 @@ Swift最简单的闭包形式是嵌套函数也就是定义在其他函数的
每次调用`incrementor`时,其会以`amount`作为增量增加`runningTotal`的值。 每次调用`incrementor`时,其会以`amount`作为增量增加`runningTotal`的值。
```swift ```swift
func makeIncrementor(forIncrement amount: Int) -> Void -> Int { func makeIncrementor(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0 var runningTotal = 0
func incrementor() -> Int { func incrementor() -> Int {
runningTotal += amount runningTotal += amount
@ -293,10 +297,10 @@ func makeIncrementor(forIncrement amount: Int) -> Void -> Int {
} }
``` ```
`makeIncrementor`返回类型为`Void -> Int` `makeIncrementor`返回类型为`() -> Int`
这意味着其返回的是一个函数,而不是一个简单类型值。 这意味着其返回的是一个函数,而不是一个简单类型值。
该函数在每次调用时不接受参数只返回一个`Int`类型的值。 该函数在每次调用时不接受参数只返回一个`Int`类型的值。
关于函数返回其他函数的内容,请查看[函数类型作为返回类型](../chapter2/06_Functions.html#function_types_as_return_types)。 关于函数返回其他函数的内容,请查看[函数类型作为返回类型](./06_Functions.html#function_types_as_return_types)。
`makeIncrementor`函数定义了一个整型变量`runningTotal`(初始为0) 用来存储当前跑步总数。 `makeIncrementor`函数定义了一个整型变量`runningTotal`(初始为0) 用来存储当前跑步总数。
该值通过`incrementor`返回。 该值通过`incrementor`返回。
@ -315,16 +319,11 @@ func incrementor() -> Int {
} }
``` ```
`incrementor`函数并没有获取任何参数,但是在函数体内访问了`runningTotal``amount`变量。这是因为其通过捕获在包含它的函数体内已经存在的`runningTotal``amount`变量而实现 `incrementer`函数并没有任何参数,但是在函数体内访问了`runningTotal``amount`变量。这是因为其通过捕获在包含它的函数体内已经存在的`runningTotal``amount`变量的引用(reference)而实现。捕捉了变量引用,保证了`runningTotal``amount`变量在调用完`makeIncrementer`后不会消失,并且保证了在下一次执行`incrementer`函数时,`runningTotal`可以继续增加
由于没有修改`amount`变量,`incrementor`实际上捕获并存储了该变量的一个副本,而该副本随着`incrementor`一同被存储。
然而,因为每次调用该函数的时候都会修改`runningTotal`的值,`incrementor`捕获了当前`runningTotal`变量的引用,而不是仅仅复制该变量的初始值。捕获一个引用保证了当`makeIncrementor`结束时候并不会消失,也保证了当下一次执行`incrementor`函数时,`runningTotal`可以继续增加。
> 注意: > 注意:
> Swift 会决定捕获引用还是拷贝值 > 为了优化,Swift可能会捕捉和保存一份对值的拷贝,如果这个值是不可变或是在闭包外的
> 您不需要标注`amount`或者`runningTotal`来声明在嵌入的`incrementor`函数中的使用方式 > Swift同样负责被捕捉的所有变量的内存管理,包括释放不被需要的变量
> Swift 同时也处理`runingTotal`变量的内存管理操作,如果不再被`incrementor`函数使用,则会被清除。
下面代码为一个使用`makeIncrementor`的例子: 下面代码为一个使用`makeIncrementor`的例子:
@ -357,7 +356,7 @@ incrementByTen()
> 注意: > 注意:
> 如果您将闭包赋值给一个类实例的属性,并且该闭包通过指向该实例或其成员来捕获了该实例,您将创建一个在闭包和实例间的强引用环。 > 如果您将闭包赋值给一个类实例的属性,并且该闭包通过指向该实例或其成员来捕获了该实例,您将创建一个在闭包和实例间的强引用环。
> Swift 使用捕获列表来打破这种强引用环。更多信息,请参考 [闭包引起的循环强引用](../chapter2/16_Automatic_Reference_Counting.html#strong_reference_cycles_for_closures)。 > Swift 使用捕获列表来打破这种强引用环。更多信息,请参考 [闭包引起的循环强引用](./16_Automatic_Reference_Counting.html#strong_reference_cycles_for_closures)。
<a name="closures_are_reference_types"></a> <a name="closures_are_reference_types"></a>
## 闭包是引用类型Closures Are Reference Types ## 闭包是引用类型Closures Are Reference Types

View File

@ -1,15 +1,20 @@
> 翻译:[yankuangshi](https://github.com/yankuangshi)
> 校对:[shinyzhu](https://github.com/shinyzhu)
# 枚举Enumerations # 枚举Enumerations
--- ---
> 1.0
> 翻译:[yankuangshi](https://github.com/yankuangshi)
> 校对:[shinyzhu](https://github.com/shinyzhu)
> 2.0
> 翻译+校对:[futantan](https://github.com/futantan)
本页内容包含: 本页内容包含:
- [枚举语法Enumeration Syntax](#enumeration_syntax) - [枚举语法Enumeration Syntax](#enumeration_syntax)
- [匹配枚举值与`Swith`语句Matching Enumeration Values with a Switch Statement](#matching_enumeration_values_with_a_switch_statement) - [匹配枚举值与`Swith`语句Matching Enumeration Values with a Switch Statement](#matching_enumeration_values_with_a_switch_statement)
- [相关值Associated Values](#associated_values) - [相关值Associated Values](#associated_values)
- [原始值Raw Values](#raw_values) - [原始值Raw Values](#raw_values)
- [递归枚举Recursive Enumerations](#recursive_enumerations)
*枚举*定义了一个通用类型的一组相关值,使你可以在你的代码中以一种安全的方式来使用这些值。 *枚举*定义了一个通用类型的一组相关值,使你可以在你的代码中以一种安全的方式来使用这些值。
@ -19,7 +24,7 @@
在 Swift 中枚举类型是一等公民first-class。它们采用了很多传统上只被类class所支持的特征例如计算型属性computed properties用于提供关于枚举当前值的附加信息 实例方法instance methods用于提供和枚举所代表的值相关联的功能。枚举也可以定义构造函数initializers来提供一个初始值可以在原始的实现基础上扩展它们的功能可以遵守协议protocols来提供标准的功能。 在 Swift 中枚举类型是一等公民first-class。它们采用了很多传统上只被类class所支持的特征例如计算型属性computed properties用于提供关于枚举当前值的附加信息 实例方法instance methods用于提供和枚举所代表的值相关联的功能。枚举也可以定义构造函数initializers来提供一个初始值可以在原始的实现基础上扩展它们的功能可以遵守协议protocols来提供标准的功能。
欲了解更多相关信息,请参见[属性Properties](10_Properties.html)[方法Methods](11_Methods.html)[构造过程Initialization](14_Initialization.html)[扩展Extensions](20_Extensions.html)和[协议Protocols](21_Protocols.html)。 欲了解更多相关信息,请参见[属性Properties](./10_Properties.html)[方法Methods](./11_Methods.html)[构造过程Initialization](./14_Initialization.html)[扩展Extensions](./20_Extensions.html)和[协议Protocols](./21_Protocols.html)。
<a name="enumeration_syntax"></a> <a name="enumeration_syntax"></a>
## 枚举语法 ## 枚举语法
@ -96,7 +101,7 @@ case .West:
等等以此类推。 等等以此类推。
正如在[控制流Control Flow](05_Control_Flow.html)中介绍的那样,在判断一个枚举类型的值时,`switch`语句必须穷举所有情况。如果忽略了`.West`这种情况,上面那段代码将无法通过编译,因为它没有考虑到`CompassPoint`的全部成员。强制性全部穷举的要求确保了枚举成员不会被意外遗漏。 正如在[控制流Control Flow](./05_Control_Flow.html)中介绍的那样,在判断一个枚举类型的值时,`switch`语句必须穷举所有情况。如果忽略了`.West`这种情况,上面那段代码将无法通过编译,因为它没有考虑到`CompassPoint`的全部成员。强制性全部穷举的要求确保了枚举成员不会被意外遗漏。
当不需要匹配每个枚举成员的时候,你可以提供一个默认`default`分支来涵盖所有未明确被提出的枚举成员: 当不需要匹配每个枚举成员的时候,你可以提供一个默认`default`分支来涵盖所有未明确被提出的枚举成员:
@ -186,7 +191,7 @@ case let .QRCode(productCode):
<a name="raw_values"></a> <a name="raw_values"></a>
## 原始值Raw Values ## 原始值Raw Values
在[Associated Values](#raw_values)小节的条形码例子中演示了一个枚举的成员如何声明它们存储不同类型的相关值。作为相关值的另一种选择,枚举成员可以被默认值(称为原始值)赋值,其中这些原始值具有相同的类型。 在[相关值](#raw_values)小节的条形码例子中演示了一个枚举的成员如何声明它们存储不同类型的相关值。作为相关值的另一种选择,枚举成员可以被默认值(称为原始值)赋值,其中这些原始值具有相同的类型。
这里是一个枚举成员存储 ASCII 码的例子: 这里是一个枚举成员存储 ASCII 码的例子:
@ -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 在太阳系中的顺序: 下面的枚举是对之前`Planet`这个枚举的一个细化,利用原始整型值来表示每个 planet 在太阳系中的顺序:
@ -212,30 +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`属性可以访问该枚举成员的原始值: 使用枚举成员的`rawValue`属性可以访问该枚举成员的原始值:
```swift ```swift
let earthsOrder = Planet.Earth.rawValue let earthsOrder = Planet.Earth.rawValue
// earthsOrder is 3 // earthsOrder 值为 3
let sunsetDirection = CompassPoint.West.rawValue
// sunsetDirection 值为 "West"
``` ```
### 使用原始值来初始化(Initializing from a Raw Value) ### 使用原始值来初始化(Initializing from a Raw Value)
如果你使用原始值的方式创建一个枚举类型,这个枚举将自动获得一个包含原始值参数(参数名为rawValue)的构造器并返回相应的枚举类型或者nil。你可以使用这个构造器来创建新的枚举成员。 ### 使用原始值初始化枚举变量Initializing from a Raw Value
下面这个例子通过原始值`7`创建了`Uranus`枚举类型: 如果在定义枚举类型的时候使用了原始值,那么将会自动获得一个初始化方法,这个方法将原始值类型作为参数,返回枚举成员或者`nil`。你可以使用这种初始化方法来创建一个新的枚举变量。
这个例子通过原始值`7`从而创建枚举成员:
```swift ```swift
let possiblePlanet = Planet(rawValue: 7) 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`”。
> 注意:
> 使用原始值构造器是可失败构造器,因为并不是所有的原始值都会返回一个对应的枚举成员。欲了解更多相关信息,请参见[可失败构造器Failable Initializers](TODO)
>注意:
>原始值构造器是一个可失败构造器,因为并不是每一个原始值都有与之对应的枚举成员。更多信息请参见[可失败构造器](../chapter3/05_Declarations#failable_initializers)
如果你试图寻找一个位置为9的行星通过参数为`rawValue`构造函数返回的可选`Planet`值将是`nil` 如果你试图寻找一个位置为9的行星通过参数为`rawValue`构造函数返回的可选`Planet`值将是`nil`
@ -254,4 +283,61 @@ if let somePlanet = Planet(rawValue: positionToFind) {
// 输出 "There isn't a planet at position 9 // 输出 "There isn't a planet at position 9
``` ```
这个范例使用可选绑定optional binding通过原始值`9`试图取得一个行星的引用`if let somePlanet = Planet(rawValue: 9)`语句获得一个可选`Planet`,如果可选`Planet`可以被获得,把`somePlanet`设置成该可选`Planet`的内容。在这个范例中,无法检索到位置为`9`的行星,所以`else`分支被执行。 这个范例使用可选绑定optional binding通过原始值`9`试图访问一个行星。`if let somePlanet = Planet(rawValue: 9)`语句获得一个可选`Planet`,如果可选`Planet`可以被获得,把`somePlanet`设置成该可选`Planet`的内容。在这个范例中,无法检索到位置为`9`的行星,所以`else`分支被执行。
<a name="recursive_enumerations"></a>
## 递归枚举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"
```
该函数如果遇到纯数字,就直接返回该数字的值。如果遇到的是加法或乘法元算,则分别计算左边表达式和右边表达式的值,然后相加或相乘。

View File

@ -1,7 +1,11 @@
# 类和结构体Classes and Structures
> 1.0
> 翻译:[JaySurplus](https://github.com/JaySurplus) > 翻译:[JaySurplus](https://github.com/JaySurplus)
> 校对:[sg552](https://github.com/sg552) > 校对:[sg552](https://github.com/sg552)
# 类和结构体 > 2.0
> 翻译+校对:[SkyJean](https://github.com/SkyJean)
本页包含内容: 本页包含内容:
@ -9,9 +13,10 @@
- [结构体和枚举是值类型](#structures_and_enumerations_are_value_types) - [结构体和枚举是值类型](#structures_and_enumerations_are_value_types)
- [类是引用类型](#classes_are_reference_types) - [类是引用类型](#classes_are_reference_types)
- [类和结构体的选择](#choosing_between_classes_and_structures) - [类和结构体的选择](#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 并不要求你为自定义类和结构去创建独立的接口和实现文件。你所要做的是在一个单一文件中定义一个类或者结构体,系统将会自动生成面向其它代码的外部接口。 与其他编程语言所不同的是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`类型。 在上面的示例中我们定义了一个名为`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() let someVideoMode = VideoMode()
``` ```
结构体和类都使用构造器语法来生成新的实例。构造器语法的最简单形式是在结构体或者类的类型名称后跟随一空括,如`Resolution()``VideoMode()`。通过这种方式所创建的类或者结构体实例,其属性均会被初始化为默认值。[构造过程](14_Initialization.html)章节会对类和结构体的初始化进行更详细的讨论。 结构体和类都使用构造器语法来生成新的实例。构造器语法的最简单形式是在结构体或者类的类型名称后跟随一空括,如`Resolution()``VideoMode()`。通过这种方式所创建的类或者结构体实例,其属性均会被初始化为默认值。[构造过程](./14_Initialization.html)章节会对类和结构体的初始化进行更详细的讨论。
### 属性访问 ### 属性访问
通过使用*点语法**dot syntax*),你可以访问实例中所含有的属性。其语法规则是,实例名后面紧跟属性名,两者通过点号(.)连接: 通过使用*点语法**dot syntax*),你可以访问实例中所含有的属性。其语法规则是,实例名后面紧跟属性名,两者通过点号(.)连接:
```swift ```swift
println("The width of someResolution is \(someResolution.width)") print("The width of someResolution is \(someResolution.width)")
// 输出 "The width of someResolution is 0" // 输出 "The width of someResolution is 0"
``` ```
@ -106,7 +111,7 @@ println("The width of someResolution is \(someResolution.width)")
你也可以访问子属性,如`VideoMode``Resolution`属性的`width`属性: 你也可以访问子属性,如`VideoMode``Resolution`属性的`width`属性:
```swift ```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" // 输出 "The width of someVideoMode is 0"
``` ```
@ -114,31 +119,31 @@ println("The width of someVideoMode is \(someVideoMode.resolution.width)")
```swift ```swift
someVideoMode.resolution.width = 1280 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" // 输出 "The width of someVideoMode is now 1280"
``` ```
> 注意: > 注意:
与 Objective-C 语言不同的是Swift 允许直接设置结构体属性的子属性。上面的最后一个例子,就是直接设置了`someVideoMode``resolution`属性的`width`这个子属性,以上操作并不需要重新设置`resolution`属性。 与 Objective-C 语言不同的是Swift 允许直接设置结构体属性的子属性。上面的最后一个例子,就是直接设置了`someVideoMode``resolution`属性的`width`这个子属性,以上操作并不需要重新设置`resolution`属性。
### 结构体类型的成员逐一构造器(Memberwise Initializers for structure Types) ### 结构体类型的成员逐一构造器(Memberwise Initializers for Structure Types)
所有结构体都有一个自动生成的成员逐一构造器,用于初始化新结构体实例中成员的属性。新实例中各个属性的初始值可以通过属性的名称传递到成员逐一构造器之中: 所有结构体都有一个自动生成的*成员逐一构造器*,用于初始化新结构体实例中成员的属性。新实例中各个属性的初始值可以通过属性的名称传递到成员逐一构造器之中:
```swift ```swift
let vga = Resolution(width:640, height: 480) let vga = Resolution(width:640, height: 480)
``` ```
与结构体不同,类实例没有默认的成员逐一构造器。[构造过程](14_Initialization.html)章节会对构造器进行更详细的讨论。 与结构体不同,类实例没有默认的成员逐一构造器。[构造过程](./14_Initialization.html)章节会对构造器进行更详细的讨论。
<a name="structures_and_enumerations_are_value_types"></a> <a name="structures_and_enumerations_are_value_types"></a>
## 结构体和枚举是值类型 ## 结构体和枚举是值类型
值类型被赋予给一个变量,常数或者本身被传递给一个函数的时候,实际上操作的是其的拷贝。 *值类型*被赋予给一个变量、常量或者本身被传递给一个函数的时候,实际上操作的是其的*拷贝*
在之前的章节中,我们已经大量使用了值类型。实际上,在 Swift 中所有的基本类型整数Integer、浮点数floating-point、布尔值Booleans、字符串string)、数组array和字典dictionaries),都是值类型,并且都是以结构体的形式在后台所实现。 在之前的章节中,我们已经大量使用了值类型。实际上,在 Swift 中所有的基本类型整数Integer、浮点数floating-point、布尔值Boolean、字符串string)、数组array和字典dictionary),都是值类型,并且都是以结构体的形式在后台所实现。
在 Swift 中,所有的结构体和枚举都是值类型。这意味着它们的实例,以及实例中所包含的任何值类型属性,在代码中传递的时候都会被复制。 在 Swift 中,所有的结构体和枚举类型都是值类型。这意味着它们的实例,以及实例中所包含的任何值类型属性,在代码中传递的时候都会被复制。
请看下面这个示例,其使用了前一个示例中`Resolution`结构体: 请看下面这个示例,其使用了前一个示例中`Resolution`结构体:
@ -160,18 +165,18 @@ cinema.width = 2048
这里,将会显示`cinema``width`属性确已改为了`2048` 这里,将会显示`cinema``width`属性确已改为了`2048`
```swift ```swift
println("cinema is now \(cinema.width) pixels wide") print("cinema is now \(cinema.width) pixels wide")
// 输出 "cinema is now 2048 pixels wide" // 输出 "cinema is now 2048 pixels wide"
``` ```
然而,初始的`hd`实例中`width`属性还是`1920` 然而,初始的`hd`实例中`width`属性还是`1920`
```swift ```swift
println("hd is still \(hd.width ) pixels wide") print("hd is still \(hd.width ) pixels wide")
// 输出 "hd is still 1920 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 let rememberedDirection = currentDirection
currentDirection = .East currentDirection = .East
if rememberedDirection == .West { if rememberedDirection == .West {
println("The remembered direction is still .West") print("The remembered direction is still .West")
} }
// 输出 "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` 下面,通过查看`tenEighty``frameRate`属性,我们会发现它正确的显示了基本`VideoMode`实例的新帧率,其值为`30.0`
```swift ```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" // 输出 "The frameRate property of theEighty is now 30.0"
``` ```
@ -238,7 +243,7 @@ println("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
```swift ```swift
if tenEighty === alsoTenEighty { 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." //输出 "tenEighty and alsoTenEighty refer to the same Resolution instance."
``` ```
@ -248,11 +253,11 @@ if tenEighty === alsoTenEighty {
* “等价于”表示两个类类型class type的常量或者变量引用同一个类实例。 * “等价于”表示两个类类型class type的常量或者变量引用同一个类实例。
* “等于”表示两个实例的值“相等”或“相同”,判定时要遵照类设计者定义定义的评判标准,因此相比于“相等”,这是一种更加合适的叫法。 * “等于”表示两个实例的值“相等”或“相同”,判定时要遵照类设计者定义定义的评判标准,因此相比于“相等”,这是一种更加合适的叫法。
当你在定义你的自定义类和结构体的时候,你有义务来决定判定两个实例“相等”的标准。在章节[运算符函数(Operator Functions)](24_Advanced_Operators.html#operator_functions)中将会详细介绍实现自定义“等于”和“不等于”运算符的流程。 当你在定义你的自定义类和结构体的时候,你有义务来决定判定两个实例“相等”的标准。在章节[等价操作符](./24_Advanced_Operators.html#equivalence_operators)中将会详细介绍实现自定义“等于”和“不等于”运算符的流程。
### 指针 ### 指针
如果你有 CC++ 或者 Objective-C 语言的经验,那么你也许会知道这些语言使用指针来引用内存中的地址。一个 Swift 常量或者变量引用一个引用类型的实例与 C 语言中的指针类似,不同的是并不直接指向内存中的某个地址,而且也不要求你使用星号(*来表明你在创建一个引用。Swift 中这些引用与其它的常量或变量的定义方式相同。 如果你有 CC++ 或者 Objective-C 语言的经验,那么你也许会知道这些语言使用*指针*来引用内存中的地址。一个 Swift 常量或者变量引用一个引用类型的实例与 C 语言中的指针类似,不同的是并不直接指向内存中的某个地址,而且也不要求你使用星号(*来表明你在创建一个引用。Swift 中这些引用与其它的常量或变量的定义方式相同。
<a name="choosing_between_classes_and_structures"></a> <a name="choosing_between_classes_and_structures"></a>
## 类和结构体的选择 ## 类和结构体的选择
@ -268,7 +273,7 @@ if tenEighty === alsoTenEighty {
* 任何在结构体中储存的值类型属性,也将会被拷贝,而不是被引用。 * 任何在结构体中储存的值类型属性,也将会被拷贝,而不是被引用。
* 结构体不需要去继承另一个已存在类型的属性或者行为。 * 结构体不需要去继承另一个已存在类型的属性或者行为。
合适的结构体候选者包括 举例来说,以下情境中适合使用结构体
* 几何形状的大小,封装一个`width`属性和`height`属性,两者均为`Double`类型。 * 几何形状的大小,封装一个`width`属性和`height`属性,两者均为`Double`类型。
* 一定范围内的路径,封装一个`start`属性和`length`属性,两者均为`Int`类型。 * 一定范围内的路径,封装一个`start`属性和`length`属性,两者均为`Int`类型。
@ -276,15 +281,14 @@ if tenEighty === alsoTenEighty {
在所有其它案例中,定义一个类,生成一个它的实例,并通过引用来管理和传递。实际中,这意味着绝大部分的自定义数据构造都应该是类,而非结构体。 在所有其它案例中,定义一个类,生成一个它的实例,并通过引用来管理和传递。实际中,这意味着绝大部分的自定义数据构造都应该是类,而非结构体。
<a name="assignment_and_copy_behavior_for_collection_types"></a> <a name="assignment_and_copy_behavior_for_strings_arrays_and_dictionaries"></a>
## 集合Collection类型的赋值和拷贝行为 ## 字符串(String)、数组(Array)、和字典(Dictionary)类型的赋值与复制行为
Swift 中`字符串String`,`数组Array`和`字典Dictionary`类型均以结构体的形式实现。这意味着StringArrayDictionary类型数据被赋值给新的常量(或变量,或者被传入函数或方法中时,它们的值会发生拷贝行为(值传递方式)。 Swift 中`字符串String`,`数组Array`和`字典Dictionary`类型均以结构体的形式实现。这意味着StringArrayDictionary类型数据被赋值给新的常量或变量或者被传入函数或方法中时它们的值会发生拷贝行为值传递方式
Objective-C中`字符串NSString`,`数组NSArray`和`字典NSDictionary`类型均以类的形式实现这与Swfit中以值传递方式是不同的。NSStringNSArrayNSDictionary在发生赋值或者传入函数或方法不会发生值拷贝而是传递已存在实例的引用。 Objective-C中`字符串NSString`,`数组NSArray`和`字典NSDictionary`类型均以类的形式实现这与Swfit中以值传递方式是不同的。NSStringNSArrayNSDictionary在发生赋值或者传入函数或方法不会发生值拷贝而是传递已存在实例的引用。
> 注意: > 注意:
以上是对于数组字典,字符串和其它值的`拷贝`的描述。 以上是对于字符串、数组字典和其它值的`拷贝`的描述。
在你的代码中,拷贝好像是确实是在有拷贝行为的地方产生过。然而,在 Swift 的后台中,只有确有必要,`实际actual`拷贝才会被执行。Swift 管理所有的值拷贝以确保性能最优化的性能,所以你也没有必要去避免赋值以保证最优性能。(实际赋值由系统管理优化) 在你的代码中,拷贝好像是确实是在有拷贝行为的地方产生过。然而,在 Swift 的后台中,只有确有必要,`实际actual`拷贝才会被执行。Swift 管理所有的值拷贝以确保性能最优化的性能,所以你也没有必要去避免赋值以保证最优性能。(实际赋值由系统管理优化)

View File

@ -1,8 +1,12 @@
> 翻译:[shinyzhu](https://github.com/shinyzhu) # 属性 (Properties)
---
> 1.0
> 翻译:[shinyzhu](https://github.com/shinyzhu)
> 校对:[pp-prog](https://github.com/pp-prog) [yangsiy](https://github.com/yangsiy) > 校对:[pp-prog](https://github.com/pp-prog) [yangsiy](https://github.com/yangsiy)
# 属性 (Properties) > 2.0
--- > 翻译+校对:[yangsiy](https://github.com/yangsiy)
本页包含内容: 本页包含内容:
@ -23,7 +27,7 @@
简单来说,一个存储属性就是存储在特定类或结构体的实例里的一个常量或变量。存储属性可以是*变量存储属性*(用关键字`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`的结构体,它描述了一个在创建后无法修改值域宽度的区间: 下面的例子定义了一个名为`FixedLengthRange`的结构体,它描述了一个在创建后无法修改值域宽度的区间:
@ -87,7 +91,7 @@ class DataManager {
} }
let manager = DataManager() let manager = DataManager()
manager.data.append("Some data") manager.data.append("Some data")
manager.data.append("Some more data") manager.data.append("Some more data")
// DataImporter 实例的 importer 属性还没有被创建 // DataImporter 实例的 importer 属性还没有被创建
``` ```
@ -104,9 +108,9 @@ manager.data.append("Some more data")
print(manager.importer.fileName) print(manager.importer.fileName)
// DataImporter 实例的 importer 属性现在被创建了 // DataImporter 实例的 importer 属性现在被创建了
// 输出 "data.txt” // 输出 "data.txt”
``` ```
> 注意: > 注意:
> 如果一个被标记为`lazy`的属性在没有初始化时就同时被多个线程访问,则无法保证该属性只会被初始化一次。 > 如果一个被标记为`lazy`的属性在没有初始化时就同时被多个线程访问,则无法保证该属性只会被初始化一次。
<a name="stored_properties_and_instance_variables"></a> <a name="stored_properties_and_instance_variables"></a>
@ -219,7 +223,7 @@ print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
*属性观察器*监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,甚至新的值和现在的值相同的时候也不例外。 *属性观察器*监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,甚至新的值和现在的值相同的时候也不例外。
可以为除了延迟存储属性之外的其他存储属性添加属性观察器,也可以通过重载属性的方式为继承的属性(包括存储属性和计算属性)添加属性观察器。属性重载请参考[继承](chapter/13_Inheritance.html)一章的[重载](chapter/13_Inheritance.html#overriding)。 可以为除了延迟存储属性之外的其他存储属性添加属性观察器,也可以通过重载属性的方式为继承的属性(包括存储属性和计算属性)添加属性观察器。属性重载请参考[重载](./13_Inheritance.html#overriding)。
> 注意: > 注意:
> 不需要为非重载的计算属性添加属性观察器,因为可以通过它的 setter 直接监控和响应值的变化。 > 不需要为非重载的计算属性添加属性观察器,因为可以通过它的 setter 直接监控和响应值的变化。
@ -233,9 +237,9 @@ print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
类似地,`didSet`观察器会将旧的属性值作为参数传入,可以为该参数命名或者使用默认参数名`oldValue` 类似地,`didSet`观察器会将旧的属性值作为参数传入,可以为该参数命名或者使用默认参数名`oldValue`
> 注意: > 注意:
> 父类的属性在子类的构造器中被赋值时,它在父类中的`willSet`和`didSet`观察器会被调用。 > 父类的属性在子类的构造器中被赋值时,它在父类中的`willSet`和`didSet`观察器会被调用。
> 有关构造器代理的更多信息,请参考[值类型的构造器代理](chapter/14_Initialization.html#initializer_delegation_for_value_types)和[构造器链](chapter/14_Initialization.html#initialization_chain)。 > 有关构造器代理的更多信息,请参考[值类型的构造器代理](./14_Initialization.html#initializer_delegation_for_value_types)和[类的构造器代理规则](./14_Initialization.html#initializer_delegation_for_class_types)。
这里是一个`willSet``didSet`的实际例子,其中定义了一个名为`StepCounter`的类,用来统计当人步行时的总步数。这个类可以跟计步器或其他日常锻炼的统计装置的输入数据配合使用。 这里是一个`willSet``didSet`的实际例子,其中定义了一个名为`StepCounter`的类,用来统计当人步行时的总步数。这个类可以跟计步器或其他日常锻炼的统计装置的输入数据配合使用。
@ -322,13 +326,13 @@ enum SomeEnumeration {
return 6 return 6
} }
} }
class SomeClass { class SomeClass {
static var storedTypeProperty = "Some value." static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int { static var computedTypeProperty: Int {
return 27 return 27
} }
class var overrideableComputedTypeProperty: Int { class var overrideableComputedTypeProperty: Int {
return 107 return 107
} }
} }
``` ```
@ -341,12 +345,12 @@ class SomeClass {
跟实例的属性一样,类型属性的访问也是通过点运算符来进行。但是,类型属性是通过类型本身来获取和设置,而不是通过实例。比如: 跟实例的属性一样,类型属性的访问也是通过点运算符来进行。但是,类型属性是通过类型本身来获取和设置,而不是通过实例。比如:
```swift ```swift
print(SomeStructure.storedTypeProperty) print(SomeStructure.storedTypeProperty)
// 输出 "Some value." // 输出 "Some value."
SomeStructure.storedTypeProperty = "Another value." SomeStructure.storedTypeProperty = "Another value."
print(SomeStructure.storedTypeProperty) print(SomeStructure.storedTypeProperty)
// 输出 "Another value.” // 输出 "Another value.”
print(SomeEnumeration.computedTypeProperty) print(SomeEnumeration.computedTypeProperty)
// 输出 "6" // 输出 "6"
print(SomeClass.computedTypeProperty) print(SomeClass.computedTypeProperty)

View File

@ -1,8 +1,12 @@
> 翻译:[pp-prog](https://github.com/pp-prog) # 方法Methods
-----------------
> 1.0
> 翻译:[pp-prog](https://github.com/pp-prog)
> 校对:[zqp](https://github.com/zqp) > 校对:[zqp](https://github.com/zqp)
# 方法Methods > 2.0
----------------- > 翻译+校对:[DianQK](https://github.com/DianQK)
本页包含内容: 本页包含内容:
@ -14,19 +18,19 @@
结构体和枚举能够定义方法是 Swift 与 C/Objective-C 的主要区别之一。在 Objective-C 中,类是唯一能定义方法的类型。但在 Swift 中,你不仅能选择是否要定义一个类/结构体/枚举,还能灵活的在你创建的类型(类/结构体/枚举)上定义方法。 结构体和枚举能够定义方法是 Swift 与 C/Objective-C 的主要区别之一。在 Objective-C 中,类是唯一能定义方法的类型。但在 Swift 中,你不仅能选择是否要定义一个类/结构体/枚举,还能灵活的在你创建的类型(类/结构体/枚举)上定义方法。
<a name="instance_methods"></a> <a name="instance_methods"></a>
## 实例方法(Instance Methods) ## 实例方法 (Instance Methods)
**实例方法**是属于某个特定类、结构体或者枚举类型实例的方法。实例方法提供访问和修改实例属性的方法或提供与实例目的相关的功能,并以此来支撑实例的功能。实例方法的语法与函数完全一致,详情参见[函数](../charpter2/06_Functions.md)。 **实例方法**是属于某个特定类、结构体或者枚举类型实例的方法。实例方法提供访问和修改实例属性的方法或提供与实例目的相关的功能,并以此来支撑实例的功能。实例方法的语法与函数完全一致,详情参见[函数](./06_Functions.md)。
实例方法要写在它所属的类型的前后大括号之间。实例方法能够隐式访问它所属类型的所有的其他实例方法和属性。实例方法只能被它所属的类的某个特定实例调用。实例方法不能脱离于现存的实例而被调用。 实例方法要写在它所属的类型的前后大括号之间。实例方法能够隐式访问它所属类型的所有的其他实例方法和属性。实例方法只能被它所属的类的某个特定实例调用。实例方法不能脱离于现存的实例而被调用。
下面的例子,定义一个很简单的`Counter``Counter`能被用来对一个动作发生的次数进行计数: 下面的例子,定义一个很简单的`Counter``Counter`能被用来对一个动作发生的次数进行计数:
```swift ```swift
class Counter { class Counter {
var count = 0 var count = 0
func increment() { func increment() {
count++ ++count
} }
func incrementBy(amount: Int) { func incrementBy(amount: Int) {
count += amount count += amount
@ -60,13 +64,13 @@ class Counter {
<a name="local_and_external_parameter"></a> <a name="local_and_external_parameter"></a>
### 方法的局部参数名称和外部参数名称(Local and External Parameter Names for Methods) ### 方法的局部参数名称和外部参数名称(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 ```swift
class Counter { class Counter {
@ -77,35 +81,28 @@ class Counter {
} }
``` ```
`incrementBy`方法有两个参数: `amount``numberOfTimes`。默认情况下Swift 只把`amount`当作一个局部名称,但是把`numberOfTimes`即看作局部名称又看作外部名称。下面调用这个方法: `incrementBy(_:numverOfTimes:)`方法有两个参数: `amount``numberOfTimes`。默认情况下Swift 只把`amount`当作一个局部名称,但是把`numberOfTimes`即看作局部名称又看作外部名称。下面调用这个方法:
```swift ```swift
let counter = Counter() let counter = Counter()
counter.incrementBy(5, numberOfTimes: 3) counter.incrementBy(5, numberOfTimes: 3)
// counter value is now 15 // counter 的值现在是 15
```
你不必为第一个参数值再定义一个外部变量名:因为从函数名`incrementBy`已经能很清楚地看出它的作用。但是第二个参数,就要被一个外部参数名称所限定,以便在方法被调用时明确它的作用。
这种默认的行为能够有效的处理方法method,类似于在参数`numberOfTimes`前写一个井号(`#`
```swift
func incrementBy(amount: Int, #numberOfTimes: Int) {
count += amount * numberOfTimes
}
``` ```
你不必为第一个参数值再定义一个外部变量名:因为从函数名`incrementBy(_numberOfTimes:)`已经能很清楚地看出它的作用。但是第二个参数,就要被一个外部参数名称所限定,以便在方法被调用时明确它的作用。
这种默认行为使上面代码意味着:在 Swift 中定义方法使用了与 Objective-C 同样的语法风格,并且方法将以自然表达式的方式被调用。 这种默认行为使上面代码意味着:在 Swift 中定义方法使用了与 Objective-C 同样的语法风格,并且方法将以自然表达式的方式被调用。
<a name="modifying_external_parameter"></a>
<a name="modifying_external_parameter_name_behavior_for_methods"></a>
### 修改方法的外部参数名称(Modifying External Parameter Name Behavior for Methods) ### 修改方法的外部参数名称(Modifying External Parameter Name Behavior for Methods)
有时为方法的第一个参数提供一个外部参数名称是非常有用的,尽管这不是默认的行为。你可以自己添加一个显式的外部名称或者用一个井号(`#`)作为第一个参数的前缀来把这个局部名称当作外部名称使用。 有时为方法的第一个参数提供一个外部参数名称是非常有用的,尽管这不是默认的行为。你可以自己添加一个显式的外部名称或者用一个井号(`#`)作为第一个参数的前缀来把这个局部名称当作外部名称使用。
相反,如果你不想为方法的第二个及后续的参数提供一个外部名称,可以通过使用下划线(`_`)作为该参数的显式外部名称,这样做将覆盖默认行为。 相反,如果你不想为方法的第二个及后续的参数提供一个外部名称,可以通过使用下划线(`_`)作为该参数的显式外部名称,这样做将覆盖默认行为。
<a name="self_property"></a>
## `self`属性(The self Property) <a name="the_self_property"></a>
### self 属性(The self Property)
类型的每一个实例都有一个隐含属性叫做`self``self`完全等同于该实例本身。你可以在一个实例的实例方法中使用这个隐含的`self`属性来引用当前实例。 类型的每一个实例都有一个隐含属性叫做`self``self`完全等同于该实例本身。你可以在一个实例的实例方法中使用这个隐含的`self`属性来引用当前实例。
@ -132,14 +129,14 @@ struct Point {
} }
let somePoint = Point(x: 4.0, y: 5.0) let somePoint = Point(x: 4.0, y: 5.0)
if somePoint.isToTheRightOfX(1.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`的函数参数。 如果不使用`self`前缀Swift 就认为两次使用的`x`都指的是名称为`x`的函数参数。
<a name="modifying_value_types"></a> <a name="modifying_value_types_from_within_instance_methods"></a>
### 在实例方法中修改值类型(Modifying Value Types from Within Instance Methods) ### 在实例方法中修改值类型(Modifying Value Types from Within Instance Methods)
结构体和枚举是**值类型**。一般情况下,值类型的属性不能在它的实例方法中被修改。 结构体和枚举是**值类型**。一般情况下,值类型的属性不能在它的实例方法中被修改。
@ -158,21 +155,21 @@ struct Point {
} }
var somePoint = Point(x: 1.0, y: 1.0) var somePoint = Point(x: 1.0, y: 1.0)
somePoint.moveByX(2.0, y: 3.0) somePoint.moveByX(2.0, y: 3.0)
println("The point is now at (\(somePoint.x), \(somePoint.y))") print("The point is now at (\(somePoint.x), \(somePoint.y))")
// 输出 "The point is now at (3.0, 4.0)" // 打印输出: "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 ```swift
let fixedPoint = Point(x: 3.0, y: 3.0) let fixedPoint = Point(x: 3.0, y: 3.0)
fixedPoint.moveByX(2.0, y: 3.0) fixedPoint.moveByX(2.0, y: 3.0)
// this will report an error // 这里将会抛出一个错误
``` ```
<a name="mutating_method_self"></a> <a name="assigning_to_self_within_a_mutating_method"></a>
### 在变异方法中给self赋值(Assigning to self Within a Mutating Method) ### 在变异方法中给self赋值(Assigning to self Within a Mutating Method)
变异方法能够赋给隐含属性`self`一个全新的实例。上面`Point`的例子可以用下面的方式改写: 变异方法能够赋给隐含属性`self`一个全新的实例。上面`Point`的例子可以用下面的方式改写:
@ -186,7 +183,7 @@ struct Point {
} }
``` ```
新版的变异方法`moveByX`创建了一个新的结构(它的 x 和 y 的值都被设定为目标值)。调用这个版本的方法和调用上个版本的最终结果是一样的。 新版的变异方法`moveByX(_:y:)`创建了一个新的结构(它的 x 和 y 的值都被设定为目标值)。调用这个版本的方法和调用上个版本的最终结果是一样的。
枚举的变异方法可以把`self`设置为相同的枚举类型中不同的成员: 枚举的变异方法可以把`self`设置为相同的枚举类型中不同的成员:
@ -214,9 +211,9 @@ ovenLight.next()
上面的例子中定义了一个三态开关的枚举。每次调用`next`方法时,开关在不同的电源状态(`Off``Low``High`)之前循环切换。 上面的例子中定义了一个三态开关的枚举。每次调用`next`方法时,开关在不同的电源状态(`Off``Low``High`)之前循环切换。
<a name="type_methods"></a> <a name="type_methods"></a>
## 类型方法(Type Methods) ## 类型方法 (Type Methods)
实例方法是被类型的某个实例调用的方法。你也可以定义类型本身调用的方法,这种方法就叫做**类型方法**。声明类的类型方法,在方法的`func`关键字之前加上关键字`class`;声明结构体和枚举的类型方法,在方法的`func`关键字之前加上关键字`static` 实例方法是被类型的某个实例调用的方法。你也可以定义类型本身调用的方法,这种方法就叫做**类型方法**。声明结构体和枚举的类型方法,在方法的`func`关键字之前加上关键字`static`类可能会用关键字`class`来允许子类重写父类的实现方法。
> 注意: > 注意:
> 在 Objective-C 里面,你只能为 Objective-C 的类定义类型方法type-level methods。在 Swift 中,你可以为所有的类、结构体和枚举定义类型方法:每一个类型方法都被它所支持的类型显式包含。 > 在 Objective-C 里面,你只能为 Objective-C 的类定义类型方法type-level methods。在 Swift 中,你可以为所有的类、结构体和枚举定义类型方法:每一个类型方法都被它所支持的类型显式包含。
@ -242,22 +239,22 @@ SomeClass.someTypeMethod()
```swift ```swift
struct LevelTracker { struct LevelTracker {
static var highestUnlockedLevel = 1 static var highestUnlockedLevel = 1
static func unlockLevel(level: Int) { static func unlockLevel(level: Int) {
if level > highestUnlockedLevel { highestUnlockedLevel = level } if level > highestUnlockedLevel { highestUnlockedLevel = level }
} }
static func levelIsUnlocked(level: Int) -> Bool { static func levelIsUnlocked(level: Int) -> Bool {
return level <= highestUnlockedLevel return level <= highestUnlockedLevel
} }
var currentLevel = 1 var currentLevel = 1
mutating func advanceToLevel(level: Int) -> Bool { mutating func advanceToLevel(level: Int) -> Bool {
if LevelTracker.levelIsUnlocked(level) { if LevelTracker.levelIsUnlocked(level) {
currentLevel = level currentLevel = level
return true return true
} else { } else {
return false return false
}
} }
}
} }
``` ```
@ -285,25 +282,25 @@ class Player {
} }
``` ```
`Player`类创建一个新的`LevelTracker`实例来监测这个用户的发展进度。它提供了`completedLevel`方法:一旦玩家完成某个指定等级就调用它。这个方法为所有玩家解锁下一等级,并且将当前玩家的进度更新为下一等级。(我们忽略了`advanceToLevel`返回的布尔值,因为之前调用`LevelTracker.unlockLevel`时就知道了这个等级已经被解锁了)。 `Player`类创建一个新的`LevelTracker`实例来监测这个用户的进度。它提供了`completedLevel`方法:一旦玩家完成某个指定等级就调用它。这个方法为所有玩家解锁下一等级,并且将当前玩家的进度更新为下一等级。(我们忽略了`advanceToLevel`返回的布尔值,因为之前调用`LevelTracker.unlockLevel`时就知道了这个等级已经被解锁了)。
你还可以为一个新的玩家创建一个`Player`的实例,然后看这个玩家完成等级一时发生了什么: 你还可以为一个新的玩家创建一个`Player`的实例,然后看这个玩家完成等级一时发生了什么:
```swift ```swift
var player = Player(name: "Argyrios") var player = Player(name: "Argyrios")
player.completedLevel(1) player.completedLevel(1)
println("highest unlocked level is now \(LevelTracker.highestUnlockedLevel)") print("highest unlocked level is now \(LevelTracker.highestUnlockedLevel)")
// 输出 "highest unlocked level is now 2"最高等级现在是2 // 打印输出:highest unlocked level is now 2
``` ```
如果你创建了第二个玩家,并尝试让开始一个没有被任何玩家解锁的等级,那么这次设置玩家当前等级的尝试将会失败: 如果你创建了第二个玩家,并尝试让开始一个没有被任何玩家解锁的等级,那么这次设置玩家当前等级的尝试将会失败:
```swift ```swift
player = Player(name: "Beto") player = Player(name: "Beto")
if player.tracker.advanceToLevel(6) { if player.tracker.advanceToLevel(6) {
println("player is now on level 6") print("player is now on level 6")
} else { } 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
``` ```

View File

@ -1,9 +1,12 @@
> 翻译:[siemenliu](https://github.com/siemenliu) # 下标脚本Subscripts
-----------------
> 1.0
> 翻译:[siemenliu](https://github.com/siemenliu)
> 校对:[zq54zquan](https://github.com/zq54zquan) > 校对:[zq54zquan](https://github.com/zq54zquan)
> 2.0
# 下标脚本Subscripts > 翻译+校对:[shanksyang](https://github.com/shanksyang)
-----------------
本页包含内容: 本页包含内容:
@ -54,7 +57,7 @@ struct TimesTable {
} }
} }
let threeTimesTable = TimesTable(multiplier: 3) let threeTimesTable = TimesTable(multiplier: 3)
println("3的6倍是\(threeTimesTable[6])") print("3的6倍是\(threeTimesTable[6])")
// 输出 "3的6倍是18" // 输出 "3的6倍是18"
``` ```
@ -79,7 +82,7 @@ numberOfLegs["bird"] = 2
上例定义一个名为`numberOfLegs`的变量并用一个字典字面量初始化出了包含三对键值的字典实例。`numberOfLegs`的字典存放值类型推断为`[String:Int]`。字典实例创建完成之后通过下标脚本的方式将整型值`2`赋值到字典实例的索引为`bird`的位置中。 上例定义一个名为`numberOfLegs`的变量并用一个字典字面量初始化出了包含三对键值的字典实例。`numberOfLegs`的字典存放值类型推断为`[String:Int]`。字典实例创建完成之后通过下标脚本的方式将整型值`2`赋值到字典实例的索引为`bird`的位置中。
更多关于字典Dictionary下标脚本的信息请参考[读取和修改字典](../chapter2/04_Collection_Types.html) 更多关于字典Dictionary下标脚本的信息请参考[读取和修改字典](./04_Collection_Types.html#accessing_and_modifying_a_dictionary)
> 注意: > 注意:
> Swift 中字典的附属脚本实现中,在`get`部分返回值是`Int?`,上例中的`numberOfLegs`字典通过附属脚本返回的是一个`Int?`或者说“可选的int”不是每个字典的索引都能得到一个整型值对于没有设过值的索引的访问返回的结果就是`nil`;同样想要从字典实例中删除某个索引下的值也只需要给这个索引赋值为`nil`即可。 > Swift 中字典的附属脚本实现中,在`get`部分返回值是`Int?`,上例中的`numberOfLegs`字典通过附属脚本返回的是一个`Int?`或者说“可选的int”不是每个字典的索引都能得到一个整型值对于没有设过值的索引的访问返回的结果就是`nil`;同样想要从字典实例中删除某个索引下的值也只需要给这个索引赋值为`nil`即可。
@ -118,7 +121,7 @@ struct Matrix {
} }
``` ```
`Matrix`提供了一个两个入参的构造方法,入参分别是`rows``columns`,创建了一个足够容纳`rows * columns`个数的`Double`类型数组。通过传入数组长度和初始值0.0到数组的一个构造器,将`Matrix`中每个元素初始值0.0。关于数组的构造方法和析构方法请参考[创建并且构造一个数组](../chapter2/04_Collection_Types.html)。 `Matrix`提供了一个两个入参的构造方法,入参分别是`rows``columns`,创建了一个足够容纳`rows * columns`个数的`Double`类型数组。通过传入数组长度和初始值0.0到数组的一个构造器,将`Matrix`中每个元素初始值0.0。关于数组的构造方法和析构方法请参考[创建一个数组](./04_Collection_Types.html#creating_an_empty_array)。
你可以通过传入合适的`row``column`的数量来构造一个新的`Matrix`实例: 你可以通过传入合适的`row``column`的数量来构造一个新的`Matrix`实例:

View File

@ -1,8 +1,12 @@
> 翻译:[Hawstein](https://github.com/Hawstein) # 继承Inheritance
-------------------
> 1.0
> 翻译:[Hawstein](https://github.com/Hawstein)
> 校对:[menlongsheng](https://github.com/menlongsheng) > 校对:[menlongsheng](https://github.com/menlongsheng)
# 继承Inheritance > 2.0
------------------- > 翻译+校对:[shanksyang](https://github.com/shanksyang)
本页包含内容: 本页包含内容:
@ -25,34 +29,34 @@
> 注意: > 注意:
Swift 中的类并不是从一个通用的基类继承而来。如果你不为你定义的类指定一个超类的话,这个类就自动成为基类。 Swift 中的类并不是从一个通用的基类继承而来。如果你不为你定义的类指定一个超类的话,这个类就自动成为基类。
下面的例子定义了一个叫`Vehicle`的基类。这个基类声明了一个名为`currentSpeed `默认值是0.0的存储属性(属性类型推断为`Double `)。`currentSpeed `属性的值被一个`String` 类型的只读计算型属性`description`使用,用来创建车辆的描述。 下面的例子定义了一个叫`Vehicle`的基类。这个基类声明了一个名为`currentSpeed `默认值是0.0的存储属性(属性类型推断为`Double `)。`currentSpeed `属性的值被一个`String` 类型的只读计算型属性`description`使用,用来创建车辆的描述。
`Vehicle`基类也定义了一个名为`makeNoise`的方法。这个方法实际上不为`Vehicle`实例做任何事,但之后将会被`Vehicle`的子类定制: `Vehicle`基类也定义了一个名为`makeNoise`的方法。这个方法实际上不为`Vehicle`实例做任何事,但之后将会被`Vehicle`的子类定制:
```swift ```swift
class Vehicle { class Vehicle {
var currentSpeed = 0.0 var currentSpeed = 0.0
var description: String { var description: String {
return "traveling at \(currentSpeed) miles per hour" return "traveling at \(currentSpeed) miles per hour"
} }
func makeNoise() { func makeNoise() {
// 什么也不做-因为车辆不一定会有噪音 // 什么也不做-因为车辆不一定会有噪音
} }
} }
``` ```
您可以用初始化语法创建一个`Vehicle `的新实例,即类名后面跟一个空括号: 您可以用初始化语法创建一个`Vehicle `的新实例,即类名后面跟一个空括号:
```swift ```swift
let someVehicle = Vehicle() let someVehicle = Vehicle()
``` ```
现在已经创建了一个`Vehicle`的新实例,你可以访问它的`description`属性来打印车辆的当前速度。 现在已经创建了一个`Vehicle`的新实例,你可以访问它的`description`属性来打印车辆的当前速度。
```swift ```swift
println("Vehicle: \(someVehicle.description)") print("Vehicle: \(someVehicle.description)")
// Vehicle: traveling at 0.0 miles per hour // Vehicle: traveling at 0.0 miles per hour
``` ```
`Vehicle`类定义了一个通用特性的车辆类,实际上没什么用处。为了让它变得更加有用,需要改进它能够描述一个更加具体的车辆类。 `Vehicle`类定义了一个通用特性的车辆类,实际上没什么用处。为了让它变得更加有用,需要改进它能够描述一个更加具体的车辆类。
<a name="subclassing"></a> <a name="subclassing"></a>
@ -68,51 +72,51 @@ class SomeClass: SomeSuperclass {
} }
``` ```
下一个例子,定义一个叫`Bicycle`的子类,继承成父类`Vehicle` 下一个例子,定义一个叫`Bicycle`的子类,继承成父类`Vehicle`
```swift
class Bicycle: Vehicle {
var hasBasket = false
}
```
新的`Bicycle`类自动获得`Vehicle`类的所有特性,比如 `currentSpeed ``description`属性,还有它的`makeNoise`方法。 ```swift
class Bicycle: Vehicle {
除了它所继承的特性,`Bicycle`类还定义了一个默认值为`false`的存储型属性`hasBasket`(属性推断为`Bool`)。 var hasBasket = false
}
默认情况下,你创建任何新的`Bicycle`实例将不会有一个篮子,创建该实例之后,你可以为特定的`Bicycle`实例设置`hasBasket `属性为`ture` ```
```swift 新的`Bicycle`类自动获得`Vehicle`类的所有特性,比如 `currentSpeed ``description`属性,还有它的`makeNoise`方法。
let bicycle = Bicycle()
bicycle.hasBasket = true 除了它所继承的特性,`Bicycle`类还定义了一个默认值为`false`的存储型属性`hasBasket`(属性推断为`Bool`)。
```
默认情况下,你创建任何新的`Bicycle`实例将不会有一个篮子,创建该实例之后,你可以为特定的`Bicycle`实例设置`hasBasket `属性为`ture`
你还可以修改`Bicycle `实例所继承的`currentSpeed `属性,和查询实例所继承的`description `属性:
```swift
```swift let bicycle = Bicycle()
bicycle.currentSpeed = 15.0 bicycle.hasBasket = true
println("Bicycle: \(bicycle.description)") ```
// Bicycle: traveling at 15.0 miles per hour
你还可以修改`Bicycle `实例所继承的`currentSpeed `属性,和查询实例所继承的`description `属性:
```swift
bicycle.currentSpeed = 15.0
print("Bicycle: \(bicycle.description)")
// Bicycle: traveling at 15.0 miles per hour
``` ```
子类还可以继续被其它类继承,下面的示例为`Bicycle `创建了一个名为`Tandem `(双人自行车)的子类: 子类还可以继续被其它类继承,下面的示例为`Bicycle `创建了一个名为`Tandem `(双人自行车)的子类:
```swift ```swift
class Tandem: Bicycle { class Tandem: Bicycle {
var currentNumberOfPassengers = 0 var currentNumberOfPassengers = 0
} }
``` ```
`Tandem``Bicycle`继承了所有的属性与方法,这又使它同时继承了`Vehicle`的所有属性与方法。`Tandem`也增加了一个新的叫做`currentNumberOfPassengers`的存储型属性默认值为0。 `Tandem``Bicycle`继承了所有的属性与方法,这又使它同时继承了`Vehicle`的所有属性与方法。`Tandem`也增加了一个新的叫做`currentNumberOfPassengers`的存储型属性默认值为0。
如果你创建了一个`Tandem`的实例,你可以使用它所有的新属性和继承的属性,还能查询从`Vehicle`继承来的只读属性`description ` 如果你创建了一个`Tandem`的实例,你可以使用它所有的新属性和继承的属性,还能查询从`Vehicle`继承来的只读属性`description `
```swift ```swift
let tandem = Tandem() let tandem = Tandem()
tandem.hasBasket = true tandem.hasBasket = true
tandem.currentNumberOfPassengers = 2 tandem.currentNumberOfPassengers = 2
tandem.currentSpeed = 22.0 tandem.currentSpeed = 22.0
println("Tandem: \(tandem.description)") print("Tandem: \(tandem.description)")
// Tandem: traveling at 22.0 miles per hour // Tandem: traveling at 22.0 miles per hour
``` ```
@ -142,18 +146,18 @@ println("Tandem: \(tandem.description)")
下面的例子定义了`Vehicle`的一个新的子类,叫`Train `,它重写了从`Vehicle`类继承来的`makeNoise `方法: 下面的例子定义了`Vehicle`的一个新的子类,叫`Train `,它重写了从`Vehicle`类继承来的`makeNoise `方法:
```swift ```swift
class Train: Vehicle { class Train: Vehicle {
override func makeNoise() { override func makeNoise() {
println("Choo Choo") print("Choo Choo")
} }
} }
``` ```
如果你创建一个`Train `的新实例,并调用了它的`makeNoise `方法,你就会发现`Train `版本的方法被调用: 如果你创建一个`Train `的新实例,并调用了它的`makeNoise `方法,你就会发现`Train `版本的方法被调用:
```swift ```swift
let train = Train() let train = Train()
train.makeNoise() train.makeNoise()
// prints "Choo Choo" // prints "Choo Choo"
``` ```
@ -173,23 +177,23 @@ train.makeNoise()
以下的例子定义了一个新类,叫`Car`,它是`Vehicle `的子类。这个类引入了一个新的存储型属性叫做`gear `默认为整数1。`Car`类重写了继承自`Vehicle `的description属性提供自定义的包含当前档位的描述 以下的例子定义了一个新类,叫`Car`,它是`Vehicle `的子类。这个类引入了一个新的存储型属性叫做`gear `默认为整数1。`Car`类重写了继承自`Vehicle `的description属性提供自定义的包含当前档位的描述
```swift ```swift
class Car: Vehicle { class Car: Vehicle {
var gear = 1 var gear = 1
override var description: String { override var description: String {
return super.description + " in gear \(gear)" return super.description + " in gear \(gear)"
} }
} }
``` ```
重写的`description `属性,首先要调用`super.description`返回`Vehicle`类的`description`属性。之后,`Car `类版本的`description`在末尾增加了一些额外的文本来提供关于当前档位的信息。 重写的`description `属性,首先要调用`super.description`返回`Vehicle`类的`description`属性。之后,`Car `类版本的`description`在末尾增加了一些额外的文本来提供关于当前档位的信息。
如果你创建了`Car `的实例并且设置了它的`gear``currentSpeed`属性,你可以看到它的`description`返回了`Car`中定义的`description` 如果你创建了`Car `的实例并且设置了它的`gear``currentSpeed`属性,你可以看到它的`description`返回了`Car`中定义的`description`
```swift ```swift
let car = Car() let car = Car()
car.currentSpeed = 25.0 car.currentSpeed = 25.0
car.gear = 3 car.gear = 3
println("Car: \(car.description)") print("Car: \(car.description)")
// Car: traveling at 25.0 miles per hour in gear 3 // Car: traveling at 25.0 miles per hour in gear 3
``` ```
@ -203,21 +207,21 @@ println("Car: \(car.description)")
下面的例子定义了一个新类叫`AutomaticCar`,它是`Car`的子类。`AutomaticCar`表示自动挡汽车,它可以根据当前的速度自动选择合适的挡位: 下面的例子定义了一个新类叫`AutomaticCar`,它是`Car`的子类。`AutomaticCar`表示自动挡汽车,它可以根据当前的速度自动选择合适的挡位:
```swift ```swift
class AutomaticCar: Car { class AutomaticCar: Car {
override var currentSpeed: Double { override var currentSpeed: Double {
didSet { didSet {
gear = Int(currentSpeed / 10.0) + 1 gear = Int(currentSpeed / 10.0) + 1
} }
} }
} }
``` ```
当你设置`AutomaticCar``currentSpeed `属性,属性的`didSet`观察器就会自动地设置`gear`属性为新的速度选择一个合适的挡位。具体来说就是属性观察器将新的速度值除以10然后向下取得最接近的整数值最后加1来得到档位`gear`的值。例如速度为10.0时挡位为1速度为35.0时挡位为4 当你设置`AutomaticCar``currentSpeed `属性,属性的`didSet`观察器就会自动地设置`gear`属性为新的速度选择一个合适的挡位。具体来说就是属性观察器将新的速度值除以10然后向下取得最接近的整数值最后加1来得到档位`gear`的值。例如速度为10.0时挡位为1速度为35.0时挡位为4
```swift ```swift
let automatic = AutomaticCar() let automatic = AutomaticCar()
automatic.currentSpeed = 35.0 automatic.currentSpeed = 35.0
println("AutomaticCar: \(automatic.description)") print("AutomaticCar: \(automatic.description)")
// AutomaticCar: traveling at 35.0 miles per hour in gear 4 // AutomaticCar: traveling at 35.0 miles per hour in gear 4
``` ```
@ -229,4 +233,3 @@ println("AutomaticCar: \(automatic.description)")
如果你重写了`final`方法,属性或下标脚本,在编译时会报错。在类扩展中的方法,属性或下标脚本也可以在扩展的定义里标记为 final。 如果你重写了`final`方法,属性或下标脚本,在编译时会报错。在类扩展中的方法,属性或下标脚本也可以在扩展的定义里标记为 final。
你可以通过在关键字`class`前添加`final`特性(`final class`)来将整个类标记为 final 的,这样的类是不可被继承的,任何子类试图继承此类时,在编译时会报错。 你可以通过在关键字`class`前添加`final`特性(`final class`)来将整个类标记为 final 的,这样的类是不可被继承的,任何子类试图继承此类时,在编译时会报错。

File diff suppressed because it is too large Load Diff

View File

@ -1,108 +1,112 @@
> 翻译:[bruce0505](https://github.com/bruce0505) # 析构过程Deinitialization
> 校对:[fd5788](https://github.com/fd5788) ---------------------------
# 析构过程Deinitialization > 1.0
--------------------------- > 翻译:[bruce0505](https://github.com/bruce0505)
> 校对:[fd5788](https://github.com/fd5788)
本页包含内容:
> 2.0
- [析构过程原理](#how_deinitialization_works) > 翻译+校对:[chenmingbiao](https://github.com/chenmingbiao)
- [析构函数操作](#deinitializers_in_action)
本页包含内容:
在一个类的实例被释放之前,析构函数被立即调用。用关键字`deinit`来标示析构函数,类似于初始化函数用`init`来标示。析构函数只适用于类类型。
- [析构过程原理](#how_deinitialization_works)
<a name="how_deinitialization_works"></a> - [析构器操作](#deinitializers_in_action)
##析构过程原理
析构器只适用于类类型,当一个类的实例被释放之前,析构器会被立即调用。析构器用关键字`deinit`来标示,类似于构造器要用`init`来标示。
Swift 会自动释放不再需要的实例以释放资源。如[自动引用计数](16_Automatic_Reference_Counting.html)那一章描述Swift 通过_自动引用计数_ARC处理实例的内存管理。通常当你的实例被释放时不需要手动地去清理。但是当使用自己的资源时你可能需要进行一些额外的清理。例如如果创建了一个自定义的类来打开一个文件并写入一些数据你可能需要在类实例被释放之前关闭该文件。
<a name="how_deinitialization_works"></a>
在类的定义中,每个类最多只能有一个析构函数。析构函数不带任何参数,在写法上不带括号: ##析构过程原理
```swift Swift 会自动释放不再需要的实例以释放资源。如[自动引用计数](./16_Automatic_Reference_Counting.html)章节中所讲述Swift 通过`自动引用计数ARC`处理实例的内存管理。通常当你的实例被释放时不需要手动地去清理。但是,当使用自己的资源时,你可能需要进行一些额外的清理。例如,如果创建了一个自定义的类来打开一个文件,并写入一些数据,你可能需要在类实例被释放之前手动去关闭该文件。
deinit {
// 执行析构过程 在类的定义中,每个类最多只能有一个析构器,而且析构器不带任何参数,如下所示:
}
``` ```swift
deinit {
析构函数是在实例释放发生前一步被自动调用。不允许主动调用自己的析构函数。子类继承了父类的析构函数,并且在子类析构函数实现的最后,父类的析构函数被自动调用。即使子类没有提供自己的析构函数,父类的析构函数也总是被调用。 // 执行析构过程
}
因为直到实例的析构函数被调用时,实例才会被释放,所以析构函数可以访问所有请求实例的属性,并且根据那些属性可以修改它的行为(比如查找一个需要被关闭的文件的名称)。 ```
<a name="deinitializers_in_action"></a> 析构器是在实例释放发生前被自动调用。析构器是不允许被主动调用的。子类继承了父类的析构器,并且在子类析构器实现的最后,父类的析构器会被自动调用。即使子类没有提供自己的析构器,父类的析构器也同样会被调用。
##析构函数操作
因为直到实例的析构器被调用时,实例才会被释放,所以析构器可以访问所有请求实例的属性,并且根据那些属性可以修改它的行为(比如查找一个需要被关闭的文件)。
这里是一个析构函数操作的例子。这个例子是一个简单的游戏,定义了两种新类型,`Bank``Player``Bank`结构体管理一个虚拟货币的流通,在这个流通中`Bank`永远不可能拥有超过 10,000 的硬币。在这个游戏中有且只能有一个`Bank`存在,因此`Bank`由带有静态属性和静态方法的结构体实现,从而存储和管理其当前的状态。
<a name="deinitializers_in_action"></a>
```swift ##析构器操作
struct Bank {
static var coinsInBank = 10_000 这是一个析构器操作的例子。这个例子描述了一个简单的游戏,这里定义了两种新类型,分别是`Bank``Player``Bank`结构体管理一个虚拟货币的流通,在这个流通中我们设定`Bank`永远不可能拥有超过 10,000 的硬币,而且在游戏中有且只能有一个`Bank`存在,因此`Bank`结构体在实现时会带有静态属性和静态方法来存储和管理其当前的状态。
static func vendCoins(var numberOfCoinsToVend: Int) -> Int {
numberOfCoinsToVend = min(numberOfCoinsToVend, coinsInBank) ```swift
coinsInBank -= numberOfCoinsToVend struct Bank {
return numberOfCoinsToVend static var coinsInBank = 10_000
} static func vendCoins(var numberOfCoinsToVend: Int) -> Int {
static func receiveCoins(coins: Int) { numberOfCoinsToVend = min(numberOfCoinsToVend, coinsInBank)
coinsInBank += coins coinsInBank -= numberOfCoinsToVend
} return numberOfCoinsToVend
} }
``` static func receiveCoins(coins: Int) {
coinsInBank += coins
`Bank`根据它的`coinsInBank`属性来跟踪当前它拥有的硬币数量。银行还提供两个方法——`vendCoins``receiveCoins`——用来处理硬币的分发和收集。 }
}
`vendCoins`方法在 bank 分发硬币之前检查是否有足够的硬币。如果没有足够多的硬币,`Bank`返回一个比请求时小的数字(如果没有硬币留在 bank 中就返回 0)。`vendCoins`方法声明`numberOfCoinsToVend`为一个变量参数,这样就可以在方法体的内部修改数字,而不需要定义一个新的变量。`vendCoins`方法返回一个整型值,表明了提供的硬币的实际数目。 ```
`receiveCoins`方法只是将 bank 的硬币存储和接收到的硬币数目相加,再保存回 bank `Bank`根据它的`coinsInBank`属性来跟踪当前它拥有的硬币数量。`Bank`还提供两个方法——`vendCoins(_:)``receiveCoins(_:)`,分别用来处理硬币的分发和收集
`Player`类描述了游戏中的一个玩家。每一个 player 在任何时刻都有一定数量的硬币存储在他们的钱包中。这通过 player 的`coinsInPurse`属性来体现: `vendCoins(_:)`方法在bank对象分发硬币之前检查是否有足够的硬币。如果没有足够多的硬币`Bank`会返回一个比请求时要小的数字(如果没有硬币留在bank对象中就返回 0)。`vendCoins`方法声明`numberOfCoinsToVend`为一个变量参数,这样就可以在方法体的内部修改数字,而不需要定义一个新的变量。`vendCoins`方法返回一个整型值,表明了提供的硬币的实际数目。
```swift `receiveCoins`方法只是将bank对象的硬币存储和接收到的硬币数目相加再保存回bank对象。
class Player {
var coinsInPurse: Int `Player`类描述了游戏中的一个玩家。每一个 player 在任何时刻都有一定数量的硬币存储在他们的钱包中。这通过 player 的`coinsInPurse`属性来体现:
init(coins: Int) {
coinsInPurse = Bank.vendCoins(coins) ```swift
} class Player {
func winCoins(coins: Int) { var coinsInPurse: Int
coinsInPurse += Bank.vendCoins(coins) init(coins: Int) {
} coinsInPurse = Bank.vendCoins(coins)
deinit { }
Bank.receiveCoins(coinsInPurse) func winCoins(coins: Int) {
} coinsInPurse += Bank.vendCoins(coins)
} }
``` deinit {
Bank.receiveCoins(coinsInPurse)
}
每个`Player`实例都由一个指定数目硬币组成的启动额度初始化,这些硬币在 bank 初始化的过程中得到。如果没有足够的硬币可用,`Player`实例可能收到比指定数目少的硬币。 }
```
`Player`类定义了一个`winCoins`方法,该方法从银行获取一定数量的硬币,并把它们添加到玩家的钱包。`Player`类还实现了一个析构函数,这个析构函数在`Player`实例释放前一步被调用。这里析构函数只是将玩家的所有硬币都返回给银行:
```swift 每个`Player`实例构造时都会设定由硬币组成的启动额度值这些硬币在bank对象初始化的过程中得到。如果在bank对象中没有足够的硬币可用`Player`实例可能收到比指定数目少的硬币。
var playerOne: Player? = Player(coins: 100)
println("A new player has joined the game with \(playerOne!.coinsInPurse) coins") `Player`类定义了一个`winCoins(_:)`方法该方法从bank对象获取一定数量的硬币并把它们添加到玩家的钱包。`Player`类还实现了一个析构器,这个析构器在`Player`实例释放前被调用。在这里析构器的作用只是将玩家的所有硬币都返回给bank对象
// 输出 "A new player has joined the game with 100 coins"
println("There are now \(Bank.coinsInBank) coins left in the bank") ```swift
// 输出 "There are now 9900 coins left in the bank" var playerOne: Player? = Player(coins: 100)
``` print("A new player has joined the game with \(playerOne!.coinsInPurse) coins")
// 输出 "A new player has joined the game with 100 coins"
一个新的`Player`实例随着一个 100 个硬币(如果有)的请求而被创建。这`个Player`实例存储在一个名为`playerOne`的可选`Player`变量中。这里使用一个可选变量,是因为玩家可以随时离开游戏。设置为可选使得你可以跟踪当前是否有玩家在游戏中。 print("There are now \(Bank.coinsInBank) coins left in the bank")
// 输出 "There are now 9900 coins left in the bank"
因为`playerOne`是可选的,所以由一个感叹号(`!`)来修饰,每当其`winCoins`方法被调用时,`coinsInPurse`属性被访问并打印出它的默认硬币数目。 ```
```swift 一个新的`Player`实例被创建时会设定有 100 个硬币如果bank对象中硬币的数目足够。这`个Player`实例存储在一个名为`playerOne`的可选`Player`变量中。这里使用一个可选变量,是因为玩家可以随时离开游戏。设置为可选使得你可以跟踪当前是否有玩家在游戏中。
playerOne!.winCoins(2_000)
println("PlayerOne won 2000 coins & now has \(playerOne!.coinsInPurse) coins") 因为`playerOne`是可选的,所以用一个感叹号(`!`)作为修饰符,每当其`winCoins(_:)`方法被调用时,`coinsInPurse`属性就会被访问并打印出它的默认硬币数目。
// 输出 "PlayerOne won 2000 coins & now has 2100 coins"
println("The bank now only has \(Bank.coinsInBank) coins left") ```swift
// 输出 "The bank now only has 7900 coins left" playerOne!.winCoins(2_000)
``` print("PlayerOne won 2000 coins & now has \(playerOne!.coinsInPurse) coins")
// 输出 "PlayerOne won 2000 coins & now has 2100 coins"
这里player 已经赢得了 2,000 硬币。player 的钱包现在有 2,100 硬币bank 只剩余 7,900 硬币。 print("The bank now only has \(Bank.coinsInBank) coins left")
// 输出 "The bank now only has 7900 coins left"
```swift ```
playerOne = nil
println("PlayerOne has left the game") 这里player 已经赢得了 2,000 硬币所以player 的钱包现在有 2,100 硬币而bank对象只剩余 7,900 硬币。
// 输出 "PlayerOne has left the game"
println("The bank now has \(Bank.coinsInBank) coins") ```swift
// 输出 "The bank now has 10000 coins" playerOne = nil
``` print("PlayerOne has left the game")
// 输出 "PlayerOne has left the game"
玩家现在已经离开了游戏。这表明是要将可选的`playerOne`变量设置为`nil`,意思是“没有`Player`实例”。当这种情况发生的时候,`playerOne`变量对`Player`实例的引用被破坏了。没有其它属性或者变量引用`Player`实例,因此为了清空它占用的内存从而释放它。在这发生前一步,其析构函数被自动调用,其硬币被返回到银行。 print("The bank now has \(Bank.coinsInBank) coins")
// 输出 "The bank now has 10000 coins"
```
玩家现在已经离开了游戏。这表明是要将可选的`playerOne`变量设置为`nil`,意思是“不存在`Player`实例”。当这种情况发生的时候,`playerOne`变量对`Player`实例的引用被破坏了。没有其它属性或者变量引用`Player`实例因此为了清空它占用的内存从而释放它。在这发生前其析构器会被自动调用从而使其硬币被返回到bank对象中。

View File

@ -1,8 +1,12 @@
> 翻译:[TimothyYe](https://github.com/TimothyYe) # 自动引用计数Automatic Reference Counting
-----------------
> 1.0
> 翻译:[TimothyYe](https://github.com/TimothyYe)
> 校对:[Hawstein](https://github.com/Hawstein) > 校对:[Hawstein](https://github.com/Hawstein)
# 自动引用计数 > 2.0
----------------- > 翻译+校对:[Channe](https://github.com/Channe)
本页包含内容: 本页包含内容:
@ -23,8 +27,8 @@ Swift 使用自动引用计数ARC机制来跟踪和管理你的应用程
<a name="how_arc_works"></a> <a name="how_arc_works"></a>
## 自动引用计数的工作机制 ## 自动引用计数的工作机制
当你每次创建一个类的新的实例的时候ARC 会分配一大块内存用来储存实例的信息。内存中会包含实例的类型信息,以及这个实例所有相关属性的值。 当你每次创建一个类的新的实例的时候ARC 会分配一大块内存用来储存实例的信息。内存中会包含实例的类型信息,以及这个实例所有相关属性的值。
此外当实例不再被使用时ARC 释放实例所占用的内存,并让释放的内存能挪作他用。这确保了不再被使用的实例,不会一直占用内存空间。 此外当实例不再被使用时ARC 释放实例所占用的内存,并让释放的内存能挪作他用。这确保了不再被使用的实例,不会一直占用内存空间。
然而,当 ARC 收回和释放了正在被使用中的实例,该实例的属性和方法将不能再被访问和调用。实际上,如果你试图访问这个实例,你的应用程序很可能会崩溃。 然而,当 ARC 收回和释放了正在被使用中的实例,该实例的属性和方法将不能再被访问和调用。实际上,如果你试图访问这个实例,你的应用程序很可能会崩溃。
@ -86,8 +90,8 @@ reference3 = reference1
```swift ```swift
reference1 = nil reference1 = nil
reference2 = nil reference2 = nil
``` ```
在你清楚地表明不再使用这个`Person`实例时即第三个也就是最后一个强引用被断开时ARC 会销毁它。 在你清楚地表明不再使用这个`Person`实例时即第三个也就是最后一个强引用被断开时ARC 会销毁它。
```swift ```swift
@ -158,7 +162,7 @@ number73!.tenant = john
在将两个实例联系在一起之后,强引用的关系如图所示: 在将两个实例联系在一起之后,强引用的关系如图所示:
![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/referenceCycle02_2x.png) ![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/referenceCycle02_2x.png)
不幸的是,这两个实例关联后会产生一个循环强引用。`Person`实例现在有了一个指向`Apartment`实例的强引用,而`Apartment`实例也有了一个指向`Person`实例的强引用。因此,当你断开`john``number73`变量所持有的强引用时,引用计数并不会降为 0实例也不会被 ARC 销毁: 不幸的是,这两个实例关联后会产生一个循环强引用。`Person`实例现在有了一个指向`Apartment`实例的强引用,而`Apartment`实例也有了一个指向`Person`实例的强引用。因此,当你断开`john``number73`变量所持有的强引用时,引用计数并不会降为 0实例也不会被 ARC 销毁:
```swift ```swift
@ -184,7 +188,7 @@ Swift 提供了两种办法用来解决你在使用类的属性时所遇到的
对于生命周期中会变为`nil`的实例使用弱引用。相反地,对于初始化赋值后再也不会被赋值为`nil`的实例,使用无主引用。 对于生命周期中会变为`nil`的实例使用弱引用。相反地,对于初始化赋值后再也不会被赋值为`nil`的实例,使用无主引用。
### 弱引用 ### 弱引用
弱引用不会对其引用的实例保持强引用,因而不会阻止 ARC 销毁被引用的实例。这个特性阻止了引用变为循环强引用。声明属性或者变量时,在前面加上`weak`关键字表明这是一个弱引用。 弱引用不会对其引用的实例保持强引用,因而不会阻止 ARC 销毁被引用的实例。这个特性阻止了引用变为循环强引用。声明属性或者变量时,在前面加上`weak`关键字表明这是一个弱引用。
在实例的生命周期中,如果某些时候引用没有值,那么弱引用可以避免循环强引用。如果引用总是有值,则可以使用无主引用,在[无主引用](#2)中有描述。在上面`Apartment`的例子中,一个公寓的生命周期中,有时是没有“居民”的,因此适合使用弱引用来解决循环强引用。 在实例的生命周期中,如果某些时候引用没有值,那么弱引用可以避免循环强引用。如果引用总是有值,则可以使用无主引用,在[无主引用](#2)中有描述。在上面`Apartment`的例子中,一个公寓的生命周期中,有时是没有“居民”的,因此适合使用弱引用来解决循环强引用。
@ -297,9 +301,9 @@ class CreditCard {
} }
deinit { print("Card #\(number) is being deinitialized") } deinit { print("Card #\(number) is being deinitialized") }
} }
``` ```
> 注意: > 注意:
> `CreditCard`类的`number`属性被定义为`UInt64`类型而不是`Int`类型,以确保`number`属性的存储量在32位和64位系统上都能足够容纳16位的卡号。 > `CreditCard`类的`number`属性被定义为`UInt64`类型而不是`Int`类型,以确保`number`属性的存储量在32位和64位系统上都能足够容纳16位的卡号。
下面的代码片段定义了一个叫`john`的可选类型`Customer`变量,用来保存某个特定客户的引用。由于是可选类型,所以变量被初始化为`nil` 下面的代码片段定义了一个叫`john`的可选类型`Customer`变量,用来保存某个特定客户的引用。由于是可选类型,所以变量被初始化为`nil`
@ -335,6 +339,8 @@ john = nil
最后的代码展示了在`john`变量被设为`nil``Customer`实例和`CreditCard`实例的构造函数都打印出了“销毁”的信息。 最后的代码展示了在`john`变量被设为`nil``Customer`实例和`CreditCard`实例的构造函数都打印出了“销毁”的信息。
<a name="unowned_references_and_implicitly_unwrapped_optional_properties"></a>
###无主引用以及隐式解析可选属性 ###无主引用以及隐式解析可选属性
上面弱引用和无主引用的例子涵盖了两种常用的需要打破循环强引用的场景。 上面弱引用和无主引用的例子涵盖了两种常用的需要打破循环强引用的场景。
@ -373,9 +379,9 @@ class City {
为了建立两个类的依赖关系,`City`的构造函数有一个`Country`实例的参数,并且将实例保存为`country`属性。 为了建立两个类的依赖关系,`City`的构造函数有一个`Country`实例的参数,并且将实例保存为`country`属性。
`Country`的构造函数调用了`City`的构造函数。然而,只有`Country`的实例完全初始化完后,`Country`的构造函数才能把`self`传给`City`的构造函数。([两段式构造过程中有具体描述](14_Initialization.html) `Country`的构造函数调用了`City`的构造函数。然而,只有`Country`的实例完全初始化完后,`Country`的构造函数才能把`self`传给`City`的构造函数。([两段式构造过程](./14_Initialization.html#two_phase_initialization)中有具体描述
为了满足这种需求,通过在类型结尾处加上感叹号(`City!`)的方式,将`Country``capitalCity`属性声明为隐式解析可选类型的属性。这表示像其他可选类型一样,`capitalCity`属性的默认值为`nil`,但是不需要展开它的值就能访问它。([隐式解析可选类型中有描述](01_The_Basics.html) 为了满足这种需求,通过在类型结尾处加上感叹号(`City!`)的方式,将`Country``capitalCity`属性声明为隐式解析可选类型的属性。这表示像其他可选类型一样,`capitalCity`属性的默认值为`nil`,但是不需要展开它的值就能访问它。([隐式解析可选类型](./01_The_Basics.html#implicityly_unwrapped_optionals)中有描述
由于`capitalCity`默认值为`nil`,一旦`Country`的实例在构造函数中给`name`属性赋值后,整个初始化过程就完成了。这代表一旦`name`属性被赋值后,`Country`的构造函数就能引用并传递隐式的`self``Country`的构造函数在赋值`capitalCity`时,就能将`self`作为参数传递给`City`的构造函数。 由于`capitalCity`默认值为`nil`,一旦`Country`的实例在构造函数中给`name`属性赋值后,整个初始化过程就完成了。这代表一旦`name`属性被赋值后,`Country`的构造函数就能引用并传递隐式的`self``Country`的构造函数在赋值`capitalCity`时,就能将`self`作为参数传递给`City`的构造函数。
@ -456,7 +462,7 @@ print(paragraph!.asHTML())
![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/closureReferenceCycle01_2x.png) ![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/closureReferenceCycle01_2x.png)
实例的`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`实例的一个强引用。 虽然闭包多次使用了`self`,它只捕获`HTMLElement`实例的一个强引用。
@ -477,9 +483,9 @@ paragraph = nil
>注意: >注意:
Swift 有如下要求:只要在闭包内使用`self`的成员,就要用`self.someProperty`或者`self.someMethod`(而不只是`someProperty``someMethod`)。这提醒你可能会一不小心就捕获了`self` Swift 有如下要求:只要在闭包内使用`self`的成员,就要用`self.someProperty`或者`self.someMethod`(而不只是`someProperty``someMethod`)。这提醒你可能会一不小心就捕获了`self`
###定义捕获列表 ###定义捕获列表
捕获列表中的每一项都由一对元素组成,一个元素是`weak``unowned`关键字,另一个元素是类实例的引用(如`self`)或初始化过的变量(如`delegate = self.delegate!`)。这些项在方括号中用逗号分开。 捕获列表中的每一项都由一对元素组成,一个元素是`weak``unowned`关键字,另一个元素是类实例的引用(如`self`)或初始化过的变量(如`delegate = self.delegate!`)。这些项在方括号中用逗号分开。
如果闭包有参数列表和返回类型,把捕获列表放在它们前面: 如果闭包有参数列表和返回类型,把捕获列表放在它们前面:
@ -557,4 +563,3 @@ print(paragraph!.asHTML())
paragraph = nil paragraph = nil
// prints "p is being deinitialized" // prints "p is being deinitialized"
``` ```

View File

@ -1,105 +1,99 @@
> 翻译:[Jasonbroker](https://github.com/Jasonbroker) # 可空链式调用Optional Chaining
> 校对:[numbbbbb](https://github.com/numbbbbb), [stanzhai](https://github.com/stanzhai)
# 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) > 2.0
- [为可选链定义模型类](#defining_model_classes_for_optional_chaining) > 翻译+校对:[lyojo](https://github.com/lyojo)
- [通过可选链调用属性](#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)
可选链Optional Chaining是一种可以请求和调用属性、方法及下标脚本的过程它的可选性体现于请求或调用的目标当前可能为空`nil`)。如果可选的目标有值,那么调用就会成功;相反,如果选择的目标为空(`nil`),则这种调用将返回空(`nil`)。多次请求或调用可以被链接在一起形成一个链,如果任何一个节点为空(`nil`)将导致整个链失效。
> 注意: 可空链式调用Optional Chaining是一种可以请求和调用属性、方法及下标的过程它的可空性体现于请求或调用的目标当前可能为空nil。如果可空的目标有值那么调用就会成功如果选择的目标为空nil那么这种调用将返回空nil。多个连续的调用可以被链接在一起形成一个调用链如果其中任何一个节点为空nil将导致整个链调用失败。
Swift 的可选链和 Objective-C 中的消息为空有些相像,但是 Swift 可以使用在任意类型中,并且失败与否可以被检测到。
>
注意:
Swift 的可空链式调用和 Objective-C 中的消息为空有些相像,但是 Swift 可以使用在任意类型中,并且能够检查调用是否成功。
<a name="optional_chaining_as_an_alternative_to_forced_unwrapping"></a> <a name="optional_chaining_as_an_alternative_to_forced_unwrapping"></a>
## 可选链可替代强制解析 ##使用可空链式调用来强制展开
通过在想调用非空的属性、方法、或下标的可空值optional value后面放一个问号可以定义一个可空链。这一点很像在可空值后面放一个叹号来强制展开其中值。它们的主要的区别在于当可空值为空时可空链式只是调用失败然而强制展开将会触发运行时错误。
通过在想调用的属性、方法、下标脚本的可选值(`optional value`)(非空)后面放一个问号,可以定义一个可选链。这一点很像在可选值后面放一个叹号来强制拆得其封包内的值。它们的主要的区别在于当可选值为空时可选链即刻失败,然而一般的强制解析将会引发运行时错误 为了反映可空链式调用可以在空对象nil上调用不论这个调用的属性、方法、下标等返回的值是不是可空值,它的返回结果都是一个可空值。你可以利用这个返回值来判断你的可空链式调用是否调用成功,如果调用有返回值则说明调用成功,返回`nil`则说明调用失败
为了反映可选链可以调用空(`nil`不论你调用的属性、方法、下标脚本等返回的值是不是可选值它的返回结果都是一个可选值。你可以利用这个返回值来检测你的可选链是否调用成功有返回值即成功返回nil则失败 特别地,可空链式调用的返回结果与原本的返回结果具有相同的类型,但是被包装成了一个可空类型值。当可空链式调用成功时,一个本应该返回`Int`的类型的结果将会返回`Int?`类型
调用可选链的返回结果与原本的返回结果具有相同的类型,但是原本的返回结果被包装成了一个可选值,当可选链调用成功时,一个应该返回`Int`的属性将会返回`Int?`
下面几段代码将解释可选链和强制解析的不同。
下面几段代码将解释可空链式调用和强制展开的不同。
首先定义两个类`Person``Residence` 首先定义两个类`Person``Residence`
```swift ```swift
class Person { class Person {
var residence: Residence? var residence: Residence?
} }
class 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 ```swift
let john = Person() let john = Person()
``` ```
如果你想使用叹号(`!`)强制解析获得这个`residence`属性`numberOfRooms`属性值,将会引发运行时错误,因为这时没有可以供解析`residence`值。 如果使用叹号()强制展开获得这个`john``residence`属性中的`numberOfRooms`值,会触发运行时错误,因为这时没有可以展开`residence`
```swift ```swift
let roomCount = john.residence!.numberOfRooms let roomCount = john.residence!.numberOfRooms
//将导致运行时错误 // this triggers a runtime error
``` ```
`john.residence`不是`nil`时,会运行通过,且会将`roomCount` 设置为一个`int`类型的合理值。然而,如上所述,当`residence`为空时,这个代码将会导致运行时错误。
可选链提供了一种另一种获得`numberOfRooms`的方法。利用可选链,使用问号来代替原来`!`的位置: `john.residence`非空的时候,上面的调用成功,并且把`roomCount`设置为`Int`类型的房间数量。正如上面说到的,当`residence`为空的时候上面这段代码会触发运行时错误。
可空链式调用提供了一种另一种访问`numberOfRooms`的方法,使用问号(?)来代替原来叹号(!)的位置:
```swift ```swift
if let roomCount = john.residence?.numberOfRooms { if let roomCount = john.residence?.numberOfRooms {
println("John's residence has \(roomCount) room(s).") print("John's residence has \(roomCount) room(s).")
} else { } 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 ```swift
john.residence = Residence() john.residence = Residence()
``` ```
`john.residence` 现在有了实际存在的实例而不是nil了。如果你想使用和前面一样的可选链来获得`numberOfRoooms`它将返回一个包含默认值 1 的`Int?` 这样`john.residence`不为`nil`了。现在就可以正常访问`john.residence.numberOfRooms`其值为默认的1类型为`Int?`
```swift ```swift
if let roomCount = john.residence?.numberOfRooms { if let roomCount = john.residence?.numberOfRooms {
println("John's residence has \(roomCount) room(s).") print("John's residence has \(roomCount) room(s).")
} else { } 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)."
``` ```
<a name="defining_model_classes_for_optional_chaining"></a> ##为可空链式调用定义模型类
##为可选链定义模型类 通过使用可空链式调用可以调用多层属性,方法,和下标。这样可以通过各种模型向下访问各种子属性。并且判断能否访问子属性的属性,方法或下标。
你可以使用可选链来多层调用属性,方法,和下标脚本。这让你可以利用它们之间的复杂模型来获取更底层的属性,并检查是否可以成功获取此类底层属性 下面这段代码定义了四个模型类,这些例子包括多层可空链式调用。为了方便说明,在`Person``Residence`的基础上增加了`Room``Address`,以及相关的属性,方法以及下标
后面的代码定义了四个将在后面使用的模型类,其中包括多层可选链。这些类是由上面的`Person``Residence`模型通过添加一个`Room`和一个`Address`类拓展来。 Person类定义基本保持不变
`Person`类定义与之前相同。
```swift ```swift
class Person { class Person {
@ -107,32 +101,32 @@ class Person {
} }
``` ```
`Residence`类比之前复杂些。这次,它定义了一个变量 `rooms`,它被初始化为一个`Room[]`类型的空数组 `Residence`类比之前复杂些,增加了一个`Room`类型的空数组`room`
```swift ```swift
class Residence { class Residence {
var rooms = [Room]() var rooms = [Room]()
var numberOfRooms: Int { var numberOfRooms: Int {
return rooms.count return rooms.count
} }
subscript(i: Int) -> Room { subscript(i: Int) -> Room {
return rooms[i] get {
return rooms[i]
}
set {
rooms[i] = newValue
}
} }
func printNumberOfRooms() { func printNumberOfRooms() {
println("The number of rooms is \(numberOfRooms)") print("The number of rooms is \(numberOfRooms)")
} }
var address: Address? var address: Address?
} }
``` ```
因为`Residence`存储了一个`Room`实例的数组,它的`numberOfRooms`属性值不是一个固定的存储值,而是通过计算而来的。`numberOfRooms`属性值是由返回`rooms`数组的`count`属性值得到的 现在`Residence`了一个存储`Room`类型的数组,`numberOfRooms`属性需要计算,而不是作为单纯的变量。计算后的`numberOfRooms`返回`rooms`数组的`count`属性值。现在的`Residence`还提供访问`rooms`数组的快捷方式, 通过可读写的下标来访问指定位置的数组元素。此外,还提供`printNumberOfRooms`方法,这个方法的作用就是输出这个房子中房间的数量。最后,`Residence`定义了一个可空属性`address`,其类型为`Address?``Address`类的定义在下面会说明
为了能快速访问`rooms`数组,`Residence`定义了一个只读的下标脚本,通过插入数组的元素角标就可以成功调用。如果该角标存在,下标脚本则将该元素返回。 `Room`是一个简单类,只包含一个属性`name`,以及一个初始化函数:
`Residence`中也提供了一个`printNumberOfRooms`的方法,即简单的打印房间个数。
最后,`Residence`定义了一个可选属性叫`address``address?`)。`Address`类的属性将在后面定义。
用于`rooms`数组的`Room`类是一个很简单的类,它只有一个`name`属性和一个设定`room`名的初始化器。
```swift ```swift
class Room { class Room {
@ -141,8 +135,7 @@ class Room {
} }
``` ```
最后一个类是`Address`,这个类有三个`String?`类型的可空属性。`buildingName`以及`buildingNumber`属性表示建筑的名称和号码,用来表示某个特定的建筑。第三个属性表示建筑所在街道的名称:
这个模型中的最终类叫做`Address`。它有三个类型是`String?`的可选属性。前面两个可选属性`buildingName``buildingNumber`作为地址的一部分,是定义某个建筑物的两种方式。第三个属性`street`,用于命名地址的街道名:
```swift ```swift
class Address { class Address {
@ -150,9 +143,9 @@ class Address {
var buildingNumber: String? var buildingNumber: String?
var street: String? var street: String?
func buildingIdentifier() -> String? { func buildingIdentifier() -> String? {
if buildingName { if buildingName != nil {
return buildingName return buildingName
} else if buildingNumber { } else if buildingNumber != nil {
return buildingNumber return buildingNumber
} else { } else {
return nil return nil
@ -161,161 +154,201 @@ class Address {
} }
``` ```
`Address`类还提供了一个`buildingIdentifier`方法,它的返回值类型`String?`这个方法检查`buildingName``buildingNumber`的属性,如果`buildingName`有值则将其返回,或者如果`buildingNumber`有值则将其返回,再或如果没有一个属性有值,返回空 `Address`提供`buildingIdentifier()`方法,返回值为`String?` 如果`buildingName`不为空则返回`buildingName` 如果`buildingNumber`不为空则返回`buildingNumber`。如果这两个属性都为空则返回`nil`
<a name="calling_properties_through_optional_chaining"></a> ##通过可空链式调用访问属性
##通过可选链调用属性 正如[使用可空链式调用来强制展开](#optional_chaining_as_an_alternative_to_forced_unwrapping)中所述,可以通过可空链式调用访问属性的可空值,并且判断访问是否成功。
正如上面“ [可选链可替代强制解析](#optional_chaining_as_an_alternative_to_forced_unwrapping)”中所述,你可以利用可选链的可选值获取属性,并且检查属性是否获取成功。然而,你不能使用可选链为属性赋值。 下面的代码创建了一个`Person`实例,然后访问`numberOfRooms`属性:
使用上述定义的类来创建一个人实例,并再次尝试后去它的`numberOfRooms`属性:
```swift ```swift
let john = Person() let john = Person()
if let roomCount = john.residence?.numberOfRooms { if let roomCount = john.residence?.numberOfRooms {
println("John's residence has \(roomCount) room(s).") print("John's residence has \(roomCount) room(s).")
} else { } 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`,所以毫无疑问这个可空链式调用失败
<a name="calling_methods_through_optional_chaining"></a> 通过可空链式调用来设定属性值:
##通过可选链调用方法
你可以使用可选链的来调用可选值的方法并检查方法调用是否成功。即使这个方法没有返回值,你依然可以使用可选链来达成这一目的。
`Residence``printNumberOfRooms`方法会打印`numberOfRooms`的当前值。方法如下:
```swift ```swift
func printNumberOfRooms(){ let someAddress = Address()
println(The number of rooms is \(numberOfRooms)) 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 ```swift
if john.residence?.printNumberOfRooms?() { func printNumberOfRooms() {
println("It was possible to print the number of rooms.") print("The number of rooms is \(numberOfRooms)")
} else {
println("It was not possible to print the number of rooms.")
} }
// 打印 "It was not possible to print the number of rooms."。
``` ```
<a name="calling_subscripts_through_optional_chaining"></a> 这个方法没有返回值。但是没有返回值的方法隐式返回`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 ```swift
if let firstRoomName = john.residence?[0].name { if let firstRoomName = john.residence?[0].name {
println("The first room name is \(firstRoomName).") print("The first room name is \(firstRoomName).")
} else { } 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 ```swift
let johnsHouse = Residence() let johnsHouse = Residence()
johnsHouse.rooms += Room(name: "Living Room") johnsHouse.rooms.append(Room(name: "Living Room"))
johnsHouse.rooms += Room(name: "Kitchen") johnsHouse.rooms.append(Room(name: "Kitchen"))
john.residence = johnsHouse john.residence = johnsHouse
if let firstRoomName = john.residence?[0].name { if let firstRoomName = john.residence?[0].name {
println("The first room name is \(firstRoomName).") print("The first room name is \(firstRoomName).")
} else { } 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."
``` ```
<a name="linking_multiple_levels_of_chaining"></a> ##访问可空类型的下标:
##连接多层链接 如果下标返回可空类型值比如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 ```swift
if let johnsStreet = john.residence?.address?.street { if let johnsStreet = john.residence?.address?.street {
println("John's street name is \(johnsStreet).") print("John's street name is \(johnsStreet).")
} else { } 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 ```swift
let johnsAddress = Address() let johnsAddress = Address()
johnsAddress.buildingName = "The Larches" johnsAddress.buildingName = "The Larches"
johnsAddress.street = "Laurel Street" johnsAddress.street = "Laurel Street"
john.residence!.address = johnsAddress john.residence?.address = johnsAddress
```
```swift
if let johnsStreet = john.residence?.address?.street { if let johnsStreet = john.residence?.address?.street {
println("John's street name is \(johnsStreet).") print("John's street name is \(johnsStreet).")
} else { } 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`属性赋值成功
<a name="chaining_on_methods_with_optional_return_values"></a> ##对返回可空值的函数进行链接
##链接可选返回值的方法 上面的例子说明了如何通过可空链式调用来获取可空属性值。我们还可以通过可空链式调用来调用返回可空值的方法,并且可以继续对可空值进行链接。
前面的例子解释了如何通过可选链来获得可选类型属性值。你也可以通过可选链调用一个返回可选类型值的方法并按需链接该方法的返回值。
下面的例子通过可选链调用了`Address`类中的`buildingIdentifier` 方法。这个方法的返回值类型是`String?`。如上所述,这个方法在可选链调用后最终的返回值类型依然是`String?`
在下面的例子中,通过可空链式调用来调用`Address``buildingIdentifier()`方法。这个方法返回`String?`类型。正如上面所说,通过可空链式调用的方法的最终返回值还是`String?`
```swift ```swift
if let buildingIdentifier = john.residence?.address?.buildingIdentifier() { 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 ```swift
if let upper = john.residence?.address?.buildingIdentifier()?.uppercaseString { if let beginsWithThe =
println("John's uppercase building identifier is \(upper).") 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()`的返回值是可空值,而不是方法本身是可空的。

View File

@ -1,2 +1,177 @@
# 错误处理 # 错误处理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`表示。
<a name="throwing_errors"></a>
错误抛出
通过在函数或方法声明的参数后面加上`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`。这个调用不管是否有抛出都会执行。

View File

@ -1,9 +1,13 @@
> 翻译:[xiehurricane](https://github.com/xiehurricane)
> 校对:[happyming](https://github.com/happyming) [yangsiy](https://github.com/yangsiy)
# 类型转换Type Casting # 类型转换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) - [定义一个类层次作为例子](#defining_a_class_hierarchy_for_type_casting)
@ -16,7 +20,7 @@ _类型转换_可以判断实例的类型也可以将实例看做是其父类
类型转换在 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)部分讲述的一样。
<a name="defining_a_class_hierarchy_for_type_casting"></a> <a name="defining_a_class_hierarchy_for_type_casting"></a>
## 定义一个类层次作为例子 ## 定义一个类层次作为例子
@ -155,6 +159,7 @@ Swift为不确定类型提供了两种特殊类型别名
> 注意: > 注意:
> 只有当你明确的需要它的行为和功能时才使用`Any``AnyObject`。在你的代码里使用你期望的明确的类型总是更好的。 > 只有当你明确的需要它的行为和功能时才使用`Any``AnyObject`。在你的代码里使用你期望的明确的类型总是更好的。
<a name="anyobject"></a>
### `AnyObject`类型 ### `AnyObject`类型
当在工作中使用 Cocoa APIs我们一般会接收一个`[AnyObject]`类型的数组,或者说“一个任何对象类型的数组”。这是因为 Objective-C 没有明确的类型化数组。但是,你常常可以从 API 提供的信息中清晰地确定数组中对象的类型。 当在工作中使用 Cocoa APIs我们一般会接收一个`[AnyObject]`类型的数组,或者说“一个任何对象类型的数组”。这是因为 Objective-C 没有明确的类型化数组。但是,你常常可以从 API 提供的信息中清晰地确定数组中对象的类型。

View File

@ -1,8 +1,12 @@
> 翻译:[Lin-H](https://github.com/Lin-H) # 嵌套类型Nested Types
-----------------
> 1.0
> 翻译:[Lin-H](https://github.com/Lin-H)
> 校对:[shinyzhu](https://github.com/shinyzhu) > 校对:[shinyzhu](https://github.com/shinyzhu)
# 嵌套类型 > 2.0
----------------- > 翻译+校对:[SergioChan](https://github.com/SergioChan)
本页包含内容: 本页包含内容:
@ -45,7 +49,7 @@ struct BlackjackCard {
} }
} }
} }
// BlackjackCard 的属性和方法 // BlackjackCard 的属性和方法
let rank: Rank, suit: Suit let rank: Rank, suit: Suit
var description: String { var description: String {
@ -72,7 +76,7 @@ struct BlackjackCard {
`BlackjackCard`结构体自身有两个属性—`rank``suit`,也同样定义了一个计算属性`description``description`属性用`rank``suit`的中内容来构建对这张扑克牌名字和数值的描述,并用可选类型`second`来检查是否存在第二个值,若存在,则在原有的描述中增加对第二数值的描述。 `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 ```swift
let theAceOfSpades = BlackjackCard(rank: .Ace, suit: .Spades) let theAceOfSpades = BlackjackCard(rank: .Ace, suit: .Spades)
@ -93,4 +97,3 @@ let heartsSymbol = BlackjackCard.Suit.Hearts.rawValue
``` ```
对于上面这个例子,这样可以使`Suit`, `Rank`, 和 `Values`的名字尽可能的短,因为它们的名字会自然的由定义它们的上下文来限定。 对于上面这个例子,这样可以使`Suit`, `Rank`, 和 `Values`的名字尽可能的短,因为它们的名字会自然的由定义它们的上下文来限定。

View File

@ -1,8 +1,12 @@
> 翻译:[lyuka](https://github.com/lyuka) # 扩展Extensions
----
> 1.0
> 翻译:[lyuka](https://github.com/lyuka)
> 校对:[Hawstein](https://github.com/Hawstein) > 校对:[Hawstein](https://github.com/Hawstein)
#扩展Extensions > 2.0
---- > 翻译+校对:[shanksyang](https://github.com/shanksyang)
本页包含内容: 本页包含内容:
@ -13,22 +17,24 @@
- [下标](#subscripts) - [下标](#subscripts)
- [嵌套类型](#nested_types) - [嵌套类型](#nested_types)
*扩展*就是向一个已有的类、结构体枚举类型添加新功能functionality。这包括在没有权限获取原始源代码的情况下扩展类型的能力即*逆向建模*)。扩展和 Objective-C 中的分类categories类似。不过与Objective-C不同的是Swift 的扩展没有名字。) *扩展*就是向一个已有的类、结构体枚举类型或者协议类型添加新功能functionality。这包括在没有权限获取原始源代码的情况下扩展类型的能力即*逆向建模*)。扩展和 Objective-C 中的分类categories类似。不过与 Objective-C 不同的是Swift 的扩展没有名字。)
Swift 中的扩展可以: Swift 中的扩展可以:
- 添加计算型属性和计算静态属性 - 添加计算型属性和计算静态属性
- 定义实例方法和类型方法 - 定义实例方法和类型方法
- 提供新的构造器 - 提供新的构造器
- 定义下标 - 定义下标
- 定义和使用新的嵌套类型 - 定义和使用新的嵌套类型
- 使一个已有类型符合某个协议 - 使一个已有类型符合某个协议
TODO
在 Swift 中,你甚至可以对一个协议(Procotol)进行扩展,提供协议需要的实现,或者添加额外的功能能够对合适的类型带来额外的好处。你可以从[协议扩展](./22_Protocols.html#protocol_extensions)获取更多的细节。
>注意: >注意:
如果你定义了一个扩展向一个已有类型添加新功能,那么这个新功能对该类型的所有已有实例中都是可用的,即使它们是在你的这个扩展的前面定义的 扩展可以对一个类型添加新功能,但是不能重写已有的功能
<a name="extension_syntax"></a> <a name="extension_syntax"></a>
## 扩展语法Extension Syntax ## 扩展语法Extension Syntax
声明一个扩展使用关键字`extension` 声明一个扩展使用关键字`extension`
@ -38,7 +44,6 @@ extension SomeType {
// 加到SomeType的新功能写到这里 // 加到SomeType的新功能写到这里
} }
``` ```
一个扩展可以扩展一个已有类型使其能够适配一个或多个协议protocol。当这种情况发生时协议的名字应该完全按照类或结构体的名字的方式进行书写 一个扩展可以扩展一个已有类型使其能够适配一个或多个协议protocol。当这种情况发生时协议的名字应该完全按照类或结构体的名字的方式进行书写
```swift ```swift
@ -47,12 +52,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)
>注意:
如果你定义了一个扩展向一个已有类型添加新功能,那么这个新功能对该类型的所有已有实例中都是可用的,即使它们是在你的这个扩展的前面定义的。
<a name="computed_properties"></a> <a name="computed_properties"></a>
## 计算型属性Computed Properties ## 计算型属性Computed Properties
扩展可以向已有类型添加计算型实例属性和计算型类型属性。下面的例子向 Swift 的内建`Double`类型添加了5个计算型实例属性从而提供与距离单位协作的基本支持 扩展可以向已有类型添加计算型实例属性和计算型类型属性。下面的例子向 Swift 的内建`Double`类型添加了5个计算型实例属性从而提供与距离单位协作的基本支持
```swift ```swift
extension Double { extension Double {
@ -63,10 +71,10 @@ extension Double {
var ft: Double { return self / 3.28084 } var ft: Double { return self / 3.28084 }
} }
let oneInch = 25.4.mm let oneInch = 25.4.mm
println("One inch is \(oneInch) meters") print("One inch is \(oneInch) meters")
// 打印输出:"One inch is 0.0254 meters" // 打印输出:"One inch is 0.0254 meters"
let threeFeet = 3.ft let threeFeet = 3.ft
println("Three feet is \(threeFeet) meters") print("Three feet is \(threeFeet) meters")
// 打印输出:"Three feet is 0.914399970739201 meters" // 打印输出:"Three feet is 0.914399970739201 meters"
``` ```
@ -80,11 +88,10 @@ println("Three feet is \(threeFeet) meters")
```swift ```swift
let aMarathon = 42.km + 195.m 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" // 打印输出:"A marathon is 42195.0 meters long"
``` ```
>注意: >注意:
扩展可以添加新的计算属性,但是不可以添加存储属性,也不可以向已有属性添加属性观测器(property observers)。 扩展可以添加新的计算属性,但是不可以添加存储属性,也不可以向已有属性添加属性观测器(property observers)。
@ -93,13 +100,12 @@ println("A marathon is \(aMarathon) meters long")
扩展可以向已有类型添加新的构造器。这可以让你扩展其它类型,将你自己的定制类型作为构造器参数,或者提供该类型的原始实现中没有包含的额外初始化选项。 扩展可以向已有类型添加新的构造器。这可以让你扩展其它类型,将你自己的定制类型作为构造器参数,或者提供该类型的原始实现中没有包含的额外初始化选项。
扩展能向类中添加新的便利构造器,但是它们不能向类中添加新的指定构造器或析构函数。指定构造器和析构函数必须总是由原始的类实现来提供。 扩展能向类中添加新的便利构造器,但是它们不能向类中添加新的指定构造器或析构。指定构造器和析构必须总是由原始的类实现来提供。
> 注意: > 注意:
如果你使用扩展向一个值类型添加一个构造器在该值类型已经向所有的存储属性提供默认值而且没有定义任何定制构造器custom initializers你可以在值类型的扩展构造器中调用默认构造器(default initializers)和逐一成员构造器(memberwise initializers)。 如果你使用扩展向一个值类型添加一个构造器在该值类型已经向所有的存储属性提供默认值而且没有定义任何定制构造器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`作为所有属性的默认值: 下面的例子定义了一个用于描述几何矩形的定制结构体`Rect`。这个例子同时定义了两个辅助结构体`Size``Point`,它们都把`0.0`作为所有属性的默认值:
@ -115,8 +121,7 @@ struct Rect {
var size = Size() var size = Size()
} }
``` ```
因为结构体`Rect`提供了其所有属性的默认值,所以正如[默认构造器](./14_Initialization.html#default_initializers)中描述的,它可以自动接受一个默认构造器和一个逐一成员构造器。这些构造器可以用于构造新的`Rect`实例:
因为结构体`Rect`提供了其所有属性的默认值,所以正如默认构造器中描述的,它可以自动接受一个默认的构造器和一个成员级构造器。这些构造器可以用于构造新的`Rect`实例:
```swift ```swift
let defaultRect = Rect() let defaultRect = Rect()
@ -135,8 +140,7 @@ extension Rect {
} }
} }
``` ```
这个新的构造器首先根据提供的`center``size`值计算一个合适的原点。然后调用该结构体自动的逐一成员构造器`init(origin:size:)`,该构造器将新的原点和大小存到了合适的属性中:
这个新的构造器首先根据提供的`center``size`值计算一个合适的原点。然后调用该结构体自动的成员构造器`init(origin:size:)`,该构造器将新的原点和大小存到了合适的属性中:
```swift ```swift
let centerRect = Rect(center: Point(x: 4.0, y: 4.0), let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
@ -169,7 +173,7 @@ extension Int {
```swift ```swift
3.repetitions({ 3.repetitions({
println("Hello!") print("Hello!")
}) })
// Hello! // Hello!
// Hello! // Hello!
@ -180,7 +184,7 @@ extension Int {
```swift ```swift
3.repetitions{ 3.repetitions{
println("Goodbye!") print("Goodbye!")
} }
// Goodbye! // Goodbye!
// Goodbye! // Goodbye!
@ -250,52 +254,49 @@ extension Int {
扩展可以向已有的类、结构体和枚举添加新的嵌套类型: 扩展可以向已有的类、结构体和枚举添加新的嵌套类型:
```swift ```swift
extension Character { extension Int {
enum Kind { enum Kind {
case Vowel, Consonant, Other case Negative, Zero, Positive
} }
var kind: Kind { var kind: Kind {
switch String(self).lowercaseString { switch self {
case "a", "e", "i", "o", "u": case 0:
return .Vowel return .Zero
case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", case let x where x > 0:
"n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z": return .Positive
return .Consonant
default: default:
return .Other return .Negative
} }
} }
} }
``` ```
该例子向`Character`添加了新的嵌套枚举。这个名为`Kind`的枚举表示特定字符的类型。具体来说,就是表示一个标准的拉丁脚本中的字符是元音还是辅音(不考虑口语和地方变种),或者是其它类型 该例子向`Int`添加了新的嵌套枚举。这个名为`Kind`的枚举表示特定整数的类型。具体来说,就是表示整数是正数,零或者负数
这个例子还向`Character`添加了一个新的计算实例属性,即`kind`,用来返回合适的`Kind`枚举成员。 这个例子还向`Int`添加了一个新的计算实例属性,即`kind`,用来返回合适的`Kind`枚举成员。
现在,这个嵌套枚举可以和一个`Int`值联合使用了:
现在,这个嵌套枚举可以和一个`Character`值联合使用了:
```swift ```swift
func printLetterKinds(word: String) { func printIntegerKinds(numbers: [Int]) {
println("'\(word)' is made up of the following kinds of letters:") for number in numbers {
for character in word { switch number.kind {
switch character.kind { case .Negative:
case .Vowel: print("- ", appendNewline: false)
print("vowel ") case .Zero:
case .Consonant: print("0 ", appendNewline: false)
print("consonant ") case .Positive:
case .Other: print("+ ", appendNewline: false)
print("other ")
} }
} }
print("\n") print("")
} }
printLetterKinds("Hello") printIntegerKinds([3, 19, -27, 0, -6, 0, 7])
// 'Hello' is made up of the following kinds of letters: // prints "+ + - 0 - 0 +"
// consonant vowel consonant consonant vowel
``` ```
函数`printLetterKinds`的输入是一个`String`值并对其字符进行迭代。在每次迭代过程中,考虑当前字符的`kind`计算属性,并打印出合适的类别描述。所以`printLetterKinds`就可以用来打印一个完整单词中所有字母的类型,正如上述单词`"hello"`所展示的。 函数`printIntegerKinds`的输入是一个`Int`数组值并对其字符进行迭代。在每次迭代过程中,考虑当前字符的`kind`计算属性,并打印出合适的类别描述。
>注意:
由于已知`character.kind``Character.Kind`型,所以`Character.Kind`中的所有成员值都可以使用`switch`语句里的形式简写,比如使用 `.Vowel`代替`Character.Kind.Vowel`
>注意:
由于已知`number.kind ``Int.Kind`型,所以`Int.Kind`中的所有成员值都可以使用`switch`语句里的形式简写,比如使用 `. Negative`代替`Int.Kind.Negative`

View File

@ -1,15 +1,19 @@
> 翻译:[geek5nan](https://github.com/geek5nan) # 协议Protocols
-----------------
> 1.0
> 翻译:[geek5nan](https://github.com/geek5nan)
> 校对:[dabing1022](https://github.com/dabing1022) > 校对:[dabing1022](https://github.com/dabing1022)
# 协议 > 2.0
----------------- > 翻译+校对:[futantan](https://github.com/futantan)
本页包含内容: 本页包含内容:
- [协议的语法Protocol Syntax](#protocol_syntax) - [协议的语法Protocol Syntax](#protocol_syntax)
- [对属性的规定Property Requirements](#property_requirements) - [对属性的规定Property Requirements](#property_requirements)
- [对方法的规定Method Requirements](#method_requirements) - [对方法的规定Method Requirements](#method_requirements)
- [突变方法的规定Mutating Method Requirements](#mutating_method_requirements) - [Mutating方法的规定Mutating Method Requirements](#mutating_method_requirements)
- [对构造器的规定Initializer Requirements](#initializer_requirements) - [对构造器的规定Initializer Requirements](#initializer_requirements)
- [协议类型Protocols as Types](#protocols_as_types) - [协议类型Protocols as Types](#protocols_as_types)
- [委托(代理)模式Delegation](#delegation) - [委托(代理)模式Delegation](#delegation)
@ -21,15 +25,15 @@
- [协议合成Protocol Composition](#protocol_composition) - [协议合成Protocol Composition](#protocol_composition)
- [检验协议的一致性Checking for Protocol Conformance](#checking_for_protocol_conformance) - [检验协议的一致性Checking for Protocol Conformance](#checking_for_protocol_conformance)
- [对可选协议的规定Optional Protocol Requirements](#optional_protocol_requirements) - [对可选协议的规定Optional Protocol Requirements](#optional_protocol_requirements)
- [协议扩展Protocol Extensions](#protocol_extensions)
`协议(Protocol)`用于定义完成某项任务或功能所必须的方法和属性,协议实际上并不提供这些功能或任务的具体`实现(Implementation)`--而只用来描述这些实现应该是什么样的。类,结构体,枚举通过提供协议所要求的方法,属性的具体实现来`采用(adopt)`协议。任意能够满足协议要求的类型被称为协议的`遵循者`
`协议`可以要求其`遵循者`提供特定的实例属性,实例方法,类方法,操作符或下标脚本等 `协议`定义了一个蓝图,规定了用来实现某一特定工作或者功能所必需的方法和属性。类,结构体或枚举类型都可以遵循协议,并提供具体实现来完成协议定义的方法和功能。任意能够满足协议要求的类型被称为`遵循(conform)`这个协议
<a name="protocol_syntax"></a> <a name="protocol_syntax"></a>
## 协议的语法 ## 协议的语法
`协议`的定义方式与`类,结构体,枚举`的定义非常相似,如下所示: 协议的定义方式与类,结构体,枚举的定义非常相似
```swift ```swift
protocol SomeProtocol { protocol SomeProtocol {
@ -37,7 +41,7 @@ protocol SomeProtocol {
} }
``` ```
在类型名称后加上`协议名称`,中间以冒号`:`分隔即可实现协议;实现多个协议时,各协议之间用逗号`,`分隔,如下所示: 要使类遵循某个协议,需要在类型名称后加上协议名称,中间以冒号`:`分隔,作为类型定义的一部分。遵循多个协议时,各协议之间用逗号`,`分隔
```swift ```swift
struct SomeStructure: FirstProtocol, AnotherProtocol { struct SomeStructure: FirstProtocol, AnotherProtocol {
@ -45,7 +49,7 @@ struct SomeStructure: FirstProtocol, AnotherProtocol {
} }
``` ```
如果一个类在含有`父类`的同时也采用了协议,应当把`父类`放在所有的`协议`之前,如下所示: 如果类在遵循协议的同时拥有父类,应该将父类名放在协议名之前,以逗号分隔。
```swift ```swift
class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol { class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
@ -56,11 +60,11 @@ class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
<a name="property_requirements"></a> <a name="property_requirements"></a>
## 对属性的规定 ## 对属性的规定
协议可以规定其`遵循者`提供特定名称类型的`实例属性(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 ```swift
protocol SomeProtocol { protocol SomeProtocol {
@ -69,15 +73,15 @@ protocol SomeProtocol {
} }
``` ```
如下所示,通常在协议定义中使用`class`前缀表示该属性为类成员;在枚举和结构体实现协议时中,需要使用`static`关键字作为前缀 在协议定义类属性(type property)时,总是使用`static`关键字作为前缀。当协议的遵循者是类时,可以使用`class``static`关键字来声明类属性,但是在协议的定义中,仍然要使用`static`关键字。
```swift ```swift
protocol AnotherProtocol { protocol AnotherProtocol {
class var someTypeProperty: Int { get set } static var someTypeProperty: Int { get set }
} }
``` ```
如下所示,这是一个含有一个实例属性要求的协议: 如下所示,这是一个含有一个实例属性要求的协议
```swift ```swift
protocol FullyNamed { protocol FullyNamed {
@ -85,9 +89,9 @@ protocol FullyNamed {
} }
``` ```
`FullyNamed`协议定义了任何拥有`fullName`的类型。它并不指定具体类型,而只是要求类型必须提供一个`fullName`。任何`FullyNamed`类型都得有一个读的`fullName`属性,类型为`String` `FullyNamed`协议除了要求协议的遵循者提供fullName属性外对协议对遵循者的类型并没有特别的要求。这个协议表示任何遵循`FullyNamed`协议的类型,都具有一个读的`String`类型实例属性`fullName`
如下所示,这是一个实现了`FullyNamed`协议的简单结构体: 下面是一个遵循`FullyNamed`协议的简单结构体
```swift ```swift
struct Person: FullyNamed{ struct Person: FullyNamed{
@ -97,47 +101,44 @@ let john = Person(fullName: "John Appleseed")
//john.fullName 为 "John Appleseed" //john.fullName 为 "John Appleseed"
``` ```
这个例子中定义了一个叫做`Person`的结构体,用来表示具有指定名字的人。从第一行代码中可以看出,它采用`FullyNamed`协议。 这个例子中定义了一个叫做`Person`的结构体,用来表示具有名字的人。从第一行代码中可以看出,它遵循`FullyNamed`协议。
`Person`结构体的每一个实例都有一个叫做`fullName``String`类型的存储型属性这正好匹配`FullyNamed`协议的要求,也就意味着,`Person`结构体完整的`遵循`了协议。(如果协议要求未被完全满足,在编译时会报错) `Person`结构体的每一个实例都有一个叫做`fullName``String`类型的存储型属性这正好满足`FullyNamed`协议的要求,也就意味着,`Person`结构体完整的`遵循`了协议。(如果协议要求未被完全满足,在编译时会报错)
这有一个更为复杂的类,它采用并实现`FullyNamed`协议,如下所示: 下面是一个更为复杂的类,它采用并遵循`FullyNamed`协议:
```swift ```swift
class Starship: FullyNamed { class Starship: FullyNamed {
var prefix: String? var prefix: String?
var name: String var name: String
init(name: String, prefix: String? = nil ) { init(name: String, prefix: String? = nil) {
self.name = name self.name = name
self.prefix = prefix self.prefix = prefix
} }
var fullName: String { var fullName: String {
return (prefix != nil ? prefix! + " " : " ") + name return (prefix != nil ? prefix! + " " : "") + name
} }
} }
var ncc1701 = Starship(name: "Enterprise", prefix: "USS") 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`
<a name="method_requirements"></a> <a name="method_requirements"></a>
## 对方法的规定 ## 对方法的规定
`协议`可以要求其`遵循者`实现某些指定的`实例方法``类方法`。这些方法作为协议的一部分,像普通的方法一样清晰的放在协议的定义中,不需要大括号和方法体。 协议可以要求其遵循者实现某些指定的实例方法类方法。这些方法作为协议的一部分,像普通的方法一样放在协议的定义中,但是不需要大括号和方法体。可以在协议中定义具有可变参数的方法,和普通方法的定义方式相同。但是在协议的方法定义中,不支持参数默认值。
>注意: 正如对属性的规定中所说的,在协议中定义类方法的时候,总是使用`static`关键字作为前缀。当协议的遵循者是类的时候,虽然你可以在类的实现中使用`class`或者`static`来实现类方法,但是在协议中声明类方法,仍然要使用`static`关键字。
>协议中的方法支持`变长参数(variadic parameter)`,不支持`参数默认值(default value)`
如下所示,协议中类方法的定义与类属性的定义相似,在协议定义的方法前置`class`关键字来表示。当在`枚举``结构体`实现类方法时,需要使用`static`关键字来代替。
```swift ```swift
protocol SomeProtocol { protocol SomeProtocol {
class func someTypeMethod() static func someTypeMethod()
} }
``` ```
如下所示,定义了含有一个实例方法的协议。 下面的例子定义了含有一个实例方法的协议。
```swift ```swift
protocol RandomNumberGenerator { protocol RandomNumberGenerator {
@ -145,7 +146,7 @@ protocol RandomNumberGenerator {
} }
``` ```
`RandomNumberGenerator`协议要求其`遵循者`必须拥有一个名为`random` 返回值类型为`Double`的实例方法。 (尽管这里并未指明,但是我们假设返回值在[01]区间内) `RandomNumberGenerator`协议要求其遵循者必须拥有一个名为`random` 返回值类型为`Double`的实例方法。尽管这里并未指明,但是我们假设返回值在[01)区间内。
`RandomNumberGenerator`协议并不在意每一个随机数是怎样生成的,它只强调这里有一个随机数生成器。 `RandomNumberGenerator`协议并不在意每一个随机数是怎样生成的,它只强调这里有一个随机数生成器。
@ -164,24 +165,26 @@ class LinearCongruentialGenerator: RandomNumberGenerator {
} }
} }
let generator = LinearCongruentialGenerator() 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" // 输出 : "Here's a random number: 0.37464991998171"
println("And another one: \(generator.random())") print("And another one: \(generator.random())")
// 输出 : "And another one: 0.729023776863283" // 输出 : "And another one: 0.729023776863283"
``` ```
<a name="mutating_method_requirements"></a> <a name="mutating_method_requirements"></a>
## 对突变方法的规定 ## 对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 ```swift
protocol Togglable { 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 ```swift
enum OnOffSwitch: Togglable { enum OnOffSwitch: Togglable {
@ -213,7 +216,7 @@ lightSwitch.toggle()
<a name="initializer_requirements"></a> <a name="initializer_requirements"></a>
## 对构造器的规定 ## 对构造器的规定
协议可以要求它的遵循类型实现定的构造器。你可以像书写普通的构造器那样,在协议的定义里写下构造器的需求,但不需要写花括号和构造器的实体: 协议可以要求它的遵循实现定的构造器。你可以像书写普通的构造器那样,在协议的定义里写下构造器的声明,但不需要写花括号和构造器的实体:
```swift ```swift
protocol SomeProtocol { protocol SomeProtocol {
@ -221,9 +224,9 @@ protocol SomeProtocol {
} }
``` ```
**协议构造器规定在类中的实现** ### 协议构造器规定在类中的实现
你可以在遵循该协议的类中实现构造器,并指定其为类的定构造器或者便构造器。在这两种情况下,你都必须给构造器实现标上"required"修饰符: 你可以在遵循该协议的类中实现构造器,并指定其为类的定构造器(designated initializer)或者便构造器(convenience initializer)。在这两种情况下,你都必须给构造器实现标上"required"修饰符:
```swift ```swift
class SomeClass: SomeProtocol { class SomeClass: SomeProtocol {
@ -235,11 +238,10 @@ class SomeClass: SomeProtocol {
使用`required`修饰符可以保证:所有的遵循该协议的子类,同样能为构造器规定提供一个显式的实现或继承实现。 使用`required`修饰符可以保证:所有的遵循该协议的子类,同样能为构造器规定提供一个显式的实现或继承实现。
关于`required`构造器的更多内容,请参考<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_454">`required`构造器 </a> 关于`required`构造器的更多内容,请参考[必要构造器](./14_Initialization.html#required_initializers)。
>注意 >注意
> >如果类已经被标记为`final`,那么不需要在协议构造器的实现中使用`required`修饰符。因为final类不能有子类。关于`final`修饰符的更多内容,请参见[防止重写](./13_Inheritance.html#preventing_overrides)。
>如果类已经被“final”修饰符所标示你就不需要在协议构造器规定的实现中使用"required"修饰符。因为final类不能有子类。关于`final`修饰符的更多内容,请参见<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html#//apple_ref/doc/uid/TP40014097-CH18-XID_339">防止重写</a>
如果一个子类重写了父类的指定构造器,并且该构造器遵循了某个协议的规定,那么该构造器的实现需要被同时标示`required``override`修饰符 如果一个子类重写了父类的指定构造器,并且该构造器遵循了某个协议的规定,那么该构造器的实现需要被同时标示`required``override`修饰符
@ -251,39 +253,39 @@ protocol SomeProtocol {
class SomeSuperClass { class SomeSuperClass {
init() { init() {
//协议定义 // 构造器的实现
} }
} }
class SomeSubClass: SomeSuperClass, SomeProtocol { class SomeSubClass: SomeSuperClass, SomeProtocol {
// "required" from SomeProtocol conformance; "override" from SomeSuperClass // 因为遵循协议,需要加上"required"; 因为继承自父类,需要加上"override"
required override init() { required override init() {
// 构造器实现 // 构造器实现
} }
} }
``` ```
**可失败构造器的规定** ### 可失败构造器的规定
可以通过给协议```Protocols```中添加可失败构造器来使遵循该协议的类型必须实现该可失败构造器。 可以通过给协议`Protocols`中添加[可失败构造器](./14_Initialization.html#failable_initializers)来使遵循该协议的类型必须实现该可失败构造器。
如果在协议中定义一个可失败构造器,则在遵顼该协议的类型中必须添加同名同参数的可失败构造器或非可失败构造器。 如果在协议中定义一个可失败构造器,则在遵顼该协议的类型中必须添加同名同参数的可失败构造器或非可失败构造器。如果在协议中定义一个非可失败构造器,则在遵循该协议的类型中必须添加同名同参数的非可失败构造器或隐式解析类型的可失败构造器(`init!`)。
如果在协议中定义一个非可失败构造器,则在遵循该协议的类型中必须添加同名同参数的非可失败构造器或隐式解析类型的可失败构造器(`init!`)。
<a name="protocols_as_types"></a> <a name="protocols_as_types"></a>
## 协议类型 ## 协议类型
尽管`协议`本身并不实现任何功能,但是`协议`可以被当做类型来使用。 尽管协议本身并不实现任何功能,但是协议可以被当做类型来使用。
使用场景: 协议可以像其他普通类型一样使用,使用场景:
* `协议类型`作为函数、方法或构造器中的参数类型或返回值类型 * 作为函数、方法或构造器中的参数类型或返回值类型
* `协议类型`作为常量、变量或属性的类型 * 作为常量、变量或属性的类型
* `协议类型`作为数组、字典或其他容器中的元素类型 * 作为数组、字典或其他容器中的元素类型
> 注意: 协议是一种类型,因此协议类型的名称应与其他类型(IntDoubleString)的写法相同,使用驼峰式写法 > 注意
> 协议是一种类型,因此协议类型的名称应与其他类型(IntDoubleString)的写法相同,使用大写字母开头的驼峰式写法,例如(`FullyNamed`和`RandomNumberGenerator`)
如下所示,这个示例中将协议当做类型来使用 如下所示,这个示例中将协议当做类型来使用
@ -301,20 +303,20 @@ class Dice {
} }
``` ```
例子中又一个`Dice`类用来代表桌游中的拥有N个面的骰子。`Dice`的实例含有`sides`和`generator`两个属性,前者是整型,用来表示骰子有几个面,后者为骰子提供一个随机数生成器。 例子中定义了一个`Dice`用来代表桌游中的拥有N个面的骰子。`Dice`的实例含有`sides``generator`两个属性,前者是整型,用来表示骰子有几个面,后者为骰子提供一个随机数生成器。
`generator`属性的类型为`RandomNumberGenerator`,因此任何遵循了`RandomNumberGenerator`协议的类型的实例都可以赋值给`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 ```swift
var d6 = Dice(sides: 6,generator: LinearCongruentialGenerator()) var d6 = Dice(sides: 6, generator: LinearCongruentialGenerator())
for _ in 1...5 { for _ in 1...5 {
println("Random dice roll is \(d6.roll())") print("Random dice roll is \(d6.roll())")
} }
//输出结果 //输出结果
//Random dice roll is 3 //Random dice roll is 3
@ -327,13 +329,9 @@ for _ in 1...5 {
<a name="delegation"></a> <a name="delegation"></a>
## 委托(代理)模式 ## 委托(代理)模式
委托是一种设计模式(*译者注: 想起了那年 UITableViewDelegate 中的奔跑那是我逝去的Objective-C。。。*),它允许``或`结构体`将一些需要它们负责的功能`交由(委托)`给其他的类型的实例。 委托是一种设计模式,它允许``或`结构体`将一些需要它们负责的功能`交由(委托)`给其他的类型的实例。委托模式的实现很简单: 定义协议来封装那些需要被委托的函数和方法, 使其`遵循者`拥有这些被委托的`函数和方法`。委托模式可以用来响应特定的动作或接收外部数据源提供的数据,而无需要知道外部数据源的类型信息。
委托模式的实现很简单: 定义`协议`来`封装`那些需要被委托的`函数和方法` 使其`遵循者`拥有这些被委托的`函数和方法`。 下面的例子是两个基于骰子游戏的协议:
委托模式可以用来响应特定的动作或接收外部数据源提供的数据,而无需要知道外部数据源的所属类型(*译者注:只要求外部数据源`遵循`某协议*)。
下文是两个基于骰子游戏的协议:
```swift ```swift
protocol DiceGame { protocol DiceGame {
@ -348,9 +346,9 @@ protocol DiceGameDelegate {
} }
``` ```
`DiceGame`协议可以在任意含有骰子的游戏中实现`DiceGameDelegate`协议可以用来追踪`DiceGame`的游戏过程 `DiceGame`协议可以在任意含有骰子的游戏中实现。`DiceGameDelegate`协议可以用来追踪`DiceGame`的游戏过程
如下所示,`SnakesAndLadders`是`Snakes and Ladders`(译者注:[Control Flow](2)章节有该游戏的详细介绍)游戏的新版本。新版本使用`Dice`作为骰子,并且实现了`DiceGame`和`DiceGameDelegate`协议,后者用来记录游戏的过程: 如下所示,`SnakesAndLadders`是`Snakes and Ladders`([Control Flow](./05_Control_Flow.html)章节有该游戏的详细介绍)游戏的新版本。新版本使用`Dice`作为骰子,并且实现了`DiceGame``DiceGameDelegate`协议,后者用来记录游戏的过程:
```swift ```swift
class SnakesAndLadders: DiceGame { 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()`方法内。分别在游戏开始时,新一轮开始时,游戏结束时被调用。 `DicegameDelegate`协议提供了三个方法用来追踪游戏过程。被放置于游戏的逻辑中,即`play()`方法内。分别在游戏开始时,新一轮开始时,游戏结束时被调用。
因为`delegate`是一个遵循`DiceGameDelegate`的可选属性,因此在`play()`方法中使用了`可选链`来调用委托方法。 若`delegate`属性为`nil` 则delegate所调用的方法失效。若`delegate`不为`nil`,则方法能够被调用 因为`delegate`是一个遵循`DiceGameDelegate`的可选属性,因此在`play()`方法中使用了`可选链`来调用委托方法。 若`delegate`属性为`nil` 则delegate所调用的方法失效,并不会产生错误。若`delegate`不为`nil`,则方法能够被调用
如下所示,`DiceGameTracker`遵循了`DiceGameDelegate`协议 如下所示,`DiceGameTracker`遵循了`DiceGameDelegate`协议
@ -403,25 +401,25 @@ class DiceGameTracker: DiceGameDelegate {
func gameDidStart(game: DiceGame) { func gameDidStart(game: DiceGame) {
numberOfTurns = 0 numberOfTurns = 0
if game is SnakesAndLadders { 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) { func game(game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) {
++numberOfTurns ++numberOfTurns
println("Rolled a \(diceRoll)") print("Rolled a \(diceRoll)")
} }
func gameDidEnd(game: DiceGame) { 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`类型的实例,如果是,则打印出相应的内容。 `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`的运行情况,如下所示: `DiceGameTracker`的运行情况,如下所示:
@ -442,11 +440,12 @@ game.play()
<a name="adding_protocol_conformance_with_an_extension"></a> <a name="adding_protocol_conformance_with_an_extension"></a>
## 在扩展中添加协议成员 ## 在扩展中添加协议成员
即便无法修改源代码,依然可以通过`扩展(Extension)`来扩充已存在类型(*译者注: 类,结构体,枚举等*)。`扩展`可以为已存在的类型添加`属性``方法``下标脚本``协议`等成员。详情请在[扩展](4)章节中查看。 即便无法修改源代码,依然可以通过扩展(Extension)来扩充已存在类型(*译者注: 类,结构体,枚举等*)。扩展可以为已存在的类型添加属性,方法,下标脚本,协议等成员。详情请在[扩展](./21_Extensions.html)章节中查看。
> 注意: 通过`扩展`为已存在的类型`遵循`协议时,该类型的所有实例也会随之添加协议中的方法 > 注意
> 通过扩展为已存在的类型遵循协议时,该类型的所有实例也会随之添加协议中的方法
`TextRepresentable`协议含有一个`asText`,如下所示: 例如`TextRepresentable`协议,任何想要表示一些文本内容的类型都可以遵循该协议。这些想要表示的内容可以是类型本身的描述,也可以是当前内容的版本:
```swift ```swift
protocol TextRepresentable { protocol TextRepresentable {
@ -454,7 +453,7 @@ protocol TextRepresentable {
} }
``` ```
通过`扩展`为上一节中提到的`Dice`类遵循`TextRepresentable`协议 可以通过扩展,为上一节中提到的`Dice`增加类遵循`TextRepresentable`协议的功能
```swift ```swift
extension Dice: TextRepresentable { extension Dice: TextRepresentable {
@ -463,16 +462,17 @@ extension Dice: TextRepresentable {
} }
} }
``` ```
现在,通过扩展使得`Dice`类型遵循了一个新的协议,这和`Dice`类型在定义的时候声明为遵循`TextRepresentable`协议的效果相同。在扩展的时候,协议名称写在类型名之后,以冒号隔开,在大括号内写明新添加的协议内容。
从现在起,`Dice`类型的实例可被当作`TextRepresentable`类型: 现在所有`Dice`的实例都遵循了`TextRepresentable`协议:
```swift ```swift
let d12 = Dice(sides: 12,generator: LinearCongruentialGenerator()) let d12 = Dice(sides: 12,generator: LinearCongruentialGenerator())
println(d12.asText()) print(d12.asText())
// 输出 "A 12-sided dice" // 输出 "A 12-sided dice"
``` ```
`SnakesAndLadders`类也可以通过`扩展`的方式来遵循协议: 同样`SnakesAndLadders`类也可以通过`扩展`的方式来遵循`TextRepresentable`协议:
```swift ```swift
extension SnakesAndLadders: TextRepresentable { extension SnakesAndLadders: TextRepresentable {
@ -480,14 +480,14 @@ extension SnakesAndLadders: TextRepresentable {
return "A game of Snakes and Ladders with \(finalSquare) squares" 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" // 输出 "A game of Snakes and Ladders with 25 squares"
``` ```
<a name="declaring_protocol_adoption_with_an_extension"></a> <a name="declaring_protocol_adoption_with_an_extension"></a>
## 通过扩展补充协议声明 ## 通过扩展补充协议声明
当一个类型已经实现了协议中的所有要求,却没有声明时,可以通过`扩展`来补充协议声明: 当一个类型已经实现了协议中的所有要求,却没有声明为遵循该协议时,可以通过扩展(空的扩展体)来补充协议声明:
```swift ```swift
struct Hamster { struct Hamster {
@ -504,26 +504,27 @@ extension Hamster: TextRepresentable {}
```swift ```swift
let simonTheHamster = Hamster(name: "Simon") let simonTheHamster = Hamster(name: "Simon")
let somethingTextRepresentable: TextRepresentable = simonTheHamster let somethingTextRepresentable: TextRepresentable = simonTheHamster
println(somethingTextRepresentable.asText()) print(somethingTextRepresentable.asText())
// 输出 "A hamster named Simon" // 输出 "A hamster named Simon"
``` ```
> 注意: 即使满足了协议的所有要求,类型也不会自动转变,因此你必须为它做出明显的协议声明 > 注意
> 即使满足了协议的所有要求,类型也不会自动转变,因此你必须为它做出显式的协议声明
<a name="collections_of_protocol_types"></a> <a name="collections_of_protocol_types"></a>
## 集合中的协议类型 ## 集合中的协议类型
协议类型可以集合使用,表示集合中的元素均为协议类型: 协议类型可以集合使用,表示集合中的元素均为协议类型,下面的例子创建了一个类型为`TextRepresentable`的数组:
```swift ```swift
let things: [TextRepresentable] = [game,d12,simonTheHamster] let things: [TextRepresentable] = [game,d12,simonTheHamster]
``` ```
如下所示,`things`数组可以被直接遍历,并调用其中元素的`asText()`函数: 如下所示,`things`数组可以被直接遍历,并打印每个元素的文本表示:
```swift ```swift
for thing in things { for thing in things {
println(thing.asText()) print(thing.asText())
} }
// A game of Snakes and Ladders with 25 squares // A game of Snakes and Ladders with 25 squares
// A 12-sided dice // A 12-sided dice
@ -535,7 +536,7 @@ for thing in things {
<a name="protocol_inheritance"></a> <a name="protocol_inheritance"></a>
## 协议的继承 ## 协议的继承
协议能够继承一多个其他协议语法与类的继承相似,多个协议间用逗号``分隔 协议能够继承一个或多个其他协议,可以在继承的协议基础上增加新的内容要求。协议的继承语法与类的继承相似,多个被继承的协议间用逗号分隔:
```swift ```swift
protocol InheritingProtocol: SomeProtocol, AnotherProtocol { 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 ```swift
extension SnakesAndLadders: PrettyTextRepresentable { 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()`方法。 任意`SankesAndLadders`的实例都可以使用`asPrettyText()`方法。
```swift ```swift
println(game.asPrettyText()) print(game.asPrettyText())
// A game of Snakes and Ladders with 25 squares: // A game of Snakes and Ladders with 25 squares:
// ○ ○ ▲ ○ ○ ▲ ○ ○ ▲ ▲ ○ ○ ○ ▼ ○ ○ ○ ○ ▼ ○ ○ ▼ ○ ▼ ○ // ○ ○ ▲ ○ ○ ▲ ○ ○ ▲ ▲ ○ ○ ○ ▼ ○ ○ ○ ○ ▼ ○ ○ ▼ ○ ▼ ○
``` ```
<a name="class_only_protocol"></a> <a name="class_only_protocol"></a>
## 类专属协议 ## 类专属协议
你可以在协议的继承列表中,通过添加class关键字,限制协议只能适配到类class类型。结构体或枚举不能遵循该协议。该class关键字必须是第一个出现在协议的继承列表中,其后,才是其他继承协议。 你可以在协议的继承列表中,通过添加`class`关键字,限制协议只能适配到类class类型。结构体或枚举不能遵循该协议。该`class`关键字必须是第一个出现在协议的继承列表中,其后,才是其他继承协议。
```swift ```swift
protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol { protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
// class-only protocol definition goes here // class-only protocol definition goes here
} }
``` ```
在以上例子中协议SomeClassOnlyProtocol只能被类class类型适配。如果尝试让结构体或枚举类型适配该协议则会出现编译错误。
>注意 在以上例子中,协议`SomeClassOnlyProtocol`只能被类class类型适配。如果尝试让结构体或枚举类型适配该协议则会出现编译错误。
>
>当协议需求定义的行为,要求(或假设)它的遵循类型必须是引用语义而非值语义时,应该采用类专属协议。关于引用语义,值语义的更多内容,请查看<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html#//apple_ref/doc/uid/TP40014097-CH13-XID_145">结构体和枚举是值类型</a>和<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html#//apple_ref/doc/uid/TP40014097-CH13-XID_146">类是引用类型</a>
<!--TODO 链接-->
>注意
>当协议想要定义的行为,要求(或假设)它的遵循类型必须是引用语义而非值语义时,应该采用类专属协议。关于引用语义,值语义的更多内容,请查看[结构体和枚举是值类型](./09_Classes_and_Structures.html#structures_and_enumerations_are_value_types)和[类是引用类型](./09_Classes_and_Structures.html#classes_are_reference_types)。
<a name="protocol_composition"></a> <a name="protocol_composition"></a>
## 协议合成 ## 协议合成
一个协议可由多个协议采用`protocol<SomeProtocol AnotherProtocol>`这样的格式进行组合,称为`协议合成(protocol composition)`。 有时候需要同时遵循多个协议。你可以将多个协议采用`protocol<SomeProtocol AnotherProtocol>`这样的格式进行组合,称为`协议合成(protocol composition)`。你可以在`<>`中罗列任意多个你想要遵循的协议,以逗号分隔
举个例子: 下面的例子中,将`Named``Aged`两个协议按照上述的语法组合成一个协议:
```swift ```swift
protocol Named { protocol Named {
@ -624,7 +626,7 @@ struct Person: Named, Aged {
var age: Int var age: Int
} }
func wishHappyBirthday(celebrator: protocol<Named, Aged>) { func wishHappyBirthday(celebrator: protocol<Named, Aged>) {
println("Happy birthday \(celebrator.name) - you're \(celebrator.age)!") print("Happy birthday \(celebrator.name) - you're \(celebrator.age)!")
} }
let birthdayPerson = Person(name: "Malcolm", age: 21) let birthdayPerson = Person(name: "Malcolm", age: 21)
wishHappyBirthday(birthdayPerson) wishHappyBirthday(birthdayPerson)
@ -633,27 +635,31 @@ wishHappyBirthday(birthdayPerson)
`Named`协议包含`String`类型的`name`属性;`Aged`协议包含`Int`类型的`age`属性。`Person`结构体`遵循`了这两个协议。 `Named`协议包含`String`类型的`name`属性;`Aged`协议包含`Int`类型的`age`属性。`Person`结构体`遵循`了这两个协议。
`wishHappyBirthday`函数的形参`celebrator`的类型为`protocol<NamedAged>`。可以传入任意`遵循`这两个协议的类型的实例 `wishHappyBirthday`函数的形参`celebrator`的类型为`protocol<NamedAged>`。可以传入任意`遵循`这两个协议的类型的实例
> 注意: `协议合成`并不会生成一个新协议类型,而是将多个协议合成为一个临时的协议,超出范围后立即失效 上面的例子创建了一个名为`birthdayPerson``Person`实例,作为参数传递给了`wishHappyBirthday(_:)`函数。因为`Person`同时遵循这两个协议,所以这个参数合法,函数将输出生日问候语
> 注意
> `协议合成`并不会生成一个新协议类型,而是将多个协议合成为一个临时的协议,超出范围后立即失效。
<a name="checking_for_protocol_conformance"></a> <a name="checking_for_protocol_conformance"></a>
## 检验协议的一致性 ## 检验协议的一致性
使用`is`和`as`操作符来检查协议的一致性或转化协议类型。检查和转化的语法和之前相同(*详情查看[Typy Casting章节](5)*): <!--TODO 链接-->
你可以使用`is``as`操作符来检查是否遵循某一协议或强制转化为某一类型。检查和转化的语法和之前相同(*详情查看[类型转换](./20_Type_Casting.html)*):
* `is`操作符用来检查实例是否`遵循`了某个`协议`。 * `is`操作符用来检查实例是否`遵循`了某个`协议`
* `as?`返回一个可选值,当实例`遵循`协议时,返回该协议类型;否则返回`nil` * `as?`返回一个可选值,当实例`遵循`协议时,返回该协议类型;否则返回`nil`
* `as`用以强制向下转型。 * `as`用以强制向下转型,如果强转失败,会引起运行时错误。
下面的例子定义了一个`HasArea`的协议,要求有一个`Double`类型可读的`area`:
```swift ```swift
@objc protocol HasArea { protocol HasArea {
var area: Double { get } var area: Double { get }
} }
``` ```
> 注意: `@objc`用来表示协议是可选的,也可以用来表示暴露给`Objective-C`的代码,此外,`@objc`型协议只对``有效,因此只能在``中检查协议的一致性。详情查看*[Using Siwft with Cocoa and Objectivei-c](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216)*。
如下所示,定义了`Circle``Country`类,它们都遵循了`HasArea`协议 如下所示,定义了`Circle``Country`类,它们都遵循了`HasArea`协议
```swift ```swift
@ -680,7 +686,7 @@ class Animal {
} }
``` ```
`CircleCountryAnimal`并没有一个相同的基类,因而采用`AnyObject`类型的数组来装载在他们的实例,如下所示: `Circle``Country``Animal`并没有一个相同的基类,然而,它们都是类,它们的实例都可以作为`AnyObject`类型的变量,存储在同一个数组中:
```swift ```swift
let objects: [AnyObject] = [ let objects: [AnyObject] = [
@ -690,16 +696,16 @@ let objects: [AnyObject] = [
] ]
``` ```
`objects`数组使用字面量初始化,数组包含一个`radius`为2。0的`Circle`的实例,一个保存了英国面积的`Country`实例和一个`legs`为4的`Animal`实例。 `objects`数组使用字面量初始化,数组包含一个`radius`为2的`Circle`的实例,一个保存了英国面积的`Country`实例和一个`legs`为4的`Animal`实例。
如下所示,`objects`数组可以被迭代,对迭代出的每一个元素进行检查,看它是否遵循了`HasArea`协议: 如下所示,`objects`数组可以被迭代,对迭代出的每一个元素进行检查,看它是否遵循了`HasArea`协议:
```swift ```swift
for object in objects { for object in objects {
if let objectWithArea = object as? HasArea { if let objectWithArea = object as? HasArea {
println("Area is \(objectWithArea.area)") print("Area is \(objectWithArea.area)")
} else { } else {
println("Something that doesn't have an area") print("Something that doesn't have an area")
} }
} }
// Area is 12.5663708 // Area is 12.5663708
@ -709,20 +715,23 @@ for object in objects {
当迭代出的元素遵循`HasArea`协议时,通过`as?`操作符将其`可选绑定(optional binding)``objectWithArea`常量上。`objectWithArea``HasArea`协议类型的实例,因此`area`属性是可以被访问和打印的。 当迭代出的元素遵循`HasArea`协议时,通过`as?`操作符将其`可选绑定(optional binding)``objectWithArea`常量上。`objectWithArea``HasArea`协议类型的实例,因此`area`属性是可以被访问和打印的。
`objects`数组中元素的类型并不会因为`向下转型`而改变,它们仍然是`Circle``Country``Animal`类型。然而,当它们被赋值给`objectWithArea`常量时,则只被视为`HasArea`类型,因此只有`area`属性能够被访问。 `objects`数组中元素的类型并不会因为强转而丢失类型信息,它们仍然是`Circle``Country``Animal`类型。然而,当它们被赋值给`objectWithArea`常量时,则只被视为`HasArea`类型,因此只有`area`属性能够被访问。
<a name="optional_protocol_requirements"></a> <a name="optional_protocol_requirements"></a>
## 对可选协议的规定 ## 对可选协议的规定
可选协议含有可选成员,其`遵循者`可以选择是否实现这些成员。在协议中使用`@optional`关键字作为前缀来定义可选成员。 协议可以含有可选成员,其`遵循者`可以选择是否实现这些成员。在协议中使用`optional`关键字作为前缀来定义可选成员。
可选协议在调用时使用`可选链`,详细内容在[Optional Chaning](7)章节中查看。 <!--TODO 链接-->
可选协议在调用时使用`可选链`,因为协议的遵循者可能没有实现可选内容,详细内容在[可空链式调用](./17_Optional_Chaining.html)章节中查看。
像`someOptionalMethod?(someArgument)`这样,你可以在可选方法名称后加上`?`来检查该方法是否被实现。`可选方法`和`可选属性`都会返回一个`可选值(optional value)`,当其不可访问时,`?`之后语句不会执行,并整体返回`nil` `someOptionalMethod?(someArgument)`这样,你可以在可选方法名称后加上`?`来检查该方法是否被实现。可选方法可选属性都会返回一个`可选值(optional value)`,当其不可访问时,`?`之后语句不会执行,并整体返回`nil`
> 注意: 可选协议只能在含有`@objc`前缀的协议中生效。且`@objc`的协议只能被``遵循 > 注意
> 可选协议只能在含有`@objc`前缀的协议中生效。且`@objc`的协议只能被`类`遵循
> 这个前缀表示协议将暴露给Objective-C代码详情参见`Using Swift with Cocoa and Objective-C`。即使你不打算和Objective-C有什么交互如果你想要指明协议包含可选属性那么还是要加上`@obj`前缀
如下所示,`Counter`类使用含有两个可选成员的`CounterDataSource`协议类型的外部数据源来提供`增量值(increment amount)` 下面的例子定义了一个叫`Counter`的整数加法类,它使用外部的数据源来实现每次的增量。数据源是两个可选属性,在`CounterDataSource`协议中定义:
```swift ```swift
@objc protocol CounterDataSource { @objc protocol CounterDataSource {
@ -731,9 +740,10 @@ for object in objects {
} }
``` ```
`CounterDataSource`含有`incrementForCount`的`可选方法`和`fiexdIncrement`的`可选属性`,它们使用了不同的方法来从数据源中获取合适的增量值。 `CounterDataSource`含有`incrementForCount`可选方法`fiexdIncrement`可选属性,它们使用了不同的方法来从数据源中获取合适的增量值。
> 注意: `CounterDataSource`中的属性和方法都是可选的,因此可以在类中声明但不实现这些成员,尽管技术上允许这样做,不过最好不要这样写。 > 注意
> `CounterDataSource`中的属性和方法都是可选的,因此可以在类中声明都不实现这些成员,尽管技术上允许这样做,不过最好不要这样写。
`Counter`类含有`CounterDataSource?`类型的可选属性`dataSource`,如下所示: `Counter`类含有`CounterDataSource?`类型的可选属性`dataSource`,如下所示:
@ -751,32 +761,34 @@ for object in objects {
} }
``` ```
`count`属性用于存储当前的值,`increment`方法用来为`count`赋值。 `Counter`使用`count`来存储当前的值。该类同时定义了一个`increment`方法,每次调用该方法的时候,将会增加`count`值。
`increment`方法通过`可选链`,尝试从两种`可选成员`中获取`count` `increment()`方法首先试图使用`incrementForCount(_:)`方法来得到每次的增量。`increment()`方法使用可选链来尝试调用`incrementForCount(_:)`,并将当前的`count`值作为参数传入
1. 由于`dataSource`可能为`nil`,因此在`dataSource`后边加上了`?`标记来表明只在`dataSource`非空时才去调用`incrementForCount`方法。
2. 即使`dataSource`存在,但是也无法保证其是否实现了`incrementForCount`方法,因此在`incrementForCount`方法后边也加有`?`标记 这里使用了两种可选链方法。由于`dataSource`可能为`nil`,因此在`dataSource`后边加上了`?`标记来表明只在`dataSource`非空时才去调用`incrementForCount`方法。即使`dataSource`存在,但是也无法保证其是否实现了`incrementForCount`方法,因此在`incrementForCount`方法后边也加有`?`标记
在调用`incrementForCount`方法后,`Int`型`可选`通过`可选绑定(optional binding)`自动拆包并赋值给常量`amount` 调用`incrementForCount`方法在上述两种情形都有可能失败,所以返回值为*可选*`Int`类型。虽然在`CounterDataSource`中,`incrementForCount`被定义为一个非可选`Int`(non-optional),但是这里我们仍然需要返回*可选*`Int`类型
当`incrementForCount`不能被调用时,尝试使用可选属性`fixedIncrement`来代替 在调用`incrementForCount`方法后,`Int``可选值`通过`可选绑定(optional binding)`自动拆包并赋值给常量`amount`。如果可选值确实包含一个数值,这表示`delegate`和方法都存在,之后便将`amount`加到`count`上,增加操作完成
`ThreeSource`实现了`CounterDataSource`协议,如下所示: 如果没有从`incrementForCount(_:)`获取到值,可能是`dataSource`为nil或者它并没有实现`incrementForCount`方法——那么`increment()`方法将试图从数据源的`fixedIncrement`属性中获取增量。`fixedIncrement`也是一个可选型,所以在属性名的后面添加`?`来试图取回可选属性的值。和之前一样,返回值为可选型。
class ThreeSource: CounterDataSource { `ThreeSource`实现了`CounterDataSource`协议,它实现来可选属性`fixedIncrement`,每次返回值`3`:
let fixedIncrement = 3
}
使用`ThreeSource`作为数据源开实例化一个`Counter`: ```swift
@objc class ThreeSource: CounterDataSource {
let fixedIncrement = 3
}
```
可以使用`ThreeSource`的实例作为`Counter`实例的数据源:
```swift ```swift
var counter = Counter() var counter = Counter()
counter.dataSource = ThreeSource() counter.dataSource = ThreeSource()
for _ in 1...4 { for _ in 1...4 {
counter.increment() counter.increment()
println(counter.count) print(counter.count)
} }
// 3 // 3
// 6 // 6
@ -784,7 +796,9 @@ for _ in 1...4 {
// 12 // 12
``` ```
`TowardsZeroSource`实现了`CounterDataSource`协议中的`incrementForCount`方法,如下所示: 上述代码新建了一个`Counter`实例;将它的数据源设置为`TreeSource`实例;调用`increment()`4次。和你预想的一样每次在调用的时候`count`的值增加3.
下面是一个更为复杂的数据源`TowardsZeroSource`它将使得最后的值变为0:
```swift ```swift
class TowardsZeroSource: CounterDataSource { class TowardsZeroSource: CounterDataSource {
@ -800,14 +814,18 @@ func incrementForCount(count: Int) -> Int {
} }
``` ```
下边是执行的代码: `TowardsZeroSource`实现了`CounterDataSource`协议中的`incrementForCount(_:)`方法,以`count`参数为依据,计算出每次的增量。如果`count`已经为0方法返回0这表示之后不会再有增量。
你可以配合使用`TowardsZeroSource`实例和`Counter`实例来从`-4`增加到`0`.一旦增加到`0`,数值便不会再有变动。
在下面的例子中,将从`-4`增加到`0`。一旦结果为`0`,便不在增加:
```swift ```swift
counter.count = -4 counter.count = -4
counter.dataSource = TowardsZeroSource() counter.dataSource = TowardsZeroSource()
for _ in 1...5 { for _ in 1...5 {
counter.increment() counter.increment()
println(counter.count) print(counter.count)
} }
// -3 // -3
// -2 // -2
@ -815,3 +833,80 @@ for _ in 1...5 {
// 0 // 0
// 0 // 0
``` ```
<a name="protocol_extensions"></a>
## 协议扩展
使用扩展协议的方式可以为遵循者提供方法或属性的实现。通过这种方式,可以让你无需在每个遵循者中都实现一次,无需使用全局函数,你可以通过扩展协议的方式进行定义。
例如,可以扩展`RandomNumberGenerator`协议,让其提供`randomBool()`方法。该方法使用协议中要求的`random()`方法来实现:
```swift
extension RandomNumberGenerator {
func randomBool() -> Bool {
return random() > 0.5
}
}
```
通过扩展协议,所有协议的遵循者,在不用任何修改的情况下,都自动得到了这个扩展所增加的方法。
```swift
let generator = LinearCongruentialGenerator()
print("Here's a random number: \(generator.random())")
// 输出 "Here's a random number: 0.37464991998171"
print("And here's a random Boolean: \(generator.randomBool())")
// 输出 "And here's a random Boolean: true"
```
### 提供默认实现
可以通过协议扩展的方式来为协议规定的属性和方法提供默认的实现。如果协议的遵循者对规定的属性和方法提供了自己的实现,那么遵循者提供的实现将被使用。
> 注意
> 通过扩展协议提供的协议实现和可选协议规定有区别。虽然协议遵循者无需自己实现,通过扩展提供的默认实现,可以不是用可选链调用。
例如,`PrettyTextRepresentable`协议,继承了`TextRepresentable`协议,可以为其提供一个默认的`asPrettyText()`方法来简化返回值
```swift
extension PrettyTextRepresentable {
func asPrettyText() -> String {
return asText()
}
}
```
### 为协议扩展添加限制条件
在扩展协议的时候,可以指定一些限制,只有满足这些限制的协议遵循者,才能获得协议扩展提供的属性和方法。这些限制写在协议名之后,使用`where`关键字来描述限制情况。([Where语句](./23_Generics.html#where_clauses))。:
例如,你可以扩展`CollectionType`协议,但是只适用于元素遵循`TextRepresentable`的情况:
```swift
extension CollectionType where Generator.Element : TextRepresentable {
func asList() -> String {
return "(" + ", ".join(map({$0.asText()})) + ")"
}
}
```
`asList()`方法将每个元素以`asText()`的方式表示,最后以逗号分隔链接起来。
现在我们来看`Hamster`,它遵循`TextRepresentable`:
```swift
let murrayTheHamster = Hamster(name: "Murray")
let morganTheHamster = Hamster(name: "Morgan")
let mauriceTheHamster = Hamster(name: "Maurice")
let hamsters = [murrayTheHamster, morganTheHamster, mauriceTheHamster]
```
因为`Array`遵循`CollectionType`协议,数组的元素又遵循`TextRepresentable`协议,所以数组可以使用`asList()`方法得到数组内容的文本表示:
```swift
print(hamsters.asList())
// 输出 "(A hamster named Murray, A hamster named Morgan, A hamster named Maurice)"
```
> 注意
> 如果有多个协议扩展,而一个协议的遵循者又同时满足它们的限制,那么将会使用所满足限制最多的那个扩展。

View File

@ -1,11 +1,14 @@
# 泛型Generics
> 翻译:[takalard](https://github.com/takalard) [SergioChan](https://github.com/SergioChan)
> 校对:[lifedim](https://github.com/lifedim)
# 泛型
------ ------
> 1.0
> 翻译:[takalard](https://github.com/takalard)
> 校对:[lifedim](https://github.com/lifedim)
> 2.0
> 翻译+校对: [SergioChan](https://github.com/SergioChan)
本页包含内容: 本页包含内容:
- [泛型所解决的问题](#the_problem_that_generics_solve) - [泛型所解决的问题](#the_problem_that_generics_solve)
@ -35,7 +38,7 @@ func swapTwoInts(inout a: Int, inout _ b: Int) {
} }
``` ```
这个函数使用写入读出in-out参数来交换`a``b`的值,请参考[写入读出参数](../chapter2/06_Functions.html)。 这个函数使用写入读出in-out参数来交换`a``b`的值,请参考[输入输出参数](./06_Functions.html#in_out_parameters)。
`swapTwoInts(_:_:)`函数可以交换`b`的原始值到`a`也可以交换a的原始值到`b`,你可以调用这个函数交换两个`Int`变量值: `swapTwoInts(_:_:)`函数可以交换`b`的原始值到`a`也可以交换a的原始值到`b`,你可以调用这个函数交换两个`Int`变量值:
@ -260,7 +263,7 @@ if let topItem = stackOfStrings.topItem {
`swapTwoValues(_:_:)`函数和`Stack`类型可以作用于任何类型,不过,有的时候对使用在泛型函数和泛型类型上的类型强制约束为某种特定类型是非常有用的。类型约束指定了一个必须继承自指定类的类型参数,或者遵循一个特定的协议或协议构成。 `swapTwoValues(_:_:)`函数和`Stack`类型可以作用于任何类型,不过,有的时候对使用在泛型函数和泛型类型上的类型强制约束为某种特定类型是非常有用的。类型约束指定了一个必须继承自指定类的类型参数,或者遵循一个特定的协议或协议构成。
例如Swift 的`Dictionary`类型对作用于其键的类型做了些限制。在[字典](../chapter2/04_Collection_Types.html)的描述中,字典的键类型必须是*可哈希*,也就是说,必须有一种方法可以使其被唯一的表示。`Dictionary`之所以需要其键是可哈希是为了以便于其检查其是否已经包含某个特定键的值。如无此需求,`Dictionary`既不会告诉是否插入或者替换了某个特定键的值,也不能查找到已经存储在字典里面的给定键值。 例如Swift 的`Dictionary`类型对作用于其键的类型做了些限制。在[字典](./04_Collection_Types.html#dictionaries)的描述中,字典的键类型必须是*可哈希*,也就是说,必须有一种方法可以使其被唯一的表示。`Dictionary`之所以需要其键是可哈希是为了以便于其检查其是否已经包含某个特定键的值。如无此需求,`Dictionary`既不会告诉是否插入或者替换了某个特定键的值,也不能查找到已经存储在字典里面的给定键值。
这个需求强制加上一个类型约束作用于`Dictionary`的键上,当然其键类型必须遵循`Hashable`协议Swift 标准库中定义的一个特定协议)。所有的 Swift 基本类型(如`String``Int` `Double``Bool`)默认都是可哈希。 这个需求强制加上一个类型约束作用于`Dictionary`的键上,当然其键类型必须遵循`Hashable`协议Swift 标准库中定义的一个特定协议)。所有的 Swift 基本类型(如`String``Int` `Double``Bool`)默认都是可哈希。
@ -443,9 +446,9 @@ struct Stack<T>: Container {
### 扩展一个存在的类型为一指定关联类型 ### 扩展一个存在的类型为一指定关联类型
在[使用扩展添加协议兼容性](../chapter2/21_Protocols.html)中有描述扩展一个存在的类型添加遵循一个协议。这个类型包含一个关联类型的协议。 在[扩展添加协议成员](./21_Protocols.html#adding_protocol_conformance_with_an_extension)中有描述扩展一个存在的类型添加遵循一个协议。这个类型包含一个关联类型的协议。
Swift的`Array`已经提供`append(_:)`方法,一个`count`属性和通过下标来查找一个自己的元素。这三个功能都达到`Container`协议的要求。也就意味着你可以扩展`Array`去遵循`Container`协议,只要通过简单声明`Array`适用于该协议而已。如何实践这样一个空扩展,在[使用扩展来声明协议的采纳](../chapter2/21_Protocols.html)中有描述这样一个实现一个空扩展的行为: Swift的`Array`已经提供`append(_:)`方法,一个`count`属性和通过下标来查找一个自己的元素。这三个功能都达到`Container`协议的要求。也就意味着你可以扩展`Array`去遵循`Container`协议,只要通过简单声明`Array`适用于该协议而已。如何实践这样一个空扩展,在[通过扩展补充协议声明](./21_Protocols.html#declaring_protocol_adoption_with_an_extension)中有描述这样一个实现一个空扩展的行为:
```swift ```swift
extension Array: Container {} extension Array: Container {}

View File

@ -1,8 +1,12 @@
> 翻译:[JaceFu](http://www.devtalking.com/) # 访问控制Access Control
------------------
> 1.0
> 翻译:[JaceFu](http://www.devtalking.com/)
> 校对:[ChildhoodAndy](http://childhood.logdown.com) > 校对:[ChildhoodAndy](http://childhood.logdown.com)
# 访问控制 > 2.0
------------------ > 翻译+校对:[mmoaay](https://github.com/mmoaay)
本页内容包括: 本页内容包括:
@ -146,7 +150,7 @@ private class SomePrivateClass { // 显式的 private 类
### 函数类型 ### 函数类型
函数的访问级别需要根据该函数的参数类型和返回类型的访问级别得出。如果根据参数类型和返回类型得出的函数访问级别不符合默认上下文,那么就需要明确地申明该函数的访问级别。 函数的访问级别需要根据该函数的参数类型和返回类型的访问级别得出。如果根据参数类型和返回类型得出的函数访问级别不符合默认上下文,那么就需要明确地申明该函数的访问级别。
下面的例子定义了一个名为`someFunction`全局函数,并且没有明确地申明其访问级别。也许你会认为该函数应该拥有默认的访问级别`internal`,但事实并非如此。事实上,如果按下面这种写法,带埋是无法编译通过的: 下面的例子定义了一个名为`someFunction`全局函数,并且没有明确地申明其访问级别。也许你会认为该函数应该拥有默认的访问级别`internal`,但事实并非如此。事实上,如果按下面这种写法,代码是无法编译通过的:
```swift ```swift
func someFunction() -> (SomeInternalClass, SomePrivateClass) { func someFunction() -> (SomeInternalClass, SomePrivateClass) {
@ -255,7 +259,7 @@ struct TrackedString {
``` ```
`TrackedString`结构体定义了一个用于存储`String`类型的属性`value`,并将初始化值设为`""`(即一个空字符串)。该结构体同时也定义了另一个用于存储`Int`类型的属性名`numberOfEdits`,它用于记录属性`value`被修改的次数。这个功能的实现通过属性`value``didSet`方法实现,每当给`value`赋新值时就会调用`didSet`方法,然后将`numberOfEdits`的值加一。 `TrackedString`结构体定义了一个用于存储`String`类型的属性`value`,并将初始化值设为`""`(即一个空字符串)。该结构体同时也定义了另一个用于存储`Int`类型的属性名`numberOfEdits`,它用于记录属性`value`被修改的次数。这个功能的实现通过属性`value``didSet`方法实现,每当给`value`赋新值时就会调用`didSet`方法,然后将`numberOfEdits`的值加一。
结构体`TrackedString`和它的属性`value`均没有申明显式访问级别,所以它们都拥有默认的访问级别`internal`。但是该结构体的`numberOfEdits`属性使用`private(set)`修饰符进行申明,这意味着`numberOfEdits`属性只能在定义该结构体的源文件中赋值。`numberOfEdits`属性的`Getter`依然是默认的访问级别`internal`,但是`Setter`的访问级别是`private`,这表示该属性只有在当前的源文件中是可读写的,而在当前源文件所属的模块中它只是一个可读的属性。 结构体`TrackedString`和它的属性`value`均没有申明显式访问级别,所以它们都拥有默认的访问级别`internal`。但是该结构体的`numberOfEdits`属性使用`private(set)`修饰符进行申明,这意味着`numberOfEdits`属性只能在定义该结构体的源文件中赋值。`numberOfEdits`属性的`Getter`依然是默认的访问级别`internal`,但是`Setter`的访问级别是`private`,这表示该属性只有在当前的源文件中是可读写的,而在当前源文件所属的模块中它只是一个可读的属性。
@ -266,7 +270,7 @@ var stringToEdit = TrackedString()
stringToEdit.value = "This string will be tracked." stringToEdit.value = "This string will be tracked."
stringToEdit.value += " This edit will increment numberOfEdits." stringToEdit.value += " This edit will increment numberOfEdits."
stringToEdit.value += " So will this one." stringToEdit.value += " So will this one."
println("The number of edits is \(stringToEdit.numberOfEdits)") print("The number of edits is \(stringToEdit.numberOfEdits)")
// prints "The number of edits is 3" // prints "The number of edits is 3"
``` ```
@ -288,13 +292,13 @@ public struct TrackedString {
<a name="initializers"></a> <a name="initializers"></a>
## 初始化 ## 初始化
我们可以给自定义的初始化方法申明访问级别,但是要不高于它所属类的访问级别。但[必要构造器](TODO)例外,它的访问级别必须和所属类的访问级别相同。 我们可以给自定义的初始化方法申明访问级别,但是要不高于它所属类的访问级别。但[必要构造器](./14_Initialization.html#required_initializers)例外,它的访问级别必须和所属类的访问级别相同。
如同函数或方法参数,初始化方法参数的访问级别也不能低于初始化方法的访问级别。 如同函数或方法参数,初始化方法参数的访问级别也不能低于初始化方法的访问级别。
<a name="default_initializers"></a> <a name="default_initializers"></a>
### 默认初始化方法 ### 默认初始化方法
Swift为结构体、类都提供了一个默认的无参初始化方法用于给它们的所有属性提供赋值操作但不会给出具体值。默认初始化方法可以参阅[Default Initializers](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html#//apple_ref/doc/uid/TP40014097-CH18-XID_336)。默认初始化方法的访问级别与所属类型的访问级别相同。 Swift为结构体、类都提供了一个默认的无参初始化方法用于给它们的所有属性提供赋值操作但不会给出具体值。默认初始化方法可以参阅[默认构造器](./14_Initialization.html#default_initializers)。默认初始化方法的访问级别与所属类型的访问级别相同。
> 注意:如果一个类型被申明为`public`级别,那么默认的初始化方法的访问级别为`internal`。如果你想让无参的初始化方法在其他模块中可以被使用,那么你必须提供一个具有`public`访问级别的无参初始化方法。 > 注意:如果一个类型被申明为`public`级别,那么默认的初始化方法的访问级别为`internal`。如果你想让无参的初始化方法在其他模块中可以被使用,那么你必须提供一个具有`public`访问级别的无参初始化方法。
@ -345,6 +349,3 @@ Swift为结构体、类都提供了一个默认的无参初始化方法用于
任何你定义的类型别名都会被当作不同的类型,以便于进行访问控制。一个类型别名的访问级别不可高于原类型的访问级别。比如说,一个`private`级别的类型别名可以设定给一个`public``internal``private`的类型,但是一个`public`级别的类型别名只能设定给一个`public`级别的类型,不能设定给`internal``private` 级别的类型。 任何你定义的类型别名都会被当作不同的类型,以便于进行访问控制。一个类型别名的访问级别不可高于原类型的访问级别。比如说,一个`private`级别的类型别名可以设定给一个`public``internal``private`的类型,但是一个`public`级别的类型别名只能设定给一个`public`级别的类型,不能设定给`internal``private` 级别的类型。
> 注意:这条规则也适用于为满足协议一致性而给相关类型命名别名的情况。 > 注意:这条规则也适用于为满足协议一致性而给相关类型命名别名的情况。

View File

@ -1,8 +1,12 @@
> 翻译:[xielingwang](https://github.com/xielingwang) # 高级运算符Advanced Operators
-----------------
> 1.0
> 翻译:[xielingwang](https://github.com/xielingwang)
> 校对:[numbbbbb](https://github.com/numbbbbb) > 校对:[numbbbbb](https://github.com/numbbbbb)
# 高级运算符 > 2.0
----------------- > 翻译+校对:[buginux](https://github.com/buginux)
本页内容包括: 本页内容包括:
@ -12,75 +16,75 @@
- [运算符函数(Operator Functions)](#operator_functions) - [运算符函数(Operator Functions)](#operator_functions)
- [自定义运算符](#custom_operators) - [自定义运算符](#custom_operators)
除了[基本操作](02_Basic_Operators.html)中所讲的运算符Swift还有许多复杂的高级运算符包括了C语言和Objective-C的位运算符和移位运算。 除了在之前介绍过的[基本运算](./02_Basic_Operators.html)Swift还有许多可以对数值进行复杂操作的高级运算符。这些高级运算符包含了在 C 和 Objective-C 中已经被大家所熟知的位运算符和移位运算
不同于C语言中的数值计算Swift的数值计算默认是不溢出的。溢出行为会被捕获并报告为错误。你是故意的?好吧,你可以使用Swift为你准备的另一套默认允许溢出的数值运算符,如可溢出的加号为`&+`。所有允许溢出运算符都是以`&`开始的。 C语言中的算术运算符不同Swift 中的算术运算符默认是不溢出的。所有溢出行为会被捕获并报告为错误。如果想让系统允许溢出行为,可以选择使用 Swift另一套默认支持溢出的运算符,比如溢出加法运算符(`&+`)。所有的这些溢出运算符都是以 `&` 开头的。
定义的结构类和枚举是否可以使用标准的运算符来定义操作当然可以在Swift中你可以为你创建的所有类型定制运算符的操作 定义自有的结构体、类和枚举最好也同时为它们提供标准Swift运算符的实现。Swift简化了运算符的自定义实现也使判断不同类型所对应的行为更为简单
可定制的运算符并不限于那些预设的运算符,你可以自定义中置,前置,后置及赋值运算符,当然还有优先级结合性。这些运算符在代码中可以像预设的运算符一样使用,你也可以扩展已有的类型以支持自定义的运算符。 我们不用被预定义的运算符所限制。在 Swift 当中可以自由地定义中缀、前缀、后缀和赋值运算符,以及相应的优先级结合性。这些运算符在代码中可以像预设的运算符一样使用,我们甚至可以扩展已有的类型以支持自定义的运算符。
<a name="bitwise_operators"></a> <a name="bitwise_operators"></a>
## 位运算符 ## 位运算符
操作符可以操作数据结构中原始数据的每个比特位。位操作符通常在诸如图像处理和创建设备驱动等底层开发中使用,位操作符在同外部资源的数据进行交互的时候也很有用,比如在使用用户协议进行通信的时候,运用位运算符来对原始数据进行编码和解码。 运算符(`Bitwise operators`)可以操作一个数据结构中每个独立的位。它们通常被用在底层开发中,比如图形编程和创建设备驱动。位运算符在处理外部资源的原始数据时也十分有用,比如对自定义通信协议传输的数据进行编码和解码。
Swift支持如下所有C语言的位运算符 Swift 支持C语言中的全部位运算符具体如下:
### 按位取反运算符 ### 按位取反运算符(`bitwise NOT operator`)
按位取反运算符`~`对一个操作数的每一位都取反 按位取反运算符(`~`) 可以对一个数值的全部位进行取反
![Art/bitwiseNOT_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitwiseNOT_2x.png "Art/bitwiseNOT_2x.png") ![Art/bitwiseNOT_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitwiseNOT_2x.png)
这个运算符是前置的,所以请不加任何空格地写在操作数之前 按位取反操作符是一个前置运算符,需要直接放在操作数的之前,并且它们之间不能添加任何空格
```swift ```
let initialBits: UInt8 = 0b00001111 let initialBits: UInt8 = 0b00001111
let invertedBits = ~initialBits // 等于 0b11110000 let invertedBits = ~initialBits // 等于 0b11110000
``` ```
`UInt8`是8位无符整型可以存储0~255之间的任意数。这个例子初始化一个整型为二进制`00001111`(前4位`0`,后4位`1`),它的十进制值为`15` `UInt8` 类型的整数有 8 个比特位,可以存储 0 ~ 255之间的任意数。这个例子初始化一个 `UInt8` 类型的整数,并赋值为二进制`00001111`,它的前 4 位都`0`,后 4 位都`1`。这个值等价于十进制`15`
使用按位取反运算`~``initialBits`操作,然后赋值给`invertedBits`这个新常量这个常量的值等于所有位都取反的`initialBits`,即`1`变成`0``0`变成`1`,变成了`11110000`十进制值为`240` 接着使用按位取反运算符创建了一个名为 `invertedBits`常量这个常量的值与全部位取反后的 `initialBits` 相等。即所有的 `0` 都变成了 `1`同时所有的 `1`变成 `0``invertedBits` 的二进制值为 `11110000`等价于无符号十进制数的 `240`
### 按位与运算符 ### 按位与运算符(Bitwise AND Operator)
按位与运算符对两个数进行操作然后返回一个新的数这个数的每个位都需要两个输入数的同一位都为1时才为1 按位与运算符(`&`)可以对两个数的比特位进行合并。它返回一个新的数,只有当两个操作数的对应位*都*为 `1` 的时候,该数的对应位才为 `1`
![Art/bitwiseAND_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitwiseAND_2x.png "Art/bitwiseAND_2x.png") ![Art/bitwiseAND_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitwiseAND_2x.png "Art/bitwiseAND_2x.png")
以下代码`firstSixBits``lastSixBits`中间4个位都为1。对它进行按位与运算后,就得到了`00111100`即十进制的`60` 在下面的示例当中`firstSixBits``lastSixBits` 中间 4 个位的值都为 1 。按位与运算符对它进行了运算,得到二进制数值 `00111100`等价于无符号十进制数的 `60`
```swift ```
let firstSixBits: UInt8 = 0b11111100 let firstSixBits: UInt8 = 0b11111100
let lastSixBits: UInt8 = 0b00111111 let lastSixBits: UInt8 = 0b00111111
let middleFourBits = firstSixBits & lastSixBits // 等于 00111100 let middleFourBits = firstSixBits & lastSixBits // 等于 00111100
``` ```
### 按位或运算 ### 按位或运算符(Bitwise OR Operator)
按位或运算符`|`比较两个数然后返回一个新的数这个数的每一位设置1的条件是两个输入数的同一位都不为0(即任意一个为1或都为1) 按位或运算符(`|`)可以对两个数的比特位进行比较。它返回一个新的数,只要两个操作数的对应位中有*任意*一个为 `1` 时,该数的对应位就为 `1`
![Art/bitwiseOR_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitwiseOR_2x.png "Art/bitwiseOR_2x.png") ![Art/bitwiseOR_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitwiseOR_2x.png "Art/bitwiseOR_2x.png")
如下代码`someBits``moreBits`在不同位上有`1`位或运行的结果是`11111110`即十进制的`254` 在下面的示例当中`someBits``moreBits` 将不同的位设置为 `1`位或运算符对它们进行了运算,得到二进制数值 `11111110`等价于无符号十进制数的 `254`
```swift ```
let someBits: UInt8 = 0b10110010 let someBits: UInt8 = 0b10110010
let moreBits: UInt8 = 0b01011110 let moreBits: UInt8 = 0b01011110
let combinedbits = someBits | moreBits // 等于 11111110 let combinedbits = someBits | moreBits // 等于 11111110
``` ```
### 按位异或运算符 ### 按位异或运算符(Bitwise XOR Opoerator)
按位异或运算符`^`比较两个数,然后返回一个数,这个数的每个位设为`1`的条件是两个输入数的同一位不同,如果相同就设为`0` 按位异或运算符(`^`)可以对两个数的比特位进行比较。它返回一个新的数,当两个操作数的对应位不相同时,该数的对应位就为 `1`
![Art/bitwiseXOR_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitwiseXOR_2x.png "Art/bitwiseXOR_2x.png") ![Art/bitwiseXOR_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitwiseXOR_2x.png "Art/bitwiseXOR_2x.png")
以下代码`firstBits``otherBits`都有一个`1`跟另一个数不同的。所以按位异或的结果是把它这些位置为`1`其他都置为`0` 在下面的示例当中`firstBits``otherBits` 都有一个自己设置为 `1` 而对方设置为 `0` 的位。 按位异或运算符将这两个位都设置为 `1`同时将其它位都设置为 `0`
```swift ```
let firstBits: UInt8 = 0b00010100 let firstBits: UInt8 = 0b00010100
let otherBits: UInt8 = 0b00000101 let otherBits: UInt8 = 0b00000101
let outputBits = firstBits ^ otherBits // 等于 00010001 let outputBits = firstBits ^ otherBits // 等于 00010001
@ -88,21 +92,27 @@ let outputBits = firstBits ^ otherBits // 等于 00010001
### 按位左移/右移运算符 ### 按位左移/右移运算符
左移运算符`<<`右移运算符`>>`会把一个数的所有比特位按以下定义的规则向左或向右移动指定位数 按位左移运算符(`<<`)和按位右移运算符(`>>`)可以对一个数进行指定位数的左移和右移,但是需要遵守下面定义的规则
按位左移按位右移的效果相当把一个整数乘于或除于一个因子为`2`的整数。向左移动一个整型的比特位相当于把这个数乘于`2`,向右移一位就是除于`2` 对一个数进行按位左移按位右移,相当于对这个数进行乘以 2 或除以 2 的运算。将一个整数左移一位,等价于将这个数乘以 2同样地将一个整数右移一位等价于将这个数除以 2
#### 无符整型的移位操作 #### 无符整型的移位操作
对无符整型移位的效果如下: 对无符整型进行移位的规则如下:
已经存在的比特位向左或向右移动指定的位数。被移出整型存储边界的的位数直接抛弃,移动留下的空白位用零`0`来填充。这种方法称为逻辑移位 1. 已经存在的比特位按指定的位数进行左移和右移
2. 任何移动超出整型存储边界的位都会被丢弃。
3. 用 0 来填充移动后产生的空白位。
以下这张把展示了 `11111111 << 1`(`11111111`向左移1位),和 `11111111 >> 1`(`11111111`向右移1位)。蓝色的是被移位的,灰色是被抛弃的,橙色的`0`是被填充进来的 这种方法称为逻辑移位(`logical shift`)
以下这张图展示了 `11111111 << 1`(即把 `11111111` 向左移动 1 位),和 `11111111 >> 1`(即把 `11111111` 向右移动 1 位) 的结果。蓝色的部分是被移位的,灰色的部分是被抛弃的,橙色的部分则是被填充进来的。
![Art/bitshiftUnsigned_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitshiftUnsigned_2x.png "Art/bitshiftUnsigned_2x.png") ![Art/bitshiftUnsigned_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitshiftUnsigned_2x.png "Art/bitshiftUnsigned_2x.png")
```swift 下面的代码演示了 Swift 中的移位操作:
```
let shiftBits: UInt8 = 4 // 即二进制的00000100 let shiftBits: UInt8 = 4 // 即二进制的00000100
shiftBits << 1 // 00001000 shiftBits << 1 // 00001000
shiftBits << 2 // 00010000 shiftBits << 2 // 00010000
@ -111,221 +121,205 @@ shiftBits << 6 // 00000000
shiftBits >> 2 // 00000001 shiftBits >> 2 // 00000001
``` ```
可以使用移位操作进行其他数据类型编码和解码 可以使用移位操作其他数据类型进行编码和解码
```swift ```
let pink: UInt32 = 0xCC6699 let pink: UInt32 = 0xCC6699
let redComponent = (pink & 0xFF0000) >> 16 // redComponent 是 0xCC, 即 204 let redComponent = (pink & 0xFF0000) >> 16 // redComponent 是 0xCC, 即 204
let greenComponent = (pink & 0x00FF00) >> 8 // greenComponent 是 0x66, 即 102 let greenComponent = (pink & 0x00FF00) >> 8 // greenComponent 是 0x66, 即 102
let blueComponent = pink & 0x0000FF // blueComponent 是 0x99, 即 153 let blueComponent = pink & 0x0000FF // blueComponent 是 0x99, 即 153
``` ```
这个例使用了一个`UInt32`命名为`pink`常量来存储层叠样式表`CSS`中粉色的颜色值`CSS`颜色`#CC6699`Swift用十六进制`0xCC6699`来表示。然后使用按位与(&)和按位右移就可以从这个颜色值中解出红(CC),绿(66),蓝(99)三个部分。 这个例使用了一个命名为 `pink``UInt32`常量来存储层叠样式表(`CSS`)中粉色的颜色值。该 `CSS` 的十六进制颜色值 `#CC6699`Swift 中表示为 `0xCC6699`。然后用按位与运算符(`&`)和按位右移运算符(`>>`)从这个颜色值中解出红(`CC`)、绿(`66`)以及蓝(`99`)三个部分。
`0xCC6699``0xFF0000`进行按位与`&`操作就可以得到红色部分`0xFF0000`中的`0`了遮盖了`OxCC6699`的第二和第三个字节,这样`6699`被忽略,只留下`0xCC0000` 红色部分是通过对 `0xCC6699``0xFF0000` 进行按位与运算后得到的`0xFF0000` 中的 `0` 部分作为*掩码*,掩盖了 `OxCC6699`的第二和第三个字节,使得数值中的 `6699` 被忽略,只留下 `0xCC0000`
然后按向右移动16位,即 `>> 16`。十六进制中每两个字符是8比特位所以移动16位的结果是把`0xCC0000`变成`0x0000CC`。这和`0xCC`等的,就是十进制`204` 然后,再将这个数按向右移动 16 位(`>> 16`)。十六进制中每两个字符表示 8 个比特位,所以移动 16 位后 `0xCC0000` 就变为 `0x0000CC`。这个数`0xCC`是等的,就是十进制数值的 `204`
同样的,绿色部分来自于`0xCC6699``0x00FF00`的按位操作得到`0x006600`。然后向右移动8位,得到`0x66`即十进制的`102` 同样的,绿色部分通过对 `0xCC6699``0x00FF00` 进行按位与运算得到 `0x006600`。然后将这个数向右移动 8 位,得到 `0x66`也就是十进制数值的 `102`
最后,蓝色部分`0xCC6699``0x0000FF`进行按位与运算得到`0x000099`,无需向右移位,所以结果就是`0x99`,即十进制的`153` 最后,蓝色部分通过对 `0xCC6699``0x0000FF` 进行按位与运算得到 `0x000099`。并且不需要进行向右移位,所以结果`0x99` ,也就是十进制数值的 `153`
#### 有符整型的移位操作 #### 有符整型的移位操作
有符整型的移位操作相对复杂得多,因为正负号也是用二进制位表示的。(这里举的例子虽然都是8位的但它的原理是通用的。) 对比无符号整型来说,有符整型的移位操作相对复杂得多,这种复杂性源于有符号整数的二进制表现形式。(为了简单起见,以下的示例都是基于 8 位有符号整数的,但是其中的原理对任何位数的有符号整数都是通用的。)
有符整型通过第1个比特位(称为符号位)来表这个整数是正数还是负数。`0`代表正数,`1`代表负数。 有符号整数使用第 1 个比特位(通常被称为符号位)来表这个数的正负。符号位为 `0` 代表正数,`1` 代表负数。
其余的比特位(称为数值位)存储实值。有符正整数和无符正整数在计算机里的存储结果是一样的,下来我们来看`+4`内部的二进制结构。 其余的比特位(通常被称为数值位)存储了这个数的真实值。有符正整数和无符号数的存储方式是一样的,都是从 `0` 开始算起。这是值为 `4``Int8` 型整数的二进制位表现形式:
![Art/bitshiftSignedFour_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitshiftSignedFour_2x.png "Art/bitshiftSignedFour_2x.png") ![Art/bitshiftSignedFour_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitshiftSignedFour_2x.png "Art/bitshiftSignedFour_2x.png")
符号位为`0`代表正数另外7比特位二进制表示的实际值就刚好是`4` 符号位为 `0`说明这是一个正数,另外 7 位则代表了十进制数值 `4` 的二进制表示
负数呢,跟正数不同。负数存储的是2的n次方减去它的绝对值,n为数值位的位数。一个8比特的数有7个数值位,所以是2的7次方即128。 负数的存储方式略有不同。存储的是 `2` 的 n 次方减去它的真实值绝对值,这里的 n 为数值位的位数。一个 8 位的数有 7 个数值位,所以是 2 的 7 次方,即 128。
我们来看`-4`存储的二进制结构。 这是值为 `-4``Int8` 型整数的二进制位表现形式:
![Art/bitshiftSignedMinusFour_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitshiftSignedMinusFour_2x.png "Art/bitshiftSignedMinusFour_2x.png") ![Art/bitshiftSignedMinusFour_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitshiftSignedMinusFour_2x.png "Art/bitshiftSignedMinusFour_2x.png")
现在符号位为`1`代表负数7个数值位要表达的二进制值是124即128 - 4 这次的符号位为 `1`说明这是一个负数,另外 7 个位则代表了数值 `124`(即 `128 - 4`) 的二进制表示
![Art/bitshiftSignedMinusFourValue_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitshiftSignedMinusFourValue_2x.png "Art/bitshiftSignedMinusFourValue_2x.png") ![Art/bitshiftSignedMinusFourValue_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitshiftSignedMinusFourValue_2x.png "Art/bitshiftSignedMinusFourValue_2x.png")
负数的编码方式称为二进制补码表示。这种表示方式看起来奇怪,但它有几个优点。 负数的表示通常被称为二进制补码(`two's complement`)表示法。用这种方法来表示负数乍看起来有点奇怪,但它有几个优点。
首先,只需要对全部8个比特位(包括符号)做标准的二进制加法就可以完成 `-1 + -4` 的操作忽略加法过程产生的超过8个比特位表达的任何信息。 首先,如果想对 `-1``-4` 进行加法操作,我们只需要将这两个数的全部 8 个比特位进行相加,并且将计算结果中超出 8 位的数值丢弃:
![Art/bitshiftSignedAddition_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitshiftSignedAddition_2x.png "Art/bitshiftSignedAddition_2x.png") ![Art/bitshiftSignedAddition_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitshiftSignedAddition_2x.png "Art/bitshiftSignedAddition_2x.png")
第二,由于使用二进制补码表示,我们可以和正数一样对负数进行按位左移右移同样也是左移1位时乘于`2`右移1位时除于`2`。要达到此目的,对有符整型的右移有一个特别的要求 其次,使用二进制补码可以使负数的按位左移右移操作得到跟正数同样的效果,即每向左移一位就将自身的数值乘以 2每向右一位就将自身的数值除以 2。要达到此目的,对有符号整数的右移有一个额外的规则
对有符整型按位右移时不使用0填充空白位而是根据符号位(正数为`0`,负数为`1`)填充空白位 * 当对正整数进行按位右移操作时,遵循与无符号整数相同的规则,但是对于移位产生的空白位使用*符号位*进行填充,而不是用 0
![Art/bitshiftSigned_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitshiftSigned_2x.png "Art/bitshiftSigned_2x.png") ![Art/bitshiftSigned_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitshiftSigned_2x.png "Art/bitshiftSigned_2x.png")
就确保了在右移的过程中,有符整型的符号不会发生变化。这称为算术移位 个行为可以确保有符号整数的符号不会因为右移操作而改变,这通常被称为算术移位(`arithmetic shift`)
正因为正数和负数特殊存储方式,向右移位使它接近于`0`移位过程中保持符号不变,数在接近`0`的过程中一直是负数 由于正数和负数特殊存储方式,在对它们进行右移的时候,会使它们越来越接近 0。在移位过程中保持符号不变,意味着负整数在接近 `0` 的过程中一直保持为负
<a name="overflow_operators"></a> <a name="overflow_operators"></a>
## 溢出运算符 ## 溢出运算符
默认情况下,当你往一个整型常量或变量赋于一个它不能承载的大数时Swift不会让你这么干的它会报错。这样操作过大或过小的数的时候就很安全 默认情况下,当一个整数赋超过它容量的值时Swift 默认会报错,而不是生成一个无效的数。这个行为给我们操作过大或过小的数的时候提供了额外的安全
例如,`Int16`整型能承载的整数范围是`-32768``32767`如果给它赋上超过这个范围的数,就会报错: 例如,`Int16` 型整数能容纳的有符号整数范围是 `-32768``32767`当为一个 `Int16` 型变量赋的值超过这个范围时,系统就会报错:
```swift ```
var potentialOverflow = Int16.max var potentialOverflow = Int16.max
// potentialOverflow 等于 32767, 这是 Int16 能承载的最大整数 // potentialOverflow 的值是 32767, 这是 Int16 能容纳的最大整数
potentialOverflow += 1 potentialOverflow += 1
// 噢, 出错了 // 这里会报错
``` ```
过大或过小的数值进行错误处理让你的数值边界条件更灵活。 过大或过小的数值提供错误处理,能让我们在处理边界值时更加灵活。
当然你有意在溢出时对有效位进行截断你可采用溢出运算而非错误处理。Swfit为整型计算提供了5个`&`符号开头的溢出运算符。 然而,也可以选择让系统在数值溢出的时候采取截断操作,而非报错。可以使用 Swift 提供的三个溢出操作符(`overflow operators`)来让系统支持整数溢出运算。这些操作符都是以 `&` 开头的:
- 溢出加法 `&+` * 溢出加法 `&+`
- 溢出减法 `&-` * 溢出减法 `&-`
- 溢出乘法 `&*` * 溢出乘法 `&*`
- 溢出除法 `&/`
- 溢出求余 `&%`
### 值的上溢出 ### 值溢出
下面例子使用了溢出加法`&+`来解剖的无符整数的上溢出 数值有可能出现上溢或者下溢。
```swift 这个示例演示了当我们对一个无符号整数使用溢出加法(`&+`)进行上溢运算时会发生什么:
var willOverflow = UInt8.max ```
// willOverflow 等于UInt8的最大整数 255 var unsignedOverflow = UInt8.max
willOverflow = willOverflow &+ 1 // unsignedOverflow 等于 UInt8 所能容纳的最大整数 255
// 此时 willOverflow 等于 0
unsignedOverflow = unsignedOverflow &+ 1
// 此时 unsignedOverflow 等于 0
``` ```
`willOverflow``Int8`所能承载的最大`255`(二进制`11111111`)然后`&+`加1。然后`UInt8`就无法表达这个新值的二进制了,也就导致了这个新值上溢出了,大家可以看下图。溢出后,新值在`UInt8`的承载范围内的那部分是`00000000`,也就是`0` `unsignedOverflow` 被初始化为 `UInt8` 所能容纳的最大整数(`255`,以二进制表示即 `11111111`)然后使用了溢出加法运算符(`&+`)对其进行加 1 操作。这使得它的二进制表示正好超出 `UInt8` 所能容纳的位数,也就导致了数值的溢出,如下图所示。数值溢出后,留在 `UInt8` 边界内的值是 `00000000`,也就是十进制数值的 0
![Art/overflowAddition_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/overflowAddition_2x.png "Art/overflowAddition_2x.png") ![Art/overflowAddition_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/overflowAddition_2x.png "Art/overflowAddition_2x.png")
### 值的下溢出 同样地,当我们对一个无符号整数使用溢出减法(`&-`)进行下溢运算时也会产生类似的现象:
数值也有可能因为太小而越界。举个例子: ```
var unsignedOverflow = UInt8.min
// unsignedOverflow 等于 UInt8 所能容纳的最小整数 0
`UInt8`的最小值是`0`(二进制为`00000000`)。使用`&-`进行溢出减1就会得到二进制的`11111111`即十进制的`255` unsignedOverflow = unsignedOverflow &- 1
// 此时 unsignedOverflow 等于 255
![Art/overflowUnsignedSubtraction_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/overflowUnsignedSubtraction_2x.png "Art/overflowUnsignedSubtraction_2x.png")
Swift代码是这样的:
```swift
var willUnderflow = UInt8.min
// willUnderflow 等于UInt8的最小值0
willUnderflow = willUnderflow &- 1
// 此时 willUnderflow 等于 255
``` ```
有符整型也有类似的下溢出,有符整型所有的减法也都是对包括在符号位在内的二进制数进行二进制减法的,这在 "按位左移/右移运算符" 一节提到过。最小的有符整数是`-128`,即二进制的`10000000`。用溢出减法减去去1后变成了`01111111`即UInt8所能承载的最大整数`127` `UInt8` 型整数能容纳的最小值是 0以二进制表示即 `00000000`当使用溢出减法运算符对其进行减 1 操作时,数值会产生下溢并被截断为 `11111111` 也就是十进制数值的 255
![Art/overflowUnsignedSubtraction_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/overflowUnsignedSubtraction_2x.png "Art/overflowAddition_2x.png")
溢出也会发生在有符号整型数值上。在对有符号整型数值进行溢出加法或溢出减法运算时,符号位也需要参与计算,正如[按位左移/右移运算符](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-ID34)所描述的。
```
var signedOverflow = Int8.min
// signedOverflow 等于 Int8 所能容纳的最小整数 -128
signedOverflow = signedOverflow &- 1
// 此时 signedOverflow 等于 127
```
`Int8` 型整数能容纳的最小值是 -128以二进制表示即 `10000000`。当使用溢出减法操作符对其进行减 1 操作时,符号位被翻转,得到二进制数值 `01111111`,也就是十进制数值的 `127`,这个值也是 `Int8` 型整数所能容纳的最大值。
![Art/overflowSignedSubtraction_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/overflowSignedSubtraction_2x.png "Art/overflowSignedSubtraction_2x.png") ![Art/overflowSignedSubtraction_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/overflowSignedSubtraction_2x.png "Art/overflowSignedSubtraction_2x.png")
来看看Swift代码 对于无符号与有符号整型数值来说,当出现上溢时,它们会从数值所能容纳的最大数变成最小的数。同样地,当发生下溢时,它们会从所能容纳的最小数变成最大的数。
```swift
var signedUnderflow = Int8.min
// signedUnderflow 等于最小的有符整数 -128
signedUnderflow = signedUnderflow &- 1
// 此时 signedUnderflow 等于 127
```
### 除零溢出
一个数除以0 `i / 0`或者对0求余数 `i % 0`,就会产生一个错误。
```swift
let x = 1
let y = x / 0
```
使用它们对应的可溢出的版本的运算符`&/``&%`进行除0操作时就会得到`0`值。
```swift
let x = 1
let y = x &/ 0
// y 等于 0
```
<a name="precedence_and_associativity"></a> <a name="precedence_and_associativity"></a>
## 优先级和结合性 ## 优先级和结合性
运算符的优先级使得一些运算符优先于其他运算符,高优先级的运算符会先被计算。 运算符的优先级(`precedence`)使得一些运算符优先于其他运算符,高优先级的运算符会先被计算。
结合性定义相同优先级的运算符在一起时是怎么组合或关联的,是和左边的一组,还是右边的一组。意思就是,到底是和左边的表达式结合呢,还是和右边的表达式结合 结合性(`associativity`)定义相同优先级的运算符是如何结合(或关联)的 —— 是与左边结合为一组,还是右边结合为一组。可以将这意思理解为“它们是与左边的表达式结合的”或者“它们是与右边的表达式结合的”。
合表达式中,运算符的优先级和结合性是非常重要的。举个例子,为什么下表达式的结果为`4` 合表达式的运算顺序中,运算符的优先级和结合性是非常重要的。举例来说,为什么下面这个表达式的运算结果是 `4`
```swift ```swift
2 + 3 * 4 % 5 2 + 3 * 4 % 5
// 结果是 4 // 结果是 4
``` ```
如果严格地从左计算到右,计算过程是这样: 如果严格地从左到右进行运算,则运算的过程是这样
- 2 + 3 = 5 - 2 + 3 = 5
- 5 * 4 = 20 - 5 * 4 = 20
- 20 / 5 = 4 余 0 - 20 % 5 = 0
但是正确答案是`4`而不是`0`。优先级高的运算符要先计算在Swift和C语言中都是先乘除后加减的。所以执行完乘法和求余运算才能执行加减运算。 但是正确答案是 `4` 而不是 `0`。优先级高的运算符要先于优先级低的运算符进行计算。与C语言类似,在 Swift 当中,乘法运算符(`*`)与取余运算符(`%`)的优先级高于加法运算符(`+`)。因此,它们的计算顺序要先于加法运算。
乘法和求余拥有相同的优先级,在运算过程中,我们还需要结合性乘法和求余运算都是左结合的。这相当于在表达式中有隐藏的括号让运算从左开始。 乘法与取余的优先级相同。这时为了得到正确的运算顺序,还需要考虑结合性乘法与取余运算都是左结合的。可以将这考虑成为这两部分表达式都隐式地加上了括号:
```swift ```swift
2 + ((3 * 4) % 5) 2 + ((3 * 4) % 5)
``` ```
3 * 4 = 12所以相当于: `(3 * 4) = 12`,所以表达式相当于:
```swift ```swift
2 + (12 % 5) 2 + (12 % 5)
``` ```
12 % 5 = 2,所这又相当于 `12 % 5 = 2`,所以表达式相当于
```swift ```swift
2 + 2 2 + 2
``` ```
计算结果为 4 此时可以容易地看出计算结果为 `4`
查阅Swift运算符优先级和结合性的完整列表,请看[表达式](../chapter3/04_Expressions.html)。 如果想查看完整的 Swift 运算符优先级和结合性规则,请参考[表达式](../chapter3/04_Expressions.html)。
> 注意: > 注意:
Swift的运算符较C语言和Objective-C来得更简单和保守,这意味着基于C的语言可能不一样。所以在移植已有代码到Swift时注意确保代码按你想的那样去执行。 > 对于C语言和 Objective-C 来说Swift 的运算符优先级和结合性规则是更加简洁和可预测的。但是,这意味着它们于那些基于C的语言不是完全一致的。在对现有的代码进行移植的时候,要注意确保运算符的行为仍然是按照你所想的那样去执行。
<a name="operator_functions"></a> <a name="operator_functions"></a>
## 运算符函数 ## 运算符函数
让已有的运算符也可以对自定义的类和结构进行运算,这称为运算符重载 类和结构可以为现有的操作符提供自定义的实现,这通常被称为运算符重载(`overloading`)
这个例子展示了如何`+`让一个自定义的结构做加法。算术运算符`+`是一个两目运算符,因为它有两个操作数,而且它必须出现在两个操作数之间。 下面的例子展示了如何为自定义的结构实现加法操作符(`+`)。算术加法运算符是一个两目运算符(`binary operator`),因为它可以对两个目标进行操作,同时它还是中缀(`infix`)运算符,因为它出现在两个目标中间。
例子中定义了一个名为`Vector2D`二维坐标向量 `(xy)` 的结构,然后定义了让两个`Vector2D`的对象相加的运算符函数 例子中定义了一个名为 `Vector2D` 的结构体用来表示二维坐标向量`(x, y)`,紧接着定义了一个可以对两个 `Vector2D` 结构体进行相加的运算符函数(`operator function`)
```swift ```swift
struct Vector2D { struct Vector2D {
var x = 0.0, y = 0.0 var x = 0.0, y = 0.0
} }
@infix func + (left: Vector2D, right: Vector2D) -> Vector2D {
func + (left: Vector2D, right: Vector2D) -> Vector2D {
return Vector2D(x: left.x + right.x, y: left.y + right.y) return Vector2D(x: left.x + right.x, y: left.y + right.y)
} }
``` ```
该运算符函数定义一个全局`+`函数,这个函数需要两个`Vector2D`类型的参数,返回值也是`Vector2D`类型。需要定义和实现一个中置运算的时候,在关键字`func`之前写上属性 `@infix` 就可以了 该运算符函数定义一个全局函数,并且函数的名字与它要进行重载的 `+` 名字一致。因为算术加法运算符是双目运算符,所以这个运算符函数接收两个类型为 `Vector2D` 的输入参数,同时有一个 `Vector2D` 类型的返回值
在这个代码实现中,参数被命名为`left``right`,代表`+`左边和右边的两个`Vector2D`对象。函数返回了一个新的`Vector2D`的对象,这个对象的`x``y`分别等于两个参数对象的`x``y`和。 在这个实现中,输入参数分别被命名为 `left``right`,代表`+` 运算符左边和右边的两个 `Vector2D` 对象。函数返回了一个新的 `Vector2D` 的对象,这个对象的 `x``y` 分别等于两个参数对象的 `x``y` 的值之和。
这个函数全局的,而不是`Vector2D`结构的成员方法,所以任意两个`Vector2D`对象都可以使用这个中运算符 这个函数被定义成全局的,而不是 `Vector2D` 结构的成员方法,所以任意两个 `Vector2D` 对象都可以使用这个中运算符
```swift ```swift
let vector = Vector2D(x: 3.0, y: 1.0) let vector = Vector2D(x: 3.0, y: 1.0)
@ -334,151 +328,155 @@ let combinedVector = vector + anotherVector
// combinedVector 是一个新的Vector2D, 值为 (5.0, 5.0) // combinedVector 是一个新的Vector2D, 值为 (5.0, 5.0)
``` ```
这个例子实现两个向量 `(3.01.0)``(2.04.0)` 相加,得到向量 `(5.05.0)`过程如下图示: 这个例子实现两个向量 `(3.01.0)``(2.04.0)` 相加,得到新的向量 `(5.05.0)`。这个过程如下图示:
![Art/vectorAddition_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/vectorAddition_2x.png "Art/vectorAddition_2x.png") ![Art/vectorAddition_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/vectorAddition_2x.png "Art/vectorAddition_2x.png")
### 前和后运算符 ### 前和后运算符
上个例子演示了一个双目中运算符的自定义实现,同样我们也可以玩标准单目运算符的实现。单目运算符只有一个操作数,在操作数之前就是前置的,如`-a`; 在操作数之后就是后置的,如`i++` 上个例子演示了一个双目中运算符的自定义实现。类与结构体也能提供标准单目运算符(`unary operators`)的实现。单目运算符只有一个操作目标。当运算符出现在操作目标之前时,它就是前缀(`prefix`)的(比如 `-a`),而当它出现在操作目标之后时,它就是后缀(`postfix`)的(比如 `i++`)
实现一个前置或后置运算符时,在定义该运算符的时候于关键字`func`之前标注 `@prefix``@postfix` 属性。 实现前缀或者后缀运算符,需要在声明运算符函数的时候`func` 关键字之前指定 `prefix` `postfix` 限定符:
```swift ```swift
@prefix func - (vector: Vector2D) -> Vector2D { prefix func - (vector: Vector2D) -> Vector2D {
return Vector2D(x: -vector.x, y: -vector.y) return Vector2D(x: -vector.x, y: -vector.y)
} }
``` ```
这段代码为`Vector2D`类型提供了单目减运算`-a``@prefix`属性表明这是个前置运算符。 这段代码为 `Vector2D` 类型实现了单目减运算符(`-a`)。由于单目减运算符是前缀运算符,所以这个函数需要加上 `prefix` 限定符。
对于数值,单目减运算符可以把正数变负数,把负数变正数。对于`Vector2D`,单目减运算将其`x``y`都进进行单目减运算 对于简单数值,单目减运算符可以对它们的正负性进行改变。对于 `Vector2D` 来说,单目减运算将其 `x``y` 属性的正负性都进行了改变
```swift ```swift
let positive = Vector2D(x: 3.0, y: 4.0) let positive = Vector2D(x: 3.0, y: 4.0)
let negative = -positive let negative = -positive
// negative 为 (-3.0, -4.0) // negative 是一个值为 (-3.0, -4.0) 的 Vector2D 实例
let alsoPositive = -negative let alsoPositive = -negative
// alsoPositive 为 (3.0, 4.0) // alsoPositive 是一个值为 (3.0, 4.0) 的 Vector2D 实例
``` ```
### 合赋值运算符 ### 合赋值运算符
合赋值是其他运算符和赋值运算符一起执行的运算。如`+=`把加运算和赋值运算组合成一个操作。实现一个组合赋值符号需要使用`@assignment`属性,还需要把运算符的左参数设置成`inout`,因为这个参数会在运算符函数内直接修改它的值 合赋值运算符(`Compound assignment operators`)将赋值运算符(`=`)与其它运算符进行结合。比如,将加法与赋值结合成加法赋值运算符(`+=`)。在实现的时候,需要把运算符的左参数设置成 `inout` 类型,因为这个参数的值会在运算符函数内直接修改。
```swift ```swift
@assignment func += (inout left: Vector2D, right: Vector2D) { func += (inout left: Vector2D, right: Vector2D) {
left = left + right left = left + right
} }
``` ```
因为加法运算在之前定义过了,这里无需重新定义。所以,加赋运算符函数使用已经存在的高级加法运算符函数来执行左值右值的运算。 因为加法运算在之前已经定义过了,所以在这里无需重新定义。在这里可以直接利用现有的加法运算符函数,用它来对左值右值进行相加,并再次赋值给左值:
```swift ```swift
var original = Vector2D(x: 1.0, y: 2.0) var original = Vector2D(x: 1.0, y: 2.0)
let vectorToAdd = Vector2D(x: 3.0, y: 4.0) let vectorToAdd = Vector2D(x: 3.0, y: 4.0)
original += vectorToAdd original += vectorToAdd
// original 现在为 (4.0, 6.0) // original 的值现在为 (4.0, 6.0)
``` ```
可以将 `@assignment` 属性和 `@prefix``@postfix` 属性起来组合,实现一个`Vector2D`的前置运算符。 可以将赋值与 `prefix``postfix` 限定符结合起来,下面的代码为 `Vector2D` 实例实现了前缀自增运算符(`++a`)
```swift ```swift
@prefix @assignment func ++ (inout vector: Vector2D) -> Vector2D { prefix func ++ (inout vector: Vector2D) -> Vector2D {
vector += Vector2D(x: 1.0, y: 1.0) vector += Vector2D(x: 1.0, y: 1.0)
return vector return vector
} }
``` ```
这个前置使用了已经定义好的高级加赋运算,将自己加上一个值为 `(1.01.0)` 的对象然后赋给自己,然后再将自己返回 这个前缀自增运算符使用了前面定义的加法赋值操作。它对 `Vector2D``x``y` 属性都进行了加 `1` 操作,再将结果返回
```swift ```swift
var toIncrement = Vector2D(x: 3.0, y: 4.0) var toIncrement = Vector2D(x: 3.0, y: 4.0)
let afterIncrement = ++toIncrement let afterIncrement = ++toIncrement
// toIncrement 现在 (4.0, 5.0) // toIncrement 的值现在 (4.0, 5.0)
// afterIncrement 现在也是 (4.0, 5.0) // afterIncrement 的值同样为 (4.0, 5.0)
``` ```
>注意: > 注意:
默认的赋值符(=)是不可重载。只有组合赋值符可以重载。三目条件运算符 `abc` 也是不可重载。 > 不能对默认的赋值运算符(`=`)进行重载。只有组合赋值符可以重载。同样地,也无法对三目条件运算符 `a ? b : c` 进行重载。
### 比较运算符 <a name="equivalence_operators"></a>
### 等价操作符
Swift无所知道自定义类型是否相等或不等,因为等于或者不等于由你的代码说了算了。所以自定义的类和结构要使用比较符`==``!=`就需要重载 自定义的类和结构体没有对等价操作符(`equivalence operators`)进行默认实现,等价操作符通常被称为“相等”操作符(`==`)与“不等”操作符(`!=`)。对于自定义类型Swift 无法判断其是否相等”,因为“相等”的含义取决于这些自定义类型在你的代码中所扮演的角色
定义相等运算符函数跟定义其他中置运算符雷同 为了使用等价操作符来对自定义的类型进行判等操作,需要为其提供自定义实现,实现的方法与其它中缀运算符一样
```swift ```swift
@infix func == (left: Vector2D, right: Vector2D) -> Bool { func == (left: Vector2D, right: Vector2D) -> Bool {
return (left.x == right.x) && (left.y == right.y) return (left.x == right.x) && (left.y == right.y)
} }
func != (left: Vector2D, right: Vector2D) -> Bool {
@infix func != (left: Vector2D, right: Vector2D) -> Bool {
return !(left == right) return !(left == right)
} }
``` ```
上述代码实现了相等运算符`==`来判断两个`Vector2D`对象是否有相等的值,相等的概念就是它们有相同的`x`值和相同的`y`值,我们就用这个逻辑来实现。接着使用`==`的结果实现了不相等运算符`!=` 上述代码实现了相等运算符(`==`)来判断两个 `Vector2D` 对象是否有相等。对于 `Vector2D` 类型来说,“相等”意味“两个实例的 `x`属性 和 `y` 属性都相等”,这也是代码中用来进行判等的逻辑。示例里同时也实现了“不等”操作符(`!=`),它简单地将“相等”操作符进行取反后返回
现在我们可以使用这两个运算符来判断两个`Vector2D`对象是否相等。 现在我们可以使用这两个运算符来判断两个 `Vector2D` 对象是否相等。
```swift ```swift
let twoThree = Vector2D(x: 2.0, y: 3.0) let twoThree = Vector2D(x: 2.0, y: 3.0)
let anotherTwoThree = Vector2D(x: 2.0, y: 3.0) let anotherTwoThree = Vector2D(x: 2.0, y: 3.0)
if twoThree == anotherTwoThree { if twoThree == anotherTwoThree {
println("这两个向量是相等的.") print("These two vectors are equivalent.")
} }
// prints "这两个向量是相等的." // prints "These two vectors are equivalent."
``` ```
<a name="custom_operators"></a>
### 自定义运算符 ### 自定义运算符
标准运算符不够玩,那你可以声明一些个性的运算符,但个性的运算符只能使用这些字符 `/ = - + * % < > ! & | ^ . ~` 除了实现标准运算符,在 Swift 当中还可以声明和实现自定义运算符(`custom operators`)。可以用来自定义运算符的字符列表请参考[操作符](../chapter3/02_Lexical_Structure.html#operators)
新的运算符声明需在全局域使用`operator`关键字声明,可以声明为前置,中置或后置的。 新的运算符要在全局作用域内,使用 `operator` 关键字进行声明,同时还要指定 `prefix``infix` 或者 `postfix` 限定符:
```swift ```swift
operator prefix +++ {} prefix operator +++ {}
``` ```
上面的代码定义了一个新的名为 `+++` 的前缀运算符。对于这个运算符,在 Swift 中并没有意义,因为我们针对 `Vector2D` 的实例来定义它的意义。对这个示例来讲,`+++` 被实现为“前缀双自增”运算符。它使用了前面定义的复合加法操作符来让矩阵对自身进行相加,从而让 `Vector2D` 实例的 `x` 属性和 `y`属性的值翻倍:
这段代码定义了一个新的前置运算符叫`+++`此前Swift并不存在这个运算符。此处为了演示我们让`+++``Vector2D`对象的操作定义为 `双自增` 这样一个独有的操作,这个操作使用了之前定义的加赋运算实现了自已加上自己然后返回的运算。
```swift ```swift
@prefix @assignment func +++ (inout vector: Vector2D) -> Vector2D { prefix func +++ (inout vector: Vector2D) -> Vector2D {
vector += vector vector += vector
return vector return vector
} }
``` ```
`Vector2D``+++` 的实现和 `++` 的实现很接近, 唯一不同的是前者是加自己, 后者是值为 `(1.0, 1.0)` 的向量. `Vector2D``+++` 的实现和 `++` 的实现很相似, 唯一不同的是前者对自身进行相加, 后者是与另一个值为 `(1.0, 1.0)` 的向量相加.
```swift ```swift
var toBeDoubled = Vector2D(x: 1.0, y: 4.0) var toBeDoubled = Vector2D(x: 1.0, y: 4.0)
let afterDoubling = +++toBeDoubled let afterDoubling = +++toBeDoubled
// toBeDoubled 现在 (2.0, 8.0) // toBeDoubled 现在的值为 (2.0, 8.0)
// afterDoubling 现在也是 (2.0, 8.0) // afterDoubling 现在的值也为 (2.0, 8.0)
``` ```
### 自定义中运算符的优先级和结合性 ### 自定义中运算符的优先级和结合性
可以为自定义的中置运算符指定优先级和结合性。可以回头看看[优先级和结合性](#PrecedenceandAssociativity)解释这两个因素是如何影响多种中置运算符混合的表达式的计算的。 自定义的中缀(`infix`)运算符也可以指定优先级(`precedence`)和结合性(`associativity`)。[优先级和结合性](#PrecedenceandAssociativity)中详细阐述了这两个特性是如何对中缀运算符的运算产生影响的。
结合性(associativity)的值可取的值有`left``right``none`。左结合运算符跟其他优先级相同的左结合运算符写在一起时,会跟左边的操作数结合。同理,右结合运算符会跟右边的操作数结合。而非结合运算符不能跟其他相同优先级的运算符写在一起。 结合性(`associativity`)可取的值有` left``right``none`左结合运算符跟其他相同优先级的左结合运算符写在一起时,会跟左边的操作数进行结合。同理,右结合运算符跟其他相同优先级的右结合运算符写在一起时,会跟右边的操作数进行结合。而非结合运算符不能跟其他相同优先级的运算符写在一起。
结合性(associativity)的默认`none`,优先级(precedence)默认为`100` 结合性(`associativity`)的默认值是 `none`,优先级(`precedence`)如果没有指定,则默认为 `100`
以下例子定义了一个新的中置符`+-`,是左结合的`left`,优先级为`140` 以下例子定义了一个新的中缀运算符 `+-`此操作符是左结合的,并且它的优先级为 `140`
```swift ```swift
operator infix +- { associativity left precedence 140 } infix operator +- { associativity left precedence 140 }
func +- (left: Vector2D, right: Vector2D) -> Vector2D { func +- (left: Vector2D, right: Vector2D) -> Vector2D {
return Vector2D(x: left.x + right.x, y: left.y - right.y) return Vector2D(x: left.x + right.x, y: left.y - right.y)
} }
let firstVector = Vector2D(x: 1.0, y: 2.0) let firstVector = Vector2D(x: 1.0, y: 2.0)
let secondVector = Vector2D(x: 3.0, y: 4.0) let secondVector = Vector2D(x: 3.0, y: 4.0)
let plusMinusVector = firstVector +- secondVector let plusMinusVector = firstVector +- secondVector
// plusMinusVector 此时的值为 (4.0, -2.0) // plusMinusVector 是一个 Vector2D 类型,并且它的值为 (4.0, -2.0)
``` ```
这个运算符把两个向量的`x`相加,向量的`y`相减。因为他实际是属于加减运算,所以让它保持了和加法一样的结合性和优先级(`left``140`)。查阅完整的Swift默认结合性优先级的设置,请移步[表达式](../chapter3/04_Expressions.html); 这个运算符把两个向量的 `x`相加,同时用第一个向量的 `y` 值减去第二个向量的 `y` 值。因为它本质上是属于“加型”运算,所以将它的结合性和优先级被设置为(`left``140`),这与 `+``-` 等默认的中缀加型操作符是相同的。完整的 Swift 操作符默认结合性优先级请参考[表达式](../chapter3/04_Expressions.html)
> 注意:
> 当定义前缀与后缀操作符的时候,我们并没有指定优先级。然而,如果对同一个操作数同时使用前缀与后缀操作符,则后缀操作符会先被执行。

View File

@ -1,10 +1,13 @@
> 翻译:[dabing1022](https://github.com/dabing1022) # 关于语言参考About the Language Reference
> 校对:[numbbbbb](https://github.com/numbbbbb), [KYawn](https://github.com/KYawn)
# 关于语言附注
----------------- -----------------
> 1.0
> 翻译:[dabing1022](https://github.com/dabing1022)
> 校对:[numbbbbb](https://github.com/numbbbbb)
> 2.0
> 翻译+校对:[KYawn](https://github.com/KYawn)
本页内容包括: 本页内容包括:
- [如何阅读语法](#how_to_read_the_grammar) - [如何阅读语法](#how_to_read_the_grammar)

View File

@ -1,8 +1,13 @@
> 翻译:[superkam](https://github.com/superkam) # 词法结构Lexical Structure
-----------------
> 1.0
> 翻译:[superkam](https://github.com/superkam)
> 校对:[numbbbbb](https://github.com/numbbbbb) > 校对:[numbbbbb](https://github.com/numbbbbb)
# 词法结构 > 2.0
----------------- > 翻译+校对:[buginux](https://github.com/buginux)
本页包含内容: 本页包含内容:
@ -202,14 +207,14 @@ true // 布尔型字面量
let x = 3; "1 2 \(x)" let 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)。 字符串字面量的默认推导类型为 `String`。组成字符串的字符默认推导类型为 `Character`。更多有关 `String``Character` 的信息请参照 [字符串和字符](../chapter2/03_Strings_and_Characters.html)。
> 字符型字面量语法 > 字符型字面量语法
> *字符串字面量* → **"** [*引用文本*](#quoted_text)<sub>可选</sub> **"** > *字符串字面量* → **"** [*引用文本*](#quoted_text)<sub>可选</sub> **"**
<a id="quoted_text"></a> <a id="quoted_text"></a>
> *引用文本* → [*引用文本条目*](#quoted_text_item) [*引用文本*](#quoted_text) <sub>可选</sub> > *引用文本* → [*引用文本条目*](#quoted_text_item) [*引用文本*](#quoted_text) <sub>可选</sub>
> *引用文本条目* → [*转义字符*](#escaped_character) > *引用文本条目* → [*转义字符*](#escaped_character)
> *引用文本条目* → **\(** [*表达式*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/doc/uid/TP40014097-CH32-ID383) **)** > *引用文本条目* → **\(** [*表达式*](./04_Expressions.html) **)**
> *引用文本条目* → **除了"­, \­, U+000A, 或者 U+000D的所有Unicode的字符** > *引用文本条目* → **除了"­, \­, U+000A, 或者 U+000D的所有Unicode的字符**
> *转义字符* → **\0** | **\\** | **\t** | **\n** | **\r** | **\"** | **\'** > *转义字符* → **\0** | **\\** | **\t** | **\n** | **\r** | **\"** | **\'**
> *转义字符* → **\u {** [*unicode标量数字*](#unicode_scalar_digits) **}** > *转义字符* → **\u {** [*unicode标量数字*](#unicode_scalar_digits) **}**
@ -218,7 +223,7 @@ let x = 3; "1 2 \(x)"
<a id="operators"></a> <a id="operators"></a>
## 运算符 ## 运算符
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) 中进行了阐述。这一小节将描述哪些字符能用于自定义运算符。 Swift 标准库定义了许多可供使用的运算符,其中大部分在 [基础运算符](../chapter2/02_Basic_Operators.html) 和 [高级运算符](../chapter2/25_Advanced_Operators.html) 中进行了阐述。这一小节将描述哪些字符能用于自定义运算符。
自定义运算符可以由以下其中之一的 ASCII 字符 `/``=``-``+``!``*``%``<``>``&``|``^``?` 以及 `~`, 或者后面语法中规定的任一个 Unicode 字符开始。在第一个字符之后,允许使用组合型 Unicode 字符。也可以使用两个或者多个的点号来自定义运算符(比如, `....`)。虽然可以自定义包含问号`?`的运算符,但是这个运算符不能只包含单独的一个问号。 自定义运算符可以由以下其中之一的 ASCII 字符 `/``=``-``+``!``*``%``<``>``&``|``^``?` 以及 `~`, 或者后面语法中规定的任一个 Unicode 字符开始。在第一个字符之后,允许使用组合型 Unicode 字符。也可以使用两个或者多个的点号来自定义运算符(比如, `....`)。虽然可以自定义包含问号`?`的运算符,但是这个运算符不能只包含单独的一个问号。
@ -238,7 +243,7 @@ Swift 标准库定义了许多可供使用的运算符,其中大部分在 [基
在某些特定的构造中 ,以 `<``>` 开头的运算符会被分离成两个或多个标记,剩余部分以同样的方式会被再次分离。因此,在 `Dictionary<String, Array<Int>>` 中没有必要添加空白来消除闭合字符 `>` 的歧义。在这个例子中, 闭合字符 `>` 不会被视为单独的标记,因而不会被误解析为 `>>` 运算符的一部分。 在某些特定的构造中 ,以 `<``>` 开头的运算符会被分离成两个或多个标记,剩余部分以同样的方式会被再次分离。因此,在 `Dictionary<String, Array<Int>>` 中没有必要添加空白来消除闭合字符 `>` 的歧义。在这个例子中, 闭合字符 `>` 不会被视为单独的标记,因而不会被误解析为 `>>` 运算符的一部分。
要学习如何自定义运算符,请参考 [自定义操作符](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-XID_48) 和 [运算符声明](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-XID_644)。要学习如何重载运算符,请参考 [运算符方法](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-XID_43)。 要学习如何自定义运算符,请参考 [自定义操作符](../chapter2/25_Advanced_Operators.html#custom_operators) 和 [运算符声明](./05_Declarations.html#operator_declaration)。要学习如何重载运算符,请参考 [运算符方法](../chapter2/25_Advanced_Operators.html#operator_functions)。
> 运算符语法语法 > 运算符语法语法
> *运算符* → [*头部运算符*](#operator_head) [*运算符字符组*](#operator_characters)<sub>可选</sub> > *运算符* → [*头部运算符*](#operator_head) [*运算符字符组*](#operator_characters)<sub>可选</sub>

163
source/chapter3/03_Types.md Executable file → Normal file
View File

@ -1,8 +1,12 @@
> 翻译:[lyuka](https://github.com/lyuka) # 类型Types
-----------------
> 1.0
> 翻译:[lyuka](https://github.com/lyuka)
> 校对:[numbbbbb](https://github.com/numbbbbb), [stanzhai](https://github.com/stanzhai) > 校对:[numbbbbb](https://github.com/numbbbbb), [stanzhai](https://github.com/stanzhai)
# 类型Types > 2.0
----------------- > 翻译+校对:[EudeMorgen](https://github.com/EudeMorgen)
本页包含内容: 本页包含内容:
@ -18,16 +22,16 @@
- [类型继承子句Type Inheritance Clause](#type_inheritance_clause) - [类型继承子句Type Inheritance Clause](#type_inheritance_clause)
- [类型推断Type Inference](#type_inference) - [类型推断Type Inference](#type_inference)
Swift 语言存在两种类型:命名型类型和复合型类型。*命名型类型*是指定义时可以给定名字的类型。命名型类型包括类、结构体、枚举和协议。比如,一个用户定义的类`MyClass`的实例拥有类型`MyClass`。除了用户定义的命名型类型Swift 标准库也定义了很多常用的命名型类型,包括那些表示数组、字典和可选值的类型。 Swift 语言存在两种类型命名型类型和复合型类型。命名型类型是指定义时可以给定名字的类型。命名型类型包括类、结构体、枚举和协议。比如一个用户定义的类MyClass的实例拥有类型MyClass。除了用户定义的命名型类型Swift 标准库也定义了很多常用的命名型类型,包括那些表示数组、字典和可选值的类型。
那些通常被其它语言认为是基本或初级的数据型类型Data types——比如表示数字、字符和字符串——实际上就是命名型类型Swift 标准库是使用结构体定义和实现它们的。因为它们是命名型类型,因此你可以按照“扩展和扩展声明”章节里讨论的那样,声明一个扩展来增加它们的行为以适应你程序的需求。 那些通常被其它语言认为是基本或初级的数据型类型Data types——比如表示数字、字符和字符串的类型——实际上就是命名型类型,这些类型在Swift 标准库是使用结构体定义和实现的。因为它们是命名型类型,因此你可以按照“扩展和扩展声明”章节里讨论的那样,声明一个扩展来增加它们的行为以迎合你程序的需求。
*复合型类型*是没有名字的类型,它由 Swift 本身定义。Swift 存在两种复合型类型:函数类型和元组类型。一个复合型类型可以包含命名型类型和其它复合型类型。例如,元组类型`(Int, (Int, Int))`包含两个元素:第一个是命名型类型`Int`,第二个是另一个复合型类型`(Int, Int)`. *复合型类型*是没有名字的类型,它由 Swift 本身定义。Swift 存在两种复合型类型:函数类型和元组类型。一个复合型类型可以包含命名型类型和其它复合型类型。例如,元组类型(Int, (Int, Int))包含两个元素第一个是命名型类型Int第二个是另一个复合型类型(Int, Int).
本节讨论 Swift 语言本身定义的类型,并描述 Swift 中的类型推断行为。 本节讨论 Swift 语言本身定义的类型,并描述 Swift 中的类型推断行为。
> 类型语法 > 类型语法
> *类型* → [*数组类型*](..\chapter3\03_Types.html#array_type) | [*函数类型*](..\chapter3\03_Types.html#function_type) | [*类型标识*](..\chapter3\03_Types.html#type_identifier) | [*元组类型*](..\chapter3\03_Types.html#tuple_type) | [*可选类型*](..\chapter3\03_Types.html#optional_type) | [*隐式解析可选类型*](..\chapter3\03_Types.html#implicitly_unwrapped_optional_type) | [*协议合成类型*](..\chapter3\03_Types.html#protocol_composition_type) | [*元型类型*](..\chapter3\03_Types.html#metatype_type) > *类型* → [*数组类型*](#array_type) | [*字典类型*](../chapter3/03_Types.html#dictionary_type) | [*函数类型*](../chapter3/03_Types.html#function_type) | [*类型标识*](../chapter3/03_Types.html#type_identifier) | [*元组类型*](../chapter3/03_Types.html#tuple_type) | [*可选类型*](../chapter3/03_Types.html#optional_type) | [*隐式解析可选类型*](../chapter3/03_Types.html#implicitly_unwrapped_optional_type) | [*协议合成类型*](../chapter3/03_Types.html#protocol_composition_type) | [*元型类型*](../chapter3/03_Types.html#metatype_type)
<a name="type_annotation"></a> <a name="type_annotation"></a>
##类型注解 ##类型注解
@ -43,16 +47,16 @@ func someFunction(a: Int){ /* ... */ }
类型注解可以在类型之前包含一个类型特性type attributes的可选列表。 类型注解可以在类型之前包含一个类型特性type attributes的可选列表。
> 类型注解语法 > 类型注解语法
> *类型注解* → **:** [*特性(Attributes)列表*](..\chapter3\06_Attributes.html#attributes) _可选_ [*类型*](..\chapter3\03_Types.html#type) > *类型注解* → **:** [*特性(Attributes)列表*](../chapter3/06_Attributes.html#attributes) _可选_ [*类型*](../chapter3/03_Types.html#type)
<a name="type_identifier"></a> <a name="type_identifier"></a>
##类型标识符 ##类型标识符
类型标识符引用命名型类型或者是命名型/复合型类型的别名。 类型标识符引用命名型类型或者是命名型/复合型类型的别名。
大多数情况下,类型标识符引用的是同名的命名型类型。例如类型标识符`Int`引用命名型类型`Int`,同样,类型标识符`Dictionary<String, Int>`引用命名型类型`Dictionary<String, Int>` 大多数情况下,类型标识符引用的是与之同名的命名型类型。例如类型标识符`Int`引用命名型类型`Int`,同样,类型标识符`Dictionary<String, Int>`引用命名型类型`Dictionary<String, Int>`
在两种情况下类型标识符引用的不是同名的类型。情况一,类型标识符引用的是命名型/复合型类型的类型别名。比如,在下面的例子中,类型标识符使用`Point`来引用元组`(Int, Int)` 在两种情况下类型标识符引用同名的类型。情况一,类型标识符引用的是命名型/复合型类型的类型别名。比如,在下面的例子中,类型标识符使用`Point`来引用元组`(Int, Int)`
```swift ```swift
typealias Point = (Int, Int) typealias Point = (Int, Int)
@ -66,7 +70,7 @@ var someValue: ExampleModule.MyType
``` ```
> 类型标识语法 > 类型标识语法
> *类型标识* → [*类型名称*](..\chapter3\03_Types.html#type_name) [*泛型参数子句*](GenericParametersAndArguments.html#generic_argument_clause) _可选_ | [*类型名称*](..\chapter3\03_Types.html#type_name) [*泛型参数子句*](GenericParametersAndArguments.html#generic_argument_clause) _可选_ **.** [*类型标识*](..\chapter3\03_Types.html#type_identifier) > *类型标识* → [*类型名称*](../chapter3/03_Types.html#type_name) [*泛型参数子句*](GenericParametersAndArguments.html#generic_argument_clause) _可选_ | [*类型名称*](../chapter3/03_Types.html#type_name) [*泛型参数子句*](GenericParametersAndArguments.html#generic_argument_clause) _可选_ **.** [*类型标识*](../chapter3/03_Types.html#type_identifier)
> *类名* → [*标识符*](LexicalStructure.html#identifier) > *类名* → [*标识符*](LexicalStructure.html#identifier)
<a name="tuple_type"></a> <a name="tuple_type"></a>
@ -74,15 +78,15 @@ var someValue: ExampleModule.MyType
元组类型使用逗号隔开并使用括号括起来的0个或多个类型组成的列表。 元组类型使用逗号隔开并使用括号括起来的0个或多个类型组成的列表。
你可以使用元组类型作为一个函数的返回类型,这样就可以使函数返回多个值。你也可以命名元组类型中的元素,然后用这些名字来引用每个元素的值。元素的名字由一个标识符`:`组成。“函数和多返回值”章节里有一个展示上述特性的例子。 你可以使用元组类型作为一个函数的返回类型,这样就可以使函数返回多个值。你也可以命名元组类型中的元素,然后用这些名字来引用每个元素的值。元素的名字由一个标识符紧跟一个冒号`(:)`组成。“函数和多返回值”章节里有一个展示上述特性的例子。
`void`是空元组类型`()`的别名。如果括号内只有一个元素,那么该类型就是括号内元素的类型。比如,`(Int)`的类型是`Int`而不是`(Int)`。所以,只有当元组类型包含两个元素以上时才可以标记元组元素。 `void`是空元组类型`()`的别名。如果括号内只有一个元素,那么该类型就是括号内元素的类型。比如,`(Int)`的类型是`Int`而不是`(Int)`。所以,只有当元组类型包含的元素个数在两个及以上时才可以命名元组元素。
> 元组类型语法 > 元组类型语法
> *元组类型* → **(** [*元组类型主体*](..\chapter3\03_Types.html#tuple_type_body) _可选_ **)** > *元组类型* → **(** [*元组类型主体*](../chapter3/03_Types.html#tuple_type_body) _可选_ **)**
> *元组类型主体* → [*元组类型的元素列表*](..\chapter3\03_Types.html#tuple_type_element_list) **...** _可选_ > *元组类型主体* → [*元组类型的元素列表*](../chapter3/03_Types.html#tuple_type_element_list) **...** _可选_
> *元组类型的元素列表* → [*元组类型的元素*](..\chapter3\03_Types.html#tuple_type_element) | [*元组类型的元素*](..\chapter3\03_Types.html#tuple_type_element) **,** [*元组类型的元素列表*](..\chapter3\03_Types.html#tuple_type_element_list) > *元组类型的元素列表* → [*元组类型的元素*](../chapter3/03_Types.html#tuple_type_element) | [*元组类型的元素*](../chapter3/03_Types.html#tuple_type_element) **,** [*元组类型的元素列表*](../chapter3/03_Types.html#tuple_type_element_list)
> *元组类型的元素* → [*特性(Attributes)列表*](..\chapter3\06_Attributes.html#attributes) _可选_ **inout** _可选_ [*类型*](..\chapter3\03_Types.html#type) | **inout** _可选_ [*元素名*](..\chapter3\03_Types.html#element_name) [*类型注解*](..\chapter3\03_Types.html#type_annotation) > *元组类型的元素* → [*特性(Attributes)列表*](../chapter3/06_Attributes.html#attributes) _可选_ **inout** _可选_ [*类型*](../chapter3/03_Types.html#type) | **inout** _可选_ [*元素名*](../chapter3/03_Types.html#element_name) [*类型注解*](../chapter3/03_Types.html#type_annotation)
> *元素名* → [*标识符*](LexicalStructure.html#identifier) > *元素名* → [*标识符*](LexicalStructure.html#identifier)
<a name="function_type"></a> <a name="function_type"></a>
@ -90,77 +94,78 @@ var someValue: ExampleModule.MyType
函数类型表示一个函数、方法或闭包的类型,它由一个参数类型和返回值类型组成,中间用箭头`->`隔开: 函数类型表示一个函数、方法或闭包的类型,它由一个参数类型和返回值类型组成,中间用箭头`->`隔开:
- `parameter type` -> `return type` `parameter type` -> `return type`
由于 *参数类型**返回值类型* 可以是元组类型,所以函数类型可以让函数与方法支持多参数与多返回值。 由于 *参数类型**返回值类型* 可以是元组类型,所以函数类型支持多参数与多返回值的函数与方法。
你可以对函数类型应用带有参数类型`()`并返回表达式类型的`auto_closure`属性(见类型属性章节)。一个自动闭包函数捕获特定表达式上的隐式闭包而非表达式本身。下面的例子使用`auto_closure`属性来定义一个很简单的assert函数 对于参数类型是空元组类型`()`以及返回值类型为表达式类型的函数类型,你可以对其参数声明使用`autoclosure`(见声明属性章节)。一个自动闭包函数捕获特定表达式上的隐式闭包而非表达式本身。下面的例子使用`autoclosure`属性来定义一个很简单的assert函数
```swift ```swift
func simpleAssert(condition: @auto_closure () -> Bool, message: String){ func simpleAssert(@autoclosure condition: Void -> Bool, _ message: String) {
if !condition(){ if !condition() {
println(message) print(message)
} }
} }
let testNumber = 5 let testNumber = 5
simpleAssert(testNumber % 2 == 0, "testNumber isn't an even number.") simpleAssert(testNumber % 2 == 0, "testNumber isn't an even number.")
// prints "testNumber isn't an even number." // prints "testNumber isn't an even number."
``` ```
函数类型可以拥有一个可变长参数作为参数类型中的最后一个参数。从语法角度上讲,可变长参数由一个基础类型名字`...`组成,如`Int...`。可变长参数被认为是一个包含了基础类型元素的数组。即`Int...`就是`Int[]`。关于使用可变长参数的例子,见章节“可变长参数” 函数类型可以拥有一个可变长参数作为参数类型中的最后一个参数。从语法角度上讲,可变长参数由一个基础类型名字紧随三个点`(...)`组成,如`Int...`。可变长参数被认为是一个包含了基础类型元素的数组。即`Int...`就是`[Int]`。关于使用可变长参数的例子,见章节Variadic Parameters
为了指定一个`in-out`参数,可以在参数类型前加`inout`前缀。但是你不可以对可变长参数或返回值类型使用`inout`。关于In-Out参数的讨论见章节In-Out参数部分。 为了指定一个`in-out`参数,可以在参数类型前加`inout`前缀。但是你不可以对可变长参数或返回值类型使用`inout`。关于In-Out参数的讨论见章节In-Out参数部分。
柯里化函数(curried function类型相当于一个嵌套函数类型。例如,下面的柯里化函数`addTwoNumber()()`的类型是`Int -> Int -> Int` 柯里化函数(Curried fuction函数类型从右向左递归地组成一组。例如,函数类型`Int -> Int -> Int`可以被理解为`Int -> (Int -> Int)`——也就是说,一个函数的参数为`Int`类型,其返回类型是一个参数类型为`Int`返回类型为`Int`的函数类型。关于柯里化函数的讨论见章节Curried Fuctions。
```swift 函数类型若要抛出错误就必须使用`throws`关键字来标记,若要重抛错误则必须使用`rethrows`关键字来标记。`throws`关键字是函数类型的一部分不抛出函数nonthrowing function是抛出函数throwing function函数的一个子类型。因此在使用抛出函数的地方也可以使用不抛出函数。对于柯里化函数`throws`关键字只应用于最里层的函数。抛出和重抛函数rethrowing function的相关描述见章节抛出函数与方法和重抛函数与方法。
func addTwoNumbers(a: Int)(b: Int) -> Int{
return a + b
}
addTwoNumbers(4)(5) // returns 9
```
柯里化函数的函数类型从右向左组成一组。例如,函数类型`Int -> Int -> Int`可以被理解为`Int -> (Int -> Int)`——也就是说,一个函数传入一个`Int`然后输出作为另一个函数的输入,然后又返回一个`Int`。例如,你可以使用如下嵌套函数来重写柯里化函数`addTwoNumbers()()`
```swift
func addTwoNumbers(a: Int) -> (Int -> Int){
func addTheSecondNumber(b: Int) -> Int{
return a + b
}
return addTheSecondNumber
}
addTwoNumbers(4)(5) // Returns 9
```
> 函数类型语法 > 函数类型语法
> *函数类型* → [*类型*](..\chapter3\03_Types.html#type) **->** [*类型*](..\chapter3\03_Types.html#type) > *函数类型* → [*类型*](../chapter3/03_Types.html#type) _抛出_ _可选_ **->** [*类型*](../chapter3/03_Types.html#type)
> *函数类型* → [*类型*](../chapter3/03_Types.html#type)_重抛_ **->** [*类型*](../chapter3/03_Types.html#type)
<a name="array_type"></a> <a name="array_type"></a>
##数组类型 ##数组类型
Swift语言使用类型名紧接中括号`[]`来简化标准库中定义的命名型类型`Array<T>`换句话说,下面两个声明是等价的: Swift语言使用[`type`]来简化标准库中定义`Array<T>`类型的操作
换句话说,下面两个声明是等价的:
```swift ```swift
let someArray: String[] = ["Alex", "Brian", "Dave"] let someArray: [String] = ["Alex", "Brian", "Dave"]
let someArray: Array<String> = ["Alex", "Brian", "Dave"] let someArray: Array<String> = ["Alex", "Brian", "Dave"]
``` ```
上面两种情况下,常量`someArray`都被声明为字符串数组。数组的元素也可以通过`[]`获取访问:`someArray[0]`是指第0个元素`“Alex”` 上面两种情况下,常量`someArray`都被声明为字符串数组。数组的元素也可以通过`[]`获取访问:`someArray[0]`是指第0个元素`“Alex”`
上面的例子同时显示,你可以使用`[]`作为初始值构造数组,空的`[]`则用来来构造指定类型的空数组。 你也可以嵌套多对方括号来创建多维数组,最里面的方括号中指明数组元素的基本类型。比如,下面例子中使用三对方括号创建三维整数数组。
```swift ```swift
var emptyArray: Double[] = [] var array3D: [[[Int]]] = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
```
你也可以使用链接起来的多个`[]`集合来构造多维数组。例如,下例使用三个`[]`集合来构造三维整型数组:
```swift
var array3D: Int[][][] = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
``` ```
访问一个多维数组的元素时,最左边的下标指向最外层数组的相应位置元素。接下来往右的下标指向第一层嵌入的相应位置元素,依次类推。这就意味着,在上面的例子中,`array3D[0]`是指`[[1, 2], [3, 4]]``array3D[0][1]`是指`[3, 4]``array3D[0][1][1]`则是指值`4` 访问一个多维数组的元素时,最左边的下标指向最外层数组的相应位置元素。接下来往右的下标指向第一层嵌入的相应位置元素,依次类推。这就意味着,在上面的例子中,`array3D[0]`是指`[[1, 2], [3, 4]]``array3D[0][1]`是指`[3, 4]``array3D[0][1][1]`则是指值`4`
关于Swift标准库中`Array`类型的细节讨论见章节Arrays。 关于Swift标准库中`Array`类型的细节讨论见章节Arrays。
> 数组类型语法 > 数组类型语法
> *数组类型* → [*类型*](..\chapter3\03_Types.html#type) **[** **]** | [*数组类型*](..\chapter3\03_Types.html#array_type) **[** **]** > *数组类型* → [*类型*](../chapter3/03_Types.html#type)
<a name="dictionary_type"></a>
##字典类型
Swift语言中使用[`key type: value type`]来简化标准库中定义`Dictionary<Key,Value>`类型的操作。
换句话说,下面两个声明是等价的:
```swift
let someDictionary: [String: Int] = ["Alex": 31, "Paul": 39]
let someDictionary: Dictionary<String, Int> = ["Alex": 31, "Paul": 39]
```
上面两种情况,常量`someDictionary`被声明为一个字典其中键为String类型值为Int类型。
字典中的值可以通过下标来访问,这个下标在方括号中指明了具体的键:`someDictionary["Alex"]`返回键`Alex`对应的值。如果键在字典中不存在的话,则这个下标返回`nil`
字典中键的类型必须遵循Swift标准库中的可哈希协议。
关于Swift标准库中`Dictionary`类型的更多细节可查看章节Dictionaries。
> 字典类型语法
> *字典类型* → **[**[*类型*](../chapter3/03_Types.html#type) **:** [*类型*](../chapter3/03_Types.html#type) **]**
<a name="optional_type"></a> <a name="optional_type"></a>
##可选类型 ##可选类型
@ -177,8 +182,6 @@ var optionalInteger: Optional<Int>
如果你在声明或定义可选变量或特性的时候没有提供初始值,它的值则会自动赋成缺省值`nil` 如果你在声明或定义可选变量或特性的时候没有提供初始值,它的值则会自动赋成缺省值`nil`
可选符合`LogicValue`协议,因此可以出现在布尔值环境下。此时,如果一个可选类型`T?`实例包含有类型为`T`的值(也就是说值为`Optional.Some(T)`),那么此可选类型就为`true`,否则为`false`
如果一个可选类型的实例包含一个值,那么你就可以使用后缀操作符`!`来获取该值,正如下面描述的: 如果一个可选类型的实例包含一个值,那么你就可以使用后缀操作符`!`来获取该值,正如下面描述的:
```swift ```swift
@ -189,10 +192,10 @@ optionalInteger! // 42
你也可以使用可选链和可选绑定来选择性的执行可选表达式上的操作。如果值为`nil`,不会执行任何操作因此也就没有运行错误产生。 你也可以使用可选链和可选绑定来选择性的执行可选表达式上的操作。如果值为`nil`,不会执行任何操作因此也就没有运行错误产生。
更多细节以及更多如何使用可选类型的例子,见章节“可选” 更多细节以及更多如何使用可选类型的例子,见章节Optionals
> 可选类型语法 > 可选类型语法
> *可选类型* → [*类型*](..\chapter3\03_Types.html#type) **?** > *可选类型* → [*类型*](../chapter3/03_Types.html#type) **?**
<a name="implicitly_unwrapped_optional_type"></a> <a name="implicitly_unwrapped_optional_type"></a>
##隐式解析可选类型 ##隐式解析可选类型
@ -205,7 +208,7 @@ var implicitlyUnwrappedString: ImplicitlyUnwrappedOptional<String>
``` ```
上述两种情况下,变量`implicitlyUnwrappedString`被声明为一个隐式解析可选类型的字符串。注意类型与`!`之间没有空格。 上述两种情况下,变量`implicitlyUnwrappedString`被声明为一个隐式解析可选类型的字符串。注意类型与`!`之间没有空格。
你可以在使用可选的地方同样使用隐式解析可选。比如,你可以将隐式解析可选的值赋给变量、常量和可选特性,反之亦然。 你可以在使用可选类型的地方同样使用隐式解析可选类型。比如,你可以将隐式解析可选类型的值赋给变量、常量和可选特性,反之亦然。
有了可选,你在声明隐式解析可选变量或特性的时候就不用指定初始值,因为它有缺省值`nil` 有了可选,你在声明隐式解析可选变量或特性的时候就不用指定初始值,因为它有缺省值`nil`
@ -213,15 +216,15 @@ var implicitlyUnwrappedString: ImplicitlyUnwrappedOptional<String>
使用可选链会选择性的执行隐式解析可选表达式上的某一个操作。如果值为`nil`,就不会执行任何操作,因此也不会产生运行错误。 使用可选链会选择性的执行隐式解析可选表达式上的某一个操作。如果值为`nil`,就不会执行任何操作,因此也不会产生运行错误。
关于隐式解析可选的更多细节,见章节“隐式解析可选” 关于隐式解析可选的更多细节,见章节Implicitly Unwrapped Optionals
> 隐式解析可选类型(Implicitly Unwrapped Optional Type)语法 > 隐式解析可选类型(Implicitly Unwrapped Optional Type)语法
> *隐式解析可选类型* → [*类型*](..\chapter3\03_Types.html#type) **!** > *隐式解析可选类型* → [*类型*](../chapter3/03_Types.html#type) **!**
<a name="protocol_composition_type"></a> <a name="protocol_composition_type"></a>
##协议合成类型 ##协议合成类型
协议合成类型是一种符合每个协议的指定协议列表类型。协议合成类型可能会用在类型注解和泛型参数中。 协议合成类型是一种遵循具体协议列表中每个协议的类型。协议合成类型可能会用在类型注解和泛型参数中。
协议合成类型的形式如下: 协议合成类型的形式如下:
@ -229,23 +232,23 @@ var implicitlyUnwrappedString: ImplicitlyUnwrappedOptional<String>
protocol<Protocol 1, Procotol 2> protocol<Protocol 1, Procotol 2>
``` ```
协议合成类型允许你指定一个值,其类型可以适配多个协议的条件,而且不需要定义一个新的命名型协议来继承其它想要适配的各个协议。比如,协议合成类型`protocol<Protocol A, Protocol B, Protocol C>`等效于一个从`Protocol A``Protocol B` `Protocol C`继承而来的新协议`Protocol D`,很显然这样做有效率的多,甚至不需引入一个新名字。 协议合成类型允许你指定一个值,其类型遵循多个协议的条件且不需要定义一个新的命名型协议来继承其它想要遵循的各个协议。比如,协议合成类型`protocol<Protocol A, Protocol B, Protocol C>`等效于一个从`Protocol A``Protocol B` `Protocol C`继承而来的新协议`Protocol D`,很显然这样做有效率的多,甚至不需引入一个新名字。
协议合成列表中的每项必须是协议名或协议合成类型的类型别名。如果列表为空,它就会指定一个空协议合成列表,这样每个类型都能适配 协议合成列表中的每项必须是协议名或协议合成类型的类型别名。如果列表为空,它就会指定一个空协议合成列表,这样每个类型都能遵循
> 协议合成类型语法 > 协议合成类型语法
> *协议合成类型* → **protocol** **<** [*协议标识符列表*](..\chapter3\03_Types.html#protocol_identifier_list) _可选_ **>** > *协议合成类型* → **protocol** **<** [*协议标识符列表*](../chapter3/03_Types.html#protocol_identifier_list) _可选_ **>**
> *协议标识符列表* → [*协议标识符*](..\chapter3\03_Types.html#protocol_identifier) | [*协议标识符*](..\chapter3\03_Types.html#protocol_identifier) **,** [*协议标识符列表*](..\chapter3\03_Types.html#protocol_identifier_list) > *协议标识符列表* → [*协议标识符*](../chapter3/03_Types.html#protocol_identifier) | [*协议标识符*](../chapter3/03_Types.html#protocol_identifier) **,** [*协议标识符列表*](../chapter3/03_Types.html#protocol_identifier_list)
> *协议标识符* → [*类型标识*](..\chapter3\03_Types.html#type_identifier) > *协议标识符* → [*类型标识*](../chapter3/03_Types.html#type_identifier)
<a name="metatype_type"></a> <a name="metatype_type"></a>
##元类型 ##元类型
元类型是指所有类型的类型,包括类、结构体、枚举和协议。 元类型是指所有类型的类型,包括类、结构体、枚举和协议。
类、结构体或枚举类型的元类型是相应的类型名紧跟`.Type`。协议类型的元类型——并不是运行时适配该协议的具体类型——是该协议名字紧跟`.Protocol`。比如,类`SomeClass`的元类型就是`SomeClass.Type`,协议`SomeProtocol`的元类型就是`SomeProtocal.Protocol` 类、结构体或枚举类型的元类型是相应的类型名紧跟`.Type`。协议类型的元类型——并不是运行时遵循该协议的具体类型——是该协议名字紧跟`.Protocol`。比如,类`SomeClass`的元类型就是`SomeClass.Type`,协议`SomeProtocol`的元类型就是`SomeProtocal.Protocol`
你可以使用后缀`self`表达式来获取类型。比如,`SomeClass.self`返回`SomeClass`本身,而不是`SomeClass`的一个实例。同样,`SomeProtocol.self`返回`SomeProtocol`本身,而不是运行时适配`SomeProtocol`的某个类型的实例。还可以对类型的实例使用`dynamicType`表达式来获取该实例在运行阶段的类型,如下所示: 你可以使用后缀`self`表达式来获取类型。比如,`SomeClass.self`返回`SomeClass`本身,而不是`SomeClass`的一个实例。同样,`SomeProtocol.self`返回`SomeProtocol`本身,而不是运行时遵循`SomeProtocol`的某个类型的实例。还可以对类型的实例使用`dynamicType`表达式来获取该实例在运行阶段的类型,如下所示:
```swift ```swift
class SomeBaseClass { class SomeBaseClass {
@ -264,29 +267,35 @@ let someInstance: SomeBaseClass = SomeSubClass()
someInstance.dynamicType.printClassName() someInstance.dynamicType.printClassName()
// prints "SomeSubClass // prints "SomeSubClass
``` ```
> 注意
> **不能创建元类型类的实例,因为不能保证其子类会提供初始化的代码。**
> 元(Metatype)类型语法 > 元(Metatype)类型语法
> *元类型* → [*类型*](..\chapter3\03_Types.html#type) **.** **Type** | [*类型*](..\chapter3\03_Types.html#type) **.** **Protocol** x > *元类型* → [*类型*](../chapter3/03_Types.html#type) **.** **Type** | [*类型*](../chapter3/03_Types.html#type) **.** **Protocol**
<a name="type_inheritance_clause"></a> <a name="type_inheritance_clause"></a>
##类型继承子句 ##类型继承子句
类型继承子句被用来指定一个命名型类型继承哪个类且适配哪些协议。类型继承子句开始于冒号`:`,紧跟由`,`隔开的类型标识符列表。 类型继承子句被用来指定一个命名型类型继承哪个类、遵循的哪些协议。类型继承子句也用来指定一个类需要遵循的协议。类型继承子句开始于冒号`:`,其后是类所需遵循的协议或者类型标识符列表或者两者均有
类可以继承单个超类,适配任意数量的协议。当定义一个类时,超类的名字必须出现在类型标识符列表首位,然后跟上该类需要适配的任意数量的协议。如果一个类不是从其它类继承而来,那么列表可以以协议开头。关于类继承更多的讨论和例子,见章节“继承” 类可以继承单个超类,遵循任意数量的协议。当定义一个类时,超类的名字必须出现在类型标识符列表首位,然后跟上该类需要遵循的任意数量的协议。如果一个类不是从其它类继承而来,那么列表可以以协议开头。关于类继承更多的讨论和例子,见章节Inheritance
其它命名型类型可能只继承或适配一个协议列表。协议类型可能继承于其它任意数量的协议。当一个协议类型继承于其它协议时,其它协议的条件集合会被集成在一起,然后其它从当前协议继承的任意类型必须适配所有这些条件。 其它命名型类型可能只继承或遵循一个协议列表。协议类型可能继承于其它任意数量的协议。当一个协议类型继承于其它协议时,其它协议的条件集合会被整合在一起,然后其它从当前协议继承的任意类型必须遵循所有这些条件。正如在协议声明中所讨论的那样,可以把类的关键字放到类型继承子句中的首位,这样就可以用一个类的条件来标记一个协议声明。
枚举定义中的类型继承子句可以是一个协议列表,或是指定原始值的枚举一个单独的指定原始值类型的命名型类型。使用类型继承子句来指定原始值类型的枚举定义的例子,见章节“原始值” 枚举定义中的类型继承子句可以是一个协议列表,或是指定原始值的枚举——一个单独的指定原始值类型的命名型类型。使用类型继承子句来指定原始值类型的枚举定义的例子,见章节Raw Values
> 类型继承子句语法 > 类型继承子句语法
> *类型继承子句* → **:** [*类型继承列表*](..\chapter3\03_Types.html#type_inheritance_list) > *类型继承子句* → **:** [*类需求*](../chapter3/03_Types.html#class_requirement) **,** [*类型继承列表*](../chapter3/03_Types.html#type_inheritance_list)
> *类型继承列表* → [*类型标识*](..\chapter3\03_Types.html#type_identifier) | [*类型标识*](..\chapter3\03_Types.html#type_identifier) **,** [*类型继承列表*](..\chapter3\03_Types.html#type_inheritance_list) > *类型继承子句* → **:** [*类需求*](../chapter3/03_Types.html#class_requirement)
> *类型继承子句* → **:** [*类型继承列表*](../chapter3/03_Types.html#type_inheritance_list)
> *类型继承列表* → [*类型标识*](../chapter3/03_Types.html#type_identifier) | [*类型标识*](../chapter3/03_Types.html#type_identifier) **,** [*类型继承列表*](../chapter3/03_Types.html#type_inheritance_list)
> *类需求* → **类**
<a name="type_inference"></a> <a name="type_inference"></a>
##类型推断 ##类型推断
Swift广泛的使用类型推断从而允许你可以忽略很多变量和表达式的类型或部分类型。比如对于`var x: Int = 0`,你可以完全忽略类型而简写成`var x = 0`——编译器会正确的推断出`x`的类型`Int`。类似的,当完整的类型可以从上下文推断出来时,你也可以忽略类型的一部分。比如,如果你写了`let dict: Dictionary = ["A": 1]`,编译提也能推断出`dict`的类型是`Dictionary<String, Int>` Swift广泛的使用类型推断从而允许你可以忽略代码中很多变量和表达式的类型或部分类型。比如,对于`var x: Int = 0`,你可以完全忽略类型而简写成`var x = 0`——编译器会正确的推断出`x`的类型`Int`。类似的,当完整的类型可以从上下文推断出来时,你也可以忽略类型的一部分。比如,如果你写了`let dict: Dictionary = ["A": 1]`,编译提也能推断出`dict`的类型是`Dictionary<String, Int>`
在上面的两个例子中类型信息从表达式树expression tree的叶子节点传向根节点。也就是说`var x: Int = 0``x`的类型首先根据`0`的类型进行推断,然后将该类型信息传递到根节点(变量`x`)。 在上面的两个例子中类型信息从表达式树expression tree的叶子节点传向根节点。也就是说`var x: Int = 0``x`的类型首先根据`0`的类型进行推断,然后将该类型信息传递到根节点(变量`x`)。

434
source/chapter3/04_Expressions.md Executable file → Normal file
View File

@ -1,8 +1,12 @@
> 翻译:[sg552](https://github.com/sg552) # 表达式Expressions
-----------------
> 1.0
> 翻译:[sg552](https://github.com/sg552)
> 校对:[numbbbbb](https://github.com/numbbbbb), [stanzhai](https://github.com/stanzhai) > 校对:[numbbbbb](https://github.com/numbbbbb), [stanzhai](https://github.com/stanzhai)
# 表达式Expressions > 2.0
----------------- > 翻译+校对:[EudeMorgen](https://github.com/EudeMorgen)
本页包含内容: 本页包含内容:
@ -16,35 +20,36 @@
Swift 中存在四种表达式: 前缀prefix表达式二元binary表达式主要primary表达式和后缀postfix表达式。表达式可以返回一个值以及运行某些逻辑causes a side effect Swift 中存在四种表达式: 前缀prefix表达式二元binary表达式主要primary表达式和后缀postfix表达式。表达式可以返回一个值以及运行某些逻辑causes a side effect
前缀表达式和二元表达式就是对某些表达式使用各种运算符operators。 主要表达式是最短小的表达式,它提供了获取(变量的)值的一种途径。 后缀表达式则允许你建立复杂的表达式,例如配合函数调用和成员访问。 每种表达式都在下面有详细论述 前缀表达式和二元表达式就是对某些表达式使用各种运算符operators。 主要表达式是最短小的表达式,它提供了获取(变量的)值的一种途径。 后缀表达式则允许你建立复杂的表达式,例如配合函数调用和成员访问。 每种表达式都在下面有详细论述
> 表达式语法 > 表达式语法
> *表达式* → [*前置表达式*](..\chapter3\04_Expressions.html#prefix_expression) [*二元表达式列表*](..\chapter3\04_Expressions.html#binary_expressions) _可选_ > *表达式* → [*试算子(try operator)*](../chapter3/04_Expressions.html#*) _可选_ | [*前置表达式*](../chapter3/04_Expressions.html#prefix_expression) | [*二元表达式列表*](../chapter3/04_Expressions.html#binary_expressions) _可选_
> *表达式列表* → [*表达式*](..\chapter3\04_Expressions.html#expression) | [*表达式*](..\chapter3\04_Expressions.html#expression) **,** [*表达式列表*](..\chapter3\04_Expressions.html#expression_list) > *表达式列表* → [*表达式*](../chapter3/04_Expressions.html#expression) | [*表达式*](../chapter3/04_Expressions.html#expression) **,** [*表达式列表*](../chapter3/04_Expressions.html#expression_list)
<a name="prefix_expressions"></a> <a name="prefix_expressions"></a>
## 前缀表达式Prefix Expressions ## 前缀表达式Prefix Expressions
前缀表达式由 前缀符号和表达式组成。(这个前缀符号只能接收一个参数) 前缀表达式由可选的前缀符号和表达式组成。(这个前缀符号只能接收一个参数)
Swift 标准库支持如下的前缀操作符:
- ++ 自增1 increment
- -- 自减1 decrement
- ! 逻辑否 Logical NOT
- ~ 按位否 Bitwise NOT
- \+ 加Unary plus
- \- 减Unary minus
对于这些操作符的使用,请参见: Basic Operators and Advanced Operators 对于这些操作符的使用,请参见: Basic Operators and Advanced Operators
作为对上面标准库运算符的补充,你也可以对 某个函数的参数使用 '&'运算符。 更多信息,请参见: "In-Out parameters". 作为对上面标准库运算符的补充,你也可以对 某个函数的参数使用 '&'运算符。 更多信息,请参见: "In-Out parameters".
> 前置表达式语法 > 前置表达式语法
> *前置表达式* → [*前置运算符*](LexicalStructure.html#prefix_operator) _可选_ [*后置表达式*](..\chapter3\04_Expressions.html#postfix_expression) > *前置表达式* → [*前置运算符*](LexicalStructure.html#prefix_operator) _可选_ [*后置表达式*](../chapter3/04_Expressions.html#postfix_expression)
> *前置表达式* → [*写入写出(in-out)表达式*](..\chapter3\04_Expressions.html#in_out_expression) > *前置表达式* → [*写入写出(in-out)表达式*](../chapter3/04_Expressions.html#in_out_expression)
> *写入写出(in-out)表达式* → **&** [*标识符*](LexicalStructure.html#identifier) > *写入写出(in-out)表达式* → **&** [*标识符*](LexicalStructure.html#identifier)
<a name="try_operator"></a>
## try 操作符try operator
try表达式由紧跟在可能会出错的表达式后面的`try`操作符组成,形式如下:
`try expression`
强制的try表示由紧跟在可能会出错的表达式后面的`try!`操作符组成,出错时会产生一个运行时错误,形式如下:
`try! expression`
关于`try`更多的例子和信息请参见Catching and Handling Errors.
> try表达式语法
> *try 操作符* → [*try*](LexicalStructure.html#try_operator) | *try!*
<a name="binary_expressions"></a> <a name="binary_expressions"></a>
## 二元表达式Binary Expressions ## 二元表达式Binary Expressions
@ -52,76 +57,18 @@ Swift 标准库支持如下的前缀操作符:
> `left-hand argument` `operator` `right-hand argument` > `left-hand argument` `operator` `right-hand argument`
Swift 标准库提供了如下的二元运算符:
- 求幂相关无结合优先级160
- << 按位左移Bitwise left shift
- >> 按位右移Bitwise right shift
- 乘除法相关左结合优先级150
- \* 乘
- / 除
- % 求余
- &* 乘法,忽略溢出( Multiply, ignoring overflow
- &/ 除法忽略溢出Divide, ignoring overflow
- &% 求余, 忽略溢出( Remainder, ignoring overflow
- & 位与( Bitwise AND
- 加减法相关(左结合, 优先级140
- \+ 加
- \- 减
- &+ Add with overflow
- &- Subtract with overflow
- | 按位或Bitwise OR
- ^ 按位异或Bitwise XOR
- Range (无结合,优先级 135
- ..< 半闭值域 Half-closed range
- ... 全闭值域 Closed range
- 类型转换 无结合,优先级 132
- is 类型检查 type check
- as 类型转换 type cast
- Comparative 无结合,优先级 130
- < 小于
- <= 小于等于
- &gt; 大于
- &gt;= 大于等于
- == 等于
- != 不等
- === 恒等于
- !== 不恒等
- ~= 模式匹配 Pattern match
- 合取 Conjunctive 左结合,优先级 120
- && 逻辑与Logical AND
- 析取Disjunctive 左结合,优先级 110
- || 逻辑或 Logical OR
- 三元条件Ternary Conditional 右结合,优先级 100
- ?: 三元条件 Ternary conditional
- 赋值 Assignment 右结合, 优先级 90
- = 赋值Assign
- *= Multiply and assign
- /= Divide and assign
- %= Remainder and assign
- += Add and assign
- -= Subtract and assign
- <<= Left bit shift and assign
- &gt;&gt;= Right bit shift and assign
- &= Bitwise AND and assign
- ^= Bitwise XOR and assign
- |= Bitwise OR and assign
- &&= Logical AND and assign
- ||= Logical OR and assign
关于这些运算符operators的更多信息请参见Basic Operators and Advanced Operators. 关于这些运算符operators的更多信息请参见Basic Operators and Advanced Operators.
> 注意 > 注意
> 在解析时, 一个二元表达式表示为一个一级数组a flat list, 这个数组List根据运算符的先后顺序被转换成了一个tree. 例如: 2 + 3 * 5 首先被认为是: 2, + , `` 3``, *, 5. 随后它被转换成 tree 2 + 3 * 5 > 在解析时, 一个二元表达式表示为一个一级数组a flat list, 这个数组List根据运算符的先后顺序被转换成了一个tree. 例如: 2 + 3 * 5 首先被认为是: 2, + , `` 3``, *, 5. 随后它被转换成 tree 2 + 3 * 5
<p></p>
> 二元表达式语法 > 二元表达式语法
> *二元表达式* → [*二元运算符*](LexicalStructure.html#binary_operator) [*前置表达式*](..\chapter3\04_Expressions.html#prefix_expression) > *二元表达式* → [*二元运算符*](LexicalStructure.html#binary_operator) [*前置表达式*](../chapter3/04_Expressions.html#prefix_expression)
> *二元表达式* → [*赋值运算符*](..\chapter3\04_Expressions.html#assignment_operator) [*前置表达式*](..\chapter3\04_Expressions.html#prefix_expression) > *二元表达式* → [*赋值运算符*](../chapter3/04_Expressions.html#assignment_operator) [*前置表达式*](../chapter3/04_Expressions.html#prefix_expression)
> *二元表达式* → [*条件运算符*](..\chapter3\04_Expressions.html#conditional_operator) [*前置表达式*](..\chapter3\04_Expressions.html#prefix_expression) > *二元表达式* → [*条件运算符*](../chapter3/04_Expressions.html#conditional_operator) [*前置表达式*](../chapter3/04_Expressions.html#prefix_expression)
> *二元表达式* → [*类型转换运算符*](..\chapter3\04_Expressions.html#type_casting_operator) > *二元表达式* → [*类型转换运算符*](../chapter3/04_Expressions.html#type_casting_operator)
> *二元表达式列表* → [*二元表达式*](..\chapter3\04_Expressions.html#binary_expression) [*二元表达式列表*](..\chapter3\04_Expressions.html#binary_expressions) _可选_ > *二元表达式列表* → [*二元表达式*](../chapter3/04_Expressions.html#binary_expression) [*二元表达式列表*](../chapter3/04_Expressions.html#binary_expressions) _可选_
> *赋值操作符*
<a name="assignment_operator"></a> <a name="assignment_operator"></a>
@ -155,54 +102,48 @@ Swift 标准库提供了如下的二元运算符:
想看三元条件运算符的例子,请参见: Ternary Conditional Operator. 想看三元条件运算符的例子,请参见: Ternary Conditional Operator.
> 三元条件运算符语法 > 三元条件运算符语法
> *三元条件运算符* → **?** [*表达式*](..\chapter3\04_Expressions.html#expression) **:** > *三元条件运算符* → **?** [*表达式*](../chapter3/04_Expressions.html#expression) **:**
<a name="type-casting_operators"></a> <a name="type-casting_operators"></a>
## 类型转换运算符Type-Casting Operators ## 类型转换运算符Type-Casting Operators
种类型转换操作符: as 和 is. 它们有如下的形式: 4种类型转换运算符: `is`,`as`,`? ``!`. 它们有如下的形式:
> `expression` as `type`
> `expression` as? `type`
> `expression` is `type` > `expression` is `type`
> `expression` as `type`
> `expression` is? `type`
> `expression` as! `type`
as 运算符会把`目标表达式`转换成指定的`类型`specified type过程如下 `is`运算符在程序运行时检查表达式能否向下转化为指定的类型,如果可以在返回`ture`,如果不行,则返回`false`
- 如果类型转换成功, 那么目标表达式就会返回指定类型的实例instance. 例如把子类subclass变成父类superclass时.
- 如果转换失败,则会抛出编译错误( compile-time error
- 如果上述两个情况都不是(也就是说,编译器在编译时期无法确定转换能否成功,) 那么目标表达式就会变成指定的类型的optional. is an optional of the specified type 然后在运行时,如果转换成功, 目标表达式就会作为 optional的一部分来返回 否则目标表达式返回nil. 对应的例子是: 把一个 superclass 转换成一个 subclass.
`as`运算符在程序编译时执行类型转化且总是成功比如进行向上转换upcast和桥接bridging。向上转换指把表达式转换成类型的超类的一个是实例而不使用中间的变量。以下表达式是等价的
```swift ```swift
class SomeSuperType {} func f(any: Any) { print("Function for Any") }
class SomeType: SomeSuperType {} func f(int: Int) { print("Function for Int") }
class SomeChildType: SomeType {} let x = 10
let s = SomeType() f(x)
// prints "Function for Int"
let x = s as SomeSuperType // known to succeed; type is SomeSuperType
let y = s as Int // known to fail; compile-time error let y: Any = x
let z = s as SomeChildType // might fail at runtime; type is SomeChildType? f(y)
// prints "Function for Any"
f(x as Any)
// prints "Function for Any"
``` ```
桥接运算可以让你把一个Swift标准库中的类型的表达式作为一个与之相关的基础类比如NSString来使用而不需要新建一个实例。关于桥接的更多实例参见Using Swift with Cocoa and Objective-C中的Cocoa Data Types。
使用'as'做类型转换跟正常的类型声明,对于编译器来说是一样的。例如: `as?`操作符为带条件的类型转换。`as?`操作符返回可选的转换类型。在运行时,如果转换成功,表达式的值会被覆盖掉再返回,如果转换不成功的话,则返回`nil`。如果条件转换中的条件的真值一开始就已经确定真伪了,则在编译时会报错。
```swift `a!`操作符表示强制转换,其返回指定的类型,而不是可选的类型。如果转换失败,则会出现运行时错误。表达式`x as T` 效果等同于`(x as? T)!`
let y1 = x as SomeType // Type information from 'as'
let y2: SomeType = x // Type information from an annotation
```
'is' 运算符在“运行时runtime”会做检查。 成功会返回true, 否则 false
上述检查在“编译时compile time”不能使用。 例如下面的使用是错误的:
```swift
"hello" is String
"hello" is Int
```
关于类型转换的更多内容和例子,请参见: Type Casting. 关于类型转换的更多内容和例子,请参见: Type Casting.
> 类型转换运算符(type-casting-operator)语法 > 类型转换运算符(type-casting-operator)语法
> *类型转换运算符* → **is** [*类型*](..\chapter3\03_Types.html#type) | **as** **?** _可选_ [*类型*](..\chapter3\03_Types.html#type) > *类型转换运算符* → **is** [*类型*](../chapter3/03_Types.html#type)
> *类型转换运算符* → **as** [*类型*](../chapter3/03_Types.html#type)
> *类型转换运算符* → **is** **?** [*类型*](../chapter3/03_Types.html#type)
> *类型转换运算符* → **as** **!** [*类型*](../chapter3/03_Types.html#type)
<a name="primary_expressions"></a> <a name="primary_expressions"></a>
## 主表达式Primary Expressions ## 主表达式Primary Expressions
@ -211,13 +152,13 @@ let y2: SomeType = x // Type information from an annotation
> 主表达式语法 > 主表达式语法
> *主表达式* → [*标识符*](LexicalStructure.html#identifier) [*泛型参数子句*](GenericParametersAndArguments.html#generic_argument_clause) _可选_ > *主表达式* → [*标识符*](LexicalStructure.html#identifier) [*泛型参数子句*](GenericParametersAndArguments.html#generic_argument_clause) _可选_
> *主表达式* → [*字面量表达式*](..\chapter3\04_Expressions.html#literal_expression) > *主表达式* → [*字符型表达式*](../chapter3/04_Expressions.html#literal_expression)
> *主表达式* → [*self表达式*](..\chapter3\04_Expressions.html#self_expression) > *主表达式* → [*self表达式*](../chapter3/04_Expressions.html#self_expression)
> *主表达式* → [*超类表达式*](..\chapter3\04_Expressions.html#superclass_expression) > *主表达式* → [*超类表达式*](../chapter3/04_Expressions.html#superclass_expression)
> *主表达式* → [*闭包表达式*](..\chapter3\04_Expressions.html#closure_expression) > *主表达式* → [*闭包表达式*](../chapter3/04_Expressions.html#closure_expression)
> *主表达式* → [*圆括号表达式*](..\chapter3\04_Expressions.html#parenthesized_expression) > *主表达式* → [*圆括号表达式*](../chapter3/04_Expressions.html#parenthesized_expression)
> *主表达式* → [*隐式成员表达式*](..\chapter3\04_Expressions.html#implicit_member_expression) > *主表达式* → [*隐式成员表达式*](../chapter3/04_Expressions.html#implicit_member_expression)
> *主表达式* → [*通配符表达式*](..\chapter3\04_Expressions.html#wildcard_expression) > *主表达式* → [*通配符表达式*](../chapter3/04_Expressions.html#wildcard_expression)
### 字符型表达式Literal Expression ### 字符型表达式Literal Expression
@ -225,35 +166,53 @@ let y2: SomeType = x // Type information from an annotation
字符Literal | 类型Type | 值Value 字符Literal | 类型Type | 值Value
------------- | ---------- | ---------- ------------- | ---------- | ----------
\__FILE__ | String | 所在的文件名 /__FILE__ | String | 所在的文件名
\__LINE__ | Int | 所在的行数 /__LINE__ | Int | 所在的行数
\__COLUMN__ | Int | 所在的列数 /__COLUMN__ | Int | 所在的列数
\__FUNCTION__ | String | 所在的function 的名字 /__FUNCTION__ | String | 所在的function 的名字
在某个函数function`__FUNCTION__` 会返回当前函数的名字。 在某个方法method它会返回当前方法的名字。 在某个property 的getter/setter中会返回这个属性的名字。 在特殊的成员如init/subscript中 会返回这个关键字的名字在某个文件的顶端the top level of a file它返回的是当前module的名字。 在某个函数function`__FUNCTION__` 会返回当前函数的名字。 在某个方法method它会返回当前方法的名字。 在某个property 的getter/setter中会返回这个属性的名字。 在特殊的成员如init/subscript中 会返回这个关键字的名字在某个文件的顶端the top level of a file它返回的是当前module的名字。
一个array literal是一个有序的值的集合。 它的形式是: 当作为函数或者方法时,字符型表达式的值在被调用时初始化。
```swift
func logFunctionName(string: String = __FUNCTION__) {
print(string)
}
func myFunction() {
logFunctionName() // Prints "myFunction()".
}
myFunction()
namedArgs(1, withJay: 2)
```
一个`array literal`,是一个有序的值的集合。 它的形式是:
> [`value 1`, `value 2`, `...`] > [`value 1`, `value 2`, `...`]
数组中的最后一个表达式可以紧跟一个逗号(','. []表示空数组 。 array literal的type是 T[], 这个T就是数组中元素的type. 如果该数组中有多种type, T则是跟这些type的公共supertype最接近的type.closest common supertype 数组中的最后一个表达式可以紧跟一个逗号(','. []表示空数组 。 array literal的type是 T[], 这个T就是数组中元素的type. 如果该数组中有多种type, T则是跟这些type的公共`supertype`最接近的type.空的`array literal`由一组方括号定义,可用来创建特定类型的空数组。
```swift
var emptyArray: [Double] = []
```
一个`dictionary literal` 是一个包含无序的键值对key-value pairs的集合它的形式是: 一个`dictionary literal` 是一个包含无序的键值对key-value pairs的集合它的形式是:
> [`key 1`: `value 1`, `key 2`: `value 2`, `...`] > [`key 1`: `value 1`, `key 2`: `value 2`, `...`]
dictionary 的最后一个表达式可以是一个逗号(','. [:] 表示一个空的dictionary. 它的type是 Dictionary<KeyType, ValueType> 这里KeyType表示 key的type, ValueType表示 value的type 如果这个dictionary 中包含多种 types, 那么KeyType, Value 则对应着它们的公共supertype最接近的type closest common supertype. dictionary 的最后一个表达式可以是一个逗号(','. [:] 表示一个空的dictionary. 它的type是 Dictionary<KeyType, ValueType> 这里KeyType表示 key的type, ValueType表示 value的type 如果这个dictionary 中包含多种 types, 那么KeyType, Value 则对应着它们的公共supertype最接近的type closest common supertype.一个空的dictionary literal由方括号中加一个冒号组成以此来与空array literal区分开可以使用空的dictionary literal来创建特定类型的键值对。
```swift
var emptyDictionary: [String: Double]=[:]
```
> 字面量表达式语法 > 字面量表达式语法
> *字面量表达式* → [*字面量*](LexicalStructure.html#literal) > *字面量表达式* → [*字面量*](LexicalStructure.html#literal)
> *字面量表达式* → [*数组字面量*](..\chapter3\04_Expressions.html#array_literal) | [*字典字面量*](..\chapter3\04_Expressions.html#dictionary_literal) > *字面量表达式* → [*数组字面量*](../chapter3/04_Expressions.html#array_literal) | [*字典字面量*](../chapter3/04_Expressions.html#dictionary_literal)
> *字面量表达式* → **&#95;&#95;FILE&#95;&#95;** | **&#95;&#95;LINE&#95;&#95;** | **&#95;&#95;COLUMN&#95;&#95;** | **&#95;&#95;FUNCTION&#95;&#95;** > *字面量表达式* → **&#95;&#95;FILE&#95;&#95;** | **&#95;&#95;LINE&#95;&#95;** | **&#95;&#95;COLUMN&#95;&#95;** | **&#95;&#95;FUNCTION&#95;&#95;**
> *数组字面量* → **[** [*数组字面量项列表*](..\chapter3\04_Expressions.html#array_literal_items) _可选_ **]** > *数组字面量* → **[** [*数组字面量项列表*](../chapter3/04_Expressions.html#array_literal_items) _可选_ **]**
> *数组字面量项列表* → [*数组字面量项*](..\chapter3\04_Expressions.html#array_literal_item) **,** _可选_ | [*数组字面量项*](..\chapter3\04_Expressions.html#array_literal_item) **,** [*数组字面量项列表*](..\chapter3\04_Expressions.html#array_literal_items) > *数组字面量项列表* → [*数组字面量项*](../chapter3/04_Expressions.html#array_literal_item) **,** _可选_ | [*数组字面量项*](../chapter3/04_Expressions.html#array_literal_item) **,** [*数组字面量项列表*](../chapter3/04_Expressions.html#array_literal_items)
> *数组字面量项* → [*表达式*](..\chapter3\04_Expressions.html#expression) > *数组字面量项* → [*表达式*](../chapter3/04_Expressions.html#expression)
> *字典字面量* → **[** [*字典字面量项列表*](..\chapter3\04_Expressions.html#dictionary_literal_items) **]** | **[** **:** **]** > *字典字面量* → **[** [*字典字面量项列表*](../chapter3/04_Expressions.html#dictionary_literal_items) **]** | **[** **:** **]**
> *字典字面量项列表* → [*字典字面量项*](..\chapter3\04_Expressions.html#dictionary_literal_item) **,** _可选_ | [*字典字面量项*](..\chapter3\04_Expressions.html#dictionary_literal_item) **,** [*字典字面量项列表*](..\chapter3\04_Expressions.html#dictionary_literal_items) > *字典字面量项列表* → [*字典字面量项*](../chapter3/04_Expressions.html#dictionary_literal_item) **,** _可选_ | [*字典字面量项*](../chapter3/04_Expressions.html#dictionary_literal_item) **,** [*字典字面量项列表*](../chapter3/04_Expressions.html#dictionary_literal_items)
> *字典字面量项* → [*表达式*](..\chapter3\04_Expressions.html#expression) **:** [*表达式*](..\chapter3\04_Expressions.html#expression) > *字典字面量项* → [*表达式*](../chapter3/04_Expressions.html#expression) **:** [*表达式*](../chapter3/04_Expressions.html#expression)
### self表达式Self Expression ### self表达式Self Expression
@ -262,8 +221,8 @@ self表达式是对 当前type 或者当前instance的引用。它的形式如
> self > self
> self.`member name` > self.`member name`
> self[`subscript index`] > self[`subscript index`]
> self`initializer arguments` > self(`initializer arguments`)
> self.init`initializer arguments` > self.init(`initializer arguments`)
如果在 initializer, subscript, instance method中self等同于当前type的instance. 在一个静态方法static method, 类方法class method self等同于当前的type. 如果在 initializer, subscript, instance method中self等同于当前type的instance. 在一个静态方法static method, 类方法class method self等同于当前的type.
@ -273,7 +232,7 @@ self表达式是对 当前type 或者当前instance的引用。它的形式如
```swift ```swift
class SomeClass { class SomeClass {
var greeting: String var greeting: String
initgreeting: String { init(greeting: String) {
self.greeting = greeting self.greeting = greeting
} }
} }
@ -284,8 +243,8 @@ class SomeClass {
```swift ```swift
struct Point { struct Point {
var x = 0.0, y = 0.0 var x = 0.0, y = 0.0
mutating func moveByXdeltaX: Double, y deltaY: Double { mutating func moveByX(deltaX: Double, y deltaY: Double) {
self = Pointx: x + deltaX, y: y + deltaY self = Point(x: x + deltaX, y: y + deltaY)
} }
} }
``` ```
@ -293,7 +252,7 @@ struct Point {
> Self 表达式语法 > Self 表达式语法
> *self表达式* → **self** > *self表达式* → **self**
> *self表达式* → **self** **.** [*标识符*](LexicalStructure.html#identifier) > *self表达式* → **self** **.** [*标识符*](LexicalStructure.html#identifier)
> *self表达式* → **self** **[** [*表达式*](..\chapter3\04_Expressions.html#expression) **]** > *self表达式* → **self** **[** [*表达式*](../chapter3/04_Expressions.html#expression) **]**
> *self表达式* → **self** **.** **init** > *self表达式* → **self** **.** **init**
### 超类表达式Superclass Expression ### 超类表达式Superclass Expression
@ -302,16 +261,16 @@ struct Point {
> super.`member name` > super.`member name`
> super[`subscript index`] > super[`subscript index`]
> super.init`initializer arguments` > super.init(`initializer arguments`)
形式1 用来访问超类的某个成员member. 形式2 用来访问该超类的 subscript 实现。 形式3 用来访问该超类的 initializer. 形式1 用来访问超类的某个成员member. 形式2 用来访问该超类的 subscript 实现。 形式3 用来访问该超类的 initializer.
子类subclass可以通过超类superclass表达式在它们的 member, subscripting 和 initializers 中来利用它们超类中的某些实现(既有的方法或者逻辑)。 子类subclass可以通过超类superclass表达式在它们的 member, subscripting 和 initializers 中来利用它们超类中的某些实现(既有的方法或者逻辑)。
> 超类(superclass)表达式语法 > 超类(superclass)表达式语法
> *超类表达式* → [*超类方法表达式*](..\chapter3\04_Expressions.html#superclass_method_expression) | [*超类下标表达式*](..\chapter3\04_Expressions.html#超类下标表达式) | [*超类构造器表达式*](..\chapter3\04_Expressions.html#superclass_initializer_expression) > *超类表达式* → [*超类方法表达式*](../chapter3/04_Expressions.html#superclass_method_expression) | [*超类下标表达式*](../chapter3/04_Expressions.html#超类下标表达式) | [*超类构造器表达式*](../chapter3/04_Expressions.html#superclass_initializer_expression)
> *超类方法表达式* → **super** **.** [*标识符*](LexicalStructure.html#identifier) > *超类方法表达式* → **super** **.** [*标识符*](LexicalStructure.html#identifier)
> *超类下标表达式* → **super** **[** [*表达式*](..\chapter3\04_Expressions.html#expression) **]** > *超类下标表达式* → **super** **[** [*表达式*](../chapter3/04_Expressions.html#expression) **]**
> *超类构造器表达式* → **super** **.** **init** > *超类构造器表达式* → **super** **.** **init**
### 闭包表达式Closure Expression ### 闭包表达式Closure Expression
@ -319,7 +278,7 @@ struct Point {
闭包closure 表达式可以建立一个闭包(在其他语言中也叫 lambda, 或者 匿名函数anonymous function. 跟函数function的声明一样 闭包closure包含了可执行的代码跟方法主体statement类似 以及接收capture的参数。 它的形式如下: 闭包closure 表达式可以建立一个闭包(在其他语言中也叫 lambda, 或者 匿名函数anonymous function. 跟函数function的声明一样 闭包closure包含了可执行的代码跟方法主体statement类似 以及接收capture的参数。 它的形式如下:
```swift ```swift
{ parameters -> return type in { (parameters) -> return type in
statements statements
} }
``` ```
@ -336,12 +295,12 @@ struct Point {
```swift ```swift
myFunction { myFunction {
x: Int, y: Int -> Int in (x: Int, y: Int) -> Int in
return x + y return x + y
} }
myFunction { myFunction {
x, y in (x, y) in
return x + y return x + y
} }
@ -357,28 +316,28 @@ myFunction { $0 + $1 }
在闭包的参数列表( capture list 参数可以声明为 'weak' 或者 'unowned' . 在闭包的参数列表( capture list 参数可以声明为 'weak' 或者 'unowned' .
```swift ```swift
myFunction { printself.title } // strong capture myFunction { print(self.title) } // strong capture
myFunction { [weak self] in printself!.title } // weak capture myFunction { [weak self] in print(self!.title) } // weak capture
myFunction { [unowned self] in printself.title } // unowned capture myFunction { [unowned self] in print(self.title) } // unowned capture
``` ```
在参数列表中,也可以使用任意表达式来赋值. 该表达式会在 闭包被执行时赋值然后按照不同的力度来获取这句话请慎重理解captured with the specified strength. 例如: 在参数列表中,也可以使用任意表达式来赋值. 该表达式会在 闭包被执行时赋值然后按照不同的力度来获取这句话请慎重理解captured with the specified strength. 例如:
```swift ```swift
// Weak capture of "self.parent" as "parent" // Weak capture of "self.parent" as "parent"
myFunction { [weak parent = self.parent] in printparent!.title } myFunction { [weak parent = self.parent] in print(parent!.title) }
``` ```
关于闭包表达式的更多信息和例子,请参见: Closure Expressions. 关于闭包表达式的更多信息和例子,请参见: Closure Expressions.
> 闭包表达式语法 > 闭包表达式语法
> *闭包表达式* → **{** [*闭包签名(Signational)*](..\chapter3\04_Expressions.html#closure_signature) _可选_ [*多条语句(Statements)*](..\chapter3\10_Statements.html#statements) **}** > *闭包表达式* → **{** [*闭包签名(Signational)*](../chapter3/04_Expressions.html#closure_signature) _可选_ [*多条语句(Statements)*](../chapter3/10_Statements.html#statements) **}**
> *闭包签名(Signational)* → [*参数子句*](..\chapter3\05_Declarations.html#parameter_clause) [*函数结果*](..\chapter3\05_Declarations.html#function_result) _可选_ **in** > *闭包签名(Signational)* → [*参数子句*](../chapter3/05_Declarations.html#parameter_clause) [*函数结果*](../chapter3/05_Declarations.html#function_result) _可选_ **in**
> *闭包签名(Signational)* → [*标识符列表*](LexicalStructure.html#identifier_list) [*函数结果*](..\chapter3\05_Declarations.html#function_result) _可选_ **in** > *闭包签名(Signational)* → [*标识符列表*](LexicalStructure.html#identifier_list) [*函数结果*](../chapter3/05_Declarations.html#function_result) _可选_ **in**
> *闭包签名(Signational)* → [*捕获(Capature)列表*](..\chapter3\04_Expressions.html#capture_list) [*参数子句*](..\chapter3\05_Declarations.html#parameter_clause) [*函数结果*](..\chapter3\05_Declarations.html#function_result) _可选_ **in** > *闭包签名(Signational)* → [*捕获(Capature)列表*](../chapter3/04_Expressions.html#capture_list) [*参数子句*](../chapter3/05_Declarations.html#parameter_clause) [*函数结果*](../chapter3/05_Declarations.html#function_result) _可选_ **in**
> *闭包签名(Signational)* → [*捕获(Capature)列表*](..\chapter3\04_Expressions.html#capture_list) [*标识符列表*](LexicalStructure.html#identifier_list) [*函数结果*](..\chapter3\05_Declarations.html#function_result) _可选_ **in** > *闭包签名(Signational)* → [*捕获(Capature)列表*](../chapter3/04_Expressions.html#capture_list) [*标识符列表*](LexicalStructure.html#identifier_list) [*函数结果*](../chapter3/05_Declarations.html#function_result) _可选_ **in**
> *闭包签名(Signational)* → [*捕获(Capature)列表*](..\chapter3\04_Expressions.html#capture_list) **in** > *闭包签名(Signational)* → [*捕获(Capature)列表*](../chapter3/04_Expressions.html#capture_list) **in**
> *捕获(Capature)列表* → **[** [*捕获(Capature)说明符*](..\chapter3\04_Expressions.html#capture_specifier) [*表达式*](..\chapter3\04_Expressions.html#expression) **]** > *捕获(Capature)列表* → **[** [*捕获(Capature)说明符*](../chapter3/04_Expressions.html#capture_specifier) [*表达式*](../chapter3/04_Expressions.html#expression) **]**
> *捕获(Capature)说明符* → **weak** | **unowned** | **unowned(safe)** | **unowned(unsafe)** > *捕获(Capature)说明符* → **weak** | **unowned** | **unowned(safe)** | **unowned(unsafe)**
### 隐式成员表达式Implicit Member Expression ### 隐式成员表达式Implicit Member Expression
@ -395,7 +354,7 @@ x = .AnotherValue
``` ```
> 隐式成员表达式语法 > 隐式成员表达式语法
> *隐式成员表达式* → **.** [*标识符*](..\chapter3\02_Lexical_Structure.html#identifier) > *隐式成员表达式* → **.** [*标识符*](../chapter3/02_Lexical_Structure.html#identifier)
### 圆括号表达式Parenthesized Expression ### 圆括号表达式Parenthesized Expression
@ -406,16 +365,16 @@ x = .AnotherValue
圆括号表达式用来建立tuples 然后把它做为参数传递给 function. 如果某个圆括号表达式中只有一个 子表达式那么它的type就是 子表达式的type。例如 1的 type是Int, 而不是Int 圆括号表达式用来建立tuples 然后把它做为参数传递给 function. 如果某个圆括号表达式中只有一个 子表达式那么它的type就是 子表达式的type。例如 1的 type是Int, 而不是Int
> 圆括号表达式(Parenthesized Expression)语法 > 圆括号表达式(Parenthesized Expression)语法
> *圆括号表达式* → **(** [*表达式元素列表*](..\chapter3\04_Expressions.html#expression_element_list) _可选_ **)** > *圆括号表达式* → **(** [*表达式元素列表*](../chapter3/04_Expressions.html#expression_element_list) _可选_ **)**
> *表达式元素列表* → [*表达式元素*](..\chapter3\04_Expressions.html#expression_element) | [*表达式元素*](..\chapter3\04_Expressions.html#expression_element) **,** [*表达式元素列表*](..\chapter3\04_Expressions.html#expression_element_list) > *表达式元素列表* → [*表达式元素*](../chapter3/04_Expressions.html#expression_element) | [*表达式元素*](../chapter3/04_Expressions.html#expression_element) **,** [*表达式元素列表*](../chapter3/04_Expressions.html#expression_element_list)
> *表达式元素* → [*表达式*](..\chapter3\04_Expressions.html#expression) | [*标识符*](..\chapter3\02_Lexical_Structure.html#identifier) **:** [*表达式*](..\chapter3\04_Expressions.html#expression) > *表达式元素* → [*表达式*](../chapter3/04_Expressions.html#expression) | [*标识符*](../chapter3/02_Lexical_Structure.html#identifier) **:** [*表达式*](../chapter3/04_Expressions.html#expression)
### 通配符表达式Wildcard Expression ### 通配符表达式Wildcard Expression
通配符表达式用来忽略传递进来的某个参数。例如下面的代码中10被传递给x, 20被忽略译注好奇葩的语法。。。 通配符表达式用来忽略传递进来的某个参数。例如下面的代码中10被传递给x, 20被忽略译注好奇葩的语法。。。
```swift ```swift
x, _ = 10, 20 (x, _) = (10, 20)
// x is 10, 20 is ignored // x is 10, 20 is ignored
``` ```
@ -435,77 +394,91 @@ Swift 标准库提供了下列后缀表达式:
对于这些操作符的使用,请参见: Basic Operators and Advanced Operators 对于这些操作符的使用,请参见: Basic Operators and Advanced Operators
> 后置表达式语法 > 后置表达式语法
> *后置表达式* → [*主表达式*](..\chapter3\04_Expressions.html#primary_expression) > *后置表达式* → [*主表达式*](../chapter3/04_Expressions.html#primary_expression)
> *后置表达式* → [*后置表达式*](..\chapter3\04_Expressions.html#postfix_expression) [*后置运算符*](..\chapter3\02_Lexical_Structure.html#postfix_operator) > *后置表达式* → [*后置表达式*](../chapter3/04_Expressions.html#postfix_expression) [*后置运算符*](../chapter3/02_Lexical_Structure.html#postfix_operator)
> *后置表达式* → [*函数调用表达式*](..\chapter3\04_Expressions.html#function_call_expression) > *后置表达式* → [*函数调用表达式*](../chapter3/04_Expressions.html#function_call_expression)
> *后置表达式* → [*构造器表达式*](..\chapter3\04_Expressions.html#initializer_expression) > *后置表达式* → [*构造器表达式*](../chapter3/04_Expressions.html#initializer_expression)
> *后置表达式* → [*显示成员表达式*](..\chapter3\04_Expressions.html#explicit_member_expression) > *后置表达式* → [*显示成员表达式*](../chapter3/04_Expressions.html#explicit_member_expression)
> *后置表达式* → [*后置self表达式*](..\chapter3\04_Expressions.html#postfix_self_expression) > *后置表达式* → [*后置self表达式*](../chapter3/04_Expressions.html#postfix_self_expression)
> *后置表达式* → [*动态类型表达式*](..\chapter3\04_Expressions.html#dynamic_type_expression) > *后置表达式* → [*动态类型表达式*](../chapter3/04_Expressions.html#dynamic_type_expression)
> *后置表达式* → [*下标表达式*](..\chapter3\04_Expressions.html#subscript_expression) > *后置表达式* → [*下标表达式*](../chapter3/04_Expressions.html#subscript_expression)
> *后置表达式* → [*强制取值(Forced Value)表达式*](..\chapter3\04_Expressions.html#forced_value_expression) > *后置表达式* → [*强制取值(Forced Value)表达式*](../chapter3/04_Expressions.html#forced_value_expression)
> *后置表达式* → [*可选链(Optional Chaining)表达式*](..\chapter3\04_Expressions.html#optional_chaining_expression) > *后置表达式* → [*可选链(Optional Chaining)表达式*](../chapter3/04_Expressions.html#optional_chaining_expression)
### 函数调用表达式Function Call Expression ### 函数调用表达式Function Call Expression
函数调用表达式由函数名和参数列表组成。它的形式如下: 函数调用表达式由函数名和参数列表组成。它的形式如下:
> `function name``argument value 1`, `argument value 2` > `function name`(`argument value 1`, `argument value 2`)
The function name can be any expression whose value is of a function type.
(不用翻译了, 太罗嗦)
如果该function 的声明中指定了参数的名字,那么在调用的时候也必须得写出来. 例如: 如果该function 的声明中指定了参数的名字,那么在调用的时候也必须得写出来. 例如:
> `function name``argument name 1`: `argument value 1`, `argument name 2`: `argument value 2` > `function name`(`argument name 1`: `argument value 1`, `argument name 2`: `argument value 2`)
可以在 函数调用表达式的尾部(最后一个参数之后)加上 一个闭包closure 该闭包会被目标函数理解并执行。它具有如下两种写法: 可以在 函数调用表达式的尾部(最后一个参数之后)加上 一个闭包closure 该闭包会被目标函数理解并执行。它具有如下两种写法:
```swift ```swift
// someFunction takes an integer and a closure as its arguments // someFunction takes an integer and a closure as its arguments
someFunctionx, {$0 == 13} someFunction(x, {$0 == 13}+
someFunctionx {$0 == 13} someFunction(x) {$0 == 13}
``` ```
如果闭包是该函数的唯一参数,那么圆括号可以省略。 如果闭包是该函数的唯一参数,那么圆括号可以省略。
```swift ```swift
// someFunction takes a closure as its only argument // someFunction takes a closure as its only argument
myData.someMethod {$0 == 13} myData.someMethod() {$0 == 13}
myData.someMethod {$0 == 13} myData.someMethod {$0 == 13}
``` ```
> 函数调用表达式语法 > 函数调用表达式语法
> *函数调用表达式* → [*后置表达式*](..\chapter3\04_Expressions.html#postfix_expression) [*圆括号表达式*](..\chapter3\04_Expressions.html#parenthesized_expression) > *函数调用表达式* → [*后置表达式*](../chapter3/04_Expressions.html#postfix_expression) [*圆括号表达式*](../chapter3/04_Expressions.html#parenthesized_expression)
> *函数调用表达式* → [*后置表达式*](..\chapter3\04_Expressions.html#postfix_expression) [*圆括号表达式*](..\chapter3\04_Expressions.html#parenthesized_expression) _可选_ [*后置闭包(Trailing Closure)*](..\chapter3\04_Expressions.html#trailing_closure) > *函数调用表达式* → [*后置表达式*](../chapter3/04_Expressions.html#postfix_expression) [*圆括号表达式*](../chapter3/04_Expressions.html#parenthesized_expression) _可选_ [*后置闭包(Trailing Closure)*](../chapter3/04_Expressions.html#trailing_closure)
> *后置闭包(Trailing Closure)* → [*闭包表达式*](..\chapter3\04_Expressions.html#closure_expression) > *后置闭包(Trailing Closure)* → [*闭包表达式*](../chapter3/04_Expressions.html#closure_expression)
### 初始化函数表达式Initializer Expression ### 初始化函数表达式Initializer Expression
Initializer表达式用来给某个Type初始化。 它的形式如下: Initializer表达式用来给某个Type初始化。 它的形式如下:
> `expression`.init`initializer arguments` > `expression`.init(`initializer arguments`)
初始化函数表达式在调用函数时用来初始某个Type。 也可以使用初始化函数表达式来委托调用delegate to 到superclass的initializers.
Initializer表达式用来给某个Type初始化。 跟函数function不同 initializer 不能返回值。
```swift
var x = SomeClass.someClassFunction // ok
var y = SomeClass.init // error
```
可以通过 initializer 表达式来委托调用delegate to 到superclass的initializers.
```swift ```swift
class SomeSubClass: SomeSuperClass { class SomeSubClass: SomeSuperClass {
init { init() {
// subclass initialization goes here // subclass initialization goes here
super.init super.init()
} }
} }
``` ```
和函数类似, 初始化表达式可以用作数值。 举例来说:
```swift
// Type annotation is required because String has multiple initializers.
let initializer: Int -> String = String.init
let oneTwoThree = [1, 2, 3].map(initializer).reduce("", combine: +)
print(oneTwoThree)
// prints "123"
```
如果要用名字来指定某个type 可以不用初始化函数表达式直接使用type的initializer。在其他情况下 你必须使用初始化函数表达式。
```swift
let s1 = SomeType.init(data: 3) // Valid
let s2 = SomeType(data: 1) // Also valid
let s4 = someValue.dynamicType(data: 5) // Error
let s3 = someValue.dynamicType.init(data: 7) // Valid
```
> 构造器表达式语法 > 构造器表达式语法
> *构造器表达式* → [*后置表达式*](..\chapter3\04_Expressions.html#postfix_expression) **.** **init** > *构造器表达式* → [*后置表达式*](../chapter3/04_Expressions.html#postfix_expression) **.** **init**
### 显式成员表达式Explicit Member Expression ### 显式成员表达式Explicit Member Expression
@ -519,24 +492,23 @@ class SomeSubClass: SomeSuperClass {
class SomeClass { class SomeClass {
var someProperty = 42 var someProperty = 42
} }
let c = SomeClass let c = SomeClass()
let y = c.someProperty // Member access let y = c.someProperty // Member access
``` ```
对于tuple, 要根据它们出现的顺序0, 1, 2...)来使用: 对于tuple, 要根据它们出现的顺序0, 1, 2...)来使用:
```swift ```swift
var t = 10, 20, 30 var t = (10, 20, 30)
t.0 = t.1 t.0 = t.1
// Now t is 20, 20, 30 // Now t is (20, 20, 30)
``` ```
The members of a module access the top-level declarations of that module. 对于某个module的member的调用只能调用在top-level声明中的member.
不确定对于某个module的member的调用只能调用在top-level声明中的member.
> 显式成员表达式语法 > 显式成员表达式语法
> *显示成员表达式* → [*后置表达式*](..\chapter3\04_Expressions.html#postfix_expression) **.** [*十进制数字*](..\chapter3\02_Lexical_Structure.html#decimal_digit) > *显示成员表达式* → [*后置表达式*](../chapter3/04_Expressions.html#postfix_expression) **.** [*十进制数字*](../chapter3/02_Lexical_Structure.html#decimal_digit)
> *显示成员表达式* → [*后置表达式*](..\chapter3\04_Expressions.html#postfix_expression) **.** [*标识符*](..\chapter3\02_Lexical_Structure.html#identifier) [*泛型参数子句*](GenericParametersAndArguments.html#generic_argument_clause) _可选_ > *显示成员表达式* → [*后置表达式*](../chapter3/04_Expressions.html#postfix_expression) **.** [*标识符*](../chapter3/02_Lexical_Structure.html#identifier) [*泛型参数子句*](GenericParametersAndArguments.html#generic_argument_clause) _可选_
### 后缀self表达式Postfix Self Expression ### 后缀self表达式Postfix Self Expression
@ -550,7 +522,7 @@ The members of a module access the top-level declarations of that module.
形式2返回对应的type。我们可以用它来动态的获取某个instance的type。 形式2返回对应的type。我们可以用它来动态的获取某个instance的type。
> 后置Self 表达式语法 > 后置Self 表达式语法
> *后置self表达式* → [*后置表达式*](..\chapter3\04_Expressions.html#postfix_expression) **.** **self** > *后置self表达式* → [*后置表达式*](../chapter3/04_Expressions.html#postfix_expression) **.** **self**
### dynamic表达式Dynamic Type Expression ### dynamic表达式Dynamic Type Expression
@ -564,25 +536,25 @@ dynamicType 表达式由 某个表达式 + '.dynamicType' 组成。
```swift ```swift
class SomeBaseClass { class SomeBaseClass {
class func printClassName { class func printClassName() {
println"SomeBaseClass" println("SomeBaseClass")
} }
} }
class SomeSubClass: SomeBaseClass { class SomeSubClass: SomeBaseClass {
override class func printClassName { override class func printClassName() {
println"SomeSubClass" println("SomeSubClass")
} }
} }
let someInstance: SomeBaseClass = SomeSubClass let someInstance: SomeBaseClass = SomeSubClass()
// someInstance is of type SomeBaseClass at compile time, but // someInstance is of type SomeBaseClass at compile time, but
// someInstance is of type SomeSubClass at runtime // someInstance is of type SomeSubClass at runtime
someInstance.dynamicType.printClassName someInstance.dynamicType.printClassName()
// prints "SomeSubClass" // prints "SomeSubClass"
``` ```
> 动态类型表达式语法 > 动态类型表达式语法
> *动态类型表达式* → [*后置表达式*](..\chapter3\04_Expressions.html#postfix_expression) **.** **dynamicType** > *动态类型表达式* → [*后置表达式*](../chapter3/04_Expressions.html#postfix_expression) **.** **dynamicType**
### 下标脚本表达式Subscript Expression ### 下标脚本表达式Subscript Expression
@ -595,7 +567,7 @@ someInstance.dynamicType.printClassName
关于subscript的声明请参见 Protocol Subscript Declaration. 关于subscript的声明请参见 Protocol Subscript Declaration.
> 附属脚本表达式语法 > 附属脚本表达式语法
> *附属脚本表达式* → [*后置表达式*](..\chapter3\04_Expressions.html#postfix_expression) **[** [*表达式列表*](..\chapter3\04_Expressions.html#expression_list) **]** > *附属脚本表达式* → [*后置表达式*](../chapter3/04_Expressions.html#postfix_expression) **[** [*表达式列表*](../chapter3/04_Expressions.html#expression_list) **]**
### 强制取值表达式Forced-Value Expression ### 强制取值表达式Forced-Value Expression
@ -604,9 +576,19 @@ someInstance.dynamicType.printClassName
> `expression`! > `expression`!
如果该表达式的值不是nil, 则返回对应的值。 否则抛出运行时错误runtime error 如果该表达式的值不是nil, 则返回对应的值。 否则抛出运行时错误runtime error
返回的值可能会被需改,可以是被赋值了,也可以是出现异常造成的。比如:
```swift
var x: Int? = 0
x!++
// x is now 1
var someDictionary = ["a": [1, 2, 3], "b": [10, 20]]
someDictionary["a"]![0] = 100
// someDictionary is now [b: [10, 20], a: [100, 2, 3]]
```
> 强制取值(Forced Value)语法 > 强制取值(Forced Value)语法
> *强制取值(Forced Value)表达式* → [*后置表达式*](..\chapter3\04_Expressions.html#postfix_expression) **!** > *强制取值(Forced Value)表达式* → [*后置表达式*](../chapter3/04_Expressions.html#postfix_expression) **!**
### 可选链表达式Optional-Chaining Expression ### 可选链表达式Optional-Chaining Expression
@ -622,16 +604,32 @@ someInstance.dynamicType.printClassName
```swift ```swift
var c: SomeClass? var c: SomeClass?
var result: Bool? = c?.property.performAction var result: Bool? = c?.property.performAction()
``` ```
如果不使用可选链表达式,那么 上面例子的代码跟下面例子等价: 如果不使用可选链表达式,那么 上面例子的代码跟下面例子等价:
```swift ```swift
if let unwrappedC = c { if let unwrappedC = c {
result = unwrappedC.property.performAction result = unwrappedC.property.performAction()
} }
``` ```
后缀'?' 返回目标表达式的值可能会被修改,可能是由于出现了赋值,也有可能是出现异常而产生的修改。如果可选链表达式为`nil`,则表达式右边的复制操作不会被执行。比如:
```swift
func someFunctionWithSideEffects() -> Int {
return 42 // No actual side effects.
}
var someDictionary = ["a": [1, 2, 3], "b": [10, 20]]
someDictionary["not here"]?[0] = someFunctionWithSideEffects()
// someFunctionWithSideEffects is not evaluated
// someDictionary is still [b: [10, 20], a: [1, 2, 3]]
someDictionary["a"]?[0] = someFunctionWithSideEffects()
// someFunctionWithSideEffects is evaluated and returns 42
// someDictionary is now [b: [10, 20], a: [42, 2, 3]]
```
> 可选链表达式语法 > 可选链表达式语法
> *可选链表达式* → [*后置表达式*](..\chapter3\04_Expressions.html#postfix_expression) **?** > *可选链表达式* → [*后置表达式*](../chapter3/04_Expressions.html#postfix_expression) **?**

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,13 @@
> 翻译:[Hawstein](https://github.com/Hawstein) # 特性Attributes
> 校对:[numbbbbb](https://github.com/numbbbbb), [stanzhai](https://github.com/stanzhai), [KYawn](https://github.com/KYawn)
# 特性
----------------- -----------------
> 1.0
> 翻译:[Hawstein](https://github.com/Hawstein)
> 校对:[numbbbbb](https://github.com/numbbbbb), [stanzhai](https://github.com/stanzhai)
> 2.0
> 翻译+校对:[KYawn](https://github.com/KYawn)
本页内容包括: 本页内容包括:
- [声明特性](#declaration_attributes) - [声明特性](#declaration_attributes)
@ -25,7 +29,7 @@
`autoclosure` `autoclosure`
这个特性通过把表达式自动封装成无参数的闭包来延迟表达式的计算。它可以声明返回表达式自身类型的没有参数的方法类型,也可以用于函数参数的声明。含有`autoclosure`特性的声明同时也具有`noescape`的特性,除非传递可选参数`escaping`.关于怎样使用`autoclosure`特性的例子,参见[函数类型](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Types.html#//apple_ref/doc/uid/TP40014097-CH31-ID449). 这个特性通过把表达式自动封装成无参数的闭包来延迟表达式的计算。它可以声明返回表达式自身类型的没有参数的方法类型,也可以用于函数参数的声明。含有`autoclosure`特性的声明同时也具有`noescape`的特性,除非传递可选参数`escaping`.关于怎样使用`autoclosure`特性的例子,参见[函数类型](./03_Types.html#function_type).
`available` `available`
@ -95,7 +99,7 @@ class MyClass {
该特性用于修饰任何可以在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`特性应用于枚举,每一个枚举的`case`都会以枚举名称和`case`名称组合的方式暴露在Objective-C代码中。例如一个名为`Venus``case``Planet`枚举中,这个`case`暴露在Objective-C代码中时叫做`PlanetVenus` 如果你将`objc`特性应用于枚举,每一个枚举的`case`都会以枚举名称和`case`名称组合的方式暴露在Objective-C代码中。例如一个名为`Venus``case``Planet`枚举中,这个`case`暴露在Objective-C代码中时叫做`PlanetVenus`

View File

@ -1,8 +1,12 @@
> 翻译:[honghaoz](https://github.com/honghaoz), [ray16897188](https://github.com/ray16897188) # 模式Patterns
-----------------
> 1.0
> 翻译:[honghaoz](https://github.com/honghaoz)
> 校对:[numbbbbb](https://github.com/numbbbbb), [stanzhai](https://github.com/stanzhai) > 校对:[numbbbbb](https://github.com/numbbbbb), [stanzhai](https://github.com/stanzhai)
# 模式Patterns > 2.0
----------------- > 翻译+校对:[ray16897188](https://github.com/ray16897188)
本页内容包括: 本页内容包括:
@ -24,14 +28,14 @@ swift语言中模式有2个基本的分类一类能成功和任何值的类
第二类模式用于全模式匹配这种情况下你用来相比较的值在运行时可能还不存在。此类模式包括枚举用例模式enumeration case patterns可选模式optional patterns表达式模式expression patterns和类型转换模式type-casting patterns。你在`switch`语句的case标签中`do`语句的`catch`从句中,或者在`if, while, guard``for-in`语句的case条件句中使用这类模式。 第二类模式用于全模式匹配这种情况下你用来相比较的值在运行时可能还不存在。此类模式包括枚举用例模式enumeration case patterns可选模式optional patterns表达式模式expression patterns和类型转换模式type-casting patterns。你在`switch`语句的case标签中`do`语句的`catch`从句中,或者在`if, while, guard``for-in`语句的case条件句中使用这类模式。
> 模式(Patterns) 语法 > 模式(Patterns) 语法
> *模式* → [*通配符模式*](..\chapter3\07_Patterns.html#wildcard_pattern) [*类型标注*](..\chapter3\03_Types.html#type_annotation) _可选_ > *模式* → [*通配符模式*](../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#identifier_pattern) [*类型标注*](../chapter3/03_Types.html#type_annotati(Value Binding)on) _可选_
> *模式* → [*值绑定模式*](..\chapter3\07_Patterns.html#value_binding_pattern) > *模式* → [*值绑定模式*](../chapter3/07_Patterns.html#value_binding_pattern)
> *模式* → [*元组模式*](..\chapter3\07_Patterns.html#tuple_pattern) [*类型标注*](..\chapter3\03_Types.html#type_annotation) _可选_ > *模式* → [*元组模式*](../chapter3/07_Patterns.html#tuple_pattern) [*类型标注*](../chapter3/03_Types.html#type_annotation) _可选_
> *模式* → [*枚举用例模式*](..\chapter3\07_Patterns.html#enum_case_pattern) > *模式* → [*枚举用例模式*](../chapter3/07_Patterns.html#enum_case_pattern)
> *模式* → [*可选模式*](..\chapter3\07_Patterns.html#optional_pattern) > *模式* → [*可选模式*](../chapter3/07_Patterns.html#optional_pattern)
> *模式* → [*类型转换模式*](..\chapter3\07_Patterns.html#type_casting_pattern) > *模式* → [*类型转换模式*](../chapter3/07_Patterns.html#type_casting_pattern)
> *模式* → [*表达式模式*](..\chapter3\07_Patterns.html#expression_pattern) > *模式* → [*表达式模式*](../chapter3/07_Patterns.html#expression_pattern)
<a name="wildcard_pattern"></a> <a name="wildcard_pattern"></a>
## 通配符模式Wildcard Pattern ## 通配符模式Wildcard Pattern
@ -83,7 +87,7 @@ case let (x, y):
在上面这个例子中,`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)模式语法 > 值绑定(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)
<a name="tuple_pattern"></a> <a name="tuple_pattern"></a>
## 元组模式Tuple Pattern ## 元组模式Tuple Pattern
@ -111,9 +115,9 @@ let (a): Int = 2 // a: Int = 2
``` ```
> 元组模式语法 > 元组模式语法
> *元组模式* → **(** [*元组模式元素列表*](..\chapter3\07_Patterns.html#tuple_pattern_element_list) _可选_ **)** > *元组模式* → **(** [*元组模式元素列表*](../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#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#pattern)
<a name="enumeration_case_pattern"></a> <a name="enumeration_case_pattern"></a>
## 枚举用例模式Enumeration Case Pattern ## 枚举用例模式Enumeration Case Pattern
@ -123,7 +127,7 @@ let (a): Int = 2 // a: Int = 2
如果你准备匹配的枚举用例有任何关联的值,则相应的枚举用例模式必须指定一个包含每个关联值元素的元组模式。关于使用`switch`语句来匹配包含关联值枚举用例的例子,请参阅`Associated Values`. 如果你准备匹配的枚举用例有任何关联的值,则相应的枚举用例模式必须指定一个包含每个关联值元素的元组模式。关于使用`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) _可选_
<a name="optional_pattern"></a> <a name="optional_pattern"></a>
## 可选模式Optional Pattern ## 可选模式Optional Pattern
@ -158,7 +162,7 @@ for case let number? in arrayOfOptinalInts {
``` ```
> 可选模式语法 > 可选模式语法
> *可选模式* → [*标识符模式*](..\chapter3\03_Types.html#type_identifier) ? > *可选模式* → [*标识符模式*](../chapter3/03_Types.html#type_identifier) ?
<a name="type-casting_patterns"></a> <a name="type-casting_patterns"></a>
## 类型转换模式Type-Casting Patterns ## 类型转换模式Type-Casting Patterns
@ -175,9 +179,9 @@ for case let number? in arrayOfOptinalInts {
关于使用`switch`语句来匹配`is`模式和`as`模式值的例子,请参阅`Type Casting for Any and AnyObject` 关于使用`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) > *type-casting-pattern* → [*is模式*](../chapter3/07_Patterns.html#is_pattern) | [*as模式*](../chapter3/07_Patterns.html#as_pattern)
> *is模式* → **is** [*类型*](..\chapter3\03_Types.html#type) > *is模式* → **is** [*类型*](../chapter3/03_Types.html#type)
> *as模式* → [*模式*](..\chapter3\07_Patterns.html#pattern) **as** [*类型*](..\chapter3\03_Types.html#type) > *as模式* → [*模式*](../chapter3/07_Patterns.html#pattern) **as** [*类型*](../chapter3/03_Types.html#type)
<a name="expression_pattern"></a> <a name="expression_pattern"></a>
## 表达式模式Expression Pattern ## 表达式模式Expression Pattern
@ -216,4 +220,4 @@ default:
``` ```
> 表达式模式语法 > 表达式模式语法
> *表达式模式* → [*表达式*](..\chapter3\04_Expressions.html#expression) > *表达式模式* → [*表达式*](../chapter3/04_Expressions.html#expression)

View File

@ -1,9 +1,13 @@
> 翻译:[fd5788](https://github.com/fd5788) # 泛型参数Generic Parameters and Arguments
> 校对:[yankuangshi](https://github.com/yankuangshi), [stanzhai](https://github.com/stanzhai), [wardenNScaiyi](https:github.com/wardenNScaiyi)
# 泛型参数
--------- ---------
> 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_parameter)
@ -11,7 +15,7 @@
本节涉及泛型类型、泛型函数以及泛型初始化器(**initializer**)的参数,包括形参和实参。声明泛型类型、函数或初始化器时,须指定相应的类型参数。类型参数相当于一个占位符,当实例化泛型类型、调用泛型函数或泛型初始化器时,就用具体的类型实参替代之。 本节涉及泛型类型、泛型函数以及泛型初始化器(**initializer**)的参数,包括形参和实参。声明泛型类型、函数或初始化器时,须指定相应的类型参数。类型参数相当于一个占位符,当实例化泛型类型、调用泛型函数或泛型初始化器时,就用具体的类型实参替代之。
关于 Swift 语言的泛型概述,见[泛型](../charpter2/22_Generics.md)(第二部分第22章)。 关于 Swift 语言的泛型概述,见[泛型](../chapter2/23_Generics.md)(第二部分第23章)。
<a name="generic_parameter"></a> <a name="generic_parameter"></a>
## 泛型形参子句 ## 泛型形参子句
@ -67,15 +71,15 @@ simpleMax(3.14159, 2.71828) // T被推断出为Double类型
> 泛型形参子句语法 > 泛型形参子句语法
> *泛型参数子句* → **<** [*泛型参数列表*](GenericParametersAndArguments.html#generic_parameter_list) [*约束子句*](GenericParametersAndArguments.html#requirement_clause) _可选_ **>** > *泛型参数子句* → **<** [*泛型参数列表*](GenericParametersAndArguments.html#generic_parameter_list) [*约束子句*](GenericParametersAndArguments.html#requirement_clause) _可选_ **>**
> *泛型参数列表* → [*泛形参数*](GenericParametersAndArguments.html#generic_parameter) | [*泛形参数*](GenericParametersAndArguments.html#generic_parameter) **,** [*泛型参数列表*](GenericParametersAndArguments.html#generic_parameter_list) > *泛型参数列表* → [*泛形参数*](GenericParametersAndArguments.html#generic_parameter) | [*泛形参数*](GenericParametersAndArguments.html#generic_parameter) **,** [*泛型参数列表*](GenericParametersAndArguments.html#generic_parameter_list)
> *泛形参数* → [*类型名称*](..\chapter3\03_Types.html#type_name) > *泛形参数* → [*类型名称*](../chapter3/03_Types.html#type_name)
> *泛形参数* → [*类型名称*](..\chapter3\03_Types.html#type_name) **:** [*类型标识*](..\chapter3\03_Types.html#type_identifier) > *泛形参数* → [*类型名称*](../chapter3/03_Types.html#type_name) **:** [*类型标识*](../chapter3/03_Types.html#type_identifier)
> *泛形参数* → [*类型名称*](..\chapter3\03_Types.html#type_name) **:** [*协议合成类型*](..\chapter3\03_Types.html#protocol_composition_type) > *泛形参数* → [*类型名称*](../chapter3/03_Types.html#type_name) **:** [*协议合成类型*](../chapter3/03_Types.html#protocol_composition_type)
> *约束子句* → **where** [*约束列表*](GenericParametersAndArguments.html#requirement_list) > *约束子句* → **where** [*约束列表*](GenericParametersAndArguments.html#requirement_list)
> *约束列表* → [*约束*](GenericParametersAndArguments.html#requirement) | [*约束*](GenericParametersAndArguments.html#requirement) **,** [*约束列表*](GenericParametersAndArguments.html#requirement_list) > *约束列表* → [*约束*](GenericParametersAndArguments.html#requirement) | [*约束*](GenericParametersAndArguments.html#requirement) **,** [*约束列表*](GenericParametersAndArguments.html#requirement_list)
> *约束* → [*一致性约束*](GenericParametersAndArguments.html#conformance_requirement) | [*同类型约束*](GenericParametersAndArguments.html#same_type_requirement) > *约束* → [*一致性约束*](GenericParametersAndArguments.html#conformance_requirement) | [*同类型约束*](GenericParametersAndArguments.html#same_type_requirement)
> *一致性约束* → [*类型标识*](..\chapter3\03_Types.html#type_identifier) **:** [*类型标识*](..\chapter3\03_Types.html#type_identifier) > *一致性约束* → [*类型标识*](../chapter3/03_Types.html#type_identifier) **:** [*类型标识*](../chapter3/03_Types.html#type_identifier)
> *一致性约束* → [*类型标识*](..\chapter3\03_Types.html#type_identifier) **:** [*协议合成类型*](..\chapter3\03_Types.html#protocol_composition_type) > *一致性约束* → [*类型标识*](../chapter3/03_Types.html#type_identifier) **:** [*协议合成类型*](../chapter3/03_Types.html#protocol_composition_type)
> *同类型约束* → [*类型标识*](..\chapter3\03_Types.html#type_identifier) **==** [*类型标识*](..\chapter3\03_Types.html#type_identifier) > *同类型约束* → [*类型标识*](../chapter3/03_Types.html#type_identifier) **==** [*类型标识*](../chapter3/03_Types.html#type_identifier)
<a name="generic_argument"></a> <a name="generic_argument"></a>
@ -109,4 +113,4 @@ let arrayOfArrays: Array<Array<Int>> = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
> 泛型实参子句语法 > 泛型实参子句语法
> *(泛型参数子句Generic Argument Clause)* → **<** [*泛型参数列表*](GenericParametersAndArguments.html#generic_argument_list) **>** > *(泛型参数子句Generic Argument Clause)* → **<** [*泛型参数列表*](GenericParametersAndArguments.html#generic_argument_list) **>**
> *泛型参数列表* → [*泛型参数*](GenericParametersAndArguments.html#generic_argument) | [*泛型参数*](GenericParametersAndArguments.html#generic_argument) **,** [*泛型参数列表*](GenericParametersAndArguments.html#generic_argument_list) > *泛型参数列表* → [*泛型参数*](GenericParametersAndArguments.html#generic_argument) | [*泛型参数*](GenericParametersAndArguments.html#generic_argument) **,** [*泛型参数列表*](GenericParametersAndArguments.html#generic_argument_list)
> *泛型参数* → [*类型*](..\chapter3\03_Types.html#type) > *泛型参数* → [*类型*](../chapter3/03_Types.html#type)

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,14 @@
> 翻译:[coverxit](https://github.com/coverxit) <a name="statement_statements"></a>
> 校对:[numbbbbb](https://github.com/numbbbbb), [coverxit](https://github.com/coverxit), [stanzhai](https://github.com/stanzhai) # 语句Statements
# 语句
----------------- -----------------
> 1.0
> 翻译:[coverxit](https://github.com/coverxit)
> 校对:[numbbbbb](https://github.com/numbbbbb), [coverxit](https://github.com/coverxit), [stanzhai](https://github.com/stanzhai),
> 2.0
> 翻译+校对:[littledogboy](https://github.com/littledogboy)
本页包含内容: 本页包含内容:
- [循环语句](#loop_statements) - [循环语句](#loop_statements)
@ -11,20 +16,21 @@
- [带标签的语句](#labeled_statement) - [带标签的语句](#labeled_statement)
- [控制传递语句](#control_transfer_statements) - [控制传递语句](#control_transfer_statements)
在 Swift 中,有两种类型的语句:简单语句和控制流语句。简单语句是最常见的,用于构造表达式声明。控制流语句则用于控制程序执行的流程Swift 中有三种类型的控制流语句:循环语句、分支语句和控制传递语句。 在 Swift 中,有两种类型的语句:简单语句和控制流语句。简单语句是最常见的,用于构造表达式或者声明。控制流语句则用于控制程序执行的流程Swift 中有三种类型的控制流语句:循环语句、分支语句和控制传递语句。
循环语句用于重复执行代码块;分支语句用于执行满足特定条件的代码块;控制传递语句则用于修改代码的执行顺序。在稍后的叙述中,将会详细地介绍每一种类型的控制流语句。 循环语句用于重复执行代码块;分支语句用于执行满足特定条件的代码块;控制传递语句则用于修改代码的执行顺序。在稍后的叙述中,将会详细地介绍每一种类型的控制流语句。
是否将分号(`;`)添加到语句的结尾处是可选的。但若要在同一行内写多条独立语句,请务必使用分号。 是否将分号(`;`)添加到语句的结尾处是可选的。但若要在同一行内写多条独立语句,请务必使用分号。
> 语句语法 > 语句语法
> *语句* → [*表达式*](..\chapter3\04_Expressions.html#expression) **;** _可选_ > *语句* → [*表达式*](../chapter3/04_Expressions.html#expression) **;** _可选_
> *语句* → [*声明*](..\chapter3\05_Declarations.html#declaration) **;** _可选_ > *语句* → [*声明*](../chapter3/05_Declarations.html#declaration) **;** _可选_
> *语句* → [*循环语句*](..\chapter3\10_Statements.html#loop_statement) **;** _可选_ > *语句* → [*循环语句*](../chapter3/10_Statements.html#loop_statement) **;** _可选_
> *语句* → [*分支语句*](..\chapter3\10_Statements.html#branch_statement) **;** _可选_ > *语句* → [*分支语句*](../chapter3/10_Statements.html#branch_statement) **;** _可选_
> *语句* → [*标记语句(Labeled Statement)*](..\chapter3\10_Statements.html#labeled_statement) > *语句* → [*标记语句(Labeled Statement)*](../chapter3/10_Statements.html#labeled_statement)
> *语句* → [*控制转移语句*](..\chapter3\10_Statements.html#control_transfer_statement) **;** _可选_ > *语句* → [*控制转移语句*](../chapter3/10_Statements.html#control_transfer_statement) **;** _可选_
> *多条语句(Statements)* → [*语句*](..\chapter3\10_Statements.html#statement) [*多条语句(Statements)*](..\chapter3\10_Statements.html#statements) _可选_ > *语句* → [*XXX语句*](../chapter3/10_Statements.html#control_transfer_statement) **;** _可选_
> *多条语句(Statements)* → [*语句*](../chapter3/10_Statements.html#statement) [*多条语句(Statements)*](../chapter3/10_Statements.html#statements) _可选_
<a name="loop_statements"></a> <a name="loop_statements"></a>
## 循环语句 ## 循环语句
@ -34,14 +40,15 @@
通过`break`语句和`continue`语句可以改变循环语句的控制流。有关这两条语句,详情参见 [Break 语句](#break_statement)和 [Continue 语句](#continue_statement)。 通过`break`语句和`continue`语句可以改变循环语句的控制流。有关这两条语句,详情参见 [Break 语句](#break_statement)和 [Continue 语句](#continue_statement)。
> 循环语句语法 > 循环语句语法
> *循环语句* → [*for语句*](..\chapter3\10_Statements.html#for_statement) > *循环语句* → [*for语句*](../chapter3/10_Statements.html#for_statement)
> *循环语句* → [*for-in语句*](..\chapter3\10_Statements.html#for_in_statement) > *循环语句* → [*for-in语句*](../chapter3/10_Statements.html#for_in_statement)
> *循环语句* → [*while语句*](..\chapter3\10_Statements.html#wheetatype类型ile_statement) > *循环语句* → [*while语句*](../chapter3/10_Statements.html#wheetatype类型ile_statement)
> *循环语句* → [*do-while语句*](..\chapter3\10_Statements.html#do_while_statement) > *循环语句* → [*do-while语句*](../chapter3/10_Statements.html#do_while_statement)
<a name="for_statements"></a>
### For 语句 ### For 语句
`for`语句允许在重复执行代码块的同时,递增一个计数器。 `for`语句只有在循环条件为真时重复执行代码块,此时计数器递增
`for`语句的形式如下: `for`语句的形式如下:
@ -53,18 +60,20 @@
`for`语句的执行流程如下: `for`语句的执行流程如下:
1. *initialzation* 只会被执行一次,通常用于声明和初始化在接下来的循环中需要使用的变量。 1. *initialzation* *循环变量* 只会被执行一次,通常用于声明和初始化在接下来的循环中需要使用的变量。
2. 计算 *condition* 表达式 2. 判断 *condition* 循环条件
如果为`true`*statements* 将会被执行然后转到第3步。如果为`false`*statements* 和 *increment* 都不会被执行,`for`至此执行完毕。 如果为`true`*statements* *循环体* 将会被执行然后转到第3步。如果为`false`*statements* 和 *increment* *循环增量* 都不会被执行,`for`至此执行完毕。
3. 计算 *increment* 表达式然后转到第2步。 3. 计算 *increment* 表达式然后转到第2步。
定义*initialzation* 中的变量仅在`for`语句的作用域内有效。*condition* 表达式的值的类型必须遵循`LogicValue`协议。 *initialzation*定义的变量仅在`for`循环的作用域内有效。*condition* 表达式的值的类型必须遵循`BooleanType `协议。
> For 循环语法 > For 循环语法
> *for语句* → **for** [*for初始条件*](..\chapter3\10_Statements.html#for_init) _可选_ **;** [*表达式*](..\chapter3\04_Expressions.html#expression) _可选_ **;** [*表达式*](..\chapter3\04_Expressions.html#expression) _可选_ [*代码块*](..\chapter3\05_Declarations.html#code_block) > *for语句* → **for** [*for初始条件*](../chapter3/10_Statements.html#for_init) _可选_ **;** [*表达式*](../chapter3/04_Expressions.html#expression) _可选_ **;** [*表达式*](../chapter3/04_Expressions.html#expression) _可选_ [*代码块*](../chapter3/05_Declarations.html#code_block)
> *for语句* → **for** **(** [*for初始条件*](..\chapter3\10_Statements.html#for_init) _可选_ **;** [*表达式*](..\chapter3\04_Expressions.html#expression) _可选_ **;** [*表达式*](..\chapter3\04_Expressions.html#expression) _可选_ **)** [*代码块*](..\chapter3\05_Declarations.html#code_block) > *for语句* → **for** **(** [*for初始条件*](../chapter3/10_Statements.html#for_init) _可选_ **;** [*表达式*](../chapter3/04_Expressions.html#expression) _可选_ **;** [*表达式*](../chapter3/04_Expressions.html#expression) _可选_ **)** [*代码块*](../chapter3/05_Declarations.html#code_block)
> *for初始条件* → [*变量声明*](..\chapter3\05_Declarations.html#variable_declaration) | [*表达式列表*](..\chapter3\04_Expressions.html#expression_list) > *for初始条件* → [*变量声明*](../chapter3/05_Declarations.html#variable_declaration) | [*表达式列表*](../chapter3/04_Expressions.html#expression_list)
<a name="for-in_statements"></a>
### For-In 语句 ### For-In 语句
`for-in`语句允许在重复执行代码块的同时,迭代集合(或遵循`Sequence`协议的任意类型)中的每一项。 `for-in`语句允许在重复执行代码块的同时,迭代集合(或遵循`Sequence`协议的任意类型)中的每一项。
@ -78,11 +87,13 @@
`for-in`语句在循环开始前会调用 *collection* 表达式的`generate`方法来获取一个生成器类型(这是一个遵循`Generator`协议的类型)的值。接下来循环开始,调用 *collection* 表达式的`next`方法。如果其返回值不是`None`,它将会被赋给 *item*,然后执行 *statements*,执行完毕后回到循环开始处;否则,将不会赋值给 *item* 也不会执行 *statements*`for-in`至此执行完毕。 `for-in`语句在循环开始前会调用 *collection* 表达式的`generate`方法来获取一个生成器类型(这是一个遵循`Generator`协议的类型)的值。接下来循环开始,调用 *collection* 表达式的`next`方法。如果其返回值不是`None`,它将会被赋给 *item*,然后执行 *statements*,执行完毕后回到循环开始处;否则,将不会赋值给 *item* 也不会执行 *statements*`for-in`至此执行完毕。
> For-In 循环语法 > For-In 循环语法
> *for-in语句* → **for** [*模式*](..\chapter3\07_Patterns.html#pattern) **in** [*表达式*](..\chapter3\04_Expressions.html#expression) [*代码块*](..\chapter3\05_Declarations.html#code_block) > *for-in语句* → **for** [*模式*](../chapter3/07_Patterns.html#pattern) **in** [*表达式*](../chapter3/04_Expressions.html#expression) [*代码块*](../chapter3/05_Declarations.html#code_block)
<a name="while_statements"></a>
### While 语句 ### While 语句
`while`语句允许重复执行代码块。 `while`语句当循环条件为真时,允许重复执行代码块。
`while`语句的形式如下: `while`语句的形式如下:
@ -98,34 +109,47 @@
由于 *condition* 的值在 *statements* 执行前就已计算出,因此`while`语句中的 *statements* 可能会被执行若干次,也可能不会被执行。 由于 *condition* 的值在 *statements* 执行前就已计算出,因此`while`语句中的 *statements* 可能会被执行若干次,也可能不会被执行。
*condition* 表达式的值的类型必须遵循`LogicValue`协议。同时,*condition* 表达式也可以使用可选绑定,详情参见[可选绑定](../chapter2/01_The_Basics.html#optional_binding)。 *condition* 表达式的值的类型必须遵循`BooleanType `协议。同时,*condition* 表达式也可以使用可选绑定,详情参见[可选绑定](../chapter2/01_The_Basics.html#optional_binding)。
> While 循环语法 > While 循环语法
> *while语句* → **while** [*while条件*](..\chapter3\10_Statements.html#while_condition) [*代码块*](..\chapter3\05_Declarations.html#code_block) > *while语句* → **while** [*while条件*](../chapter3/10_Statements.html#while_condition) [*代码块*](../chapter3/05_Declarations.html#code_block)
> *while条件* → [*表达式*](..\chapter3\04_Expressions.html#expression) | [*声明*](..\chapter3\05_Declarations.html#declaration) > *条件* → [*表达式*](../chapter3/04_Expressions.html#expression) | [*声明*](../chapter3/05_Declarations.html#declaration)
> *条件* → [*表达式*](../chapter3/04_Expressions.html#expression)
> *条件* → [*表达式*](../chapter3/04_Expressions.html#expression) | [*条件列表*](TODO)
> *条件* → [*可用条件*](../chapter3/10_Statement.html#availability) [*表达式*](../chapter3/04_Expressions.html#expression)
> *条件列表* → [*条件条件*](TODO) [*条件列表*](TODO)
> *条件* → [*可用条件*](../chapter3/10_Statement.html#availability) [可选绑定条件](../chapter2/01_The_Basics.html#optional_binding)
> *case条件* → **case** [*模式*](../chapter3/07_Patterns.html#pattern) [构造器](TODO) [where](DOTO)
> *可选绑定条件* → [可选绑定头](TODO) [持续可选绑定](TODO) [持续可选绑定列表](TODO)
> *可选绑定头* → **let** [*模式*](../chapter3/07_Patterns.html#pattern) [构造器](TODO) **var** [*模式*](../chapter3/07_Patterns.html#pattern) [构造器](TODO)
> *可持续绑定列表* → [*模式*](../chapter3/07_Patterns.html#pattern) | [构造器](TODO) [可选绑定头](TODO)
>
### Do-While 语句
`do-while`语句允许代码块被执行一次或多次。
`do-while`语句的形式如下: <a name="while-while_statements"></a>
### Repeat-While 语句
> do { `repeat-while`语句允许代码块被执行一次或多次。
`repeat-while`语句的形式如下:
> repeat {
> `statements` > `statements`
> } while `condition` > } while `condition`
`do-while`语句的执行流程如下: `repeat-while`语句的执行流程如下:
1. 执行 *statements*然后转到第2步。 1. 执行 *statements*然后转到第2步。
2. 计算 *condition* 表达式: 2. 计算 *condition* 表达式:
如果为`true`转到第1步。如果为`false``do-while`至此执行完毕。 如果为`true`转到第1步。如果为`false``repeat-while`至此执行完毕。
由于 *condition* 表达式的值是在 *statements* 执行后才计算出,因此`do-while`语句中的 *statements* 至少会被执行一次。 由于 *condition* 表达式的值是在 *statements* 执行后才计算出,因此`repeat-while`语句中的 *statements* 至少会被执行一次。
*condition* 表达式的值的类型必须遵循`LogicValue`协议。同时,*condition* 表达式也可以使用可选绑定,详情参见[可选绑定](../chapter2/01_The_Basics.html#optional_binding)。 *condition* 表达式的值的类型必须遵循`BooleanType `协议。同时,*condition* 表达式也可以使用可选绑定,详情参见[可选绑定](../chapter2/01_The_Basics.html#optional_binding)。
> Do-While 循环语法 > Repeat-While 循环语法
> *do-while语句* → **do** [*代码块*](..\chapter3\05_Declarations.html#code_block) **while** [*while条件*](..\chapter3\10_Statements.html#while_condition) > * repeat-while语句* → **repeat** [*代码块*](../chapter3/05_Declarations.html#code_block) **while** [*while条件*](../chapter3/10_Statements.html#while_condition)
<a name="branch_statements"></a> <a name="branch_statements"></a>
## 分支语句 ## 分支语句
@ -135,9 +159,11 @@
`switch`语句中的控制流可以用`break`语句修改,详情请见[Break 语句](#break_statement)。 `switch`语句中的控制流可以用`break`语句修改,详情请见[Break 语句](#break_statement)。
> 分支语句语法 > 分支语句语法
> *分支语句* → [*if语句*](..\chapter3\10_Statements.html#if_statement) > *分支语句* → [*if语句*](../chapter3/10_Statements.html#if_statement)
> *分支语句* → [*switch语句*](..\chapter3\10_Statements.html#switch_statement) > *分支语句* → [*switch语句*](../chapter3/10_Statements.html#switch_statement)
<a name="if_statements"></a>
### If 语句 ### If 语句
取决于一个或多个条件的值,`if`语句将决定执行哪一块代码。 取决于一个或多个条件的值,`if`语句将决定执行哪一块代码。
@ -152,10 +178,10 @@
第二种形式是在第一种形式的基础上添加 *else 语句*,当只有一个 else 语句时,像下面这样: 第二种形式是在第一种形式的基础上添加 *else 语句*,当只有一个 else 语句时,像下面这样:
> if `condition` { > if `condition` {
> `statements to execute if condition is true` > `statements to execute if condition is true`
> } else { > } else {
> `statements to execute if condition is false` > `statements to execute if condition is false`
> } > }
同时else 语句也可包含`if`语句,从而形成一条链来测试更多的条件,像下面这样: 同时else 语句也可包含`if`语句,从而形成一条链来测试更多的条件,像下面这样:
@ -172,10 +198,35 @@
`if`语句中条件的值的类型必须遵循`LogicValue`协议。同时,条件也可以使用可选绑定,详情参见[可选绑定](../chapter2/01_The_Basics.html#optional_binding)。 `if`语句中条件的值的类型必须遵循`LogicValue`协议。同时,条件也可以使用可选绑定,详情参见[可选绑定](../chapter2/01_The_Basics.html#optional_binding)。
> If语句语法 > If语句语法
> *if语句* → **if** [*if条件*](..\chapter3\10_Statements.html#if_condition) [*代码块*](..\chapter3\05_Declarations.html#code_block) [*else子句(Clause)*](..\chapter3\10_Statements.html#else_clause) _可选_ > *if语句* → **if** [*if条件*](../chapter3/10_Statements.html#if_condition) [*代码块*](../chapter3/05_Declarations.html#code_block) [*else(Clause)*](../chapter3/10_Statements.html#else_clause) _可选_
> *if条件* → [*表达式*](..\chapter3\04_Expressions.html#expression) | [*声明*](..\chapter3\05_Declarations.html#declaration) > *if条件* → [*表达式*](../chapter3/04_Expressions.html#expression) | [*声明*](../chapter3/05_Declarations.html#declaration)
> *else子句(Clause)* → **else** [*代码块*](..\chapter3\05_Declarations.html#code_block) | **else** [*if语句*](..\chapter3\10_Statements.html#if_statement) > *else(Clause)* → **else** [*代码块*](../chapter3/05_Declarations.html#code_block) | **else** [*if语句*](../chapter3/10_Statements.html#if_statement)
<a name="guard_statements"></a>
### Guard 语句
`guard` 语句用来转移程序控制出其作用域,如果一个或者多个条件不成立。
`guard` 语句的格式如下:
> guard `condition` else {
`statements`
>}
`guard`语句中条件值的类型必须遵循`LogicValue`协议。且条件可以使用可选绑定,详情参见[可选绑定](../chapter2/01_The_Basics.html#optional_binding)。
`guard`语句中声明的常量或者变量,可用范围从声明开始到作用域结束,常量和变量的值从可选绑定声明中分配。
`guard`语句需要有`else`子句,并且必须调用被`noreturn`属性标记的函数,或者使用下面的语句把程序执行转移到guard语句的作用域外。
* `return`
* `break`
* `continue`
* `throw`
执行转移语句详情参见[控制传递语句](TODO)
<a name="switch_statements"></a>
### Switch 语句 ### Switch 语句
取决于`switch`语句的*控制表达式control expression*`switch`语句将决定执行哪一块代码。 取决于`switch`语句的*控制表达式control expression*`switch`语句将决定执行哪一块代码。
@ -219,18 +270,19 @@ case let (x, y) where x == y:
当匹配的 case 分支中的代码执行完毕后,程序会终止`switch`语句,而不会继续执行下一个 case 分支。这就意味着,如果你想执行下一个 case 分支,需要显式地在你需要的 case 分支里使用`fallthrough`语句。关于`fallthrough`语句的更多信息,详情参见 [Fallthrough 语句](#fallthrough_statement)。 当匹配的 case 分支中的代码执行完毕后,程序会终止`switch`语句,而不会继续执行下一个 case 分支。这就意味着,如果你想执行下一个 case 分支,需要显式地在你需要的 case 分支里使用`fallthrough`语句。关于`fallthrough`语句的更多信息,详情参见 [Fallthrough 语句](#fallthrough_statement)。
> Switch语句语法 > Switch语句语法
> *switch语句* → **switch** [*表达式*](..\chapter3\04_Expressions.html#expression) **{** [*SwitchCase列表*](..\chapter3\10_Statements.html#switch_cases) _可选_ **}** > *switch语句* → **switch** [*表达式*](../chapter3/04_Expressions.html#expression) **{** [*SwitchCase列表*](../chapter3/10_Statements.html#switch_cases) _可选_ **}**
> *SwitchCase列表* → [*SwitchCase*](..\chapter3\10_Statements.html#switch_case) [*SwitchCase列表*](..\chapter3\10_Statements.html#switch_cases) _可选_ > *SwitchCase列表* → [*SwitchCase*](../chapter3/10_Statements.html#switch_case) [*SwitchCase列表*](../chapter3/10_Statements.html#switch_cases) _可选_
> *SwitchCase* → [*case标签*](..\chapter3\10_Statements.html#case_label) [*多条语句(Statements)*](..\chapter3\10_Statements.html#statements) | [*default标签*](..\chapter3\10_Statements.html#default_label) [*多条语句(Statements)*](..\chapter3\10_Statements.html#statements) > *SwitchCase* → [*case标签*](../chapter3/10_Statements.html#case_label) [*多条语句(Statements)*](../chapter3/10_Statements.html#statements) | [*default标签*](../chapter3/10_Statements.html#default_label) [*多条语句(Statements)*](../chapter3/10_Statements.html#statements)
> *SwitchCase* → [*case标签*](..\chapter3\10_Statements.html#case_label) **;** | [*default标签*](..\chapter3\10_Statements.html#default_label) **;** > *SwitchCase* → [*case标签*](../chapter3/10_Statements.html#case_label) **;** | [*default标签*](../chapter3/10_Statements.html#default_label) **;**
> *case标签* → **case** [*case项列表*](..\chapter3\10_Statements.html#case_item_list) **:** > *case标签* → **case** [*case项列表*](../chapter3/10_Statements.html#case_item_list) **:**
> *case项列表* → [*模式*](..\chapter3\07_Patterns.html#pattern) [*guard-clause*](..\chapter3\10_Statements.html#guard_clause) _可选_ | [*模式*](..\chapter3\07_Patterns.html#pattern) [*guard-clause*](..\chapter3\10_Statements.html#guard_clause) _可选_ **,** [*case项列表*](..\chapter3\10_Statements.html#case_item_list) > *case项列表* → [*模式*](../chapter3/07_Patterns.html#pattern) [*guard-clause*](../chapter3/10_Statements.html#guard_clause) _可选_ | [*模式*](../chapter3/07_Patterns.html#pattern) [*guard-clause*](../chapter3/10_Statements.html#guard_clause) _可选_ **,** [*case项列表*](../chapter3/10_Statements.html#case_item_list)
> *default标签* → **default** **:** > *default标签* → **default** **:**
> *guard-clause* → **where** [*guard-expression*](..\chapter3\10_Statements.html#guard_expression) > *where-clause* → **where** [*guard-expression*](../chapter3/10_Statements.html#guard)
> *guard-expression* → [*表达式*](..\chapter3\04_Expressions.html#expression) > *where-expression* → [*表达式*](../chapter3/04_Expressions.html#expression)
<a name="labeled_statement"></a> <a name="labeled_statements"></a>
<a name="control_transfer_statements"></a> 带标签的语句 <a name="control_transfer_statements"></a>
## 带标签的语句
你可以在循环语句或`switch`语句前面加上*标签*,它由标签名和紧随其后的冒号(:)组成。在`break``continue`后面跟上标签名可以显式地在循环语句或`switch`语句中更改控制流,把控制权传递给指定标签标记的语句。关于这两条语句用法,详情参见 [Break 语句](#break_statement)和 [Continue 语句](#continue_statement)。 你可以在循环语句或`switch`语句前面加上*标签*,它由标签名和紧随其后的冒号(:)组成。在`break``continue`后面跟上标签名可以显式地在循环语句或`switch`语句中更改控制流,把控制权传递给指定标签标记的语句。关于这两条语句用法,详情参见 [Break 语句](#break_statement)和 [Continue 语句](#continue_statement)。
@ -239,21 +291,23 @@ case let (x, y) where x == y:
关于使用带标签的语句的例子,详情参见[控制流](../chapter2/05_Control_Flow.html)一章的[带标签的语句](../chapter2/05_Control_Flow.html#labeled_statements)。 关于使用带标签的语句的例子,详情参见[控制流](../chapter2/05_Control_Flow.html)一章的[带标签的语句](../chapter2/05_Control_Flow.html#labeled_statements)。
> 标记语句语法 > 标记语句语法
> *标记语句(Labeled Statement)* → [*语句标签*](..\chapter3\10_Statements.html#statement_label) [*循环语句*](..\chapter3\10_Statements.html#loop_statement) | [*语句标签*](..\chapter3\10_Statements.html#statement_label) [*switch语句*](..\chapter3\10_Statements.html#switch_statement) > *标记语句(Labeled Statement)* → [*语句标签*](../chapter3/10_Statements.html#statement_label) [*循环语句*](../chapter3/10_Statements.html#loop_statement) | [*语句标签*](../chapter3/10_Statements.html#statement_label) [*switch语句*](../chapter3/10_Statements.html#switch_statement)
> *语句标签* → [*标签名称*](..\chapter3\10_Statements.html#label_name) **:** > *语句标签* → [*标签名称*](../chapter3/10_Statements.html#label_name) **:**
> *标签名称* → [*标识符*](..\chapter3\02_Lexical_Structure.html#identifier) > *标签名称* → [*标识符*](../chapter3/02_Lexical_Structure.html#identifier)
<a name="control_transfer_statements"></a>
## 控制传递语句 ## 控制传递语句
通过无条件地把控制权从一片代码传递到另一片代码控制传递语句能够改变代码执行的顺序。Swift 提供四种类型的控制传递语句:`break`语句、`continue`语句、`fallthrough`语句和`return`语句。 通过无条件地把控制权从一片代码传递到另一片代码控制传递语句能够改变代码执行的顺序。Swift 提供四种类型的控制传递语句:`break`语句、`continue`语句、`fallthrough`语句和`return`语句。
> 控制传递语句(Control Transfer Statement) 语法 > 控制传递语句(Control Transfer Statement) 语法
> *控制传递语句* → [*break语句*](..\chapter3\10_Statements.html#break_statement) > *控制传递语句* → [*break语句*](../chapter3/10_Statements.html#break_statement)
> *控制传递语句* → [*continue语句*](..\chapter3\10_Statements.html#continue_statement) > *控制传递语句* → [*continue语句*](../chapter3/10_Statements.html#continue_statement)
> *控制传递语句* → [*fallthrough语句*](..\chapter3\10_Statements.html#fallthrough_statement) > *控制传递语句* → [*fallthrough语句*](../chapter3/10_Statements.html#fallthrough_statement)
> *控制传递语句* → [*return语句*](..\chapter3\10_Statements.html#return_statement) > *控制传递语句* → [*return语句*](../chapter3/10_Statements.html#return_statement)
> *控制传递语句* → [*throw语句*](../chapter3/10_Statements.html#throw_statement)
<a name="break_statement"></a> <a name="break_statements"></a>
### Break 语句 ### Break 语句
`break`语句用于终止循环或`switch`语句的执行。使用`break`语句时,可以只写`break`这个关键词,也可以在`break`后面跟上标签名label name像下面这样 `break`语句用于终止循环或`switch`语句的执行。使用`break`语句时,可以只写`break`这个关键词,也可以在`break`后面跟上标签名label name像下面这样
@ -270,9 +324,9 @@ case let (x, y) where x == y:
关于使用`break`语句的例子,详情参见[控制流](../chapter2/05_Control_Flow.html)一章的 [Break](../chapter2/05_Control_Flow.html#break) 和[带标签的语句](../chapter2/05_Control_Flow.html#labeled_statements)。 关于使用`break`语句的例子,详情参见[控制流](../chapter2/05_Control_Flow.html)一章的 [Break](../chapter2/05_Control_Flow.html#break) 和[带标签的语句](../chapter2/05_Control_Flow.html#labeled_statements)。
> Break 语句语法 > Break 语句语法
> *break语句* → **break** [*标签名称*](..\chapter3\10_Statements.html#label_name) _可选_ > *break语句* → **break** [*标签名称*](../chapter3/10_Statements.html#label_name) _可选_
<a name="continue_statement"></a> <a name="continue_statements"></a>
### Continue 语句 ### Continue 语句
`continue`语句用于终止循环中当前迭代的执行,但不会终止该循环的执行。使用`continue`语句时,可以只写`continue`这个关键词,也可以在`continue`后面跟上标签名label name像下面这样 `continue`语句用于终止循环中当前迭代的执行,但不会终止该循环的执行。使用`continue`语句时,可以只写`continue`这个关键词,也可以在`continue`后面跟上标签名label name像下面这样
@ -291,9 +345,9 @@ case let (x, y) where x == y:
关于使用`continue`语句的例子,详情参见[控制流](../chapter2/05_Control_Flow.html)一章的 [Continue](../chapter2/05_Control_Flow.html#continue) 和[带标签的语句](../chapter2/05_Control_Flow.html#labeled_statements)。 关于使用`continue`语句的例子,详情参见[控制流](../chapter2/05_Control_Flow.html)一章的 [Continue](../chapter2/05_Control_Flow.html#continue) 和[带标签的语句](../chapter2/05_Control_Flow.html#labeled_statements)。
> Continue 语句语法 > Continue 语句语法
> *continue语句* → **continue** [*标签名称*](..\chapter3\10_Statements.html#label_name) _可选_ > *continue语句* → **continue** [*标签名称*](../chapter3/10_Statements.html#label_name) _可选_
<a name="fallthrough_statement"></a> <a name="fallthrough_statements"></a>
### Fallthrough 语句 ### Fallthrough 语句
`fallthrough`语句用于在`switch`语句中传递控制权。`fallthrough`语句会把控制权从`switch`语句中的一个 case 传递给下一个 case 。这种传递是无条件的,即使下一个 case 的模式与`switch`语句的控制表达式的值不匹配。 `fallthrough`语句用于在`switch`语句中传递控制权。`fallthrough`语句会把控制权从`switch`语句中的一个 case 传递给下一个 case 。这种传递是无条件的,即使下一个 case 的模式与`switch`语句的控制表达式的值不匹配。
@ -305,6 +359,7 @@ case let (x, y) where x == y:
> Fallthrough 语句语法 > Fallthrough 语句语法
> *fallthrough语句* → **fallthrough** > *fallthrough语句* → **fallthrough**
<a name="return_statements"></a>
### Return 语句 ### Return 语句
`return`语句用于在函数或方法的实现中将控制权传递给调用者,接着程序将会从调用者的位置继续向下执行。 `return`语句用于在函数或方法的实现中将控制权传递给调用者,接着程序将会从调用者的位置继续向下执行。
@ -319,4 +374,125 @@ case let (x, y) where x == y:
而当只写`return`时,仅仅是将控制权从该函数或方法传递给调用者,而不返回一个值。(这就是说,该函数或方法的返回类型为`Void``()` 而当只写`return`时,仅仅是将控制权从该函数或方法传递给调用者,而不返回一个值。(这就是说,该函数或方法的返回类型为`Void``()`
> Return 语句语法 > Return 语句语法
> *return语句* → **return** [*表达式*](..\chapter3\04_Expressions.html#expression) _可选_ > *return语句* → **return** [*表达式*](../chapter3/04_Expressions.html#expression) _可选_
<a name="availability_statements"></a>
### Availability 语句
可用性条件,被当做`if` `while` 语句的条件,并且 `guard` 语句在运行时会基于特定的语法格式查询接口的可用性。
avaliability 语句的形式如下:
> if #available(`platform name version`,` ...`, *) {
> `statements to execute if the APIs are available`
> } else {
> `fallback statements to execute if the APIs are unavailable`
> }
可用性条件执行一个代码块时,取决于在运行时想要使用的接口是否可用。
当编译器检查到代码块中的接口是可用的,则从可用性条件中获取相应信息。
可用性条件使用逗号分隔平台名称和版本列表。使用`iOS``OSX`,以及`watchOS`为平台名称,包括相应的版本号。*参数是必需的。在任何平台上代码块主体都被可用性条件保护起来,由满足最低部署条件的目标设备运行。
与布尔类型条件不同,不能用逻辑运算符 **&&** 和 **||** 合并可用性条件。
> 可用性条件语法
> *可用性条件* → **#available** ( [availability-arguments­](TODO) )
> *可用性条件* → [availability-argument­](TODO) | [availability-argument](TODO)­ ,­ [availability-arguments­](TODO)
> *可用性条件* → [平台名称](TODO) [版本号](TODO)
> *可用性条件* → **\***
> *平台名称* → **iOS** | **iOSApplicationExtension**
> *平台名称* → **OSX** | **OSXApplicationExtension­**
> *平台名称* → **watchOS**
> *版本号* → [十进制数字](TODO)
> *版本号* → [十进制数字](TODO) **.** [十进制数字](TODO)
> *版本号* → [十进制数字](TODO) **.** [十进制数字](TODO) **.** [十进制数字](TODO)
<a name="throw_statements"></a>
### Throw 语句
`throw`语句出现在抛出函数或者抛出方法体内,或者类型被`throws`关键字标记的表达式体内。
`throw`语句使程序结束执行当前的作用域,并在封闭作用域中传播错误。抛出的错误会一直传播,直到被`do`语句的`catch`子句处理掉。
`throw`语句由`throw`关键字 跟一个表达式组成 ,如下所示。
> throw `expression`
表达式值的类型必须遵循 `LogicValue`协议
关于如何使用`throw`语句的例子,详情参见[错误处理](TODO)一章的[抛出错误](TODO)。
> throw 语句语法
> *抛出语句* → **throw** *[表达式­](TODO)*
<a name="defer_statements"></a>
### Defer 语句
`defer` 语句用于转移程序控制出延迟语句作用域之前执行代码。
`defer` 语句中的语句无论程序控制如何转移都会执行。这意味着 `defer` 语句可以被使用在以下这些情况,像手动得执行资源管理,关闭文件描述,或者即使抛出了错误也需要去实现执行一些动作。
如果多个 `defer` 语句出现在同一范围内,那么它们执行的顺序与出现的顺序相反。给定作用域中的第一个`defer` 语句,会在最后执行,这意味着最后执行的延迟语句中的语句涉及的资源可以被其他 `defer`语句清理掉。
> 1 func f( ) {
> 2 defer { print("First") }
> 3 defer { print("Second") }
> 4 defer { print("Third") }
> 5 }
> 6 f()
> 7 // prints "Third"
> 8 // prints "Second"
> 9 // prints "First"
`defer` 语句中的语句无法转移程序控制出延迟语句。
> defer 语句语法
> *延迟语句* → **defer** *[代码块](TODO)*
<a name="do_statements"></a>
### Do 语句
`do` 语句用于引入一个新的作用域,该作用域中可以含有一个或多个`catch`子句,catch子句中定义了一些匹配错误情况的模式。`do` 语句作用域内定义的常量和变量只能在do语句作用域内访问。
swift 中的 do 语句与C 中限定代码块界限的大括号 {})很相似,并且在程序运行的时候并不会造成系统开销。
> do {
> try `expression`
> `statements`
> } catch `pattern 1` {
`statements`
> } catch `pattern 2` where condition {
`statements`
> }
如同`switch`语句,编译器会判断`catch`子句是否被遗漏。如果catch没有被遗漏则认为错误被处理。否则错误会自动传播出包含作用域,被一个封闭的`catch`语句或抛出函数处理掉,包含函数必须以`throws`关键字声明。
为了确保错误已经被处理,使用一个匹配所有错误的`catch`子句如通配符模式_。如果一个`catch`子句不指定一种模式,`catch`子句会匹配和约束任何局部变量命名的`error`。有关在`catch`子句中使用模式的更多信息,详见[模式](TODO)。
关于在一些`catch`子句中如何使用` do`语句的例子,详情参见[错误处理](TODO)一章的[抛出错误](TODO)。
> do 语句语法 → **do** *[*代码块*](../chapter3/05_Declarations.html#code_block) [catch](TODO)*
> catch → *[catch子句](TODO) [catch子句](TODO)*
> catch → **catch** *[*模式*](../chapter3/07_Patterns.html#pattern)** *可选的* [*where*]() *可选的* [*代码块*](../chapter3/05_Declarations.html#code_block)

View File

@ -1,8 +1,8 @@
# Access Control 权限控制的黑与白
> 翻译:[老码团队翻译组-Arya](http://weibo.com/littlekok/) > 翻译:[老码团队翻译组-Arya](http://weibo.com/littlekok/)
> 校对:[老码团队翻译组-Oberyn](http://weibo.com/u/5241713117) > 校对:[老码团队翻译组-Oberyn](http://weibo.com/u/5241713117)
# Access Control 权限控制的黑与白
如果您之前没有接触过权限控制,先来听一个小故事: 如果您之前没有接触过权限控制,先来听一个小故事:
> 小明是五道口工业学院的一个大一新生最近他有点烦恼因为同屋经常用他的热水壶好像那是自己家的一样可是碍于同学情面又不好意思说。直到有一天他和学姐小K吐槽。 > 小明是五道口工业学院的一个大一新生最近他有点烦恼因为同屋经常用他的热水壶好像那是自己家的一样可是碍于同学情面又不好意思说。直到有一天他和学姐小K吐槽。

View File

@ -1,9 +1,9 @@
> 翻译:[老码团队翻译组-Tyrion](http://weibo.com/u/5241713117)
> 校对:[老码团队翻译组-Oberyn](http://weibo.com/u/5241713117)
# 造个类型不是梦-白话Swift类型创建 # 造个类型不是梦-白话Swift类型创建
----------------- -----------------
> 翻译:[老码团队翻译组-Tyrion](http://weibo.com/u/5241713117)
> 校对:[老码团队翻译组-Oberyn](http://weibo.com/u/5241713117)
本页包含内容: 本页包含内容:
- [自定义原型](#prototype) - [自定义原型](#prototype)

View File

@ -1,8 +1,8 @@
# WWDC里面的那个“大炮打气球”
> 翻译:[老码团队翻译组-Arya](http://weibo.com/littlekok/) > 翻译:[老码团队翻译组-Arya](http://weibo.com/littlekok/)
> 校对:[老码团队翻译组-](Jame) > 校对:[老码团队翻译组-](Jame)
# WWDC里面的那个“大炮打气球”
![Ballon playground](https://devimages.apple.com.edgekey.net/swift/images/swift-screenshot.jpg) ![Ballon playground](https://devimages.apple.com.edgekey.net/swift/images/swift-screenshot.jpg)

View File

@ -1,9 +1,9 @@
> 翻译:[老码团队翻译组-Relly](http://weibo.com/penguinliong/)
> 校对:[老码团队翻译组-Tyrion](http://weibo.com/u/5241713117)
# Swift与C语言指针友好合作 # Swift与C语言指针友好合作
----------------- -----------------
> 翻译:[老码团队翻译组-Relly](http://weibo.com/penguinliong/)
> 校对:[老码团队翻译组-Tyrion](http://weibo.com/u/5241713117)
本页包含内容: 本页包含内容:
- [用以输入/输出的参数指针](#inout-para-pointer) - [用以输入/输出的参数指针](#inout-para-pointer)

View File

@ -1,9 +1,9 @@
> 翻译:[老码团队翻译组-Arya](http://weibo.com/littlekok/)
> 校对:[老码团队翻译组-Jame](http://weibo.com/u/5241713117)
# Swift里的值类型与引用类型 # Swift里的值类型与引用类型
----------------- -----------------
> 翻译:[老码团队翻译组-Arya](http://weibo.com/littlekok/)
> 校对:[老码团队翻译组-Jame](http://weibo.com/u/5241713117)
本页包含内容: 本页包含内容:
- [值类型与引用类型的区别](#difference-two) - [值类型与引用类型的区别](#difference-two)

View File

@ -1,9 +1,8 @@
> 翻译:[老码团队翻译组-Arya](http://weibo.com/littlekok/)
> 校对:[老码团队翻译组-Jame](http://weibo.com/u/5241713117)
# 访问控制和protected # 访问控制和protected
----------------- -----------------
> 翻译:[老码团队翻译组-Arya](http://weibo.com/littlekok/)
> 校对:[老码团队翻译组-Jame](http://weibo.com/u/5241713117)
原文再续,书折第一回。 原文再续,书折第一回。

View File

@ -1,9 +1,9 @@
> 翻译:[老码团队翻译组-Tyrion](http://weibo.com/u/5241713117)
> 校对:[老码团队翻译组-Ayra](http://weibo.com/littlekok/)
# 可选类型完美解决占位问题 # 可选类型完美解决占位问题
----------------- -----------------
> 翻译:[老码团队翻译组-Tyrion](http://weibo.com/u/5241713117)
> 校对:[老码团队翻译组-Ayra](http://weibo.com/littlekok/)
本页包含内容: 本页包含内容:
- [为Dictionary增加objectsForKeys函数](#add-function) - [为Dictionary增加objectsForKeys函数](#add-function)

89
source/v1.0.md Normal file
View File

@ -0,0 +1,89 @@
> Swift 兴趣交流群:`305014012`307017261已满
> [Swift 开发者社区](http://swiftist.org)
<!-- -->
> 如果你觉得这个项目不错,请[点击Star一下](https://github.com/numbbbbb/the-swift-programming-language-in-chinese),您的支持是我们最大的动力。
<!-- -->
> 关于文档中翻译错误,逻辑错误以及疑难问题答疑,请关注["@老码团队"](http://weibo.com/u/5241713117
)官方微博,会有技术人员统一收集答疑
# The Swift Programming Language 中文版####
###这一次,让中国和世界同步
现在是6月12日凌晨4:38我用了整整一晚上的时间来进行最后的校对终于可以在12日拿出一个可以发布的版本。
9天时间1317个 Star310个 Fork超过30人参与翻译和校对工作项目最高排名GitHub总榜第4。
设想过很多遍校对完成时的场景,仰天大笑还是泪流满面?真正到了这一刻才发现,疲倦已经不允许我有任何情绪。
说实话,刚开始发起项目的时候完全没想到会发展成今天这样,我一度计划自己一个人翻译完整本书。万万没想到,会有这么多的人愿意加入并贡献出自己的力量。
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)

BIN
swift 2.0引用图片/1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
swift 2.0引用图片/2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
swift 2.0引用图片/3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

BIN
swift 2.0引用图片/4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

BIN
swift 2.0引用图片/5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
swift 2.0引用图片/6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB