Merge branch 'numbbbbb/gh-pages' into gh-pages
57
README.md
@ -3,26 +3,59 @@
|
||||
|
||||
中文版Apple官方Swift教程《The Swift Programming Language》
|
||||
|
||||
中文Swift社区[Swiftist](http://swiftist.org/),新社区,正在建设中,感兴趣的朋友可以一起参与进来。
|
||||
[英文原版](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/index.html#//apple_ref/doc/uid/TP40014097-CH3-ID0)
|
||||
|
||||
**如果想帮忙翻译或者校对,请加QQ群:364279588,谢谢!**
|
||||
**如果想帮忙翻译或者校对,请加QQ群:480616051,谢谢!**
|
||||
|
||||
# 在线阅读
|
||||
|
||||
使用Gitbook制作,可以直接[在线阅读](http://numbbbbb.github.io/the-swift-programming-language-in-chinese/)。
|
||||
|
||||
# 电子书下载
|
||||
|
||||
CocoaChina精校PDF→[点我下载](http://vdisk.weibo.com/s/EhsPPzRRQ5CZ/1402621206)
|
||||
|
||||
其他格式可以通过PDF转换
|
||||
使用Gitbook制作,可以直接[在线阅读](http://swiftguide.cn/)。
|
||||
|
||||
# 当前阶段
|
||||
|
||||
文章已经全部翻译完成,当前阶段为自由校对阶段,可以随意提issue和pr。
|
||||
已经更新到Swift 2.0,对应苹果官方文档7.23的更新。
|
||||
|
||||
# 2.0译者记录
|
||||
|
||||
# 译者记录
|
||||
- About Swift ([xtymichael](https://github.com/xtymichael))
|
||||
- A Swift Tour([xtymichael](https://github.com/xtymichael))
|
||||
- The Basics([xtymichael](https://github.com/xtymichael))
|
||||
- Basic Operators([AlanMelody](https://github.com/AlanMelody))
|
||||
- Strings and Characters([DianQK](https://github.com/DianQK))
|
||||
- Collection Types([AlanMelody](https://github.com/AlanMelody))
|
||||
- Control Flow([AlanMelody](https://github.com/AlanMelody))
|
||||
- Functions([dreamkidd](https://github.com/dreamkidd))
|
||||
- Closures([100mango](https://github.com/100mango))
|
||||
- Enumerations([futantan](https://github.com/futantan))
|
||||
- Classes and Structures([SkyJean](https://github.com/SkyJean))
|
||||
- Properties([yangsiy](https://github.com/yangsiy))
|
||||
- Methods([DianQK](https://github.com/DianQK))
|
||||
- Subscripts([shanksyang](https://github.com/shanksyang))
|
||||
- Inheritance([shanksyang](https://github.com/shanksyang))
|
||||
- Initialization([chenmingbiao](https://github.com/chenmingbiao))
|
||||
- Deinitialization([chenmingbiao](https://github.com/chenmingbiao))
|
||||
- Automatic Reference Counting([Channe](https://github.com/Channe))
|
||||
- Optional Chaining([lyojo](https://github.com/lyojo))
|
||||
- Error Handling([lyojo](https://github.com/lyojo))
|
||||
- Type Casting([yangsiy](https://github.com/yangsiy))
|
||||
- Nested Types([SergioChan](https://github.com/SergioChan))
|
||||
- Extensions([shanksyang](https://github.com/shanksyang))
|
||||
- Protocols([futantan](https://github.com/futantan))
|
||||
- Generics([SergioChan](https://github.com/SergioChan))
|
||||
- Access Control([mmoaay](https://github.com/mmoaay))
|
||||
- Advanced Operators([buginux](https://github.com/buginux))
|
||||
- About the Language Reference([KYawn](https://github.com/KYawn))
|
||||
- Lexical Structure([buginux](https://github.com/buginux))
|
||||
- Types([EudeMorgen](https://github.com/EudeMorgen))
|
||||
- Expressions([EudeMorgen](https://github.com/EudeMorgen))
|
||||
- Statements([littledogboy](https://github.com/littledogboy))
|
||||
- Declarations([Lenhoon](https://github.com/Lenhoon))
|
||||
- Attributes([KYawn](https://github.com/KYawn))
|
||||
- Patterns([ray16897188](https://github.com/ray16897188))
|
||||
- Generic Parameters and Arguments([wardenNScaiyi](https://github.com/wardenNScaiyi))
|
||||
- Summary of the Grammar([miaosiqi](https://github.com/miaosiqi))
|
||||
|
||||
# 1.0译者记录
|
||||
|
||||
* 欢迎使用 Swift
|
||||
* 关于 Swift ([numbbbbb])
|
||||
@ -171,4 +204,4 @@ CocoaChina精校PDF→[点我下载](http://vdisk.weibo.com/s/EhsPPzRRQ5CZ/14026
|
||||
[Lin-H]:https://github.com/Lin-H
|
||||
[takalard]:https://github.com/takalard
|
||||
[dabing1022]:https://github.com/dabing1022
|
||||
[marsprince]:https://github.com/marsprince
|
||||
[marsprince]:https://github.com/marsprince
|
||||
|
||||
9
config.json
Normal 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
|
After Width: | Height: | Size: 30 KiB |
BIN
cover/cover.jpg
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
cover/logo.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
@ -3,7 +3,7 @@
|
||||
|
||||
<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/" />
|
||||
|
||||
|
||||
|
||||
|
||||
@ -301,7 +301,7 @@ func lessThanTen(number: Int) -> Bool {
|
||||
return number < 10
|
||||
}
|
||||
var numbers = [20, 19, 7, 12]
|
||||
hasAnyMatches(numbers, lessThanTen)
|
||||
hasAnyMatches(numbers, condition: lessThanTen)
|
||||
```
|
||||
|
||||
函數實際上是一種特殊的閉包,你可以使用`{}`來創建一個匿名閉包。使用`in`將參數和返回值類型聲明與閉包涵數體進行分離。
|
||||
@ -649,7 +649,7 @@ let bDescription = b.simpleDescription
|
||||
|
||||
注意聲明`SimpleStructure`時候`mutating`關鍵字用來標記一個會修改結構體的方法。`SimpleClass`的聲明不需要標記任何方法因為類中的方法經常會修改類。
|
||||
|
||||
使用`extension`來為現有的類型添加功能,比如新的方法和參數。你可以使用擴展來改造定義在別處,甚至是從外部庫或者框架引入的一個類型,使得這個類型遵循某個協議。
|
||||
使用`extension`來為現有的類型添加功能,比如新的方法和計算屬性。你可以使用擴展來改造定義在別處,甚至是從外部庫或者框架引入的一個類型,使得這個類型遵循某個協議。
|
||||
|
||||
```swift
|
||||
extension Int: ExampleProtocol {
|
||||
|
||||
@ -225,7 +225,7 @@ ovenLight.next()
|
||||
|
||||
```swift
|
||||
class SomeClass {
|
||||
class func someTypeMethod() {
|
||||
static func someTypeMethod() {
|
||||
// type method implementation goes here
|
||||
}
|
||||
}
|
||||
|
||||
135
source/README.md
@ -1,89 +1,78 @@
|
||||
> Swift 兴趣交流群:`305014012`,307017261(已满)
|
||||
> [Swift 开发者社区](http://swiftist.org)
|
||||
# 2.0 新的开始
|
||||
|
||||
> 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>,您的支持是我们最大的动力。
|
||||
|
||||
<!-- -->
|
||||
> 关于文档中翻译错误,逻辑错误以及疑难问题答疑,请关注["@老码团队"](http://weibo.com/u/5241713117
|
||||
)官方微博,会有技术人员统一收集答疑
|
||||
## 1
|
||||
|
||||
开源项目完成难,维护更难。
|
||||
|
||||
# The Swift Programming Language 中文版####
|
||||
大家看到的是发布时的瞩目和荣耀,却没有看到项目本身质量不高、错误频出。这并不是翻译者和校对者的问题,他们已经付出了足够的努力。真正的问题在我,没有建立起长期的维护团队,因此后期的校对和更新都难以实施。
|
||||
|
||||
###这一次,让中国和世界同步
|
||||
1.0发布之后,我们就再也没能和苹果的文档同步。语法错误、编译不通过、语言不通顺,阅读量直线下降,最低时每天只有不到1000人访问。
|
||||
|
||||
现在是6月12日凌晨4:38,我用了整整一晚上的时间来进行最后的校对,终于可以在12日拿出一个可以发布的版本。
|
||||
6月9日,calvingit发了一个issue“准备翻译2.0版本吗”,我没有回复,应该已经没人关注这个项目了吧,我想。
|
||||
|
||||
9天时间,1317个 Star,310个 Fork,超过30人参与翻译和校对工作,项目最高排名GitHub总榜第4。
|
||||
## 2
|
||||
|
||||
设想过很多遍校对完成时的场景,仰天大笑还是泪流满面?真正到了这一刻才发现,疲倦已经不允许我有任何情绪。
|
||||
我错了。
|
||||
|
||||
说实话,刚开始发起项目的时候完全没想到会发展成今天这样,我一度计划自己一个人翻译完整本书。万万没想到,会有这么多的人愿意加入并贡献出自己的力量。
|
||||

|
||||
|
||||
coverxit发给我最后一份文档的时候说,我要去背单词了,我问他,周末要考六级?他说是的。
|
||||

|
||||
|
||||
pp-prog告诉我,这几天太累了,校对到一半睡着了,醒来又继续做。2点17分,发给我校对完成的文档。
|
||||

|
||||
|
||||
lifedim说他平时12点就会睡,1点47分,发给我校对后的文档。
|
||||

|
||||
|
||||
团队里每个人都有自己的事情,上班、上学、创业,但是我们只用了9天就完成整本书的翻译。我不知道大家付出了多少,牺牲了多少,但是我知道,他们的付出必将被这些文字记录下来,即使再过10年,20年,依然熠熠生辉,永不被人遗忘。
|
||||

|
||||
|
||||
全体人员名单(排名不分先后):
|
||||
在我没有任何回复的情况下,不到一天时间,有五位朋友报名。看到这些回复的时候我真的很惊讶,也很感动,无论这个项目存在多少问题,只要有人关注,有人愿意为它付出,那我还有什么理由放弃呢?
|
||||
|
||||
- [numbbbbb](https://github.com/numbbbbb)
|
||||
- [stanzhai](https://github.com/stanzhai)
|
||||
- [coverxit](https://github.com/coverxit)
|
||||
- [wh1100717](https://github.com/wh1100717)
|
||||
- [TimothyYe](https://github.com/TimothyYe)
|
||||
- [honghaoz](https://github.com/honghaoz)
|
||||
- [lyuka](https://github.com/lyuka)
|
||||
- [JaySurplus](https://github.com/JaySurplus)
|
||||
- [Hawstein](https://github.com/Hawstein)
|
||||
- [geek5nan](https://github.com/geek5nan)
|
||||
- [yankuangshi](https://github.com/yankuangshi)
|
||||
- [xielingwang](https://github.com/xielingwang)
|
||||
- [yulingtianxia](https://github.com/yulingtianxia)
|
||||
- [twlkyao](https://github.com/twlkyao)
|
||||
- [dabing1022](https://github.com/dabing1022)
|
||||
- [vclwei](https://github.com/vclwei)
|
||||
- [fd5788](https://github.com/fd5788)
|
||||
- [siemenliu](https://github.com/siemenliu)
|
||||
- [youkugems](https://github.com/youkugems)
|
||||
- [haolloyin](https://github.com/haolloyin)
|
||||
- [wxstars](https://github.com/wxstars)
|
||||
- [IceskYsl](https://github.com/IceskYsl)
|
||||
- [sg552](https://github.com/sg552)
|
||||
- [superkam](https://github.com/superkam)
|
||||
- [zac1st1k](https://github.com/zac1st1k)
|
||||
- [bzsy](https://github.com/bzsy)
|
||||
- [pyanfield](https://github.com/pyanfield)
|
||||
- [ericzyh](https://github.com/ericzyh)
|
||||
- [peiyucn](https://github.com/peiyucn)
|
||||
- [sunfiled](https://github.com/sunfiled)
|
||||
- [lzw120](https://github.com/lzw120)
|
||||
- [viztor](https://github.com/viztor)
|
||||
- [wongzigii](https://github.com/wongzigii)
|
||||
- [umcsdon](https://github.com/umcsdon)
|
||||
- [zq54zquan](https://github.com/zq54zquan)
|
||||
- [xiehurricane](https://github.com/xiehurricane)
|
||||
- [Jasonbroker](https://github.com/Jasonbroker)
|
||||
- [tualatrix](https://github.com/tualatrix)
|
||||
- [pp-prog](https://github.com/pp-prog)
|
||||
- [088haizi](https://github.com/088haizi)
|
||||
- [baocaixiong](https://github.com/baocaixiong)
|
||||
- [yeahdongcn](https://github.com/yeahdongcn)
|
||||
- [shinyzhu](https://github.com/shinyzhu)
|
||||
- [lslxdx](https://github.com/lslxdx)
|
||||
- [Evilcome](https://github.com/Evilcome)
|
||||
- [zqp](https://github.com/zqp)
|
||||
- [NicePiao](https://github.com/NicePiao)
|
||||
- [LunaticM](https://github.com/LunaticM)
|
||||
- [menlongsheng](https://github.com/menlongsheng)
|
||||
- [lifedim](https://github.com/lifedim)
|
||||
- [happyming](https://github.com/happyming)
|
||||
- [bruce0505](https://github.com/bruce0505)
|
||||
- [Lin-H](https://github.com/Lin-H)
|
||||
- [takalard](https://github.com/takalard)
|
||||
- [dabing1022](https://github.com/dabing1022)
|
||||
- [marsprince](https://github.com/marsprince)
|
||||
6月28日8点55分,Swift 2.0翻译正式启动。按下发送按钮后,我不停的刷新页面,半个小时过去了,一个回复都没有。“还是不行啊”“如果再过一个小时没人回复我就把issue删掉”,类似的念头不断出现,又不断消失。
|
||||
|
||||
9:35,xtymichael第一个回复,而且一下就认领了三篇!接下来就是不断的回复认领,到中午已经有超过一半章节被认领。
|
||||
|
||||
第二天早晨,37个章节全部认领完毕。
|
||||
|
||||
## 3
|
||||
|
||||
经过一个多月的努力,我们终于完成了文档的更新。听起来似乎没什么,确实,从1到n总是没有从0到1那么振奋人心。不过真正参与了才知道,修改往往比创造更麻烦,一个需要耐心,一个需要激情,前者往往得不到应有的重视。
|
||||
|
||||
但是我还是想尽最大可能去感谢他们,这个项目能走到今天,靠的不是我,是那个issue,是那些回复,是这几十个兄弟在工作学习的空闲敲下的每一个字符。而我能做的,只是在每篇文章的开头,那个所有人都会忽略的地方,加上他们的ID。
|
||||
|
||||
下次你再打开这篇文档,可以多看看那些列在最上方的ID,哪怕不去follow和star,只是看一眼就好。他们的所有努力和付出,就存在于这短暂的一瞥中。
|
||||
|
||||
Swift 2.0 参与者名单(按照章节顺序):
|
||||
- [xtymichael](https://github.com/xtymichael)
|
||||
- [AlanMelody](https://github.com/AlanMelody)
|
||||
- [DianQK](https://github.com/DianQK)
|
||||
- [dreamkidd](https://github.com/dreamkidd)
|
||||
- [100mango](https://github.com/100mango)
|
||||
- [futantan](https://github.com/futantan)
|
||||
- [SkyJean](https://github.com/SkyJean)
|
||||
- [yangsiy](https://github.com/yangsiy)
|
||||
- [shanksyang](https://github.com/shanksyang)
|
||||
- [chenmingbiao](https://github.com/chenmingbiao)
|
||||
- [Channe](https://github.com/Channe)
|
||||
- [lyojo](https://github.com/lyojo)
|
||||
- [SergioChan](https://github.com/SergioChan)
|
||||
- [mmoaay](https://github.com/mmoaay)
|
||||
- [buginux](https://github.com/buginux)
|
||||
- [KYawn](https://github.com/KYawn)
|
||||
- [EudeMorgen](https://github.com/EudeMorgen)
|
||||
- [littledogboy](https://github.com/littledogboy)
|
||||
- [Lenhoon](https://github.com/Lenhoon)
|
||||
- [ray16897188](https://github.com/ray16897188)
|
||||
- [wardenNScaiyi](https://github.com/wardenNScaiyi)
|
||||
- [miaosiqi](https://github.com/miaosiqi)
|
||||
|
||||
最后,感谢<a target='_blank' href="http://wiki.jikexueyuan.com/">极客学院</a>提供的wiki系统,在国内访问起来速度很快,优化后的样式看起来也更舒服。
|
||||
@ -4,6 +4,7 @@
|
||||
* [关于 Swift](chapter1/01_swift.md)
|
||||
* [Swift 初见](chapter1/02_a_swift_tour.md)
|
||||
* [Swift 版本历史记录](chapter1/03_revision_history.md)
|
||||
* [Swift 1.0 发布内容](v1.0.md)
|
||||
* [Swift 教程](chapter2/chapter2.md)
|
||||
* [基础部分](chapter2/01_The_Basics.md)
|
||||
* [基本运算符](chapter2/02_Basic_Operators.md)
|
||||
@ -30,7 +31,7 @@
|
||||
* [泛型](chapter2/23_Generics.md)
|
||||
* [权限控制](chapter2/24_Access_Control.md)
|
||||
* [高级操作符](chapter2/25_Advanced_Operators.md)
|
||||
* [语言参考](chapter3/chapter3.md)
|
||||
* 语言参考
|
||||
* [关于语言参考](chapter3/01_About_the_Language_Reference.md)
|
||||
* [词法结构](chapter3/02_Lexical_Structure.md)
|
||||
* [类型](chapter3/03_Types.md)
|
||||
@ -41,7 +42,7 @@
|
||||
* [模式](chapter3/07_Patterns.md)
|
||||
* [泛型参数](chapter3/08_Generic_Parameters_and_Arguments.md)
|
||||
* [语法总结](chapter3/09_Summary_of_the_Grammar.md)
|
||||
* [苹果官方Blog官方翻译](chapter4/chapter4.md)
|
||||
* 苹果官方Blog官方翻译
|
||||
* [Access Control 权限控制的黑与白](chapter4/01_Access_Control.md)
|
||||
* [造个类型不是梦-白话Swift类型创建](chapter4/02_Type_Custom.md)
|
||||
* [WWDC里面的那个“大炮打气球”](chapter4/03_Ballons.md)
|
||||
|
||||
@ -1,16 +1,21 @@
|
||||
# 关于 Swift(About Swift)
|
||||
-----------------
|
||||
|
||||
> 1.0
|
||||
> 翻译:[numbbbbb](https://github.com/numbbbbb)
|
||||
> 校对:[yeahdongcn](https://github.com/yeahdongcn)
|
||||
|
||||
# 关于 Swift
|
||||
-----------------
|
||||
> 2.0
|
||||
> 翻译+校对:[xtymichael](https://github.com/xtymichael)
|
||||
|
||||
Swift 是一种新的编程语言,用于编写 iOS,OS X 和 watchOS应用程序。Swift 结合了 C 和 Objective-C 的优点并且不受 C 兼容性的限制。Swift 采用安全的编程模式并添加了很多新特性,这将使编程更简单,更灵活,也更有趣。Swift 是基于成熟而且倍受喜爱的 Cocoa 和 Cocoa Touch 框架,它的降临将重新定义软件开发。
|
||||
|
||||
Swift 的开发从很久之前就开始了。为了给 Swift 打好基础,苹果公司改进了编译器,调试器和框架结构。我们使用自动引用计数(Automatic Reference Counting, ARC)来简化内存管理。我们在 Foundation 和 Cocoa 的基础上构建框架栈并将其标准化。Objective-C 本身支持块、集合语法和模块,所以框架可以轻松支持现代编程语言技术。正是得益于这些基础工作,我们现在才能发布这样一个用于未来苹果软件开发的新语言。
|
||||
Swift 的开发从很久之前就开始了。为了给 Swift 打好基础,苹果公司改进了编译器,调试器和框架结构。我们使用自动引用计数(Automatic Reference Counting, ARC)来简化内存管理。我们在 Foundation 和 Cocoa 的基础上构建框架栈使其完全现代化和标准化。
|
||||
Objective-C 本身支持块、集合语法和模块,所以框架可以轻松支持现代编程语言技术。正是得益于这些基础工作,我们现在才能发布这样一个用于未来苹果软件开发的新语言。
|
||||
|
||||
Objective-C 开发者对 Swift 并不会感到陌生。它采用了 Objective-C 的命名参数以及动态对象模型,可以无缝对接到现有的 Cocoa 框架,并且可以兼容 Objective-C 代码。在此基础之上,Swift 还有许多新特性并且支持过程式编程和面向对象编程。
|
||||
|
||||
Swift 对于初学者来说也很友好。它是第一个既满足工业标准又像脚本语言一样充满表现力和趣味的编程语言。它支持代码预览,这个革命性的特性可以允许程序员在不编译和运行应用程序的前提下运行 Swift 代码并实时查看结果。
|
||||
Swift 对于初学者来说也很友好。它是第一个既满足工业标准又像脚本语言一样充满表现力和趣味的脚本语言。它支持代码预览,这个革命性的特性可以允许程序员在不编译和运行应用程序的前提下运行 Swift 代码并实时查看结果。
|
||||
|
||||
Swift 将现代编程语言的精华和苹果工程师文化的智慧结合了起来。编译器对性能进行了优化,编程语言对开发进行了优化,两者互不干扰,鱼与熊掌兼得。Swift 既可以用于开发 “hello, world” 这样的小程序,也可以用于开发一套完整的操作系统。所有的这些特性让 Swift 对于开发者和苹果来说都是一项值得的投资。
|
||||
|
||||
|
||||
@ -1,9 +1,13 @@
|
||||
# Swift 初见(A Swift Tour)
|
||||
|
||||
---
|
||||
|
||||
> 1.0
|
||||
> 翻译:[numbbbbb](https://github.com/numbbbbb)
|
||||
> 校对:[shinyzhu](https://github.com/shinyzhu), [stanzhai](https://github.com/stanzhai)
|
||||
|
||||
# Swift 初见
|
||||
|
||||
---
|
||||
> 2.0
|
||||
> 翻译+校对:[xtymichael](https://github.com/xtymichael)
|
||||
|
||||
本页内容包括:
|
||||
|
||||
@ -18,16 +22,16 @@
|
||||
通常来说,编程语言教程中的第一个程序应该在屏幕上打印“Hello, world”。在 Swift 中,可以用一行代码实现:
|
||||
|
||||
```swift
|
||||
print("Hello, world")
|
||||
print("Hello, world!")
|
||||
```
|
||||
|
||||
如果你写过 C 或者 Objective-C 代码,那你应该很熟悉这种形式——在 Swift 中,这行代码就是一个完整的程序。你不需要为了输入输出或者字符串处理导入一个单独的库。全局作用域中的代码会被自动当做程序的入口点,所以你也不需要`main`函数。你同样不需要在每个语句结尾写上分号。
|
||||
如果你写过 C 或者 Objective-C 代码,那你应该很熟悉这种形式——在 Swift 中,这行代码就是一个完整的程序。你不需要为了输入输出或者字符串处理导入一个单独的库。全局作用域中的代码会被自动当做程序的入口点,所以你也不需要`main()`函数。你同样不需要在每个语句结尾写上分号。
|
||||
|
||||
这个教程会通过一系列编程例子来让你对 Swift 有初步了解,如果你有什么不理解的地方也不用担心——任何本章介绍的内容都会在后面的章节中详细讲解。
|
||||
|
||||
> 注意:
|
||||
> 为了获得最好的体验,在 Xcode 当中使用代码预览功能。代码预览功能可以让你编辑代码并实时看到运行结果。
|
||||
> <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>
|
||||
## 简单值
|
||||
@ -40,7 +44,7 @@ myVariable = 50
|
||||
let myConstant = 42
|
||||
```
|
||||
|
||||
常量或者变量的类型必须和你赋给它们的值一样。然而,声明时类型是可选的,声明的同时赋值的话,编译器会自动推断类型。在上面的例子中,编译器推断出`myVariable`是一个整数(integer)因为它的初始值是整数。
|
||||
常量或者变量的类型必须和你赋给它们的值一样。然而,你不用明确地声明类型,声明的同时赋值的话,编译器会自动推断类型。在上面的例子中,编译器推断出`myVariable`是一个整数(integer)因为它的初始值是整数。
|
||||
|
||||
如果初始值没有提供足够的信息(或者没有初始值),那你需要在变量后面声明类型,用冒号分割。
|
||||
|
||||
@ -75,14 +79,12 @@ let fruitSummary = "I have \(apples + oranges) pieces of fruit."
|
||||
> 练习:
|
||||
> 使用`\()`来把一个浮点计算转换成字符串,并加上某人的名字,和他打个招呼。
|
||||
|
||||
使用方括号`[]`来创建数组和字典,并使用下标或者键(key)来访问元素。
|
||||
使用方括号`[]`来创建数组和字典,并使用下标或者键(key)来访问元素。最后一个元素后面允许有个逗号。
|
||||
|
||||
```swift
|
||||
var shoppingList = ["catfish", "water", "tulips", "blue paint"]
|
||||
shoppingList[1] = "bottle of water"
|
||||
```
|
||||
|
||||
```swift
|
||||
var occupations = [
|
||||
"Malcolm": "Captain",
|
||||
"Kaylee": "Mechanic",
|
||||
@ -107,7 +109,7 @@ occupations = [:]
|
||||
<a name="control_flow"></a>
|
||||
## 控制流
|
||||
|
||||
使用`if`和`switch`来进行条件操作,使用`for-in`、`for`、`while`和`do-while`来进行循环。包裹条件和循环变量括号可以省略,但是语句体的大括号是必须的。
|
||||
使用`if`和`switch`来进行条件操作,使用`for-in`、`for`、`while`和`repeat-while`来进行循环。包裹条件和循环变量括号可以省略,但是语句体的大括号是必须的。
|
||||
|
||||
```swift
|
||||
let individualScores = [75, 43, 103, 87, 12]
|
||||
@ -124,7 +126,7 @@ print(teamScore)
|
||||
|
||||
在`if`语句中,条件必须是一个布尔表达式——这意味着像`if score { ... }`这样的代码将报错,而不会隐形地与 0 做对比。
|
||||
|
||||
你可以一起使用`if`和`let`来处理值缺失的情况。有些变量的值是可选的。一个可选的值可能是一个具体的值或者是`nil`,表示值缺失。在类型后面加一个问号来标记这个变量的值是可选的。
|
||||
你可以一起使用`if`和`let`来处理值缺失的情况。这些值可由可选值来代表。一个可选的值是一个具体的值或者是`nil`以表示值缺失。在类型后面加一个问号来标记这个变量的值是可选的。
|
||||
|
||||
```swift
|
||||
var optionalString: String? = "Hello"
|
||||
@ -148,20 +150,21 @@ if let name = optionalName {
|
||||
let vegetable = "red pepper"
|
||||
switch vegetable {
|
||||
case "celery":
|
||||
let vegetableComment = "Add some raisins and make ants on a log."
|
||||
print("Add some raisins and make ants on a log.")
|
||||
case "cucumber", "watercress":
|
||||
let vegetableComment = "That would make a good tea sandwich."
|
||||
print("That would make a good tea sandwich.")
|
||||
case let x where x.hasSuffix("pepper"):
|
||||
let vegetableComment = "Is it a spicy \(x)?"
|
||||
print("Is it a spicy \(x)?")
|
||||
default:
|
||||
let vegetableComment = "Everything tastes good in soup."
|
||||
print("Everything tastes good in soup.")
|
||||
}
|
||||
```
|
||||
|
||||
> 练习:
|
||||
> 删除`default`语句,看看会有什么错误?
|
||||
|
||||
声明'let'可用于匹配某部分固定值的模式
|
||||
|
||||
注意`let`在上述例子的等式中是如何使用的,它将匹配等式的值赋给常量`x`。
|
||||
|
||||
运行`switch`中匹配到的子句之后,程序会退出`switch`语句,并不会继续向下运行,所以不需要在每个子句结尾写`break`。
|
||||
|
||||
@ -185,9 +188,9 @@ print(largest)
|
||||
```
|
||||
|
||||
> 练习:
|
||||
> 添加另一个变量来记录哪种类型的数字是最大的。
|
||||
> 添加另一个变量来记录现在和之前最大数字的类型。
|
||||
|
||||
使用`while`来重复运行一段代码直到不满足条件。循环条件可以在开头也可以在结尾。
|
||||
使用`while`来重复运行一段代码直到不满足条件。循环条件也可以在结尾,保证能至少循环一次。
|
||||
|
||||
```swift
|
||||
var n = 2
|
||||
@ -197,7 +200,7 @@ while n < 100 {
|
||||
print(n)
|
||||
|
||||
var m = 2
|
||||
do {
|
||||
repeat {
|
||||
m = m * 2
|
||||
} while m < 100
|
||||
print(m)
|
||||
@ -224,7 +227,7 @@ print(secondForLoop)
|
||||
<a name="functions_and_closures"></a>
|
||||
## 函数和闭包
|
||||
|
||||
使用`func`来声明一个函数,使用名字和参数来调用函数。使用`->`来指定函数返回值。
|
||||
使用`func`来声明一个函数,使用名字和参数来调用函数。使用`->`来指定函数返回值的类型。
|
||||
|
||||
```swift
|
||||
func greet(name: String, day: String) -> String {
|
||||
@ -319,10 +322,10 @@ func lessThanTen(number: Int) -> Bool {
|
||||
return number < 10
|
||||
}
|
||||
var numbers = [20, 19, 7, 12]
|
||||
hasAnyMatches(numbers, lessThanTen)
|
||||
hasAnyMatches(numbers, condition: lessThanTen)
|
||||
```
|
||||
|
||||
函数实际上是一种特殊的闭包,你可以使用`{}`来创建一个匿名闭包。使用`in`将参数和返回值类型声明与闭包函数体进行分离。
|
||||
函数实际上是一种特殊的闭包:它是一段能之后被调取的代码。闭包中的代码能访问闭包所建作用域中能得到的变量和函数,即使闭包是在一个不同的作用域被执行的 - 你已经在嵌套函数例子中所看到。你可以使用`{}`来创建一个匿名闭包。使用`in`将参数和返回值类型声明与闭包函数体进行分离。
|
||||
|
||||
```swift
|
||||
numbers.map({
|
||||
@ -335,17 +338,17 @@ numbers.map({
|
||||
> 练习:
|
||||
> 重写闭包,对所有奇数返回0。
|
||||
|
||||
有很多种创建闭包的方法。如果一个闭包的类型已知,比如作为一个回调函数,你可以忽略参数的类型和返回值。单个语句闭包会把它语句的值当做结果返回。
|
||||
有很多种创建更简洁的闭包的方法。如果一个闭包的类型已知,比如作为一个回调函数,你可以忽略参数的类型和返回值。单个语句闭包会把它语句的值当做结果返回。
|
||||
|
||||
```swift
|
||||
let mappedNumbers = numbers.map({ number in 3 * number })
|
||||
mappedNumbers
|
||||
print(mappedNumbers)
|
||||
```
|
||||
|
||||
你可以通过参数位置而不是参数名字来引用参数——这个方法在非常短的闭包中非常有用。当一个闭包作为最后一个参数传给一个函数的时候,它可以直接跟在括号后面。
|
||||
你可以通过参数位置而不是参数名字来引用参数——这个方法在非常短的闭包中非常有用。当一个闭包作为最后一个参数传给一个函数的时候,它可以直接跟在括号后面。当一个闭包是传给函数的唯一参数,你可以完全忽略括号。
|
||||
|
||||
```swift
|
||||
let sortedNumbers = sorted(numbers) { $0 > $1 }
|
||||
let sortedNumbers = numbers.sort { $0 > $1 }
|
||||
print(sortedNumbers)
|
||||
```
|
||||
|
||||
@ -423,9 +426,9 @@ test.simpleDescription()
|
||||
```
|
||||
|
||||
> 练习:
|
||||
> 创建`NamedShape`的另一个子类`Circle`,构造器接收两个参数,一个是半径一个是名称,实现`area`和`describe`方法。
|
||||
> 创建`NamedShape`的另一个子类`Circle`,构造器接收两个参数,一个是半径一个是名称,在子类`Circle`中实现`area()`和`simpleDescription()`方法。
|
||||
|
||||
属性可以有 getter 和 setter 。
|
||||
除了储存简单的属性之外,属性可以有 getter 和 setter 。
|
||||
|
||||
```swift
|
||||
class EquilateralTriangle: NamedShape {
|
||||
@ -438,12 +441,12 @@ class EquilateralTriangle: NamedShape {
|
||||
}
|
||||
|
||||
var perimeter: Double {
|
||||
get {
|
||||
return 3.0 * sideLength
|
||||
}
|
||||
set {
|
||||
sideLength = newValue / 3.0
|
||||
}
|
||||
get {
|
||||
return 3.0 * sideLength
|
||||
}
|
||||
set {
|
||||
sideLength = newValue / 3.0
|
||||
}
|
||||
}
|
||||
|
||||
override func simpleDescription() -> String {
|
||||
@ -471,14 +474,14 @@ print(triangle.sideLength)
|
||||
```swift
|
||||
class TriangleAndSquare {
|
||||
var triangle: EquilateralTriangle {
|
||||
willSet {
|
||||
square.sideLength = newValue.sideLength
|
||||
}
|
||||
willSet {
|
||||
square.sideLength = newValue.sideLength
|
||||
}
|
||||
}
|
||||
var square: Square {
|
||||
willSet {
|
||||
triangle.sideLength = newValue.sideLength
|
||||
}
|
||||
willSet {
|
||||
triangle.sideLength = newValue.sideLength
|
||||
}
|
||||
}
|
||||
init(size: Double, name: String) {
|
||||
square = Square(sideLength: size, name: name)
|
||||
@ -486,23 +489,10 @@ class TriangleAndSquare {
|
||||
}
|
||||
}
|
||||
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
|
||||
triangleAndSquare.square.sideLength
|
||||
triangleAndSquare.triangle.sideLength
|
||||
print(triangleAndSquare.square.sideLength)
|
||||
print(triangleAndSquare.triangle.sideLength)
|
||||
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
|
||||
triangleAndSquare.triangle.sideLength
|
||||
```
|
||||
|
||||
类中的方法和一般的函数有一个重要的区别,函数的参数名只在函数内部使用,但是方法的参数名需要在调用的时候显式说明(除了第一个参数)。默认情况下,方法的参数名和它在方法内部的名字一样,不过你也可以定义第二个名字,这个名字被用在方法内部。
|
||||
|
||||
```swift
|
||||
class Counter {
|
||||
var count: Int = 0
|
||||
func incrementBy(amount: Int, numberOfTimes times: Int) {
|
||||
count += amount * times
|
||||
}
|
||||
}
|
||||
var counter = Counter()
|
||||
counter.incrementBy(2, numberOfTimes: 7)
|
||||
print(triangleAndSquare.triangle.sideLength)
|
||||
```
|
||||
|
||||
处理变量的可选值时,你可以在操作(比如方法、属性和子脚本)之前加`?`。如果`?`之前的值是`nil`,`?`后面的东西都会被忽略,并且整个表达式返回`nil`。否则,`?`之后的东西都会被运行。在这两种情况下,整个表达式的值也是一个可选值。
|
||||
@ -544,9 +534,9 @@ let aceRawValue = ace.rawValue
|
||||
> 练习:
|
||||
> 写一个函数,通过比较它们的原始值来比较两个`Rank`值。
|
||||
|
||||
在上面的例子中,枚举原始值的类型是`Int`,所以你只需要设置第一个原始值。剩下的原始值会按照顺序赋值。你也可以使用字符串或者浮点数作为枚举的原始值。
|
||||
在上面的例子中,枚举原始值的类型是`Int`,所以你只需要设置第一个原始值。剩下的原始值会按照顺序赋值。你也可以使用字符串或者浮点数作为枚举的原始值。使用`rawValue`属性来访问一个枚举成员的原始值。
|
||||
|
||||
使用'rawValue'在原始值和枚举值之间进行转换。
|
||||
使用`init?(rawValue:)`初始化构造器在原始值和枚举值之间进行转换。
|
||||
|
||||
```swift
|
||||
if let convertedRank = Rank(rawValue: 3) {
|
||||
@ -554,7 +544,7 @@ if let convertedRank = Rank(rawValue: 3) {
|
||||
}
|
||||
```
|
||||
|
||||
枚举的成员值是实际值,并不是原始值的另一种表达方法。实际上,如果原始值没有意义,你不需要设置。
|
||||
枚举的成员值是实际值,并不是原始值的另一种表达方法。实际上,以防原始值没有意义,你不需要设置。
|
||||
|
||||
```swift
|
||||
enum Suit {
|
||||
@ -571,19 +561,17 @@ enum Suit {
|
||||
return "clubs"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
let hearts = Suit.Hearts
|
||||
let heartsDescription = hearts.simpleDescription()
|
||||
```
|
||||
|
||||
> 练习:
|
||||
> 给`Suit`添加一个`color`方法,对`spades`和`clubs`返回“black”,对`hearts`和`diamonds`返回“red”。
|
||||
> 给`Suit`添加一个`color()`方法,对`spades`和`clubs`返回“black”,对`hearts`和`diamonds`返回“red”。
|
||||
|
||||
注意,有两种方式可以引用`Hearts`成员:给`hearts`常量赋值时,枚举成员`Suit.Hearts`需要用全名来引用,因为常量没有显式指定类型。在`switch`里,枚举成员使用缩写`.Hearts`来引用,因为`self`的值已经知道是一个`suit`。已知变量类型的情况下你可以使用缩写。
|
||||
|
||||
使用`struct`来创建一个结构体。结构体和类有很多相同的地方,比如方法和构造器。它们之间最大的一个区别就是
|
||||
结构体是传值,类是传引用。
|
||||
使用`struct`来创建一个结构体。结构体和类有很多相同的地方,比如方法和构造器。它们之间最大的一个区别就是结构体是传值,类是传引用。
|
||||
|
||||
```swift
|
||||
struct Card {
|
||||
@ -609,10 +597,10 @@ enum ServerResponse {
|
||||
case Result(String, String)
|
||||
case Error(String)
|
||||
}
|
||||
|
||||
|
||||
let success = ServerResponse.Result("6:00 am", "8:09 pm")
|
||||
let failure = ServerResponse.Error("Out of cheese.")
|
||||
|
||||
|
||||
switch success {
|
||||
case let .Result(sunrise, sunset):
|
||||
let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
|
||||
@ -624,7 +612,7 @@ case let .Error(error):
|
||||
> 练习:
|
||||
> 给`ServerResponse`和`switch`添加第三种情况。
|
||||
|
||||
注意如何从`ServerResponse`中提取日升和日落时间。
|
||||
注意如何从`ServerResponse`中提取日升和日落时间并用得到的值用来和`switch`的情况作比较。
|
||||
|
||||
<a name="protocols_and_extensions"></a>
|
||||
## 协议和扩展
|
||||
@ -666,20 +654,20 @@ let bDescription = b.simpleDescription
|
||||
> 练习:
|
||||
> 写一个实现这个协议的枚举。
|
||||
|
||||
注意声明`SimpleStructure`时候`mutating`关键字用来标记一个会修改结构体的方法。`SimpleClass`的声明不需要标记任何方法因为类中的方法经常会修改类。
|
||||
注意声明`SimpleStructure`时候`mutating`关键字用来标记一个会修改结构体的方法。`SimpleClass`的声明不需要标记任何方法,因为类中的方法通常可以修改类属性(类的性质)。
|
||||
|
||||
使用`extension`来为现有的类型添加功能,比如新的方法和参数。你可以使用扩展在别处修改定义,甚至是从外部库或者框架引入的一个类型,使得这个类型遵循某个协议。
|
||||
使用`extension`来为现有的类型添加功能,比如新的方法和计算属性。你可以使用扩展在别处修改定义,甚至是从外部库或者框架引入的一个类型,使得这个类型遵循某个协议。
|
||||
|
||||
```swift
|
||||
extension Int: ExampleProtocol {
|
||||
var simpleDescription: String {
|
||||
return "The number \(self)"
|
||||
return "The number \(self)"
|
||||
}
|
||||
mutating func adjust() {
|
||||
self += 42
|
||||
}
|
||||
}
|
||||
7.simpleDescription
|
||||
print(7.simpleDescription)
|
||||
```
|
||||
|
||||
> 练习:
|
||||
@ -689,8 +677,8 @@ extension Int: ExampleProtocol {
|
||||
|
||||
```swift
|
||||
let protocolValue: ExampleProtocol = a
|
||||
protocolValue.simpleDescription
|
||||
// protocolValue.anotherProperty // Uncomment to see the error
|
||||
print(protocolValue.simpleDescription)
|
||||
// print(protocolValue.anotherProperty) // Uncomment to see the error
|
||||
```
|
||||
|
||||
即使`protocolValue`变量运行时的类型是`simpleClass`,编译器会把它的类型当做`ExampleProtocol`。这表示你不能调用类在它实现的协议之外实现的方法或者属性。
|
||||
@ -701,32 +689,32 @@ protocolValue.simpleDescription
|
||||
在尖括号里写一个名字来创建一个泛型函数或者类型。
|
||||
|
||||
```swift
|
||||
func repeat<ItemType>(item: ItemType, times: Int) -> [ItemType] {
|
||||
var result = [ItemType]()
|
||||
for i in 0..<times {
|
||||
func repeatItem<Item>(item: Item, numberOfTimes: Int) -> [Item] {
|
||||
var result = [Item]()
|
||||
for _ in 0..<numberOfTimes {
|
||||
result.append(item)
|
||||
}
|
||||
return result
|
||||
}
|
||||
repeat("knock", 4)
|
||||
repeatItem("knock", numberOfTimes:4)
|
||||
```
|
||||
|
||||
你也可以创建泛型类、枚举和结构体。
|
||||
你也可以创建泛型函数、方法、类、枚举和结构体。
|
||||
|
||||
```swift
|
||||
// Reimplement the Swift standard library's optional type
|
||||
enum OptionalValue<T> {
|
||||
enum OptionalValue<Wrapped> {
|
||||
case None
|
||||
case Some(T)
|
||||
case Some(Wrapped)
|
||||
}
|
||||
var possibleInteger: OptionalValue<Int> = .None
|
||||
possibleInteger = .Some(100)
|
||||
```
|
||||
|
||||
在类型名后面使用`where`来指定对类型的需求,比如,限定类型实现某一个协议,限定两个类型是相同的,或者限定某个类必须有一个特定的父类
|
||||
在类型名后面使用`where`来指定对类型的需求,比如,限定类型实现某一个协议,限定两个类型是相同的,或者限定某个类必须有一个特定的父类。
|
||||
|
||||
```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: SequenceType, U: SequenceType where T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, _ rhs: U) -> Bool {
|
||||
for lhsItem in lhs {
|
||||
for rhsItem in rhs {
|
||||
if lhsItem == rhsItem {
|
||||
@ -740,6 +728,6 @@ anyCommonElements([1, 2, 3], [3])
|
||||
```
|
||||
|
||||
> 练习:
|
||||
> 修改`anyCommonElements`函数来创建一个函数,返回一个数组,内容是两个序列的共有元素。
|
||||
> 修改`anyCommonElements(_:_:)`函数来创建一个函数,返回一个数组,内容是两个序列的共有元素。
|
||||
|
||||
简单起见,你可以忽略`where`,只在冒号后面写协议或者类名。` <T: Equatable>`和`<T where T: Equatable>`是等价的。
|
||||
` <T: Equatable>`和`<T where T: Equatable>`是等价的。
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
> 翻译:[成都老码团队翻译组-Arya](http://weibo.com/littlekok/)
|
||||
> 校对:[成都老码团队翻译组-Oberyn](http://weibo.com/u/5241713117)
|
||||
|
||||
# Swift 版本历史记录
|
||||
|
||||
---
|
||||
|
||||
> 翻译:[成都老码团队翻译组-Arya](http://weibo.com/littlekok/)
|
||||
> 校对:[成都老码团队翻译组-Oberyn](http://weibo.com/u/5241713117)
|
||||
|
||||
本页内容包括:
|
||||
|
||||
- [XCode6.4 Beta Swift语法文档更新](#xcode6_4_Beta)
|
||||
|
||||
@ -1,26 +1,44 @@
|
||||
# 基础部分(The Basics)
|
||||
-----------------
|
||||
|
||||
> 1.0
|
||||
> 翻译:[numbbbbb](https://github.com/numbbbbb), [lyuka](https://github.com/lyuka), [JaySurplus](https://github.com/JaySurplus)
|
||||
> 校对:[lslxdx](https://github.com/lslxdx)
|
||||
|
||||
# 基础部分
|
||||
-----------------
|
||||
> 2.0
|
||||
> 翻译+校对:[xtymichael](https://github.com/xtymichael)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [常量和变量](#constants_and_variables)
|
||||
- [声明常量和变量](#declaring)
|
||||
- [类型标注](#type_annotations)
|
||||
- [常量和变量的命名](#naming)
|
||||
- [输出常量和变量](#printing)
|
||||
- [注释](#comments)
|
||||
- [分号](#semicolons)
|
||||
- [整数](#integers)
|
||||
- [整数范围](#integer_bounds)
|
||||
- [Int](#Int)
|
||||
- [UInt](#UInt)
|
||||
- [浮点数](#floating-point_numbers)
|
||||
- [类型安全和类型推断](#type_safety_and_type_inference)
|
||||
- [数值型字面量](#numeric_literals)
|
||||
- [数值型类型转换](#numeric_type_conversion)
|
||||
- [整数转换](#integer_conversion)
|
||||
- [数整数和浮点数转换](#integer_and_floating_point_conversion)
|
||||
- [类型别名](#type_aliases)
|
||||
- [布尔值](#booleans)
|
||||
- [元组](#tuples)
|
||||
- [可选](#optionals)
|
||||
- [nil](#nil)
|
||||
- [if 语句以及强制解析](#if)
|
||||
- [可选绑定](#optional_binding)
|
||||
- [隐式解析可选类型](#implicityly_unwrapped_optionals)
|
||||
- [错误处理](#error_handling)
|
||||
- [断言](#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)。
|
||||
|
||||
@ -37,6 +55,7 @@ Swift 是一门类型安全的语言,可选类型就是一个很好的例子
|
||||
|
||||
常量和变量把一个名字(比如`maximumNumberOfLoginAttempts`或者`welcomeMessage`)和一个指定类型的值(比如数字`10`或者字符串`"Hello"`)关联起来。常量的值一旦设定就不能改变,而变量的值可以随意更改。
|
||||
|
||||
<a name="declaring"></a>
|
||||
### 声明常量和变量
|
||||
|
||||
常量和变量必须在使用前声明,用`let`来声明常量,用`var`来声明变量。下面的例子展示了如何用常量和变量来记录用户尝试登录的次数:
|
||||
@ -61,9 +80,10 @@ var x = 0.0, y = 0.0, z = 0.0
|
||||
>注意:
|
||||
如果你的代码中有不需要改变的值,请使用`let`关键字将它声明为常量。只将需要改变的值声明为变量。
|
||||
|
||||
<a name="type_annotations"></a>
|
||||
### 类型标注
|
||||
|
||||
当你声明常量或者变量的时候可以加上_类型标注(type annotation)_,说明常量或者变量中要存储的值的类型。如果要添加类型标注,需要在常量或者变量名后面加上一个冒号和空格,然后加上类型名称。
|
||||
当你声明常量或者变量的时候可以加上类型标注(type annotation),说明常量或者变量中要存储的值的类型。如果要添加类型标注,需要在常量或者变量名后面加上一个冒号和空格,然后加上类型名称。
|
||||
|
||||
这个例子给`welcomeMessage`变量添加了类型标注,表示这个变量可以存储`String`类型的值:
|
||||
|
||||
@ -83,9 +103,16 @@ var welcomeMessage: String
|
||||
welcomeMessage = "Hello"
|
||||
```
|
||||
|
||||
你可以在一行中定义多个同样类型的变量,用逗号分割,并在最后一个变量名之后添加类型标注:
|
||||
|
||||
```swift
|
||||
var red, green, blue: Double
|
||||
```
|
||||
|
||||
> 注意:
|
||||
一般来说你很少需要写类型标注。如果你在声明常量或者变量的时候赋了一个初始值,Swift可以推断出这个常量或者变量的类型,请参考[类型安全和类型推断](#type_safety_and_type_inference)。在上面的例子中,没有给`welcomeMessage`赋初始值,所以变量`welcomeMessage`的类型是通过一个类型标注指定的,而不是通过初始值推断的。
|
||||
|
||||
<a name="naming"></a>
|
||||
### 常量和变量的命名
|
||||
|
||||
你可以用任何你喜欢的字符作为常量和变量名,包括 Unicode 字符:
|
||||
@ -119,35 +146,27 @@ languageName = "Swift++"
|
||||
// 这会报编译时错误 - languageName 不可改变
|
||||
```
|
||||
|
||||
<a name="printing"></a>
|
||||
### 输出常量和变量
|
||||
|
||||
你可以用`println`函数来输出当前常量或变量的值:
|
||||
你可以用`print(_:separator:terminator:)`函数来输出当前常量或变量的值:
|
||||
|
||||
```swift
|
||||
println(friendlyWelcome)
|
||||
print(friendlyWelcome)
|
||||
// 输出 "Bonjour!"
|
||||
```
|
||||
|
||||
`println`是一个用来输出的全局函数,输出的内容会在最后换行。如果你用 Xcode,`println`将会输出内容到“console”面板上。(另一种函数叫`print`,唯一区别是在输出内容最后不会换行。)
|
||||
`print(_:separator:terminator:)`是一个用来输出一个或多个值到适当输出区的全局函数。如果你用 Xcode,`print(_:separator:terminator:)`将会输出内容到“console”面板上。`separator`和`terminator`参数具有默认值,因此你调用这个函数的时候可以忽略它们。默认情况下,该函数通过添加换行符来结束当前行。如果不想换行,可以传递一个空字符串给`terminator`参数--例如,`print(someValue, terminator:"")`。关于参数默认值的更多信息,请参考[默认参数值](./06_Functions.html#default_parameter_values)。
|
||||
|
||||
`println`函数输出传入的`String`值:
|
||||
Swift 用字符串插值(string interpolation)的方式把常量名或者变量名当做占位符加入到长字符串中,Swift 会用当前常量或变量的值替换这些占位符。将常量或变量名放入圆括号中,并在开括号前使用反斜杠将其转义:
|
||||
|
||||
```swift
|
||||
println("This is a string")
|
||||
// 输出 "This is a string"
|
||||
```
|
||||
|
||||
与 Cocoa 里的`NSLog`函数类似的是,`println`函数可以输出更复杂的信息。这些信息可以包含当前常量和变量的值。
|
||||
|
||||
Swift 用_字符串插值(string interpolation)_的方式把常量名或者变量名当做占位符加入到长字符串中,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!
|
||||
```
|
||||
|
||||
> 注意:
|
||||
字符串插值所有可用的选项,请参考[字符串插值](03_Strings_and_Characters.html#string_interpolation)。
|
||||
字符串插值所有可用的选项,请参考[字符串插值](./03_Strings_and_Characters.html#string_interpolation)。
|
||||
|
||||
<a name="comments"></a>
|
||||
## 注释
|
||||
@ -181,7 +200,7 @@ Swift 中的注释与C 语言的注释非常相似。单行注释以双正斜杠
|
||||
与其他大部分编程语言不同,Swift 并不强制要求你在每条语句的结尾处使用分号(`;`),当然,你也可以按照你自己的习惯添加分号。有一种情况下必须要用分号,即你打算在同一行内写多条独立的语句:
|
||||
|
||||
```swift
|
||||
let cat = "🐱"; println(cat)
|
||||
let cat = "🐱"; print(cat)
|
||||
// 输出 "🐱"
|
||||
```
|
||||
|
||||
@ -192,16 +211,18 @@ let cat = "🐱"; println(cat)
|
||||
|
||||
Swift 提供了8,16,32和64位的有符号和无符号整数类型。这些整数类型和 C 语言的命名方式很像,比如8位无符号整数类型是`UInt8`,32位有符号整数类型是`Int32`。就像 Swift 的其他类型一样,整数类型采用大写命名法。
|
||||
|
||||
<a name="integer_bounds"></a>
|
||||
### 整数范围
|
||||
|
||||
你可以访问不同整数类型的`min`和`max`属性来获取对应类型的最大值和最小值:
|
||||
你可以访问不同整数类型的`min`和`max`属性来获取对应类型的最小值和最大值:
|
||||
|
||||
```swift
|
||||
let minValue = UInt8.min // minValue 为 0,是 UInt8 类型
|
||||
let maxValue = UInt8.max // maxValue 为 255,是 UInt8 类型
|
||||
```
|
||||
```
|
||||
`min`和`max`所传回值的类型,正是其所对的整数类型(如上例UInt8, 所传回的类型是UInt8),可用在表达式中相同类型值旁。
|
||||
|
||||
<a name="Int"></a>
|
||||
### Int
|
||||
|
||||
一般来说,你不需要专门指定整数的长度。Swift 提供了一个特殊的整数类型`Int`,长度与当前平台的原生字长相同:
|
||||
@ -209,8 +230,9 @@ let maxValue = UInt8.max // maxValue 为 255,是 UInt8 类型
|
||||
* 在32位平台上,`Int`和`Int32`长度相同。
|
||||
* 在64位平台上,`Int`和`Int64`长度相同。
|
||||
|
||||
除非你需要特定长度的整数,一般来说使用`Int`就够了。这可以提高代码一致性和可复用性。即使是在32位平台上,`Int`可以存储的整数范围也可以达到`-2147483648`~`2147483647`,大多数时候这已经足够大了。
|
||||
除非你需要特定长度的整数,一般来说使用`Int`就够了。这可以提高代码一致性和可复用性。即使是在32位平台上,`Int`可以存储的整数范围也可以达到`-2,147,483,648`~`2,147,483,647`,大多数时候这已经足够大了。
|
||||
|
||||
<a name="UInt"></a>
|
||||
### UInt
|
||||
|
||||
Swift 也提供了一个特殊的无符号类型`UInt`,长度与当前平台的原生字长相同:
|
||||
@ -237,15 +259,15 @@ Swift 也提供了一个特殊的无符号类型`UInt`,长度与当前平台
|
||||
<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 很少需要声明类型。常量和变量虽然需要明确类型,但是大部分工作并不需要你自己来完成。
|
||||
|
||||
当你声明常量或者变量并赋初值的时候类型推断非常有用。当你在声明常量或者变量的时候赋给它们一个_字面量(literal value 或 literal)_即可触发类型推断。(字面量就是会直接出现在你代码中的值,比如`42`和`3.14159`。)
|
||||
当你声明常量或者变量并赋初值的时候类型推断非常有用。当你在声明常量或者变量的时候赋给它们一个字面量(literal value 或 literal)即可触发类型推断。(字面量就是会直接出现在你代码中的值,比如`42`和`3.14159`。)
|
||||
|
||||
例如,如果你给一个新常量赋值`42`并且没有标明类型,Swift 可以推断出常量类型是`Int`,因为你给它赋的初始值看起来像一个整数:
|
||||
|
||||
@ -291,7 +313,7 @@ let octalInteger = 0o21 // 八进制的17
|
||||
let hexadecimalInteger = 0x11 // 十六进制的17
|
||||
```
|
||||
|
||||
浮点字面量可以是十进制(没有前缀)或者是十六进制(前缀是`0x`)。小数点两边必须有至少一个十进制数字(或者是十六进制的数字)。浮点字面量还有一个可选的_指数(exponent)_,在十进制浮点数中通过大写或者小写的`e`来指定,在十六进制浮点数中通过大写或者小写的`p`来指定。
|
||||
浮点字面量可以是十进制(没有前缀)或者是十六进制(前缀是`0x`)。小数点两边必须有至少一个十进制数字(或者是十六进制的数字)。浮点字面量还有一个可选的指数(exponent,在十进制浮点数中通过大写或者小写的`e`来指定,在十六进制浮点数中通过大写或者小写的`p`来指定。
|
||||
|
||||
如果一个十进制数的指数为`exp`,那这个数相当于基数和10^exp的乘积:
|
||||
* `1.25e2` 表示 1.25 × 10^2,等于 `125.0`。
|
||||
@ -321,8 +343,10 @@ let justOverOneMillion = 1_000_000.000_000_1
|
||||
## 数值型类型转换
|
||||
|
||||
通常来讲,即使代码中的整数常量和变量已知非负,也请使用`Int`类型。总是使用默认的整数类型可以保证你的整数常量和变量可以直接被复用并且可以匹配整数类字面量的类型推断。
|
||||
|
||||
只有在必要的时候才使用其他整数类型,比如要处理外部的长度明确的数据或者为了优化性能、内存占用等等。使用显式指定长度的类型可以及时发现值溢出并且可以暗示正在处理特殊数据。
|
||||
|
||||
<a name="integer_conversion"></a>
|
||||
### 整数转换
|
||||
|
||||
不同整数类型的变量和常量可以存储不同范围的数字。`Int8`类型的常量或者变量可以存储的数字范围是`-128`~`127`,而`UInt8`类型的常量或者变量能存储的数字范围是`0`~`255`。如果数字超出了常量或者变量可存储的范围,编译的时候会报错:
|
||||
@ -346,8 +370,9 @@ let twoThousandAndOne = twoThousand + UInt16(one)
|
||||
|
||||
现在两个数字的类型都是`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 +401,7 @@ let integerPi = Int(pi)
|
||||
<a name="type_aliases"></a>
|
||||
## 类型别名
|
||||
|
||||
_类型别名(type aliases)_就是给现有类型定义另一个名字。你可以使用`typealias`关键字来定义类型别名。
|
||||
类型别名(type aliases)就是给现有类型定义另一个名字。你可以使用`typealias`关键字来定义类型别名。
|
||||
|
||||
当你想要给现有类型起一个更有意义的名字时,类型别名非常有用。假设你正在处理特定长度的外部资源的数据:
|
||||
|
||||
@ -396,7 +421,7 @@ var maxAmplitudeFound = AudioSample.min
|
||||
<a name="booleans"></a>
|
||||
## 布尔值
|
||||
|
||||
Swift 有一个基本的_布尔(Boolean)_类型,叫做`Bool`。布尔值指_逻辑上的(logical)_,因为它们只能是真或者假。Swift 有两个布尔常量,`true`和`false`:
|
||||
Swift 有一个基本的布尔(Boolean)类型,叫做`Bool`。布尔值指逻辑上的值,因为它们只能是真或者假。Swift 有两个布尔常量,`true`和`false`:
|
||||
|
||||
```swift
|
||||
let orangesAreOrange = true
|
||||
@ -409,14 +434,14 @@ let turnipsAreDelicious = false
|
||||
|
||||
```swift
|
||||
if turnipsAreDelicious {
|
||||
println("Mmm, tasty turnips!")
|
||||
print("Mmm, tasty turnips!")
|
||||
} else {
|
||||
println("Eww, turnips are horrible.")
|
||||
print("Eww, turnips are horrible.")
|
||||
}
|
||||
// 输出 "Eww, turnips are horrible."
|
||||
```
|
||||
|
||||
条件语句,例如`if`,请参考[控制流](05_Control_Flow.html)。
|
||||
条件语句,例如`if`,请参考[控制流](./05_Control_Flow.html)。
|
||||
|
||||
如果你在需要使用`Bool`类型的地方使用了非布尔值,Swift 的类型安全机制会报错。下面的例子会报告一个编译时错误:
|
||||
|
||||
@ -436,16 +461,16 @@ if i == 1 {
|
||||
}
|
||||
```
|
||||
|
||||
`i == 1`的比较结果是`Bool`类型,所以第二个例子可以通过类型检查。类似`i == 1`这样的比较,请参考[基本操作符](05_Control_Flow.html)。
|
||||
`i == 1`的比较结果是`Bool`类型,所以第二个例子可以通过类型检查。类似`i == 1`这样的比较,请参考[基本操作符](./05_Control_Flow.html)。
|
||||
|
||||
和 Swift 中的其他类型安全的例子一样,这个方法可以避免错误并保证这块代码的意图总是清晰的。
|
||||
|
||||
<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
|
||||
let http404Error = (404, "Not Found")
|
||||
@ -456,13 +481,13 @@ let http404Error = (404, "Not Found")
|
||||
|
||||
你可以把任意顺序的类型组合成一个元组,这个元组可以包含所有类型。只要你想,你可以创建一个类型为`(Int, Int, Int)`或者`(String, Bool)`或者其他任何你想要的组合的元组。
|
||||
|
||||
你可以将一个元组的内容_分解(decompose)_成单独的常量和变量,然后你就可以正常使用它们了:
|
||||
你可以将一个元组的内容分解(decompose)成单独的常量和变量,然后你就可以正常使用它们了:
|
||||
|
||||
```swift
|
||||
let (statusCode, statusMessage) = http404Error
|
||||
println("The status code is \(statusCode)")
|
||||
print("The status code is \(statusCode)")
|
||||
// 输出 "The status code is 404"
|
||||
println("The status message is \(statusMessage)")
|
||||
print("The status message is \(statusMessage)")
|
||||
// 输出 "The status message is Not Found"
|
||||
```
|
||||
|
||||
@ -470,16 +495,16 @@ println("The status message is \(statusMessage)")
|
||||
|
||||
```swift
|
||||
let (justTheStatusCode, _) = http404Error
|
||||
println("The status code is \(justTheStatusCode)")
|
||||
print("The status code is \(justTheStatusCode)")
|
||||
// 输出 "The status code is 404"
|
||||
```
|
||||
|
||||
此外,你还可以通过下标来访问元组中的单个元素,下标从零开始:
|
||||
|
||||
```swift
|
||||
println("The status code is \(http404Error.0)")
|
||||
print("The status code is \(http404Error.0)")
|
||||
// 输出 "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"
|
||||
```
|
||||
|
||||
@ -492,95 +517,44 @@ let http200Status = (statusCode: 200, description: "OK")
|
||||
给元组中的元素命名后,你可以通过名字来获取这些元素的值:
|
||||
|
||||
```swift
|
||||
println("The status code is \(http200Status.statusCode)")
|
||||
print("The status code is \(http200Status.statusCode)")
|
||||
// 输出 "The status code is 200"
|
||||
println("The status message is \(http200Status.description)")
|
||||
print("The status message is \(http200Status.description)")
|
||||
// 输出 "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>
|
||||
## 可选类型
|
||||
|
||||
使用_可选类型(optionals)_来处理值可能缺失的情况。可选类型表示:
|
||||
使用可选类型(optionals)来处理值可能缺失的情况。可选类型表示:
|
||||
|
||||
* _有_值,等于 x
|
||||
* 有值,等于 x
|
||||
|
||||
或者
|
||||
|
||||
* _没有_值
|
||||
* 没有值
|
||||
|
||||
> 注意:
|
||||
C 和 Objective-C 中并没有可选类型这个概念。最接近的是 Objective-C 中的一个特性,一个方法要不返回一个对象要不返回`nil`,`nil`表示“缺少一个合法的对象”。然而,这只对对象起作用——对于结构体,基本的 C 类型或者枚举类型不起作用。对于这些类型,Objective-C 方法一般会返回一个特殊值(比如`NSNotFound`)来暗示值缺失。这种方法假设方法的调用者知道并记得对特殊值进行判断。然而,Swift 的可选类型可以让你暗示_任意类型_的值缺失,并不需要一个特殊值。
|
||||
|
||||
来看一个例子。Swift 的`String`类型有一个叫做`toInt`的方法,作用是将一个`String`值转换成一个`Int`值。然而,并不是所有的字符串都可以转换成一个整数。字符串`"123"`可以被转换成数字`123`,但是字符串`"hello, world"`不行。
|
||||
来看一个例子。Swift 的`String`类型有一种构造器,作用是将一个`String`值转换成一个`Int`值。然而,并不是所有的字符串都可以转换成一个整数。字符串`"123"`可以被转换成数字`123`,但是字符串`"hello, world"`不行。
|
||||
|
||||
下面的例子使用`toInt`方法来尝试将一个`String`转换成`Int`:
|
||||
下面的例子使用这种构造器来尝试将一个`String`转换成`Int`:
|
||||
|
||||
```swift
|
||||
let possibleNumber = "123"
|
||||
let convertedNumber = possibleNumber.toInt()
|
||||
let convertedNumber = Int(possibleNumber)
|
||||
// convertedNumber 被推测为类型 "Int?", 或者类型 "optional 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`,这样可选类型包含的值就会被赋给一个变量而非常量。
|
||||
因为该构造器可能会失败,所以它返回一个_可选类型_(optional)`Int`,而不是一个`Int`。一个可选的`Int`被写作`Int?`而不是`Int`。问号暗示包含的值是可选类型,也就是说可能包含`Int`值也可能不包含值。(不能包含其他任何值比如`Bool`值或者`String`值。只能是`Int`或者什么都没有。)
|
||||
|
||||
<a name="nil"></a>
|
||||
### nil
|
||||
|
||||
你可以给可选变量赋值为`nil`来表示它没有值:
|
||||
@ -603,30 +577,95 @@ 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"
|
||||
```
|
||||
|
||||
这段代码可以被理解为:
|
||||
|
||||
“如果`Int(possibleNumber)`返回的可选`Int`包含一个值,创建一个叫做`actualNumber`的新常量并将可选包含的值赋给它。”
|
||||
|
||||
如果转换成功,`actualNumber`常量可以在`if`语句的第一个分支中使用。它已经被可选类型_包含的_值初始化过,所以不需要再使用`!`后缀来获取它的值。在这个例子中,`actualNumber`只被用来输出转换结果。
|
||||
|
||||
你可以在可选绑定中使用常量和变量。如果你想在`if`语句的第一个分支中操作`actualNumber`的值,你可以改成`if var actualNumber`,这样可选类型包含的值就会被赋给一个变量而非常量。
|
||||
|
||||
你可以包含多个可选绑定在`if`语句中,并使用`where`子句做布尔值判断。
|
||||
|
||||
```swift
|
||||
if let firstNumber = Int("4"), secondNumber = Int("42") where firstNumber < secondNumber {
|
||||
print("\(firstNumber) < \(secondNumber)")
|
||||
}
|
||||
// prints "4 < 42"
|
||||
```
|
||||
|
||||
<a name="implicityly_unwrapped_optionals"></a>
|
||||
### 隐式解析可选类型
|
||||
|
||||
如上所述,可选类型暗示了常量或者变量可以“没有值”。可选可以通过`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`之间的区别:
|
||||
|
||||
```swift
|
||||
let possibleString: String? = "An optional string."
|
||||
println(possibleString!) // 需要惊叹号来获取值
|
||||
// 输出 "An optional string."
|
||||
```
|
||||
let forcedString: String = possibleString! // 需要惊叹号来获取值
|
||||
|
||||
```swift
|
||||
let assumedString: String! = "An implicitly unwrapped optional string."
|
||||
println(assumedString) // 不需要感叹号
|
||||
// 输出 "An implicitly unwrapped optional string."
|
||||
let implicitString: String = assumedString // 不需要感叹号
|
||||
```
|
||||
|
||||
你可以把隐式解析可选类型当做一个可以自动解析的可选类型。你要做的只是声明的时候把感叹号放到类型的结尾,而不是每次取值的可选名字的结尾。
|
||||
@ -637,8 +676,8 @@ println(assumedString) // 不需要感叹号
|
||||
你仍然可以把隐式解析可选类型当做普通可选类型来判断它是否包含值:
|
||||
|
||||
```swift
|
||||
if assumedString {
|
||||
println(assumedString)
|
||||
if assumedString != nil {
|
||||
print(assumedString)
|
||||
}
|
||||
// 输出 "An implicitly unwrapped optional string."
|
||||
```
|
||||
@ -647,7 +686,7 @@ if assumedString {
|
||||
|
||||
```swift
|
||||
if let definiteString = assumedString {
|
||||
println(definiteString)
|
||||
print(definiteString)
|
||||
}
|
||||
// 输出 "An implicitly unwrapped optional string."
|
||||
```
|
||||
@ -655,18 +694,68 @@ if let definiteString = assumedString {
|
||||
> 注意:
|
||||
如果一个变量之后可能变成`nil`的话请不要使用隐式解析可选类型。如果你需要在变量的生命周期中判断是否是`nil`的话,请使用普通可选类型。
|
||||
|
||||
<a name="error_handling"></a>
|
||||
## 错误处理
|
||||
你可以使用错误处理(error handling)来应对程序执行中可能会遇到的错误条件。
|
||||
|
||||
相对于可选中运用值的存在与缺失来表达函数的成功与失败,错误处理可以推断失败的原因,并传播至程序的其他部分。
|
||||
|
||||
当一个函数遇到错误条件,它能报错。调用函数的地方能抛出错误消息并合理处理。
|
||||
|
||||
```swift
|
||||
func canThrowAnError() throws {
|
||||
// 这个函数有可能抛出错误
|
||||
}
|
||||
```
|
||||
|
||||
一个函数可以通过在声明中添加`throws`关键词来抛出错误消息。当你的函数能抛出错误消息时, 你应该在表达式中前置`try`关键词。
|
||||
|
||||
```swift
|
||||
do {
|
||||
try canThrowAnError()
|
||||
// 没有错误消息抛出
|
||||
} 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>
|
||||
## 断言
|
||||
|
||||
可选类型可以让你判断值是否存在,你可以在代码中优雅地处理值缺失的情况。然而,在某些情况下,如果值缺失或者值并不满足特定的条件,你的代码可能没办法继续执行。这时,你可以在你的代码中触发一个_断言(assertion)_来结束代码运行并通过调试来找到值缺失的原因。
|
||||
可选类型可以让你判断值是否存在,你可以在代码中优雅地处理值缺失的情况。然而,在某些情况下,如果值缺失或者值并不满足特定的条件,你的代码可能没办法继续执行。这时,你可以在你的代码中触发一个断言(assertion)来结束代码运行并通过调试来找到值缺失的原因。
|
||||
|
||||
### 使用断言进行调试
|
||||
|
||||
断言会在运行时判断一个逻辑条件是否为`true`。从字面意思来说,断言“断言”一个条件是否为真。你可以使用断言来保证在运行其他代码之前,某些重要的条件已经被满足。如果条件判断为`true`,代码运行会继续进行;如果条件判断为`false`,代码运行停止,你的应用被终止。
|
||||
断言会在运行时判断一个逻辑条件是否为`true`。从字面意思来说,断言“断言”一个条件是否为真。你可以使用断言来保证在运行其他代码之前,某些重要的条件已经被满足。如果条件判断为`true`,代码运行会继续进行;如果条件判断为`false`,代码执行结束,你的应用被终止。
|
||||
|
||||
如果你的代码在调试环境下触发了一个断言,比如你在 Xcode 中构建并运行一个应用,你可以清楚地看到不合法的状态发生在哪里并检查断言被触发时你的应用的状态。此外,断言允许你附加一条调试信息。
|
||||
|
||||
你可以使用全局`assert`函数来写一个断言。向`assert`函数传入一个结果为`true`或者`false`的表达式以及一条信息,当表达式为`false`的时候这条信息会被显示:
|
||||
你可以使用全局`assert(_:_:)`函数来写一个断言。向`assert(_:_:)`函数传入一个结果为`true`或者`false`的表达式以及一条信息,当表达式的结果为`false`的时候这条信息会被显示:
|
||||
|
||||
```swift
|
||||
let age = -3
|
||||
@ -674,9 +763,9 @@ assert(age >= 0, "A person's age cannot be less than zero")
|
||||
// 因为 age < 0,所以断言会触发
|
||||
```
|
||||
|
||||
在这个例子中,只有`age >= 0`为`true`的时候,即`age`的值非负的时候,代码运行才会继续。如果`age`的值是负数,就像代码中那样,`age >= 0`为`false`,断言被触发,结束应用。
|
||||
在这个例子中,只有`age >= 0`为`true`的时候,即`age`的值非负的时候,代码才会继续执行。如果`age`的值是负数,就像代码中那样,`age >= 0`为`false`,断言被触发,终止应用。
|
||||
|
||||
断言信息如果不需要,可以被省略,就像这样:
|
||||
如果不需要断言信息,可以省略,就像这样:
|
||||
|
||||
```swift
|
||||
assert(age >= 0)
|
||||
@ -690,7 +779,7 @@ assert(age >= 0)
|
||||
* 需要给函数传入一个值,但是非法的值可能导致函数不能正常执行。
|
||||
* 一个可选值现在是`nil`,但是后面的代码运行需要一个非`nil`值。
|
||||
|
||||
请参考[下标脚本](12_Subscripts.html)和[函数](06_Functions.html)。
|
||||
请参考[下标脚本](./12_Subscripts.html)和[函数](./06_Functions.html)。
|
||||
|
||||
> 注意:
|
||||
断言可能导致你的应用终止运行,所以你应当仔细设计你的代码来让非法条件不会出现。然而,在你的应用发布之前,有时候非法条件可能出现,这时使用断言可以快速发现问题。
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
|
||||
> 翻译:[XieLingWang](https://github.com/xielingwang)
|
||||
> 校对:[EvilCome](https://github.com/Evilcome)
|
||||
> 最终校对:[老码团队翻译组-Tyrion](http://weibo.com/u/5241713117)
|
||||
|
||||
# 基本运算符
|
||||
# 基本运算符(Basic Operators)
|
||||
-----------------
|
||||
|
||||
> 1.0
|
||||
> 翻译:[XieLingWang](https://github.com/xielingwang)
|
||||
> 校对:[EvilCome](https://github.com/Evilcome)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[JackAlan](https://github.com/AlanMelody)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [术语](#terminology)
|
||||
@ -18,20 +20,20 @@
|
||||
- [区间运算符](#range_operators)
|
||||
- [逻辑运算符](#logical_operators)
|
||||
|
||||
运算符是检查、改变、合并值的特殊符号或短语。例如,加号`+`将两个数相加(如`let i = 1 + 2`)。复杂些的运算例如逻辑与运算符`&&`(如`if enteredDoorCode && passedRetinaScan`),或让 i 值加1的便捷自增运算符`++i`等。
|
||||
运算符是检查、改变、合并值的特殊符号或短语。例如,加号`+`将两个数相加(如`let i = 1 + 2`)。更复杂的运算例子包括逻辑与运算符`&&`(如`if enteredDoorCode && passedRetinaScan`),或让 i 值加1的便捷自增运算符`++i`等。
|
||||
|
||||
Swift 支持大部分标准 C 语言的运算符,且改进许多特性来减少常规编码错误。如:赋值符(`=`)不返回值,以防止把想要判断相等运算符(`==`)的地方写成赋值符导致的错误。算术运算符(`+`,`-`,`*`,`/`,`%`等)会检测并不允许值溢出,以此来避免保存变量时由于变量大于或小于其类型所能承载的范围时导致的异常结果。当然允许你使用 Swift 的溢出运算符来实现溢出。详情参见[溢出运算符](24_Advanced_Operators.html#overflow_operators)。
|
||||
Swift 支持大部分标准 C 语言的运算符,且改进许多特性来减少常规编码错误。如:赋值符(`=`)不返回值,以防止把想要判断相等运算符(`==`)的地方写成赋值符导致的错误。算术运算符(`+`,`-`,`*`,`/`,`%`等)会检测并不允许值溢出,以此来避免保存变量时由于变量大于或小于其类型所能承载的范围时导致的异常结果。当然允许你使用 Swift 的溢出运算符来实现溢出。详情参见[溢出运算符](./24_Advanced_Operators.html#overflow_operators)。
|
||||
|
||||
区别于 C 语言,在 Swift 中你可以对浮点数进行取余运算(`%`),Swift 还提供了 C 语言没有的表达两数之间的值的区间运算符(`a..<b`和`a...b`),这方便我们表达一个区间内的数值。
|
||||
|
||||
本章节只描述了 Swift 中的基本运算符,[高级运算符](24_Advanced_Operators.html)包含了高级运算符,及如何自定义运算符,及如何进行自定义类型的运算符重载。
|
||||
本章节只描述了 Swift 中的基本运算符,[高级运算符](./24_Advanced_Operators.html)包含了高级运算符,及如何自定义运算符,及如何进行自定义类型的运算符重载。
|
||||
|
||||
<a name="terminology"></a>
|
||||
## 术语
|
||||
|
||||
运算符有一元、二元和三元运算符。
|
||||
|
||||
- 一元运算符对单一操作对象操作(如`-a`)。一元运算符分前置运算符和后置运算符,前置运算符需紧排操作对象之前(如`!b`),后置运算符需紧跟操作对象之后(如`i++`)。
|
||||
- 一元运算符对单一操作对象操作(如`-a`)。一元运算符分前置运算符和后置运算符,前置运算符需紧跟在操作对象之前(如`!b`),后置运算符需紧跟在操作对象之后(如`i++`)。
|
||||
- 二元运算符操作两个操作对象(如`2 + 3`),是中置的,因为它们出现在两个操作对象之间。
|
||||
- 三元运算符操作三个操作对象,和 C 语言一样,Swift 只有一个三元运算符,就是三目运算符(`a ? b : c`)。
|
||||
|
||||
@ -42,22 +44,27 @@ Swift 支持大部分标准 C 语言的运算符,且改进许多特性来减
|
||||
|
||||
赋值运算(`a = b`),表示用`b`的值来初始化或更新`a`的值:
|
||||
|
||||
let b = 10
|
||||
var a = 5
|
||||
a = b
|
||||
// a 现在等于 10
|
||||
```swift
|
||||
let b = 10
|
||||
var a = 5
|
||||
a = b
|
||||
// a 现在等于 10
|
||||
```
|
||||
|
||||
如果赋值的右边是一个多元组,它的元素可以马上被分解多个常量或变量:
|
||||
如果赋值的右边是一个多元组,它的元素可以马上被分解成多个常量或变量:
|
||||
|
||||
let (x, y) = (1, 2)
|
||||
// 现在 x 等于 1, y 等于 2
|
||||
```swift
|
||||
let (x, y) = (1, 2)
|
||||
// 现在 x 等于 1, y 等于 2
|
||||
```
|
||||
|
||||
与 C 语言和 Objective-C 不同,Swift 的赋值操作并不返回任何值。所以以下代码是错误的:
|
||||
|
||||
if x = y {
|
||||
// 此句错误, 因为 x = y 并不返回任何值
|
||||
}
|
||||
|
||||
```swift
|
||||
if x = y {
|
||||
// 此句错误, 因为 x = y 并不返回任何值
|
||||
}
|
||||
```
|
||||
|
||||
这个特性使你无法把(`==`)错写成(`=`),由于`if x = y`是错误代码,Swift帮你避免此类错误的的发生。
|
||||
|
||||
@ -70,19 +77,21 @@ Swift 中所有数值类型都支持了基本的四则算术运算:
|
||||
- 减法(`-`)
|
||||
- 乘法(`*`)
|
||||
- 除法(`/`)
|
||||
|
||||
1 + 2 // 等于 3
|
||||
5 - 3 // 等于 2
|
||||
2 * 3 // 等于 6
|
||||
10.0 / 2.5 // 等于 4.0
|
||||
|
||||
```swift
|
||||
1 + 2 // 等于 3
|
||||
5 - 3 // 等于 2
|
||||
2 * 3 // 等于 6
|
||||
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`的拼接:
|
||||
|
||||
"hello, " + "world" // 等于 "hello, world"
|
||||
```swift
|
||||
"hello, " + "world" // 等于 "hello, world"
|
||||
```
|
||||
|
||||
### 求余运算符
|
||||
|
||||
@ -99,8 +108,9 @@ Swift 中所有数值类型都支持了基本的四则算术运算:
|
||||
|
||||
在 Swift 中可以表达为:
|
||||
|
||||
9 % 4 // 等于 1
|
||||
|
||||
```swift
|
||||
9 % 4 // 等于 1
|
||||
```
|
||||
|
||||
为了得到`a % b`的结果,`%`计算了以下等式,并输出`余数`作为结果:
|
||||
|
||||
@ -112,10 +122,11 @@ Swift 中所有数值类型都支持了基本的四则算术运算:
|
||||
|
||||
9 = (4 × 2) + 1
|
||||
|
||||
同样的方法,我们来计算 `-9 % 4`:
|
||||
|
||||
同样的方法,我来们计算 `-9 % 4`:
|
||||
|
||||
-9 % 4 // 等于 -1
|
||||
```swift
|
||||
-9 % 4 // 等于 -1
|
||||
```
|
||||
|
||||
把`-9`和`4`代入等式,`-2`是取到的最大整数:
|
||||
|
||||
@ -129,7 +140,9 @@ Swift 中所有数值类型都支持了基本的四则算术运算:
|
||||
|
||||
不同于 C 语言和 Objective-C,Swift 中是可以对浮点数进行求余的。
|
||||
|
||||
8 % 2.5 // 等于 0.5
|
||||
```swift
|
||||
8 % 2.5 // 等于 0.5
|
||||
```
|
||||
|
||||
这个例子中,`8`除于`2.5`等于`3`余`0.5`,所以结果是一个`Double`值`0.5`。
|
||||
|
||||
@ -138,11 +151,11 @@ Swift 中所有数值类型都支持了基本的四则算术运算:
|
||||
### 自增和自减运算
|
||||
|
||||
和 C 语言一样,Swift 也提供了对变量本身加1或减1的自增(`++`)和自减(`--`)的缩略算符。其操作对象可以是整形和浮点型。
|
||||
|
||||
|
||||
var i = 0
|
||||
++i // 现在 i = 1
|
||||
|
||||
```swift
|
||||
var i = 0
|
||||
++i // 现在 i = 1
|
||||
```
|
||||
|
||||
每调用一次`++i`,`i`的值就会加1。实际上,`++i`是`i = i + 1`的简写,而`--i`是`i = i - 1`的简写。
|
||||
|
||||
@ -155,9 +168,11 @@ Swift 中所有数值类型都支持了基本的四则算术运算:
|
||||
|
||||
例如:
|
||||
|
||||
var a = 0
|
||||
let b = ++a // a 和 b 现在都是 1
|
||||
let c = a++ // a 现在 2, 但 c 是 a 自增前的值 1
|
||||
```swift
|
||||
var a = 0
|
||||
let b = ++a // a 和 b 现在都是 1
|
||||
let c = a++ // a 现在 2, 但 c 是 a 自增前的值 1
|
||||
```
|
||||
|
||||
上述例子,`let b = ++a`先把`a`加1了再返回`a`的值。所以`a`和`b`都是新值`1`。
|
||||
|
||||
@ -170,19 +185,22 @@ Swift 中所有数值类型都支持了基本的四则算术运算:
|
||||
|
||||
数值的正负号可以使用前缀`-`(即一元负号)来切换:
|
||||
|
||||
let three = 3
|
||||
let minusThree = -three // minusThree 等于 -3
|
||||
let plusThree = -minusThree // plusThree 等于 3, 或 "负负3"
|
||||
|
||||
```swift
|
||||
let three = 3
|
||||
let minusThree = -three // minusThree 等于 -3
|
||||
let plusThree = -minusThree // plusThree 等于 3, 或 "负负3"
|
||||
```
|
||||
|
||||
一元负号(`-`)写在操作数之前,中间没有空格。
|
||||
|
||||
### 一元正号运算符
|
||||
|
||||
一元正号(`+`)不做任何改变地返回操作数的值。
|
||||
一元正号(`+`)不做任何改变地返回操作数的值:
|
||||
|
||||
let minusSix = -6
|
||||
let alsoMinusSix = +minusSix // alsoMinusSix 等于 -6
|
||||
```swift
|
||||
let minusSix = -6
|
||||
let alsoMinusSix = +minusSix // alsoMinusSix 等于 -6
|
||||
```
|
||||
|
||||
虽然一元`+`什么都不会改变,但当你在使用一元负号来表达负数时,你可以使用一元正号来表达正数,如此你的代码会具有对称美。
|
||||
|
||||
@ -190,11 +208,12 @@ Swift 中所有数值类型都支持了基本的四则算术运算:
|
||||
<a name="compound_assignment_operators"></a>
|
||||
## 复合赋值(Compound Assignment Operators)
|
||||
|
||||
如同强大的 C 语言,Swift 也提供把其他运算符和赋值运算(`=`)组合的复合赋值运算符,组合加运算(`+=`)是其中一个例子:
|
||||
|
||||
var a = 1
|
||||
a += 2 // a 现在是 3
|
||||
如同 C 语言,Swift 也提供把其他运算符和赋值运算(`=`)组合的复合赋值运算符,组合加运算(`+=`)是其中一个例子:
|
||||
|
||||
```swift
|
||||
var a = 1
|
||||
a += 2 // a 现在是 3
|
||||
```
|
||||
|
||||
表达式`a += 2`是`a = a + 2`的简写,一个组合加运算就是把加法运算和赋值运算组合成进一个运算符里,同时完成两个运算任务。
|
||||
|
||||
@ -206,7 +225,7 @@ Swift 中所有数值类型都支持了基本的四则算术运算:
|
||||
<a name="comparison_operators"></a>
|
||||
## 比较运算符
|
||||
|
||||
所有标准 C 语言中的比较运算都可以在 Swift 中使用。
|
||||
所有标准 C 语言中的比较运算都可以在 Swift 中使用:
|
||||
|
||||
- 等于(`a == b`)
|
||||
- 不等于(`a != b`)
|
||||
@ -216,32 +235,32 @@ Swift 中所有数值类型都支持了基本的四则算术运算:
|
||||
- 小于等于(`a <= b`)
|
||||
|
||||
> 注意:
|
||||
Swift 也提供恒等`===`和不恒等`!==`这两个比较符来判断两个对象是否引用同一个对象实例。更多细节在[类与结构](09_Classes_and_Structures.html)。
|
||||
Swift 也提供恒等`===`和不恒等`!==`这两个比较符来判断两个对象是否引用同一个对象实例。更多细节在[类与结构](./09_Classes_and_Structures.html)。
|
||||
|
||||
每个比较运算都返回了一个标识表达式是否成立的布尔值:
|
||||
|
||||
|
||||
1 == 1 // true, 因为 1 等于 1
|
||||
2 != 1 // true, 因为 2 不等于 1
|
||||
2 > 1 // true, 因为 2 大于 1
|
||||
1 < 2 // true, 因为 1 小于2
|
||||
1 >= 1 // true, 因为 1 大于等于 1
|
||||
2 <= 1 // false, 因为 2 并不小于等于 1
|
||||
|
||||
```swift
|
||||
1 == 1 // true, 因为 1 等于 1
|
||||
2 != 1 // true, 因为 2 不等于 1
|
||||
2 > 1 // true, 因为 2 大于 1
|
||||
1 < 2 // true, 因为 1 小于2
|
||||
1 >= 1 // true, 因为 1 大于等于 1
|
||||
2 <= 1 // false, 因为 2 并不小于等于 1
|
||||
```
|
||||
|
||||
比较运算多用于条件语句,如`if`条件:
|
||||
|
||||
|
||||
let name = "world"
|
||||
if name == "world" {
|
||||
println("hello, world")
|
||||
} else {
|
||||
println("I'm sorry \(name), but I don't recognize you")
|
||||
}
|
||||
// 输出 "hello, world", 因为 `name` 就是等于 "world"
|
||||
```swift
|
||||
let name = "world"
|
||||
if name == "world" {
|
||||
print("hello, world")
|
||||
} else {
|
||||
print("I'm sorry \(name), but I don't recognize you")
|
||||
}
|
||||
// 输出 "hello, world", 因为 `name` 就是等于 "world"
|
||||
```
|
||||
|
||||
|
||||
关于`if`语句,请看[控制流](05_Control_Flow.html)。
|
||||
关于`if`语句,请看[控制流](./05_Control_Flow.html)。
|
||||
|
||||
<a name="ternary_conditional_operator"></a>
|
||||
## 三目运算符(Ternary Conditional Operator)
|
||||
@ -250,32 +269,36 @@ Swift 也提供恒等`===`和不恒等`!==`这两个比较符来判断两个对
|
||||
|
||||
三目运算符是以下代码的缩写形式:
|
||||
|
||||
if question {
|
||||
answer1
|
||||
} else {
|
||||
answer2
|
||||
}
|
||||
```swift
|
||||
if question {
|
||||
answer1
|
||||
} else {
|
||||
answer2
|
||||
}
|
||||
```
|
||||
|
||||
这里有个计算表格行高的例子。如果有表头,那行高应比内容高度要高出50像素; 如果没有表头,只需高出20像素。
|
||||
|
||||
let contentHeight = 40
|
||||
let hasHeader = true
|
||||
let rowHeight = contentHeight + (hasHeader ? 50 : 20)
|
||||
// rowHeight 现在是 90
|
||||
这里有个计算表格行高的例子。如果有表头,那行高应比内容高度要高出50点;如果没有表头,只需高出20点:
|
||||
|
||||
```swift
|
||||
let contentHeight = 40
|
||||
let hasHeader = true
|
||||
let rowHeight = contentHeight + (hasHeader ? 50 : 20)
|
||||
// rowHeight 现在是 90
|
||||
```
|
||||
|
||||
这样写会比下面的代码简洁:
|
||||
|
||||
let contentHeight = 40
|
||||
let hasHeader = true
|
||||
var rowHeight = contentHeight
|
||||
if hasHeader {
|
||||
rowHeight = rowHeight + 50
|
||||
} else {
|
||||
rowHeight = rowHeight + 20
|
||||
}
|
||||
// rowHeight 现在是 90
|
||||
上面的写法比下面的代码更简洁:
|
||||
|
||||
```swift
|
||||
let contentHeight = 40
|
||||
let hasHeader = true
|
||||
var rowHeight = contentHeight
|
||||
if hasHeader {
|
||||
rowHeight = rowHeight + 50
|
||||
} else {
|
||||
rowHeight = rowHeight + 20
|
||||
}
|
||||
// rowHeight 现在是 90
|
||||
```
|
||||
|
||||
第一段代码例子使用了三目运算,所以一行代码就能让我们得到正确答案。这比第二段代码简洁得多,无需将`rowHeight`定义成变量,因为它的值无需在`if`语句中改变。
|
||||
|
||||
@ -291,7 +314,9 @@ Swift 也提供恒等`===`和不恒等`!==`这两个比较符来判断两个对
|
||||
|
||||
空合并运算符是对以下代码的简短表达方法
|
||||
|
||||
a != nil ? a! : b
|
||||
```swift
|
||||
a != nil ? a! : b
|
||||
```
|
||||
|
||||
上述代码使用了三目运算符。当可选类型`a`的值不为空时,进行强制解封(`a!`)访问`a`中值,反之当`a`中值为空时,返回默认值b。无疑空合运算符(`??`)提供了一种更为优雅的方式去封装条件判断和解封两种行为,显得简洁以及更具可读性。
|
||||
|
||||
@ -300,21 +325,24 @@ Swift 也提供恒等`===`和不恒等`!==`这两个比较符来判断两个对
|
||||
|
||||
下文例子采用空合并运算符,实现了在默认颜色名和可选自定义颜色名之间抉择:
|
||||
|
||||
```swift
|
||||
let defaultColorName = "red"
|
||||
var userDefinedColorName: String? //默认值为 nil
|
||||
|
||||
let defaultColorName = "red"
|
||||
var userDefinedColorName:String? //默认值为nil
|
||||
var colorNameToUse = userDefinedColorName ?? defaultColorName
|
||||
//userDefinedColorName的值为空 ,所以colorNameToUse的值为`red`
|
||||
var colorNameToUse = userDefinedColorName ?? defaultColorName
|
||||
// userDefinedColorName 的值为空,所以 colorNameToUse 的值为 "red"
|
||||
```
|
||||
|
||||
`userDefinedColorName`变量被定义为一个可选`String`类型,默认值为`nil`。由于`userDefinedColorName`是一个可选类型,我们可以使用空合运算符去判断其值。在上一个例子中,通过空合运算符为一个名为`colorNameToUse`的变量赋予一个字符串类型初始值。
|
||||
由于`userDefinedColorName`值为空,因此表达式`userDefinedColorName ?? defaultColorName`返回`defaultColorName`的值,即`red`。
|
||||
|
||||
`userDefinedColorName`变量被定义为一个可选字符串类型,默认值为nil。由于`userDefinedColorName`是一个可选类型,我们可以使用空合运算符去判断其值。在上一个例子中,通过空合运算符为一个名为`colorNameToUse`的变量赋予一个字符串类型初始值。
|
||||
由于`userDefinedColorName`值为空,因此表达式` userDefinedColorName ?? defaultColorName `返回默认值,即`red`。
|
||||
另一种情况,分配一个非空值(`non-nil`)给`userDefinedColorName`,再次执行空合运算,运算结果为封包在`userDefaultColorName`中的值,而非默认值。
|
||||
|
||||
另一种情况,分配一个非空值(`non-nil`)给 `userDefinedColorName`,再次执行空合运算,运算结果为封包在`userDefaultColorName`中的值,而非默认值。
|
||||
|
||||
userDefinedColorName = "green"
|
||||
colorNameToUse = userDefinedColorName ?? defaultColorName
|
||||
//userDefinedColorName非空,因此colorNameToUsede的值为绿色
|
||||
```swift
|
||||
userDefinedColorName = "green"
|
||||
colorNameToUse = userDefinedColorName ?? defaultColorName
|
||||
// userDefinedColorName 非空,因此 colorNameToUse 的值为 "green"
|
||||
```
|
||||
|
||||
<a name="range_operators"></a>
|
||||
## 区间运算符
|
||||
@ -322,42 +350,43 @@ Swift 也提供恒等`===`和不恒等`!==`这两个比较符来判断两个对
|
||||
Swift 提供了两个方便表达一个区间的值的运算符。
|
||||
|
||||
### 闭区间运算符
|
||||
闭区间运算符(`a...b`)定义一个包含从`a`到`b`(包括`a`和`b`)的所有值的区间,`b`必须大于`a`。
|
||||
闭区间运算符(`a...b`)定义一个包含从`a`到`b`(包括`a`和`b`)的所有值的区间,`b`必须大于等于`a`。
|
||||
|
||||
闭区间运算符在迭代一个区间的所有值时是非常有用的,如在`for-in`循环中:
|
||||
|
||||
for index in 1...5 {
|
||||
println("\(index) * 5 = \(index * 5)")
|
||||
}
|
||||
// 1 * 5 = 5
|
||||
// 2 * 5 = 10
|
||||
// 3 * 5 = 15
|
||||
// 4 * 5 = 20
|
||||
// 5 * 5 = 25
|
||||
```swift
|
||||
for index in 1...5 {
|
||||
print("\(index) * 5 = \(index * 5)")
|
||||
}
|
||||
// 1 * 5 = 5
|
||||
// 2 * 5 = 10
|
||||
// 3 * 5 = 15
|
||||
// 4 * 5 = 20
|
||||
// 5 * 5 = 25
|
||||
```
|
||||
|
||||
|
||||
关于`for-in`,请看[控制流](05_Control_Flow.html)。
|
||||
关于`for-in`,请看[控制流](./05_Control_Flow.html)。
|
||||
|
||||
### 半开区间运算符
|
||||
|
||||
半开区间(`a..<b`)定义一个从`a`到`b`但不包括`b`的区间。
|
||||
之所以称为半开区间,是因为该区间包含第一个值而不包括最后的值。
|
||||
|
||||
半开区间的实用性在于当你使用一个0始的列表(如数组)时,非常方便地从0数到列表的长度。
|
||||
半开区间的实用性在于当你使用一个从0开始的列表(如数组)时,非常方便地从0数到列表的长度。
|
||||
|
||||
```swift
|
||||
let names = ["Anna", "Alex", "Brian", "Jack"]
|
||||
let count = names.count
|
||||
for i in 0..<count {
|
||||
print("第 \(i + 1) 个人叫 \(names[i])")
|
||||
}
|
||||
// 第 1 个人叫 Anna
|
||||
// 第 2 个人叫 Alex
|
||||
// 第 3 个人叫 Brian
|
||||
// 第 4 个人叫 Jack
|
||||
```
|
||||
|
||||
let names = ["Anna", "Alex", "Brian", "Jack"]
|
||||
let count = names.count
|
||||
for i in 0..<count {
|
||||
println("第 \(i + 1) 个人叫 \(names[i])")
|
||||
}
|
||||
// 第 1 个人叫 Anna
|
||||
// 第 2 个人叫 Alex
|
||||
// 第 3 个人叫 Brian
|
||||
// 第 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>
|
||||
## 逻辑运算
|
||||
@ -372,16 +401,17 @@ Swift 提供了两个方便表达一个区间的值的运算符。
|
||||
|
||||
逻辑非运算(`!a`)对一个布尔值取反,使得`true`变`false`,`false`变`true`。
|
||||
|
||||
它是一个前置运算符,需出现在操作数之前,且不加空格。读作`非 a`,例子如下:
|
||||
它是一个前置运算符,需紧跟在操作数之前,且不加空格。读作`非 a`,例子如下:
|
||||
|
||||
let allowedEntry = false
|
||||
if !allowedEntry {
|
||||
println("ACCESS DENIED")
|
||||
}
|
||||
// 输出 "ACCESS DENIED"
|
||||
```swift
|
||||
let allowedEntry = false
|
||||
if !allowedEntry {
|
||||
print("ACCESS DENIED")
|
||||
}
|
||||
// 输出 "ACCESS DENIED"
|
||||
```
|
||||
|
||||
|
||||
`if !allowedEntry`语句可以读作 "如果 非 alowed entry。",接下一行代码只有在如果 "非 allow entry" 为`true`,即`allowEntry`为`false`时被执行。
|
||||
`if !allowedEntry`语句可以读作“如果非 allowedEntry。”,接下一行代码只有在“非 allowedEntry”为`true`,即`allowEntry`为`false`时被执行。
|
||||
|
||||
在示例代码中,小心地选择布尔常量或变量有助于代码的可读性,并且避免使用双重逻辑非运算,或混乱的逻辑语句。
|
||||
|
||||
@ -389,65 +419,73 @@ Swift 提供了两个方便表达一个区间的值的运算符。
|
||||
|
||||
逻辑与(`a && b`)表达了只有`a`和`b`的值都为`true`时,整个表达式的值才会是`true`。
|
||||
|
||||
只要任意一个值为`false`,整个表达式的值就为`false`。事实上,如果第一个值为`false`,那么是不去计算第二个值的,因为它已经不可能影响整个表达式的结果了。这被称做 "短路计算(short-circuit evaluation)"。
|
||||
只要任意一个值为`false`,整个表达式的值就为`false`。事实上,如果第一个值为`false`,那么是不去计算第二个值的,因为它已经不可能影响整个表达式的结果了。这被称做“短路计算(short-circuit evaluation)”。
|
||||
|
||||
以下例子,只有两个`Bool`值都为`true`的时候才允许进入:
|
||||
|
||||
let enteredDoorCode = true
|
||||
let passedRetinaScan = false
|
||||
if enteredDoorCode && passedRetinaScan {
|
||||
println("Welcome!")
|
||||
} else {
|
||||
println("ACCESS DENIED")
|
||||
}
|
||||
// 输出 "ACCESS DENIED"
|
||||
|
||||
```swift
|
||||
let enteredDoorCode = true
|
||||
let passedRetinaScan = false
|
||||
if enteredDoorCode && passedRetinaScan {
|
||||
print("Welcome!")
|
||||
} else {
|
||||
print("ACCESS DENIED")
|
||||
}
|
||||
// 输出 "ACCESS DENIED"
|
||||
```
|
||||
|
||||
### 逻辑或
|
||||
|
||||
逻辑或(`a || b`)是一个由两个连续的`|`组成的中置运算符。它表示了两个逻辑表达式的其中一个为`true`,整个表达式就为`true`。
|
||||
|
||||
同逻辑与运算类似,逻辑或也是"短路计算"的,当左端的表达式为`true`时,将不计算右边的表达式了,因为它不可能改变整个表达式的值了。
|
||||
同逻辑与运算类似,逻辑或也是“短路计算”的,当左端的表达式为`true`时,将不计算右边的表达式了,因为它不可能改变整个表达式的值了。
|
||||
|
||||
以下示例代码中,第一个布尔值(`hasDoorKey`)为`false`,但第二个值(`knowsOverridePassword`)为`true`,所以整个表达是`true`,于是允许进入:
|
||||
|
||||
let hasDoorKey = false
|
||||
let knowsOverridePassword = true
|
||||
if hasDoorKey || knowsOverridePassword {
|
||||
println("Welcome!")
|
||||
} else {
|
||||
println("ACCESS DENIED")
|
||||
}
|
||||
// 输出 "Welcome!"
|
||||
```swift
|
||||
let hasDoorKey = false
|
||||
let knowsOverridePassword = true
|
||||
if hasDoorKey || knowsOverridePassword {
|
||||
print("Welcome!")
|
||||
} else {
|
||||
print("ACCESS DENIED")
|
||||
}
|
||||
// 输出 "Welcome!"
|
||||
```
|
||||
|
||||
### 逻辑运算符组合计算
|
||||
|
||||
我们可以组合多个逻辑运算来表达一个复合逻辑:
|
||||
|
||||
if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
|
||||
println("Welcome!")
|
||||
} else {
|
||||
println("ACCESS DENIED")
|
||||
}
|
||||
// 输出 "Welcome!"
|
||||
```swift
|
||||
if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
|
||||
print("Welcome!")
|
||||
} else {
|
||||
print("ACCESS DENIED")
|
||||
}
|
||||
// 输出 "Welcome!"
|
||||
```
|
||||
|
||||
这个例子使用了含多个`&&`和`||`的复合逻辑。但无论怎样,`&&`和`||`始终只能操作两个值。所以这实际是三个简单逻辑连续操作的结果。我们来解读一下:
|
||||
|
||||
如果我们输入了正确的密码并通过了视网膜扫描; 或者我们有一把有效的钥匙; 又或者我们知道紧急情况下重置的密码,我们就能把门打开进入。
|
||||
如果我们输入了正确的密码并通过了视网膜扫描,或者我们有一把有效的钥匙,又或者我们知道紧急情况下重置的密码,我们就能把门打开进入。
|
||||
|
||||
前两种情况,我们都不满足,所以前两个简单逻辑的结果是`false`,但是我们是知道紧急情况下重置的密码的,所以整个复杂表达式的值还是`true`。
|
||||
|
||||
>注意:
|
||||
Swift 逻辑操作符`&&`和`||`是左结合的,这意味着拥有多元逻辑操作符的复合表达式优先计算最左边的子表达式。
|
||||
|
||||
### 使用括号来明确优先级
|
||||
|
||||
为了一个复杂表达式更容易读懂,在合适的地方使用括号来明确优先级是很有效的,虽然它并非必要的。在上个关于门的权限的例子中,我们给第一个部分加个括号,使用它看起来逻辑更明确:
|
||||
为了一个复杂表达式更容易读懂,在合适的地方使用括号来明确优先级是很有效的,虽然它并非必要的。在上个关于门的权限的例子中,我们给第一个部分加个括号,使它看起来逻辑更明确:
|
||||
|
||||
```swift
|
||||
if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword {
|
||||
print("Welcome!")
|
||||
} else {
|
||||
print("ACCESS DENIED")
|
||||
}
|
||||
// 输出 "Welcome!"
|
||||
```
|
||||
|
||||
if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword {
|
||||
println("Welcome!")
|
||||
} else {
|
||||
println("ACCESS DENIED")
|
||||
}
|
||||
// 输出 "Welcome!"
|
||||
|
||||
|
||||
这括号使得前两个值被看成整个逻辑表达中独立的一个部分。虽然有括号和没括号的输出结果是一样的,但对于读代码的人来说有括号的代码更清晰。可读性比简洁性更重要,请在可以让你代码变清晰地地方加个括号吧!
|
||||
这括号使得前两个值被看成整个逻辑表达中独立的一个部分。虽然有括号和没括号的输出结果是一样的,但对于读代码的人来说有括号的代码更清晰。可读性比简洁性更重要,请在可以让你代码变清晰的地方加个括号吧!
|
||||
|
||||
@ -1,42 +1,104 @@
|
||||
> 翻译:[zqp](https://github.com/zqp)
|
||||
> 校对:[shinyzhu](https://github.com/shinyzhu), [stanzhai](https://github.com/stanzhai), [feiin](https://github.com/feiin)
|
||||
|
||||
# 集合类型 (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)
|
||||
- [数组(Arrays)](#arrays)
|
||||
- [集合(Sets)](#sets)
|
||||
- [字典(Dictionaries)](#dictionaries)
|
||||
|
||||
Swift 语言提供经典的数组和字典两种集合类型来存储集合数据。数组用来按顺序存储相同类型的数据。字典虽然无序存储相同类型数据值但是需要由独有的标识符引用和寻址(就是键值对)。
|
||||
Swift 语言提供`Arrays`、`Sets`和`Dictionaries`三种基本的集合类型用来存储集合数据。数组是有序数据的集。集合是无序无重复数据的集。字典是无序的键值对的集。
|
||||
|
||||
Swift 语言里的数组和字典中存储的数据值类型必须明确。 这意味着我们不能把不正确的数据类型插入其中。 同时这也说明我们完全可以对获取出的值类型非常自信。 Swift 对显式类型集合的使用确保了我们的代码对工作所需要的类型非常清楚,也让我们在开发中可以早早地找到任何的类型不匹配错误。
|
||||

|
||||
|
||||
> 注意:
|
||||
Swift 的数组结构在被声明成常量和变量或者被传入函数与方法中时会相对于其他类型展现出不同的特性。 获取更多信息请参见[集合的可变性](#mutability_of_collections)与[集合在赋值和复制中的行为](09_Classes_and_Structures.html#assignment_and_copy_behavior_for_collection_types)章节。
|
||||
Swift 语言中的`Arrays`、`Sets`和`Dictionaries`中存储的数据值类型必须明确。这意味着我们不能把不正确的数据类型插入其中。同时这也说明我们完全可以对取回值的类型非常自信。
|
||||
|
||||
> 注意:
|
||||
Swift 的`Arrays`、`Sets`和`Dictionaries`类型被实现为泛型集合。更多关于泛型类型和集合,参见 [泛型](./23_Generics.html)章节。
|
||||
|
||||
<a name="mutability_of_collections"></a>
|
||||
## 集合的可变性
|
||||
|
||||
如果创建一个`Arrays`、`Sets`或`Dictionaries`并且把它分配成一个变量,这个集合将会是可变的。这意味着我们可以在创建之后添加更多或移除已存在的数据项来改变这个集合的大小。如果我们把`Arrays`、`Sets`或`Dictionaries`分配成常量,那么它就是不可变的,它的大小不能被改变。
|
||||
|
||||
> 注意:
|
||||
在我们不需要改变集合大小的时候创建不可变集合是很好的习惯。如此 Swift 编译器可以优化我们创建的集合。
|
||||
|
||||
<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>
|
||||
### 数组的简单语法
|
||||
|
||||
写 Swift 数组应该遵循像`Array<SomeType>`这样的形式,其中`SomeType`是这个数组中唯一允许存在的数据类型。 我们也可以使用像`[SomeType]`这样的简单语法。 尽管两种形式在功能上是一样的,但是推荐较短的那种,而且在本文中都会使用这种形式来使用数组。
|
||||
写 Swift 数组应该遵循像`Array<Element>`这样的形式,其中`Element`是这个数组中唯一允许存在的数据类型。我们也可以使用像`[Element]`这样的简单语法。尽管两种形式在功能上是一样的,但是推荐较短的那种,而且在本文中都会使用这种形式来使用数组。
|
||||
|
||||
<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 被推断为 [Double],等价于 [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]`。
|
||||
|
||||
下面这个例子创建了一个叫做`shoppingList`并且存储字符串的数组:
|
||||
下面这个例子创建了一个叫做`shoppingList`并且存储`String`的数组:
|
||||
|
||||
```swift
|
||||
var shoppingList: [String] = ["Eggs", "Milk"]
|
||||
@ -45,10 +107,10 @@ var shoppingList: [String] = ["Eggs", "Milk"]
|
||||
|
||||
`shoppingList`变量被声明为“字符串值类型的数组“,记作`[String]`。 因为这个数组被规定只有`String`一种数据结构,所以只有`String`类型可以在其中被存取。 在这里,`shoppinglist`数组由两个`String`值(`"Eggs"` 和`"Milk"`)构造,并且由字面量定义。
|
||||
|
||||
> 注意:
|
||||
> `Shoppinglist`数组被声明为变量(`var`关键字创建)而不是常量(`let`创建)是因为以后可能会有更多的数据项被插入其中。
|
||||
> 注意:
|
||||
`Shoppinglist`数组被声明为变量(`var`关键字创建)而不是常量(`let`创建)是因为以后可能会有更多的数据项被插入其中。
|
||||
|
||||
在这个例子中,字面量仅仅包含两个`String`值。匹配了该数组的变量声明(只能包含`String`的数组),所以这个字面量的分配过程就是允许用两个初始项来构造`shoppinglist`。
|
||||
在这个例子中,字面量仅仅包含两个`String`值。匹配了该数组的变量声明(只能包含`String`的数组),所以这个字面量的分配过程可以作为用两个初始项来构造`shoppinglist`的一种方式。
|
||||
|
||||
由于 Swift 的类型推断机制,当我们用字面量构造只拥有相同类型值数组的时候,我们不必把数组的类型定义清楚。 `shoppinglist`的构造也可以这样写:
|
||||
|
||||
@ -61,26 +123,27 @@ var shoppingList = ["Eggs", "Milk"]
|
||||
<a name="accessing_and_modifying_an_array"></a>
|
||||
### 访问和修改数组
|
||||
|
||||
我们可以通过数组的方法和属性来访问和修改数组,或者下标语法。
|
||||
还可以使用数组的只读属性`count`来获取数组中的数据项数量。
|
||||
我们可以通过数组的方法和属性来访问和修改数组,或者使用下标语法。
|
||||
|
||||
可以使用数组的只读属性`count`来获取数组中的数据项数量:
|
||||
|
||||
```swift
|
||||
println("The shopping list contains \(shoppingList.count) items.")
|
||||
// 输出"The shopping list contains 2 items."(这个数组有2个项)
|
||||
print("The shopping list contains \(shoppingList.count) items.")
|
||||
// 输出 "The shopping list contains 2 items."(这个数组有2个项)
|
||||
```
|
||||
|
||||
使用布尔项`isEmpty`来作为检查`count`属性的值是否为 0 的捷径。
|
||||
使用布尔值属性`isEmpty`作为检查`count`属性的值是否为 0 的捷径:
|
||||
|
||||
```swift
|
||||
if shoppingList.isEmpty {
|
||||
println("The shopping list is empty.")
|
||||
print("The shopping list is empty.")
|
||||
} 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 不是空的)
|
||||
```
|
||||
|
||||
也可以使用`append`方法在数组后面添加新的数据项:
|
||||
也可以使用`append(_:)`方法在数组后面添加新的数据项:
|
||||
|
||||
```swift
|
||||
shoppingList.append("Flour")
|
||||
@ -92,7 +155,7 @@ shoppingList.append("Flour")
|
||||
```swift
|
||||
shoppingList += ["Baking Powder"]
|
||||
// shoppingList 现在有四项了
|
||||
shoppingList += ["Chocolate Spread","Cheese","Butter"]
|
||||
shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
|
||||
// shoppingList 现在有七项了
|
||||
```
|
||||
|
||||
@ -103,7 +166,8 @@ var firstItem = shoppingList[0]
|
||||
// 第一项是 "Eggs"
|
||||
```
|
||||
|
||||
注意第一项在数组中的索引值是`0`而不是`1`。 Swift 中的数组索引总是从零开始。
|
||||
> 注意:
|
||||
第一项在数组中的索引值是`0`而不是`1`。 Swift 中的数组索引总是从零开始。
|
||||
|
||||
我们也可以用下标来改变某个已有索引值对应的数据值:
|
||||
|
||||
@ -116,13 +180,14 @@ shoppingList[0] = "Six eggs"
|
||||
|
||||
```swift
|
||||
shoppingList[4...6] = ["Bananas", "Apples"]
|
||||
// shoppingList 现在有六项
|
||||
// shoppingList 现在有6项
|
||||
```
|
||||
|
||||
> 注意:
|
||||
>我们不能使用下标语法在数组尾部添加新项。如果我们试着用这种方法对索引越界的数据进行检索或者设置新值的操作,我们会引发一个运行期错误。我们可以使用索引值和数组的`count`属性进行比较来在使用某个索引之前先检验是否有效。除了当`count`等于 0 时(说明这是个空数组),最大索引值一直是`count - 1`,因为数组都是零起索引。
|
||||
> 注意:
|
||||
不可以用下标访问的形式去在数组尾部添加新项。
|
||||
|
||||
调用数组的`insert(atIndex:)`方法来在某个具体索引值之前添加数据项:
|
||||
|
||||
调用数组的`insert(_:atIndex:)`方法来在某个具体索引值之前添加数据项:
|
||||
|
||||
```swift
|
||||
shoppingList.insert("Maple Syrup", atIndex: 0)
|
||||
@ -130,31 +195,33 @@ shoppingList.insert("Maple Syrup", atIndex: 0)
|
||||
// "Maple Syrup" 现在是这个列表中的第一项
|
||||
```
|
||||
|
||||
这次`insert`函数调用把值为`"Maple Syrup"`的新数据项插入列表的最开始位置,并且使用`0`作为索引值。
|
||||
这次`insert(_:atIndex:)`方法调用把值为`"Maple Syrup"`的新数据项插入列表的最开始位置,并且使用`0`作为索引值。
|
||||
|
||||
类似的我们可以使用`removeAtIndex`方法来移除数组中的某一项。这个方法把数组在特定索引值中存储的数据项移除并且返回这个被移除的数据项(我们不需要的时候就可以无视它):
|
||||
类似的我们可以使用`removeAtIndex(_:)`方法来移除数组中的某一项。这个方法把数组在特定索引值中存储的数据项移除并且返回这个被移除的数据项(我们不需要的时候就可以无视它):
|
||||
|
||||
```swift
|
||||
let mapleSyrup = shoppingList.removeAtIndex(0)
|
||||
// 索引值为0的数据项被移除
|
||||
// shoppingList 现在只有6项,而且不包括Maple Syrup
|
||||
// mapleSyrup常量的值等于被移除数据项的值 "Maple Syrup"
|
||||
// shoppingList 现在只有6项,而且不包括 Maple Syrup
|
||||
// mapleSyrup 常量的值等于被移除数据项的值 "Maple Syrup"
|
||||
```
|
||||
> 注意:
|
||||
如果我们试着对索引越界的数据进行检索或者设置新值的操作,会引发一个运行期错误。我们可以使用索引值和数组的`count`属性进行比较来在使用某个索引之前先检验是否有效。除了当`count`等于 0 时(说明这是个空数组),最大索引值一直是`count - 1`,因为数组都是零起索引。
|
||||
|
||||
数据项被移除后数组中的空出项会被自动填补,所以现在索引值为`0`的数据项的值再次等于`"Six eggs"`:
|
||||
数据项被移除后数组中的空出项会被自动填补,所以现在索引值为`0`的数据项的值再次等于`"Six eggs"`:
|
||||
|
||||
```swift
|
||||
firstItem = shoppingList[0]
|
||||
// firstItem 现在等于 "Six eggs"
|
||||
```
|
||||
|
||||
如果我们只想把数组中的最后一项移除,可以使用`removeLast`方法而不是`removeAtIndex`方法来避免我们需要获取数组的`count`属性。就像后者一样,前者也会返回被移除的数据项:
|
||||
如果我们只想把数组中的最后一项移除,可以使用`removeLast()`方法而不是`removeAtIndex(_:)`方法来避免我们需要获取数组的`count`属性。就像后者一样,前者也会返回被移除的数据项:
|
||||
|
||||
```swift
|
||||
let apples = shoppingList.removeLast()
|
||||
// 数组的最后一项被移除了
|
||||
// shoppingList现在只有5项,不包括cheese
|
||||
// apples 常量的值现在等于"Apples" 字符串
|
||||
// shoppingList 现在只有5项,不包括 cheese
|
||||
// apples 常量的值现在等于 "Apples" 字符串
|
||||
```
|
||||
|
||||
<a name="iterating_over_an_array"></a>
|
||||
@ -164,7 +231,7 @@ let apples = shoppingList.removeLast()
|
||||
|
||||
```swift
|
||||
for item in shoppingList {
|
||||
println(item)
|
||||
print(item)
|
||||
}
|
||||
// Six eggs
|
||||
// Milk
|
||||
@ -173,11 +240,11 @@ for item in shoppingList {
|
||||
// Bananas
|
||||
```
|
||||
|
||||
如果我们同时需要每个数据项的值和索引值,可以使用全局`enumerate`函数来进行数组遍历。`enumerate`返回一个由每一个数据项索引值和数据值组成的元组。我们可以把这个元组分解成临时常量或者变量来进行遍历:
|
||||
如果我们同时需要每个数据项的值和索引值,可以使用`enumerate()`方法来进行数组遍历。`enumerate()`返回一个由每一个数据项索引值和数据值组成的元组。我们可以把这个元组分解成临时常量或者变量来进行遍历:
|
||||
|
||||
```swift
|
||||
for (index, value) in enumerate(shoppingList) {
|
||||
println("Item \(String(index + 1)): \(value)")
|
||||
for (index, value) in shoppingList.enumerate() {
|
||||
print("Item \(String(index + 1)): \(value)")
|
||||
}
|
||||
// Item 1: Six eggs
|
||||
// Item 2: Milk
|
||||
@ -188,182 +255,159 @@ for (index, value) in enumerate(shoppingList) {
|
||||
|
||||
更多关于`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>
|
||||
## 集合
|
||||
|
||||
集合用来存储相同类型并且没有确定顺序的值。当集合元素顺序不重要时或者希望确保每个元素只出现一次时可以把集合当做是数组另一形式。
|
||||
集合(Set)用来存储相同类型并且没有确定顺序的值。当集合元素顺序不重要时或者希望确保每个元素只出现一次时可以使用集合而不是数组。
|
||||
|
||||
> 注意:
|
||||
> Swift的<code>Set</code>类型被桥接到<code>Fundation</code>中的<code>NSSet</code>类
|
||||
> 关于使用<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>
|
||||
> 注意:
|
||||
> Swift的`Set`类型被桥接到`Fundation`中的`NSSet`类。
|
||||
> 关于使用`Fundation`和`Cocoa`中`Set`的知识,请看 [*Using Swift with Cocoa and Objective-C*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216)。
|
||||
|
||||
<a name="hash_values_for_set_types"></a>
|
||||
#### 集合类型的哈希值
|
||||
|
||||
一个类型为了存储在集合中,该类型必须是可哈希化的--也就是说,该类型必须提供一个方法来计算它的哈希值。一个哈希值是`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>
|
||||
### Set类型语法
|
||||
### 集合类型语法
|
||||
|
||||
Swift中的<code>Set</code>类型被写为```Set<SomeType>```,这里的```SomeType```表示```Set```中允许存储的类型,和数组不同的是,集合没有等价的简化形式。
|
||||
Swift 中的`Set`类型被写为`Set<Element>`,这里的`Element`表示`Set`中允许存储的类型,和数组不同的是,集合没有等价的简化形式。
|
||||
|
||||
<a name="create_and_initializing_a_set"></a>
|
||||
### 创建和构造一个Set
|
||||
<a name="creating_and_initalizing_an_empty_set"></a>
|
||||
### 创建和构造一个空的集合
|
||||
|
||||
你可以通过构造器语法创建一个特定类型的空集合:
|
||||
|
||||
```swift
|
||||
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```变量的类型来自于构造器的类型,其为```Set<Character>```。
|
||||
> 注意:
|
||||
> 通过构造器,这里的`letters`变量的类型被推断为`Set<Character>`。
|
||||
|
||||
另外,如果上下文提供了类型信息,比如作为函数的参数或者已知类型的变量或常量,你可以通过一个空的数组字面量创建一个空的```Set```:
|
||||
此外,如果上下文提供了类型信息,比如作为函数的参数或者已知类型的变量或常量,我们可以通过一个空的数组字面量创建一个空的`Set`:
|
||||
|
||||
```swift
|
||||
letters.insert("a")
|
||||
// letters现在含有1个Character类型的值
|
||||
// letters 现在含有1个 Character 类型的值
|
||||
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
|
||||
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
|
||||
var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]
|
||||
```
|
||||
|
||||
由于数组字面量中的所有元素类型相同,Swift可以推断出```Set<String>```作为```favoriteGenres```变量的正确类型。
|
||||
由于数组字面量中的所有元素类型相同,Swift 可以推断出`Set<String>`作为`favoriteGenres`变量的正确类型。
|
||||
|
||||
<a name="accesing_and_modifying_a_set"></a>
|
||||
### 访问和修改一个Set
|
||||
### 访问和修改一个集合
|
||||
|
||||
你可以通过```Set```的属性和方法来访问和修改一个```Set```.
|
||||
你可以通过`Set`的属性和方法来访问和修改一个`Set`。
|
||||
|
||||
为了找出一个```Set```中元素的数量,可以使用其只读属性```count```:
|
||||
为了找出一个`Set`中元素的数量,可以使用其只读属性`count`:
|
||||
|
||||
```swift
|
||||
println("I have \(favoriteGenres.count) favorite music genres.")
|
||||
// 打印 ""I have 3 favorite music genres.""
|
||||
print("I have \(favoriteGenres.count) favorite music genres.")
|
||||
// 打印 "I have 3 favorite music genres."
|
||||
```
|
||||
|
||||
使用布尔属性```isEmpty```作为一个缩写形式去检查```count```属性是否为```0```:
|
||||
使用布尔属性`isEmpty`作为一个缩写形式去检查`count`属性是否为`0`:
|
||||
|
||||
```swift
|
||||
if favoriteGenres.isEmpty {
|
||||
println("As far as music goes, I'm not picky.")
|
||||
print("As far as music goes, I'm not picky.")
|
||||
} else {
|
||||
println("I have particular music preferences.")
|
||||
print("I have particular music preferences.")
|
||||
}
|
||||
// 打印 "I have particular music preferences."
|
||||
```
|
||||
|
||||
你可以通过调用```Set```的``` insert(_:) ```方法添加一个新的元素:
|
||||
你可以通过调用`Set`的`insert(_:)`方法来添加一个新元素:
|
||||
|
||||
```swift
|
||||
favoriteGenres.insert("Jazz")
|
||||
// favoriteGenres 现在包含4个元素
|
||||
```
|
||||
|
||||
你可以通过调用```Set```的```remove(_:)```方法去删除一个元素,如果该值是该```Set```的一个元素则删除该元素并且返回被删除的元素值,否认如果该```Set```不包含该值,则返回```nil```。另外,```Set```中的所有元素可以通过它的```removeAll()```方法删除。
|
||||
你可以通过调用`Set`的`remove(_:)`方法去删除一个元素,如果该值是该`Set`的一个元素则删除该元素并且返回被删除的元素值,否则如果该`Set`不包含该值,则返回`nil`。另外,`Set`中的所有元素可以通过它的`removeAll()`方法删除。
|
||||
|
||||
```swift
|
||||
if let removedGenre = favoriteGenres.remove("Rock") {
|
||||
println("\(removedValue)? I'm over it.")
|
||||
print("\(removedGenre)? I'm over it.")
|
||||
} else {
|
||||
println("I never much cared for that.")
|
||||
print("I never much cared for that.")
|
||||
}
|
||||
// 打印 "Rock? I'm over it."
|
||||
```
|
||||
|
||||
使用```contains(_:)```方法去检查```Set```中是否包含一个特定的值。
|
||||
使用`contains(_:)`方法去检查`Set`中是否包含一个特定的值:
|
||||
|
||||
```swift
|
||||
if favoriteGenres.contains("Funk") {
|
||||
println("I get up on the good foot.")
|
||||
print("I get up on the good foot.")
|
||||
} else {
|
||||
println("It's too funky in here.")
|
||||
print("It's too funky in here.")
|
||||
}
|
||||
// 打印 "It's too funky in here."
|
||||
```
|
||||
|
||||
<a name="iterating_over_a_set"></a>
|
||||
### 遍历一个Set
|
||||
### 遍历一个集合
|
||||
|
||||
你可以在一个```for-in```循环中遍历一个```Set```中的所有值。
|
||||
你可以在一个`for-in`循环中遍历一个`Set`中的所有值。
|
||||
|
||||
```swift
|
||||
for genre in favoriteGenres {
|
||||
println("\(genre)")
|
||||
print("\(genre)")
|
||||
}
|
||||
// Classical
|
||||
// Jazz
|
||||
// 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
|
||||
-> for genre in sorted(favoriteGenres) {
|
||||
println("\(genre)")
|
||||
for genre in favoriteGenres.sort() {
|
||||
print("\(genre)")
|
||||
}
|
||||
// prints "Classical"
|
||||
// prints "Hip hop"
|
||||
@ -373,94 +417,107 @@ Swift的```Set```类型没有确定的顺序,为了按照特定顺序来遍历
|
||||
<a name="performing_set_operations"></a>
|
||||
### 完成集合操作
|
||||
|
||||
你可以高效的完成```Set```的一些基本操作,比如把两个集合组合到一起,判断两个集合共有元素,或者判断两个集合是否全包含,部分包含或者不相交。
|
||||
你可以高效地完成`Set`的一些基本操作,比如把两个集合组合到一起,判断两个集合共有元素,或者判断两个集合是否全包含,部分包含或者不相交。
|
||||
|
||||
<a name="constructing_sets"></a>
|
||||
#### 构造集合
|
||||
<a name="fundamental_set_operations"></a>
|
||||
#### 基本集合操作
|
||||
|
||||
下面的插图描述了两个集合-```a```和```b```-以及通过阴影部分的区域显示集合各种操作的结果。
|
||||
下面的插图描述了两个集合-`a`和`b`-以及通过阴影部分的区域显示集合各种操作的结果。
|
||||
|
||||

|
||||
|
||||
* 使用```union(_:)```方法根据两个集合的值创建一个新的集合。
|
||||
* 使用```subtract(_:)```方法根据不在该集合中的值创建一个新的集合。
|
||||
* 使用```intersect(_:)```方法根据两个集合中都包含的值创建的一个新的集合。
|
||||
* 使用```exclusiveOr(_:)```方法根据值在一个集合中但不在两个集合中的值创建一个新的集合。
|
||||
* 使用`intersect(_:)`方法根据两个集合中都包含的值创建的一个新的集合。
|
||||
* 使用`exclusiveOr(_:)`方法根据在一个集合中但不在两个集合中的值创建一个新的集合。
|
||||
* 使用`union(_:)`方法根据两个集合的值创建一个新的集合。
|
||||
* 使用`subtract(_:)`方法根据不在该集合中的值创建一个新的集合。
|
||||
|
||||
```swift
|
||||
let oddDigits: Set = [1, 3, 5, 7, 9]
|
||||
let evenDigits: Set = [0, 2, 4, 6, 8]
|
||||
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]
|
||||
sorted(oddDigits.intersect(evenDigits))
|
||||
oddDigits.intersect(evenDigits).sort()
|
||||
// []
|
||||
sorted(oddDigits.subtract(singleDigitPrimeNumbers))
|
||||
oddDigits.subtract(singleDigitPrimeNumbers).sort()
|
||||
// [1, 9]
|
||||
sorted(oddDigits.exclusiveOr(singleDigitPrimeNumbers))
|
||||
oddDigits.exclusiveOr(singleDigitPrimeNumbers).sort()
|
||||
// [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`,以及通过重叠区域表述集合间共享的元素。集合`a`是集合`b`的父集合,因为`a`包含了`b`中所有的元素,相反的,集合`b`是集合`a`的子集合,因为属于`b`的元素也被`a`包含。集合`b`和集合`c`彼此不关联,因为它们之间没有共同的元素。
|
||||
|
||||

|
||||
|
||||
* 使用“是否等”运算符(```==```)来判断两个集合是否包含相同的值。
|
||||
* 使用```isSubsetOf(_:)```方法来判断一个集合中的值是否也被包含在另外一个集合中。
|
||||
* 使用```isSupersetOf(_:)```方法来判断一个集合中包含的值是另一个集合中所有的值。
|
||||
* 使用```isStrictSubsetOf(_:)```或者```isStrictSupersetOf(_:)```方法来判断一个集合是否是另外一个集合的子集合或者父集合并且和特定集合不相等。
|
||||
* 使用```isDisjointWith(_:)```方法来判断两个结合是否不含有相同的值。
|
||||
* 使用“是否相等”运算符(`==`)来判断两个集合是否包含全部相同的值。
|
||||
* 使用`isSubsetOf(_:)`方法来判断一个集合中的值是否也被包含在另外一个集合中。
|
||||
* 使用`isSupersetOf(_:)`方法来判断一个集合中包含另一个集合中所有的值。
|
||||
* 使用`isStrictSubsetOf(_:)`或者`isStrictSupersetOf(_:)`方法来判断一个集合是否是另外一个集合的子集合或者父集合并且两个集合并不相等。
|
||||
* 使用`isDisjointWith(_:)`方法来判断两个集合是否不含有相同的值。
|
||||
|
||||
```swift
|
||||
let houseAnimals: Set = ["🐶", "🐱"]
|
||||
let farmAnimals: Set = ["🐮", "🐔", "🐑", "🐶", "🐱"]
|
||||
let cityAnimals: Set = ["🐦", "🐭"]
|
||||
|
||||
houseAnimals.isSubsetOf(farmAnimals)
|
||||
// true
|
||||
farmAnimals.isSuperSetOf(houseAnimals)
|
||||
farmAnimals.isSupersetOf(houseAnimals)
|
||||
// true
|
||||
farmAnimals.isDisjointWith(cityAnimals)
|
||||
// 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>
|
||||
## 字典
|
||||
|
||||
字典是一种存储多个相同类型的值的容器。每个值(value)都关联唯一的键(key),键作为字典中的这个值数据的标识符。和数组中的数据项不同,字典中的数据项并没有具体顺序。我们在需要通过标识符(键)访问数据的时候使用字典,这种方法很大程度上和我们在现实世界中使用字典查字义的方法一样。
|
||||
|
||||
Swift 的字典使用时需要具体规定可以存储键和值类型。不同于 Objective-C 的`NSDictionary`和`NSMutableDictionary` 类可以使用任何类型的对象来作键和值并且不提供任何关于这些对象的本质信息。在 Swift 中,在某个特定字典中可以存储的键和值必须提前定义清楚,方法是通过显性类型标注或者类型推断。
|
||||
> 注意:
|
||||
> Swift 的`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) 一书。
|
||||
|
||||
Swift 的字典使用`Dictionary<KeyType, ValueType>`定义,其中`KeyType`是字典中键的数据类型,`ValueType`是字典中对应于这些键所存储值的数据类型。
|
||||
<a name="dictionary_type_shorthand_syntax"></a>
|
||||
## 字典类型快捷语法
|
||||
|
||||
`KeyType`的唯一限制就是可哈希的,这样可以保证它是独一无二的,所有的 Swift 基本类型(例如`String`,`Int`, `Double`和`Bool`)都是默认可哈希的,并且所有这些类型都可以在字典中当做键使用。未关联值的枚举成员(参见[枚举](08_Enumerations.html))也是默认可哈希的。
|
||||
Swift 的字典使用`Dictionary<Key, Value>`定义,其中`Key`是字典中键的数据类型,`Value`是字典中对应于这些键所存储值的数据类型。
|
||||
|
||||
<a name="dictionary_literals"></a>
|
||||
## 字典字面量
|
||||
> 注意:
|
||||
> 一个字典的`Key`类型必须遵循`Hashable`协议,就像`Set`的值类型。
|
||||
|
||||
我们可以使用字典字面量来构造字典,它们和我们刚才介绍过的数组字面量拥有相似语法。一个字典字面量是一个定义拥有一个或者多个键值对的字典集合的简单语句。
|
||||
我们也可以用`[Key: Value]`这样快捷的形式去创建一个字典类型。虽然这两种形式功能上相同,但是后者是首选,并且这本指导书涉及到字典类型时通篇采用后者。
|
||||
|
||||
一个键值对是一个`key`和一个`value`的结合体。在字典字面量中,每一个键值对的键和值都由冒号分割。这些键值对构成一个列表,其中这些键值对由方括号包含并且由逗号分割:
|
||||
<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>
|
||||
## 用字典字面量创建字典
|
||||
|
||||
我们可以使用字典字面量来构造字典,这和我们刚才介绍过的数组字面量拥有相似语法。字典字面量是一种将一个或多个键值对写作`Dictionary`集合的快捷途径。
|
||||
|
||||
一个键值对是一个`key`和一个`value`的结合体。在字典字面量中,每一个键值对的键和值都由冒号分割。这些键值对构成一个列表,其中这些键值对由方括号包含、由逗号分割:
|
||||
|
||||
```swift
|
||||
[key 1: value 1, key 2: value 2, key 3: value 3]
|
||||
@ -469,48 +526,51 @@ Swift 的字典使用`Dictionary<KeyType, ValueType>`定义,其中`KeyType`是
|
||||
下面的例子创建了一个存储国际机场名称的字典。在这个字典中键是三个字母的国际航空运输相关代码,值是机场名称:
|
||||
|
||||
```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`字典使用字典字面量初始化,包含两个键值对。第一对的键是`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
|
||||
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>
|
||||
### 读取和修改字典
|
||||
### 访问和修改字典
|
||||
|
||||
我们可以通过字典的方法和属性来读取和修改字典,或者使用下标语法。和数组一样,我们可以通过字典的只读属性`count`来获取某个字典的数据项数量:
|
||||
我们可以通过字典的方法和属性来访问和修改字典,或者通过使用下标语法。
|
||||
|
||||
和数组一样,我们可以通过字典的只读属性`count`来获取某个字典的数据项数量:
|
||||
|
||||
```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."(这个字典有两个数据项)
|
||||
```
|
||||
|
||||
可以使用布尔属性`isEmpty`来快捷的检查字典的`count`属性是否等于0。
|
||||
使用布尔属性`isEmpty`来快捷地检查字典的`count`属性是否等于0:
|
||||
|
||||
```swift
|
||||
if airports.isEmpty {
|
||||
println("The airports dictionary is empty.")
|
||||
print("The airports dictionary is empty.")
|
||||
} 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
|
||||
airports["LHR"] = "London"
|
||||
@ -524,124 +584,89 @@ airports["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
|
||||
if let oldValue = airports.updateValue("Dublin Internation", forKey: "DUB") {
|
||||
println("The old value for DUB was \(oldValue).")
|
||||
if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") {
|
||||
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
|
||||
if let airportName = airports["DUB"] {
|
||||
println("The name of the airport is \(airportName).")
|
||||
print("The name of the airport is \(airportName).")
|
||||
} 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`来从字典里移除一个键值对:
|
||||
|
||||
```swift
|
||||
airports["APL"] = "Apple Internation"
|
||||
// "Apple Internation"不是真的 APL机场, 删除它
|
||||
// "Apple Internation" 不是真的 APL 机场, 删除它
|
||||
airports["APL"] = nil
|
||||
// APL现在被移除了
|
||||
// APL 现在被移除了
|
||||
```
|
||||
|
||||
另外,`removeValueForKey`方法也可以用来在字典中移除键值对。这个方法在键值对存在的情况下会移除该键值对并且返回被移除的value或者在没有值的情况下返回`nil`:
|
||||
此外,`removeValueForKey(_:)`方法也可以用来在字典中移除键值对。这个方法在键值对存在的情况下会移除该键值对并且返回被移除的值或者在没有值的情况下返回`nil`:
|
||||
|
||||
```swift
|
||||
if let removedValue = airports.removeValueForKey("DUB") {
|
||||
println("The removed airport's name is \(removedValue).")
|
||||
print("The removed airport's name is \(removedValue).")
|
||||
} 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>
|
||||
### 字典遍历
|
||||
|
||||
我们可以使用`for-in`循环来遍历某个字典中的键值对。每一个字典中的数据项都由`(key, value)`元组形式返回,并且我们可以使用临时常量或者变量来分解这些元组:
|
||||
我们可以使用`for-in`循环来遍历某个字典中的键值对。每一个字典中的数据项都以`(key, value)`元组形式返回,并且我们可以使用临时常量或者变量来分解这些元组:
|
||||
|
||||
```swift
|
||||
for (airportCode, airportName) in airports {
|
||||
println("\(airportCode): \(airportName)")
|
||||
print("\(airportCode): \(airportName)")
|
||||
}
|
||||
// TYO: Tokyo
|
||||
// YYZ: Toronto Pearson
|
||||
// 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
|
||||
for airportCode in airports.keys {
|
||||
println("Airport code: \(airportCode)")
|
||||
print("Airport code: \(airportCode)")
|
||||
}
|
||||
// Airport code: TYO
|
||||
// Airport code: YYZ
|
||||
// Airport code: LHR
|
||||
|
||||
for airportName in airports.values {
|
||||
println("Airport name: \(airportName)")
|
||||
print("Airport name: \(airportName)")
|
||||
}
|
||||
// Airport name: Tokyo
|
||||
// Airport name: Toronto Pearson
|
||||
// Airport name: London Heathrow
|
||||
```
|
||||
|
||||
如果我们只是需要使用某个字典的键集合或者值集合来作为某个接受`Array`实例 API 的参数,可以直接使用`keys`或者`values`属性直接构造一个新数组:
|
||||
如果我们只是需要使用某个字典的键集合或者值集合来作为某个接受`Array`实例的 API 的参数,可以直接使用`keys`或者`values`属性构造一个新数组:
|
||||
|
||||
```swift
|
||||
let airportCodes = Array(airports.keys)
|
||||
// airportCodes is ["TYO", "LHR"]
|
||||
let airportCodes = [String](airports.keys)
|
||||
// airportCodes 是 ["YYZ", "LHR"]
|
||||
|
||||
let airportNames = Array(airports.values)
|
||||
// airportNames is ["Tokyo", "London Heathrow"]
|
||||
let airportNames = [String](airports.values)
|
||||
// airportNames 是 ["Toronto Pearson", "London Heathrow"]
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> 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 编译器可以优化我们创建的集合。
|
||||
Swift 的字典类型是无序集合类型。为了以特定的顺序遍历字典的键或值,可以对字典的`keys`或`values`属性使用`sort()`方法。
|
||||
|
||||
@ -1,8 +1,12 @@
|
||||
# 控制流(Control Flow)
|
||||
-----------------
|
||||
|
||||
> 1.0
|
||||
> 翻译:[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)
|
||||
|
||||
# 控制流
|
||||
-----------------
|
||||
> 2.0
|
||||
> 翻译+校对:[JackAlan](https://github.com/AlanMelody)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
@ -11,19 +15,19 @@
|
||||
- [条件语句](#conditional_statement)
|
||||
- [控制转移语句(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)至下一个 case,Swift 无需写`break`,所以不会发生这种贯穿(fallthrough)的情况。case 还可以匹配更多的类型模式,包括区间匹配(range matching),元组(tuple)和特定类型的描述。`switch`的 case 语句中匹配的值可以是由 case 体内部临时的常量或者变量决定,也可以由`where`分句描述更复杂的匹配条件。
|
||||
Swift 的`switch`语句比 C 语言中更加强大。在 C 语言中,如果某个 case 不小心漏写了`break`,这个 case 就会贯穿至下一个 case,Swift 无需写`break`,所以不会发生这种贯穿的情况。case 还可以匹配更多的类型模式,包括区间匹配(range matching),元组(tuple)和特定类型的描述。`switch`的 case 语句中匹配的值可以是由 case 体内部临时的常量或者变量决定,也可以由`where`分句描述更复杂的匹配条件。
|
||||
|
||||
<a name="for_loops"></a>
|
||||
## For 循环
|
||||
|
||||
`for`循环用来按照指定的次数多次执行一系列语句。Swift 提供两种`for`循环形式:
|
||||
Swift 提供两种`for`循环形式以来按照指定的次数多次执行一系列语句:
|
||||
|
||||
* `for-in`用来遍历一个区间(range),序列(sequence),集合(collection),系列(progression)里面所有的元素执行一系列语句。
|
||||
* for条件递增(`for-condition-increment`)语句,用来重复执行一系列语句直到达成特定条件达成,一般通过在每次循环完成后增加计数器的值来实现。
|
||||
* `for-in`循环对一个集合里面的每个元素执行一系列语句。
|
||||
* for循环,用来重复执行一系列语句直到达成特定条件达成,一般通过在每次循环完成后增加计数器的值来实现。
|
||||
|
||||
<a name="for_in"></a>
|
||||
### For-In
|
||||
@ -34,7 +38,7 @@ Swift 的`switch`语句比 C 语言中更加强大。在 C 语言中,如果某
|
||||
|
||||
```swift
|
||||
for index in 1...5 {
|
||||
println("\(index) times 5 is \(index * 5)")
|
||||
print("\(index) times 5 is \(index * 5)")
|
||||
}
|
||||
// 1 times 5 is 5
|
||||
// 2 times 5 is 10
|
||||
@ -43,13 +47,10 @@ for index in 1...5 {
|
||||
// 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`的值,又或者想让`index`成为一个变量而不是常量,你必须在循环之前自己进行声明。
|
||||
|
||||
如果你不需要知道区间内每一项的值,你可以使用下划线(`_`)替代变量名来忽略对值的访问:
|
||||
|
||||
```swift
|
||||
@ -59,7 +60,7 @@ var answer = 1
|
||||
for _ in 1...power {
|
||||
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"
|
||||
```
|
||||
|
||||
@ -70,7 +71,7 @@ println("\(base) to the power of \(power) is \(answer)")
|
||||
```swift
|
||||
let names = ["Anna", "Alex", "Brian", "Jack"]
|
||||
for name in names {
|
||||
println("Hello, \(name)!")
|
||||
print("Hello, \(name)!")
|
||||
}
|
||||
// Hello, Anna!
|
||||
// Hello, Alex!
|
||||
@ -78,41 +79,27 @@ for name in names {
|
||||
// Hello, Jack!
|
||||
```
|
||||
|
||||
你也可以通过遍历一个字典来访问它的键值对(key-value pairs)。遍历字典时,字典的每项元素会以`(key, value)`元组的形式返回,你可以在`for-in`循环中使用显式的常量名称来解读`(key, value)`元组。下面的例子中,字典的键(key)解读为常量`animalName`,字典的值会被解读为常量`legCount`:
|
||||
你也可以通过遍历一个字典来访问它的键值对。遍历字典时,字典的每项元素会以`(key, value)`元组的形式返回,你可以在`for-in`循环中使用显式的常量名称来解读`(key, value)`元组。下面的例子中,字典的键(key)解读为常量`animalName`,字典的值会被解读为常量`legCount`:
|
||||
|
||||
```swift
|
||||
let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
|
||||
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
|
||||
// cats have 4 legs
|
||||
// spiders have 8 legs
|
||||
```
|
||||
|
||||
字典元素的遍历顺序和插入顺序可能不同,字典的内容在内部是无序的,所以遍历元素时不能保证顺序。关于数组和字典,详情参见[集合类型](../chapter2/04_Collection_Types.html)。
|
||||
字典元素的遍历顺序和插入顺序可能不同,字典的内容在内部是无序的,所以遍历元素时不能保证顺序。关于数组和字典,详情参见[集合类型](./04_Collection_Types.html)。
|
||||
|
||||
除了数组和字典,你也可以使用`for-in`循环来遍历字符串中的字符(`Character`):
|
||||
|
||||
```swift
|
||||
for character in "Hello" {
|
||||
println(character)
|
||||
}
|
||||
// H
|
||||
// e
|
||||
// l
|
||||
// l
|
||||
// o
|
||||
```
|
||||
|
||||
<a name="for_condition_increment"></a>
|
||||
### For条件递增(for-condition-increment)
|
||||
<a name="for"></a>
|
||||
|
||||
除了`for-in`循环,Swift 提供使用条件判断和递增方法的标准 C 样式`for`循环:
|
||||
|
||||
```swift
|
||||
for var index = 0; index < 3; ++index {
|
||||
println("index is \(index)")
|
||||
print("index is \(index)")
|
||||
}
|
||||
// index is 0
|
||||
// index is 1
|
||||
@ -129,30 +116,23 @@ for var index = 0; index < 3; ++index {
|
||||
|
||||
这个循环执行流程如下:
|
||||
|
||||
1. 循环首次启动时,初始化表达式(_initialization expression_)被调用一次,用来初始化循环所需的所有常量和变量。
|
||||
2. 条件表达式(_condition expression_)被调用,如果表达式调用结果为`false`,循环结束,继续执行`for`循环关闭大括号
|
||||
(`}`)之后的代码。如果表达式调用结果为`true`,则会执行大括号内部的代码(_statements_)。
|
||||
3. 执行所有语句(_statements_)之后,执行递增表达式(_increment expression_)。通常会增加或减少计数器的值,或者根据语句(_statements_)输出来修改某一个初始化的变量。当递增表达式运行完成后,重复执行第 2 步,条件表达式会再次执行。
|
||||
1. 循环首次启动时,初始化表达式被调用一次,用来初始化循环所需的所有常量和变量。
|
||||
2. 条件表达式被调用,如果表达式调用结果为`false`,循环结束,继续执行`for`循环关闭大括号
|
||||
(`}`)之后的代码。如果表达式调用结果为`true`,则会执行大括号内部的代码。
|
||||
3. 执行所有语句之后,执行递增表达式。通常会增加或减少计数器的值,或者根据语句输出来修改某一个初始化的变量。当递增表达式运行完成后,重复执行第 2 步,条件表达式会再次执行。
|
||||
|
||||
上述描述和循环格式等同于:
|
||||
|
||||
> `initialization`
|
||||
> while `condition` {
|
||||
> `statements`
|
||||
> `increment`
|
||||
> }
|
||||
|
||||
在初始化表达式中声明的常量和变量(比如`var index = 0`)只在`for`循环的生命周期里有效。如果想在循环结束后访问`index`的值,你必须要在循环生命周期开始前声明`index`。
|
||||
|
||||
```swift
|
||||
var index: Int
|
||||
for index = 0; index < 3; ++index {
|
||||
println("index is \(index)")
|
||||
print("index is \(index)")
|
||||
}
|
||||
// index is 0
|
||||
// index is 1
|
||||
// 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
|
||||
```
|
||||
|
||||
@ -164,7 +144,7 @@ println("The loop statements were executed \(index) times")
|
||||
`while`循环运行一系列语句直到条件变成`false`。这类循环适合使用在第一次迭代前迭代次数未知的情况下。Swift 提供两种`while`循环形式:
|
||||
|
||||
* `while`循环,每次在循环开始时计算条件是否符合;
|
||||
* `do-while`循环,每次在循环结束时计算条件是否符合。
|
||||
* `repeat-while`循环,每次在循环结束时计算条件是否符合。
|
||||
|
||||
<a name="while"></a>
|
||||
###While
|
||||
@ -177,7 +157,7 @@ println("The loop statements were executed \(index) times")
|
||||
> `statements`
|
||||
> }
|
||||
|
||||
下面的例子来玩一个叫做_蛇和梯子(Snakes and Ladders)_的小游戏,也叫做_滑道和梯子(Chutes and Ladders)_:
|
||||
下面的例子来玩一个叫做蛇和梯子的小游戏,也叫做滑道和梯子:
|
||||
|
||||

|
||||
|
||||
@ -219,7 +199,7 @@ while square < finalSquare {
|
||||
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`。
|
||||
@ -233,18 +213,21 @@ println("Game over!")
|
||||
`while` 循环比较适合本例中的这种情况,因为在 `while` 循环开始时,我们并不知道游戏的长度或者循环的次数,只有在达成指定条件时循环才会结束。
|
||||
|
||||
|
||||
<a name="do_while"></a>
|
||||
###Do-While
|
||||
<a name="repeat_while"></a>
|
||||
###Repeat-While
|
||||
|
||||
`while`循环的另外一种形式是`do-while`,它和`while`的区别是在判断循环条件之前,先执行一次循环的代码块,然后重复循环直到条件为`false`。
|
||||
`while`循环的另外一种形式是`repeat-while`,它和`while`的区别是在判断循环条件之前,先执行一次循环的代码块,然后重复循环直到条件为`false`。
|
||||
|
||||
下面是一般情况下 `do-while`循环的格式:
|
||||
> 注意:
|
||||
> Swift语言的`repeat-while`循环合其他语言中的`do-while`循环是类似的。
|
||||
|
||||
> do {
|
||||
下面是一般情况下 `repeat-while`循环的格式:
|
||||
|
||||
> repeat {
|
||||
> `statements`
|
||||
> } while `condition`
|
||||
|
||||
还是蛇和梯子的游戏,使用`do-while`循环来替代`while`循环。`finalSquare`、`board`、`square`和`diceRoll`的值初始化同`while`循环一样:
|
||||
还是蛇和梯子的游戏,使用`repeat-while`循环来替代`while`循环。`finalSquare`、`board`、`square`和`diceRoll`的值初始化同`while`循环一样:
|
||||
|
||||
``` swift
|
||||
let finalSquare = 25
|
||||
@ -255,12 +238,12 @@ var square = 0
|
||||
var diceRoll = 0
|
||||
```
|
||||
|
||||
`do-while`的循环版本,循环中_第一步_就需要去检测是否在梯子或者蛇的方块上。没有梯子会让玩家直接上到第 25 个方格,所以玩家不会通过梯子直接赢得游戏。这样在循环开始时先检测是否踩在梯子或者蛇上是安全的。
|
||||
`repeat-while`的循环版本,循环中_第一步_就需要去检测是否在梯子或者蛇的方块上。没有梯子会让玩家直接上到第 25 个方格,所以玩家不会通过梯子直接赢得游戏。这样在循环开始时先检测是否踩在梯子或者蛇上是安全的。
|
||||
|
||||
游戏开始时,玩家在第 0 个方格上,`board[0]`一直等于 0, 不会有什么影响:
|
||||
|
||||
```swift
|
||||
do {
|
||||
repeat {
|
||||
// 顺着梯子爬上去或者顺着蛇滑下去
|
||||
square += board[square]
|
||||
// 掷骰子
|
||||
@ -268,12 +251,12 @@ do {
|
||||
// 根据点数移动
|
||||
square += diceRoll
|
||||
} while square < finalSquare
|
||||
println("Game over!")
|
||||
print("Game over!")
|
||||
```
|
||||
|
||||
检测完玩家是否踩在梯子或者蛇上之后,开始掷骰子,然后玩家向前移动`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>
|
||||
## 条件语句
|
||||
@ -290,7 +273,7 @@ Swift 提供两种类型的条件语句:`if`语句和`switch`语句。通常
|
||||
```swift
|
||||
var temperatureInFahrenheit = 30
|
||||
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."
|
||||
```
|
||||
@ -302,9 +285,9 @@ if temperatureInFahrenheit <= 32 {
|
||||
```swift
|
||||
temperatureInFahrenheit = 40
|
||||
if temperatureInFahrenheit <= 32 {
|
||||
println("It's very cold. Consider wearing a scarf.")
|
||||
print("It's very cold. Consider wearing a scarf.")
|
||||
} 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."
|
||||
```
|
||||
@ -316,11 +299,11 @@ if temperatureInFahrenheit <= 32 {
|
||||
```swift
|
||||
temperatureInFahrenheit = 90
|
||||
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 {
|
||||
println("It's really warm. Don't forget to wear sunscreen.")
|
||||
print("It's really warm. Don't forget to wear sunscreen.")
|
||||
} 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."
|
||||
```
|
||||
@ -330,11 +313,11 @@ if temperatureInFahrenheit <= 32 {
|
||||
实际上,最后的`else`语句是可选的:
|
||||
|
||||
```swift
|
||||
temperatureInFahrenheit = 72
|
||||
temperatureInFahrenheit = 90
|
||||
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 {
|
||||
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`语句会决定哪一条分支应该被执行。
|
||||
|
||||
`switch`语句必须是_完备的_。这就是说,每一个可能的值都必须至少有一个 case 分支与之对应。在某些不可能涵盖所有值的情况下,你可以使用默认(`default`)分支满足该要求,这个默认分支必须在`switch`语句的最后面。
|
||||
`switch`语句必须是完备的。这就是说,每一个可能的值都必须至少有一个 case 分支与之对应。在某些不可能涵盖所有值的情况下,你可以使用默认(`default`)分支满足该要求,这个默认分支必须在`switch`语句的最后面。
|
||||
|
||||
下面的例子使用`switch`语句来匹配一个名为`someCharacter`的小写字符:
|
||||
|
||||
@ -369,12 +352,12 @@ if temperatureInFahrenheit <= 32 {
|
||||
let someCharacter: Character = "e"
|
||||
switch someCharacter {
|
||||
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",
|
||||
"n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
|
||||
println("\(someCharacter) is a consonant")
|
||||
print("\(someCharacter) is a consonant")
|
||||
default:
|
||||
println("\(someCharacter) is not a vowel or a consonant")
|
||||
print("\(someCharacter) is not a vowel or a consonant")
|
||||
}
|
||||
// 输出 "e is a vowel"
|
||||
```
|
||||
@ -389,7 +372,7 @@ default:
|
||||
与 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 分支是空的:
|
||||
|
||||
@ -398,9 +381,9 @@ let anotherCharacter: Character = "a"
|
||||
switch anotherCharacter {
|
||||
case "a":
|
||||
case "A":
|
||||
println("The letter A")
|
||||
print("The letter A")
|
||||
default:
|
||||
println("Not the letter A")
|
||||
print("Not the letter A")
|
||||
}
|
||||
// this will report a compile-time error
|
||||
```
|
||||
@ -418,39 +401,42 @@ default:
|
||||
> 注意:
|
||||
如果想要贯穿至特定的 case 分支中,请使用`fallthrough`语句,详情请参考[贯穿(Fallthrough)](#fallthrough)。
|
||||
|
||||
<a name="range_matching"></a>
|
||||
#### 区间匹配(Range Matching)
|
||||
<a name="interval_matching"></a>
|
||||
#### 区间匹配
|
||||
|
||||
case 分支的模式也可以是一个值的区间。下面的例子展示了如何使用区间匹配来输出任意数字对应的自然语言格式:
|
||||
|
||||
```swift
|
||||
let count = 3_000_000_000_000
|
||||
let countedThings = "stars in the Milky Way"
|
||||
```
|
||||
let approximateCount = 62
|
||||
let countedThings = "moons orbiting Saturn"
|
||||
var naturalCount: String
|
||||
switch count {
|
||||
switch approximateCount {
|
||||
case 0:
|
||||
naturalCount = "no"
|
||||
case 1...3:
|
||||
case 1..<5:
|
||||
naturalCount = "a few"
|
||||
case 4...9:
|
||||
case 5..<12:
|
||||
naturalCount = "several"
|
||||
case 10...99:
|
||||
naturalCount = "tens of"
|
||||
case 100...999:
|
||||
case 12..<100:
|
||||
naturalCount = "dozens of"
|
||||
case 100..<1000:
|
||||
naturalCount = "hundreds of"
|
||||
case 1000...999_999:
|
||||
naturalCount = "thousands of"
|
||||
default:
|
||||
naturalCount = "millions and millions of"
|
||||
naturalCount = "many"
|
||||
}
|
||||
println("There are \(naturalCount) \(countedThings).")
|
||||
// 输出 "There are millions and millions of stars in the Milky Way."
|
||||
print("There are \(naturalCount) \(countedThings).")
|
||||
// 输出 "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>
|
||||
#### 元组(Tuple)
|
||||
|
||||
你可以使用元组在同一个`switch`语句中测试多个值。元组中的元素可以是值,也可以是区间。另外,使用下划线(`_`)来匹配所有可能的值。
|
||||
我们可以使用元组在同一个`switch`语句中测试多个值。元组中的元素可以是值,也可以是区间。另外,使用下划线(`_`)来匹配所有可能的值。
|
||||
|
||||
下面的例子展示了如何使用一个`(Int, Int)`类型的元组来分类下图中的点(x, y):
|
||||
|
||||
@ -458,15 +444,15 @@ println("There are \(naturalCount) \(countedThings).")
|
||||
let somePoint = (1, 1)
|
||||
switch somePoint {
|
||||
case (0, 0):
|
||||
println("(0, 0) is at the origin")
|
||||
print("(0, 0) is at the origin")
|
||||
case (_, 0):
|
||||
println("(\(somePoint.0), 0) is on the x-axis")
|
||||
print("(\(somePoint.0), 0) is on the x-axis")
|
||||
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):
|
||||
println("(\(somePoint.0), \(somePoint.1)) is inside the box")
|
||||
print("(\(somePoint.0), \(somePoint.1)) is inside the box")
|
||||
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"
|
||||
```
|
||||
@ -489,11 +475,11 @@ case 分支的模式允许将匹配的值绑定到一个临时的常量或变量
|
||||
let anotherPoint = (2, 0)
|
||||
switch anotherPoint {
|
||||
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):
|
||||
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):
|
||||
println("somewhere else at (\(x), \(y))")
|
||||
print("somewhere else at (\(x), \(y))")
|
||||
}
|
||||
// 输出 "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 分支里引用。在这个例子中,它们用于简化`println`的书写。
|
||||
一旦声明了这些临时的常量,它们就可以在其对应的 case 分支里引用。在这个例子中,它们用于简化`print(_:)`的书写。
|
||||
|
||||
请注意,这个`switch`语句不包含默认分支。这是因为最后一个 case ——`case let(x, y)`声明了一个可以匹配余下所有值的元组。这使得`switch`语句已经完备了,因此不需要再书写默认分支。
|
||||
|
||||
@ -521,11 +507,11 @@ case 分支的模式可以使用`where`语句来判断额外的条件。
|
||||
let yetAnotherPoint = (1, -1)
|
||||
switch yetAnotherPoint {
|
||||
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:
|
||||
println("(\(x), \(y)) is on the line x == -y")
|
||||
print("(\(x), \(y)) is on the line 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"
|
||||
```
|
||||
@ -543,27 +529,28 @@ case let (x, y):
|
||||
|
||||
控制转移语句改变你代码的执行顺序,通过它你可以实现代码的跳转。Swift有四种控制转移语句。
|
||||
|
||||
- continue
|
||||
- break
|
||||
- fallthrough
|
||||
- return
|
||||
- `continue`
|
||||
- `break`
|
||||
- `fallthrough`
|
||||
- `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>
|
||||
### Continue
|
||||
|
||||
`continue`语句告诉一个循环体立刻停止本次循环迭代,重新开始下次循环迭代。就好像在说“本次循环迭代我已经执行完了”,但是并不会离开整个循环体。
|
||||
|
||||
>注意:
|
||||
在一个for条件递增(`for-condition-increment`)循环体中,在调用`continue`语句后,迭代增量仍然会被计算求值。循环体继续像往常一样工作,仅仅只是循环体中的执行代码会被跳过。
|
||||
> 注意:
|
||||
> 在一个带有条件和递增的for循环体中,调用`continue`语句后,迭代增量仍然会被计算求值。循环体继续像往常一样工作,仅仅只是循环体中的执行代码会被跳过。
|
||||
|
||||
下面的例子把一个小写字符串中的元音字母和空格字符移除,生成了一个含义模糊的短句:
|
||||
|
||||
```swift
|
||||
let puzzleInput = "great minds think alike"
|
||||
var puzzleOutput = ""
|
||||
for character in puzzleInput {
|
||||
for character in puzzleInput.characters {
|
||||
switch character {
|
||||
case "a", "e", "i", "o", "u", " ":
|
||||
continue
|
||||
@ -571,7 +558,7 @@ for character in puzzleInput {
|
||||
puzzleOutput.append(character)
|
||||
}
|
||||
}
|
||||
println(puzzleOutput)
|
||||
print(puzzleOutput)
|
||||
// 输出 "grtmndsthnklk"
|
||||
```
|
||||
|
||||
@ -615,9 +602,9 @@ default:
|
||||
break
|
||||
}
|
||||
if let integerValue = possibleIntegerValue {
|
||||
println("The integer value of \(numberSymbol) is \(integerValue).")
|
||||
print("The integer value of \(numberSymbol) is \(integerValue).")
|
||||
} 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."
|
||||
```
|
||||
@ -633,7 +620,7 @@ if let integerValue = possibleIntegerValue {
|
||||
|
||||
Swift 中的`switch`不会从上一个 case 分支落入到下一个 case 分支中。相反,只要第一个匹配到的 case 分支完成了它需要执行的语句,整个`switch`代码块完成了它的执行。相比之下,C 语言要求你显示的插入`break`语句到每个`switch`分支的末尾来阻止自动落入到下一个 case 分支中。Swift 的这种避免默认落入到下一个分支中的特性意味着它的`switch` 功能要比 C 语言的更加清晰和可预测,可以避免无意识地执行多个 case 分支从而引发的错误。
|
||||
|
||||
如果你确实需要 C 风格的贯穿(fallthrough)的特性,你可以在每个需要该特性的 case 分支中使用`fallthrough`关键字。下面的例子使用`fallthrough`来创建一个数字的描述语句。
|
||||
如果你确实需要 C 风格的贯穿的特性,你可以在每个需要该特性的 case 分支中使用`fallthrough`关键字。下面的例子使用`fallthrough`来创建一个数字的描述语句。
|
||||
|
||||
```swift
|
||||
let integerToDescribe = 5
|
||||
@ -645,7 +632,7 @@ case 2, 3, 5, 7, 11, 13, 17, 19:
|
||||
default:
|
||||
description += " an integer."
|
||||
}
|
||||
println(description)
|
||||
print(description)
|
||||
// 输出 "The number 5 is a prime number, and also an integer."
|
||||
```
|
||||
|
||||
@ -653,13 +640,13 @@ println(description)
|
||||
|
||||
如果`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>
|
||||
### 带标签的语句(Labeled Statements)
|
||||
### 带标签的语句
|
||||
|
||||
在 Swift 中,你可以在循环体和`switch`代码块中嵌套循环体和`switch`代码块来创造复杂的控制流结构。然而,循环体和`switch`代码块两者都可以使用`break`语句来提前结束整个方法体。因此,显示地指明`break`语句想要终止的是哪个循环体或者`switch`代码块,会很有用。类似地,如果你有许多嵌套的循环体,显示指明`continue`语句想要影响哪一个循环体也会非常有用。
|
||||
|
||||
@ -671,9 +658,9 @@ println(description)
|
||||
> `statements`
|
||||
> }
|
||||
|
||||
下面的例子是在一个带有标签的`while`循环体中调用`break`和`continue`语句,该循环体是前面章节中_蛇和梯子_的改编版本。这次,游戏增加了一条额外的规则:
|
||||
下面的例子是在一个带有标签的`while`循环体中调用`break`和`continue`语句,该循环体是前面章节中*蛇和梯子*的改编版本。这次,游戏增加了一条额外的规则:
|
||||
|
||||
- 为了获胜,你必须_刚好_落在第 25 个方块中。
|
||||
- 为了获胜,你必须*刚好*落在第 25 个方块中。
|
||||
|
||||
如果某次掷骰子使你的移动超出第 25 个方块,你必须重新掷骰子,直到你掷出的骰子数刚好使你能落在第 25 个方块中。
|
||||
|
||||
@ -681,7 +668,7 @@ println(description)
|
||||
|
||||

|
||||
|
||||
值`finalSquare`、`board`、`square`和`diceRoll`的初始化也和之前一样:
|
||||
`finalSquare`、`board`、`square`和`diceRoll`值被和之前一样的方式初始化:
|
||||
|
||||
```swift
|
||||
let finalSquare = 25
|
||||
@ -712,7 +699,7 @@ gameLoop: while square != finalSquare {
|
||||
square += board[square]
|
||||
}
|
||||
}
|
||||
println("Game over!")
|
||||
print("Game over!")
|
||||
```
|
||||
|
||||
每次循环迭代开始时掷骰子。与之前玩家掷完骰子就立即移动不同,这里使用了`switch`来考虑每次移动可能产生的结果,从而决定玩家本次是否能够移动。
|
||||
@ -724,3 +711,65 @@ println("Game over!")
|
||||
>注意:
|
||||
如果上述的`break`语句没有使用`gameLoop`标签,那么它将会中断`switch`代码块而不是`while`循环体。使用`gameLoop`标签清晰的表明了`break`想要中断的是哪个代码块。
|
||||
同时请注意,当调用`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调用。如果我们尝试使用一个不可用的API,Swift会在编译期报错。
|
||||
|
||||
我们使用一个可用性条件在一个`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`
|
||||
}
|
||||
```
|
||||
|
||||
@ -1,8 +1,12 @@
|
||||
# 函数(Functions)
|
||||
-----------------
|
||||
|
||||
> 1.0
|
||||
> 翻译:[honghaoz](https://github.com/honghaoz)
|
||||
> 校对:[LunaticM](https://github.com/LunaticM)
|
||||
|
||||
# 函数(Functions)
|
||||
-----------------
|
||||
> 2.0
|
||||
> 翻译+校对:[dreamkidd](https://github.com/dreamkidd)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
@ -25,7 +29,7 @@ Swift 统一的函数语法足够灵活,可以用来表示任何函数,包
|
||||
|
||||
每个函数有个函数名,用来描述函数执行的任务。要使用一个函数时,你用函数名“调用”,并传给它匹配的输入值(称作实参,arguments)。一个函数的实参必须与函数参数表里参数的顺序一致。
|
||||
|
||||
在下面例子中的函数叫做`"greetingForPerson"`,之所以叫这个名字是因为这个函数用一个人的名字当做输入,并返回给这个人的问候语。为了完成这个任务,你定义一个输入参数-一个叫做 `personName` 的 `String` 值,和一个包含给这个人问候语的 `String` 类型的返回值:
|
||||
在下面例子中的函数叫做`"sayHello(_:)"`,之所以叫这个名字,是因为这个函数用一个人的名字当做输入,并返回给这个人的问候语。为了完成这个任务,你定义一个输入参数-一个叫做 `personName` 的 `String` 值,和一个包含给这个人问候语的 `String` 类型的返回值:
|
||||
|
||||
```swift
|
||||
func sayHello(personName: String) -> String {
|
||||
@ -36,20 +40,20 @@ func sayHello(personName: String) -> String {
|
||||
|
||||
所有的这些信息汇总起来成为函数的定义,并以 `func` 作为前缀。指定函数返回类型时,用返回箭头 `->`(一个连字符后跟一个右尖括号)后跟返回类型的名称的方式来表示。
|
||||
|
||||
该定义描述了函数做什么,它期望接收什么和执行结束时它返回的结果是什么。这样的定义使得函数可以在别的地方以一种清晰的方式被调用:
|
||||
该定义描述了函数做什么,它期望接收什么和执行结束时它返回的结果是什么类型。这样的定义使得函数可以在别的地方以一种清晰的方式被调用:
|
||||
|
||||
```swift
|
||||
println(sayHello("Anna"))
|
||||
print(sayHello("Anna"))
|
||||
// prints "Hello, Anna!"
|
||||
println(sayHello("Brian"))
|
||||
print(sayHello("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 {
|
||||
return "Hello again, " + personName + "!"
|
||||
}
|
||||
println(sayHelloAgain("Anna"))
|
||||
print(sayHelloAgain("Anna"))
|
||||
// prints "Hello again, Anna!"
|
||||
```
|
||||
|
||||
@ -76,7 +80,7 @@ println(sayHelloAgain("Anna"))
|
||||
func halfOpenRangeLength(start: Int, end: Int) -> Int {
|
||||
return end - start
|
||||
}
|
||||
println(halfOpenRangeLength(1, 10))
|
||||
print(halfOpenRangeLength(1, 10))
|
||||
// prints "9"
|
||||
```
|
||||
|
||||
@ -88,19 +92,42 @@ println(halfOpenRangeLength(1, 10))
|
||||
func sayHelloWorld() -> String {
|
||||
return "hello, world"
|
||||
}
|
||||
println(sayHelloWorld())
|
||||
print(sayHelloWorld())
|
||||
// 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)
|
||||
|
||||
函数可以没有返回值。下面是 `sayHello` 函数的另一个版本,叫 `waveGoodbye`,这个函数直接输出 `String` 值,而不是返回它:
|
||||
函数可以没有返回值。下面是 `sayHello(_:)` 函数的另一个版本,叫 `sayGoodbye(_:)`,这个函数直接输出 `String` 值,而不是返回它:
|
||||
|
||||
```swift
|
||||
func sayGoodbye(personName: String) {
|
||||
println("Goodbye, \(personName)!")
|
||||
print("Goodbye, \(personName)!")
|
||||
}
|
||||
sayGoodbye("Dave")
|
||||
// prints "Goodbye, Dave!"
|
||||
@ -109,14 +136,14 @@ sayGoodbye("Dave")
|
||||
因为这个函数不需要返回值,所以这个函数的定义中没有返回箭头(->)和返回类型。
|
||||
|
||||
> 注意:
|
||||
> 严格上来说,虽然没有返回值被定义,`sayGoodbye` 函数依然返回了值。没有定义返回类型的函数会返回特殊的值,叫 `Void`。它其实是一个空的元组(tuple),没有任何元素,可以写成`()`。
|
||||
> 严格上来说,虽然没有返回值被定义,`sayGoodbye(_:)` 函数依然返回了值。没有定义返回类型的函数会返回特殊的值,叫 `Void`。它其实是一个空的元组(tuple),没有任何元素,可以写成`()`。
|
||||
|
||||
被调用时,一个函数的返回值可以被忽略:
|
||||
|
||||
```swift
|
||||
func printAndCount(stringToPrint: String) -> Int {
|
||||
println(stringToPrint)
|
||||
return count(stringToPrint)
|
||||
print(stringToPrint)
|
||||
return stringToPrint.characters.count
|
||||
}
|
||||
func printWithoutCounting(stringToPrint: String) {
|
||||
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)
|
||||
|
||||
你可以用元组(tuple)类型让多个值作为一个复合值从函数中返回。
|
||||
|
||||
下面的这个例子中,`count` 函数用来计算一个字符串中元音,辅音和其他字母的个数(基于美式英语的标准)。
|
||||
下面的这个例子中,定义了一个名为`minMax(_:)`的函数,作用是在一个`Int`数组中找出最小值与最大值。
|
||||
|
||||
```swift
|
||||
func count(string: String) -> (vowels: Int, consonants: Int, others: Int) {
|
||||
var vowels = 0, consonants = 0, others = 0
|
||||
for character in string {
|
||||
switch String(character).lowercaseString {
|
||||
case "a", "e", "i", "o", "u":
|
||||
++vowels
|
||||
case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
|
||||
"n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
|
||||
++consonants
|
||||
default:
|
||||
++others
|
||||
func minMax(array: [Int]) -> (min: Int, max: Int) {
|
||||
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 (vowels, consonants, others)
|
||||
return (currentMin, currentMax)
|
||||
}
|
||||
```
|
||||
|
||||
你可以用 `count` 函数来处理任何一个字符串,返回的值将是一个包含三个 `Int` 型值的元组(tuple):
|
||||
`minMax(_:)`函数返回一个包含两个`Int`值的元组,这些值被标记为`min`和`max`,以便查询函数的返回值时他们可以被访问。
|
||||
|
||||
`minMax(_:)`的函数体中,在开始的时候设置两个工作变量`currentMin`和`currentMax`作为数组中的第一个`Int`值。然后函数会遍历数组中剩余的值并检查该值是否比`currentMin`和`currentMax`更小或更大。最后数组中的最小值与最大值返回两个`Int`值最为一个元组。
|
||||
|
||||
因为元组的成员值被命名为函数的返回类型的一部分,可以通过点语法来访问与取回发现的最小值与最小值:
|
||||
|
||||
```swift
|
||||
let total = count("some arbitrary string!")
|
||||
println("\(total.vowels) vowels and \(total.consonants) consonants")
|
||||
// prints "6 vowels and 13 consonants"
|
||||
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)
|
||||
可选元组返回类型(Optional Tuple Return Types)
|
||||
|
||||
以上所有的函数都给它们的参数定义了`参数名(parameter name)`:
|
||||
如果函数返回的元组类型中有可能在整个元组中含有“没有值”,你可以使用*可选的(Optional)* 元组返回类型反映整个元组可以是`nil`的事实。你可以通过在元组类型的右括号后放置一个问号来定义一个可选元组,例如`(Int,Int)?`或`(String,Int,Bool)?`
|
||||
|
||||
> 注意:
|
||||
> 可选元组类型如`(Int,Int)?`与元组包含可选属性如`(Int?,Int?)`是不同的.可选的元组类型,整个数组是可选的,而不只是元组中的每个元素值。
|
||||
|
||||
前面的`minMax(_:)`函数返回了一个包含两个`Int`值的元组。但是函数不会在数组中执行任何安全检查,如果`array`参数有一个空数组,如上定义的`minMax(_:)`在试图访问`array[0]`时会触发一个运行时错误。
|
||||
|
||||
为了安全的处理这个"空数组"问题,写一个`minMax(_:)`函数使用可选元组返回类型,并且当数组为空时返回`nil`:
|
||||
|
||||
```swift
|
||||
func someFunction(parameterName: Int) {
|
||||
// function body goes here, and can use parameterName
|
||||
// to refer to the argument value for that parameter
|
||||
func minMax(array: [Int]) -> (min: Int, max: Int)? {
|
||||
if array.isEmpty { return nil }
|
||||
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
|
||||
func someFunction(externalParameterName localParameterName: Int) {
|
||||
@ -197,127 +262,61 @@ func someFunction(externalParameterName localParameterName: Int) {
|
||||
> 注意:
|
||||
> 如果你提供了外部参数名,那么函数在被调用时,必须使用外部参数名。
|
||||
|
||||
以下是个例子,这个函数使用一个`结合者(joiner)`把两个字符串联在一起:
|
||||
这个版本的`sayHello(_:)`函数,得到了两个人的名字,会同时返回对他俩的问候:
|
||||
|
||||
```swift
|
||||
func join(s1: String, s2: String, joiner: String) -> String {
|
||||
return s1 + joiner + s2
|
||||
func sayHello(to person: String, and anotherPerson: String) -> String {
|
||||
return "Hello \(person) and \(anotherPerson)!"
|
||||
}
|
||||
print(sayHello(to: "Bill", and: "Ted"))
|
||||
// prints "Hello Bill and Ted!"
|
||||
```
|
||||
|
||||
当你调用这个函数时,这三个字符串的用途是不清楚的:
|
||||
为每个参数指定外部参数名,在你调用函数`sayHello(to:and:)`函数时时两个参数都必须被标记出来。
|
||||
|
||||
使用外部函数名可以使得函数可以用一句话表达清楚,并且使得函数体内部可读,能表达出函数的明确意图。
|
||||
|
||||
### 忽略外部参数名(Omitting External Parameter Names)
|
||||
|
||||
如果你不想为第二个及后续的参数设置参数名,用一个下划线(`_`)代替一个明确地参数名。
|
||||
|
||||
```swift
|
||||
join("hello", "world", ", ")
|
||||
// returns "hello, world"
|
||||
```
|
||||
|
||||
为了让这些字符串的用途更为明显,我们为 `join` 函数添加外部参数名:
|
||||
|
||||
```swift
|
||||
func join(string s1: String, toString s2: String, withJoiner joiner: String) -> String {
|
||||
return s1 + joiner + s2
|
||||
func someFunction(firstParameterName: Int, _ secondParameterName: Int) {
|
||||
// function body goes here
|
||||
// firstParameterName and secondParameterName refer to
|
||||
// the argument values for the first and second parameters
|
||||
}
|
||||
someFunction(1, 2)
|
||||
```
|
||||
|
||||
在这个版本的 `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”
|
||||
```
|
||||
> 注意:
|
||||
> 因为第一个参数默认忽略其外部参数名称,明确写下划线是多余的。
|
||||
|
||||
<a name="default_parameter_values"></a>
|
||||
### 默认参数值(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 12
|
||||
}
|
||||
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 parameter)`可以接受零个或多个值。函数调用时,你可以用可变参数来传入不确定数量的输入参数。通过在变量类型名后面加入`(...)`的方式来定义可变参数。
|
||||
|
||||
传入可变参数的值在函数体内当做这个类型的一个数组。例如,一个叫做 `numbers` 的 `Double...` 型可变参数,在函数体内可以当做一个叫 `numbers` 的 `Double[]` 型的数组常量。
|
||||
可变参数的传入值在函数体为此类型的一个数组。例如,一个叫做 `numbers` 的 `Double...` 型可变参数,在函数体内可以当做一个叫 `numbers` 的 `[Double]` 型的数组常量。
|
||||
|
||||
下面的这个函数用来计算一组任意长度数字的算术平均数:
|
||||
下面的这个函数用来计算一组任意长度数字的`算术平均数(arithmetic mean)`:
|
||||
|
||||
```swift
|
||||
func arithmeticMean(numbers: Double...) -> Double {
|
||||
@ -329,12 +328,13 @@ func arithmeticMean(numbers: Double...) -> Double {
|
||||
}
|
||||
arithmeticMean(1, 2, 3, 4, 5)
|
||||
// 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
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> 一个函数至多能有一个可变参数,而且它必须是参数表中最后的一个。这样做是为了避免函数调用时出现歧义。
|
||||
> 最多可以有一个可变参数函数,和它必须出现在参数列表中,为了避免歧义在调用函数有多个参数。
|
||||
> 如果你的函数有一个或多个参数有默认值,还有一个可变的参数,将可变参写在参数列表的最后。
|
||||
|
||||
如果函数有一个或多个带默认值的参数,而且还有一个可变参数,那么把可变参数放在参数表的最后。
|
||||
|
||||
@ -348,7 +348,7 @@ arithmeticMean(3, 8, 19)
|
||||
|
||||
```swift
|
||||
func alignRight(var string: String, totalLength: Int, pad: Character) -> String {
|
||||
let amountToPad = totalLength - count(string)
|
||||
let amountToPad = totalLength - string.characters.count
|
||||
if amountToPad < 1 {
|
||||
return string
|
||||
}
|
||||
@ -359,20 +359,27 @@ func alignRight(var string: String, totalLength: Int, pad: Character) -> String
|
||||
return string
|
||||
}
|
||||
let originalString = "hello"
|
||||
let paddedString = alignRight(originalString, 10, "-")
|
||||
let paddedString = alignRight(originalString, totalLength: 10, pad: "-")
|
||||
// paddedString is 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)。
|
||||
@ -384,29 +391,29 @@ let paddedString = alignRight(originalString, 10, "-")
|
||||
> 注意:
|
||||
> 输入输出参数不能有默认值,而且可变参数不能用 `inout` 标记。如果你用 `inout` 标记一个参数,这个参数不能被 `var` 或者 `let` 标记。
|
||||
|
||||
下面是例子,`swapTwoInts` 函数,有两个分别叫做 `a` 和 `b` 的输入输出参数:
|
||||
下面是例子,`swapTwoInts(_:_:)` 函数,有两个分别叫做 `a` 和 `b` 的输入输出参数:
|
||||
|
||||
```swift
|
||||
func swapTwoInts(inout a: Int, inout b: Int) {
|
||||
func swapTwoInts(inout a: Int, inout _ b: Int) {
|
||||
let temporaryA = a
|
||||
a = b
|
||||
b = temporaryA
|
||||
}
|
||||
```
|
||||
|
||||
这个 `swapTwoInts` 函数仅仅交换 `a` 与 `b` 的值。该函数先将 `a` 的值存到一个暂时常量 `temporaryA` 中,然后将 `b` 的值赋给 `a`,最后将 `temporaryA` 幅值给 `b`。
|
||||
这个 `swapTwoInts(_:_:)` 函数仅仅交换 `a` 与 `b` 的值。该函数先将 `a` 的值存到一个暂时常量 `temporaryA` 中,然后将 `b` 的值赋给 `a`,最后将 `temporaryA` 幅值给 `b`。
|
||||
|
||||
你可以用两个 `Int` 型的变量来调用 `swapTwoInts`。需要注意的是,`someInt` 和 `anotherInt` 在传入 `swapTwoInts` 函数前,都加了 `&` 的前缀:
|
||||
你可以用两个 `Int` 型的变量来调用 `swapTwoInts(_:_:)`。需要注意的是,`someInt` 和 `anotherInt` 在传入 `swapTwoInts(_:_:)` 函数前,都加了 `&` 的前缀:
|
||||
|
||||
```swift
|
||||
var someInt = 3
|
||||
var anotherInt = 107
|
||||
swapTwoInts(&someInt, &anotherInt)
|
||||
println("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
|
||||
// prints "someInt is now 107, and anotherInt is now 3”
|
||||
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
|
||||
// prints "someInt is now 107, and anotherInt is now 3"
|
||||
```
|
||||
|
||||
从上面这个例子中,我们可以看到 `someInt` 和 `anotherInt` 的原始值在 `swapTwoInts` 函数中被修改,尽管它们的定义在函数体外。
|
||||
从上面这个例子中,我们可以看到 `someInt` 和 `anotherInt` 的原始值在 `swapTwoInts(_:_:)` 函数中被修改,尽管它们的定义在函数体外。
|
||||
|
||||
> 注意:
|
||||
> 输入输出参数和返回值是不一样的。上面的 `swapTwoInts` 函数并没有定义任何返回值,但仍然修改了 `someInt` 和 `anotherInt` 的值。输入输出参数是函数对函数体外产生影响的另一种方式。
|
||||
@ -419,27 +426,27 @@ println("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
|
||||
例如:
|
||||
|
||||
```swift
|
||||
func addTwoInts(a: Int, b: Int) -> Int {
|
||||
func addTwoInts(a: Int, _ b: Int) -> Int {
|
||||
return a + b
|
||||
}
|
||||
func multiplyTwoInts(a: Int, b: Int) -> Int {
|
||||
func multiplyTwoInts(a: Int, _ b: Int) -> Int {
|
||||
return a * b
|
||||
}
|
||||
```
|
||||
|
||||
这个例子中定义了两个简单的数学函数:`addTwoInts` 和 `multiplyTwoInts`。这两个函数都传入两个 `Int` 类型, 返回一个合适的`Int`值。
|
||||
|
||||
这两个函数的类型是 `(Int, Int) -> Int`,可以读作“这个函数类型,它有两个 `Int` 型的参数并返回一个 `Int` 型的值。”。
|
||||
这两个函数的类型是 `(Int, Int) -> Int`,可以读作”这个函数类型,它有两个 `Int` 型的参数并返回一个 `Int` 型的值。”。
|
||||
|
||||
下面是另一个例子,一个没有参数,也没有返回值的函数:
|
||||
|
||||
```swift
|
||||
func printHelloWorld() {
|
||||
println("hello, world")
|
||||
print("hello, world")
|
||||
}
|
||||
```
|
||||
|
||||
这个函数的类型是:`() -> ()`,或者叫“没有参数,并返回 `Void` 类型的函数”。没有指定返回类型的函数总返回 `Void`。在Swift中,`Void` 与空的元组是一样的。
|
||||
这个函数的类型是:`() -> void`,或者叫“没有参数,并返回 `Void` 类型的函数”。
|
||||
|
||||
### 使用函数类型(Using Function Types)
|
||||
|
||||
@ -458,7 +465,7 @@ var mathFunction: (Int, Int) -> Int = addTwoInts
|
||||
现在,你可以用 `mathFunction` 来调用被赋值的函数了:
|
||||
|
||||
```swift
|
||||
println("Result: \(mathFunction(2, 3))")
|
||||
print("Result: \(mathFunction(2, 3))")
|
||||
// prints "Result: 5"
|
||||
```
|
||||
|
||||
@ -466,7 +473,7 @@ println("Result: \(mathFunction(2, 3))")
|
||||
|
||||
```swift
|
||||
mathFunction = multiplyTwoInts
|
||||
println("Result: \(mathFunction(2, 3))")
|
||||
print("Result: \(mathFunction(2, 3))")
|
||||
// prints "Result: 6"
|
||||
```
|
||||
|
||||
@ -484,18 +491,18 @@ let anotherMathFunction = addTwoInts
|
||||
下面是另一个例子,正如上面的函数一样,同样是输出某种数学运算结果:
|
||||
|
||||
```swift
|
||||
func printMathResult(mathFunction: (Int, Int) -> Int, a: Int, b: Int) {
|
||||
println("Result: \(mathFunction(a, b))")
|
||||
func printMathResult(mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
|
||||
print("Result: \(mathFunction(a, b))")
|
||||
}
|
||||
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)
|
||||
|
||||
@ -512,7 +519,7 @@ func stepBackward(input: Int) -> Int {
|
||||
}
|
||||
```
|
||||
|
||||
下面这个叫做 `chooseStepFunction` 的函数,它的返回类型是 `(Int) -> Int` 的函数。`chooseStepFunction` 根据布尔值 `backwards` 来返回 `stepForward` 函数或 `stepBackward` 函数:
|
||||
下面这个叫做 `chooseStepFunction(_:)` 的函数,它的返回类型是 `(Int) -> Int` 的函数。`chooseStepFunction(_:)` 根据布尔值 `backwards` 来返回 `stepForward(_:)` 函数或 `stepBackward(_:)` 函数:
|
||||
|
||||
```swift
|
||||
func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
|
||||
@ -520,7 +527,7 @@ func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
|
||||
}
|
||||
```
|
||||
|
||||
你现在可以用 `chooseStepFunction` 来获得一个函数,不管是那个方向:
|
||||
你现在可以用 `chooseStepFunction(_:)` 来获得一个函数,不管是那个方向:
|
||||
|
||||
```swift
|
||||
var currentValue = 3
|
||||
@ -528,22 +535,23 @@ let moveNearerToZero = chooseStepFunction(currentValue > 0)
|
||||
// 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`:
|
||||
|
||||
```swift
|
||||
println("Counting to zero:")
|
||||
print("Counting to zero:")
|
||||
// Counting to zero:
|
||||
while currentValue != 0 {
|
||||
println("\(currentValue)... ")
|
||||
print("\(currentValue)... ")
|
||||
currentValue = moveNearerToZero(currentValue)
|
||||
}
|
||||
println("zero!")
|
||||
print("zero!")
|
||||
// 3...
|
||||
// 2...
|
||||
// 1...
|
||||
// zero!
|
||||
|
||||
```
|
||||
|
||||
<a name="Nested_Functions"></a>
|
||||
@ -553,7 +561,7 @@ println("zero!")
|
||||
|
||||
默认情况下,嵌套函数是对外界不可见的,但是可以被他们封闭函数(enclosing function)来调用。一个封闭函数也可以返回它的某一个嵌套函数,使得这个函数可以在其他域中被使用。
|
||||
|
||||
你可以用返回嵌套函数的方式重写 `chooseStepFunction` 函数:
|
||||
你可以用返回嵌套函数的方式重写 `chooseStepFunction(_:)` 函数:
|
||||
|
||||
```swift
|
||||
func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
|
||||
@ -565,10 +573,10 @@ var currentValue = -4
|
||||
let moveNearerToZero = chooseStepFunction(currentValue > 0)
|
||||
// moveNearerToZero now refers to the nested stepForward() function
|
||||
while currentValue != 0 {
|
||||
println("\(currentValue)... ")
|
||||
print("\(currentValue)... ")
|
||||
currentValue = moveNearerToZero(currentValue)
|
||||
}
|
||||
println("zero!")
|
||||
print("zero!")
|
||||
// -4...
|
||||
// -3...
|
||||
// -2...
|
||||
|
||||
@ -1,8 +1,12 @@
|
||||
# 闭包(Closures)
|
||||
-----------------
|
||||
|
||||
> 1.0
|
||||
> 翻译:[wh1100717](https://github.com/wh1100717)
|
||||
> 校对:[lyuka](https://github.com/lyuka)
|
||||
|
||||
# 闭包(Closures)
|
||||
-----------------
|
||||
> 2.0
|
||||
> 翻译+校对:[100mango](https://github.com/100mango)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
@ -12,7 +16,7 @@
|
||||
- [闭包是引用类型(Closures Are Reference Types)](#closures_are_reference_types)
|
||||
|
||||
闭包是自包含的函数代码块,可以在代码中被传递和使用。
|
||||
Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的 lambdas 函数比较相似。
|
||||
Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的 匿名函数比较相似。
|
||||
|
||||
闭包可以捕获和存储其所在上下文中任意常量和变量的引用。
|
||||
这就是所谓的闭合并包裹着这些常量和变量,俗称闭包。Swift 会为您管理在捕获过程中涉及到的所有内存操作。
|
||||
@ -20,7 +24,7 @@ Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他
|
||||
> 注意:
|
||||
> 如果您不熟悉捕获(capturing)这个概念也不用担心,您可以在 [值捕获](#capturing_values) 章节对其进行详细了解。
|
||||
|
||||
在[函数](../chapter2/06_Functions.html) 章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之一:
|
||||
在[函数](./06_Functions.html) 章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之一:
|
||||
|
||||
* 全局函数是一个有名字但不会捕获任何值的闭包
|
||||
* 嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包
|
||||
@ -37,39 +41,39 @@ Swift 的闭包表达式拥有简洁的风格,并鼓励在常见场景中进
|
||||
## 闭包表达式(Closure Expressions)
|
||||
|
||||
|
||||
[嵌套函数](../chapter2/06_Functions.html#nested_function) 是一个在较复杂函数中方便进行命名和定义自包含代码模块的方式。当然,有时候撰写小巧的没有完整定义和命名的类函数结构也是很有用处的,尤其是在您处理一些函数并需要将另外一些函数作为该函数的参数时。
|
||||
[嵌套函数](./06_Functions.html#nested_function) 是一个在较复杂函数中方便进行命名和定义自包含代码模块的方式。当然,有时候撰写小巧的没有完整定义和命名的类函数结构也是很有用处的,尤其是在您处理一些函数并需要将另外一些函数作为该函数的参数时。
|
||||
|
||||
闭包表达式是一种利用简洁语法构建内联闭包的方式。
|
||||
闭包表达式提供了一些语法优化,使得撰写闭包变得简单明了。
|
||||
下面闭包表达式的例子通过使用几次迭代展示了`sorted`函数定义和语法优化的方式。
|
||||
下面闭包表达式的例子通过使用几次迭代展示了`sort(_:)`方法定义和语法优化的方式。
|
||||
每一次迭代都用更简洁的方式描述了相同的功能。
|
||||
|
||||
<a name="the_sorted_function"></a>
|
||||
### sorted 函数(The Sorted Function)
|
||||
### sort 函数(The Sort Function)
|
||||
|
||||
Swift 标准库提供了`sorted`函数,会根据您提供的基于输出类型排序的闭包函数将已知类型数组中的值进行排序。
|
||||
一旦排序完成,函数会返回一个与原数组大小相同的新数组,该数组中包含已经正确排序的同类型元素。
|
||||
Swift 标准库提供了名为`sort`的函数,会根据您提供的用于排序的闭包函数将已知类型数组中的值进行排序。
|
||||
一旦排序完成,`sort(_:)`方法会返回一个与原数组大小相同,包含同类型元素且元素已正确排序的新数组。原数组不会被`sort(_:)`方法修改。
|
||||
|
||||
下面的闭包表达式示例使用`sorted`函数对一个`String`类型的数组进行字母逆序排序,以下是初始数组值:
|
||||
下面的闭包表达式示例使用`sort(_:)`方法对一个String类型的数组进行字母逆序排序,以下是初始数组值:
|
||||
|
||||
```swift
|
||||
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
|
||||
```
|
||||
|
||||
`sorted`函数需要传入两个参数:
|
||||
`sort(_:)`方法需要传入两个参数:
|
||||
|
||||
* 已知类型的数组
|
||||
* 闭包函数,该闭包函数需要传入与数组类型相同的两个值,并返回一个布尔类型值来告诉`sorted`函数当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值前面,排序闭包函数需要返回`true`,反之返回`false`。
|
||||
* 闭包函数,该闭包函数需要传入与数组元素类型相同的两个值,并返回一个布尔类型值来表明当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值前面,排序闭包函数需要返回`true`,反之返回`false`。
|
||||
|
||||
该例子对一个`String`类型的数组进行排序,因此排序闭包函数类型需为`(String, String) -> Bool`。
|
||||
|
||||
提供排序闭包函数的一种方式是撰写一个符合其类型要求的普通函数,并将其作为`sort`函数的第二个参数传入:
|
||||
提供排序闭包函数的一种方式是撰写一个符合其类型要求的普通函数,并将其作为`sort(_:)`方法的参数传入:
|
||||
|
||||
```swift
|
||||
func backwards(s1: String, s2: String) -> Bool {
|
||||
return s1 > s2
|
||||
}
|
||||
var reversed = sorted(names, backwards)
|
||||
var reversed = names.sort(backwards)
|
||||
// reversed 为 ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
|
||||
```
|
||||
|
||||
@ -99,7 +103,7 @@ var reversed = sorted(names, backwards)
|
||||
下面的例子展示了之前`backwards`函数对应的闭包表达式版本的代码:
|
||||
|
||||
```swift
|
||||
reversed = sorted(names, { (s1: String, s2: String) -> Bool in
|
||||
reversed = names.sort({ (s1: String, s2: String) -> Bool in
|
||||
return s1 > s2
|
||||
})
|
||||
```
|
||||
@ -114,34 +118,36 @@ reversed = sorted(names, { (s1: String, s2: String) -> Bool in
|
||||
因为这个闭包的函数体部分如此短以至于可以将其改写成一行代码:
|
||||
|
||||
```swift
|
||||
reversed = sorted(names, { (s1: String, s2: String) -> Bool in return s1 > s2 } )
|
||||
reversed = names.sort( { (s1: String, s2: String) -> Bool in return s1 > s2 } )
|
||||
```
|
||||
|
||||
这说明`sorted`函数的整体调用保持不变,一对圆括号仍然包裹住了函数中整个参数集合。而其中一个参数现在变成了内联闭包(相比于`backwards`版本的代码)。
|
||||
这说明`sort(_:)`方法的整体调用保持不变,一对圆括号仍然包裹住了函数中整个参数集合。而其中一个参数现在变成了内联闭包(相比于`backwards`版本的代码)
|
||||
|
||||
<a name="inferring_type_from_context"></a>
|
||||
### 根据上下文推断类型(Inferring Type From Context)
|
||||
|
||||
因为排序闭包函数是作为`sorted`函数的参数进行传入的,Swift可以推断其参数和返回值的类型。
|
||||
因为排序闭包函数是作为`sort(_:)`方法的参数进行传入的,Swift可以推断其参数和返回值的类型。
|
||||
`sorted`期望第二个参数是类型为`(String, String) -> Bool`的函数,因此实际上`String`,`String`和`Bool`类型并不需要作为闭包表达式定义中的一部分。
|
||||
因为所有的类型都可以被正确推断,返回箭头 (`->`) 和围绕在参数周围的括号也可以被省略:
|
||||
|
||||
```swift
|
||||
reversed = sorted(names, { s1, s2 in return s1 > s2 } )
|
||||
reversed = names.sort( { s1, s2 in return s1 > s2 } )
|
||||
```
|
||||
|
||||
实际上任何情况下,通过内联闭包表达式构造的闭包作为参数传递给函数时,都可以推断出闭包的参数和返回值类型,这意味着您几乎不需要利用完整格式构造任何内联闭包。
|
||||
|
||||
然而您仍然可以明确写出有着完整格式的闭包。如果完整格式的闭包能够提高代码的可读性,则可以采用完整格式的闭包。而在`sort(_:)`方法这个例子里,闭包的目的就是排序,读者能够推测除这个闭包是用于字符串处理的,因为这个闭包是为了处理字符串数组的排序。
|
||||
|
||||
<a name="implicit_returns_from_single_expression_closures"></a>
|
||||
### 单表达式闭包隐式返回(Implicit Return From Single-Expression Clossures)
|
||||
|
||||
单行表达式闭包可以通过隐藏`return`关键字来隐式返回单行表达式的结果,如上版本的例子可以改写为:
|
||||
|
||||
```swift
|
||||
reversed = sorted(names, { s1, s2 in s1 > s2 } )
|
||||
reversed = names.sort( { s1, s2 in s1 > s2 } )
|
||||
```
|
||||
|
||||
在这个例子中,`sorted`函数的第二个参数函数类型明确了闭包必须返回一个`Bool`类型值。
|
||||
在这个例子中,`sort(_:)`方法的第二个参数函数类型明确了闭包必须返回一个`Bool`类型值。
|
||||
因为闭包函数体只包含了一个单一表达式 (`s1 > s2`),该表达式返回`Bool`类型值,因此这里没有歧义,`return`关键字可以省略。
|
||||
|
||||
<a name="shorthand_argument_names"></a>
|
||||
@ -153,7 +159,7 @@ Swift 自动为内联函数提供了参数名称缩写功能,您可以直接
|
||||
`in`关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成:
|
||||
|
||||
```swift
|
||||
reversed = sorted(names, { $0 > $1 } )
|
||||
reversed = names.sort( { $0 > $1 } )
|
||||
```
|
||||
|
||||
在这个例子中,`$0`和`$1`表示闭包中第一个和第二个`String`类型的参数。
|
||||
@ -163,14 +169,14 @@ reversed = sorted(names, { $0 > $1 } )
|
||||
|
||||
实际上还有一种更简短的方式来撰写上面例子中的闭包表达式。
|
||||
Swift 的`String`类型定义了关于大于号 (`>`) 的字符串实现,其作为一个函数接受两个`String`类型的参数并返回`Bool`类型的值。
|
||||
而这正好与`sorted`函数的第二个参数需要的函数类型相符合。
|
||||
而这正好与`sort(_:)`方法的第二个参数需要的函数类型相符合。
|
||||
因此,您可以简单地传递一个大于号,Swift可以自动推断出您想使用大于号的字符串函数实现:
|
||||
|
||||
```swift
|
||||
reversed = sorted(names, >)
|
||||
reversed = names.sort(>)
|
||||
```
|
||||
|
||||
更多关于运算符表达式的内容请查看 [运算符函数](../chapter2/24_Advanced_Operators.html#operator_functions)。
|
||||
更多关于运算符表达式的内容请查看 [运算符函数](./24_Advanced_Operators.html#operator_functions)。
|
||||
|
||||
<a name="trailing_closures"></a>
|
||||
## 尾随闭包(Trailing Closures)
|
||||
@ -180,7 +186,7 @@ reversed = sorted(names, >)
|
||||
尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。
|
||||
|
||||
```swift
|
||||
func someFunctionThatTakesAClosure(closure: () -> ()) {
|
||||
func someFunctionThatTakesAClosure(closure: () -> Void) {
|
||||
// 函数体部分
|
||||
}
|
||||
|
||||
@ -201,7 +207,7 @@ someFunctionThatTakesAClosure() {
|
||||
在上例中作为`sorted`函数参数的字符串排序闭包可以改写为:
|
||||
|
||||
```swift
|
||||
reversed = sorted(names) { $0 > $1 }
|
||||
reversed = names.sort() { $0 > $1 }
|
||||
```
|
||||
|
||||
当闭包非常长以至于不能在一行中进行书写时,尾随闭包变得非常有用。
|
||||
@ -244,7 +250,7 @@ let strings = numbers.map {
|
||||
`map`在数组中为每一个元素调用了闭包表达式。
|
||||
您不需要指定闭包的输入参数`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`字典获取所映射的字符串。
|
||||
@ -294,7 +300,7 @@ func makeIncrementor(forIncrement amount: Int) -> () -> Int {
|
||||
`makeIncrementor`返回类型为`() -> Int`。
|
||||
这意味着其返回的是一个函数,而不是一个简单类型值。
|
||||
该函数在每次调用时不接受参数只返回一个`Int`类型的值。
|
||||
关于函数返回其他函数的内容,请查看[函数类型作为返回类型](../chapter2/06_Functions.html#function_types_as_return_types)。
|
||||
关于函数返回其他函数的内容,请查看[函数类型作为返回类型](./06_Functions.html#function_types_as_return_types)。
|
||||
|
||||
`makeIncrementor`函数定义了一个整型变量`runningTotal`(初始为0) 用来存储当前跑步总数。
|
||||
该值通过`incrementor`返回。
|
||||
@ -313,16 +319,11 @@ func incrementor() -> Int {
|
||||
}
|
||||
```
|
||||
|
||||
`incrementor`函数并没有获取任何参数,但是在函数体内访问了`runningTotal`和`amount`变量。这是因为其通过捕获在包含它的函数体内已经存在的`runningTotal`和`amount`变量而实现。
|
||||
|
||||
由于没有修改`amount`变量,`incrementor`实际上捕获并存储了该变量的一个副本,而该副本随着`incrementor`一同被存储。
|
||||
|
||||
然而,因为每次调用该函数的时候都会修改`runningTotal`的值,`incrementor`捕获了当前`runningTotal`变量的引用,而不是仅仅复制该变量的初始值。捕获一个引用保证了当`makeIncrementor`结束时候并不会消失,也保证了当下一次执行`incrementor`函数时,`runningTotal`可以继续增加。
|
||||
`incrementer`函数并没有任何参数,但是在函数体内访问了`runningTotal`和`amount`变量。这是因为其通过捕获在包含它的函数体内已经存在的`runningTotal`和`amount`变量的引用(reference)而实现。捕捉了变量引用,保证了`runningTotal`和`amount`变量在调用完`makeIncrementer`后不会消失,并且保证了在下一次执行`incrementer`函数时,`runningTotal`可以继续增加。
|
||||
|
||||
> 注意:
|
||||
> Swift 会决定捕获引用还是拷贝值。
|
||||
> 您不需要标注`amount`或者`runningTotal`来声明在嵌入的`incrementor`函数中的使用方式。
|
||||
> Swift 同时也处理`runingTotal`变量的内存管理操作,如果不再被`incrementor`函数使用,则会被清除。
|
||||
> 为了优化,Swift可能会捕捉和保存一份对值的拷贝,如果这个值是不可变或是在闭包外的。
|
||||
> Swift同样负责被捕捉的所有变量的内存管理,包括释放不被需要的变量。
|
||||
|
||||
下面代码为一个使用`makeIncrementor`的例子:
|
||||
|
||||
@ -355,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>
|
||||
## 闭包是引用类型(Closures Are Reference Types)
|
||||
|
||||
@ -1,30 +1,35 @@
|
||||
> 翻译:[yankuangshi](https://github.com/yankuangshi)
|
||||
> 校对:[shinyzhu](https://github.com/shinyzhu)
|
||||
|
||||
# 枚举(Enumerations)
|
||||
---
|
||||
|
||||
> 1.0
|
||||
> 翻译:[yankuangshi](https://github.com/yankuangshi)
|
||||
> 校对:[shinyzhu](https://github.com/shinyzhu)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[futantan](https://github.com/futantan)
|
||||
|
||||
本页内容包含:
|
||||
|
||||
- [枚举语法(Enumeration Syntax)](#enumeration_syntax)
|
||||
- [匹配枚举值与`Swith`语句(Matching Enumeration Values with a Switch Statement)](#matching_enumeration_values_with_a_switch_statement)
|
||||
- [相关值(Associated Values)](#associated_values)
|
||||
- [原始值(Raw Values)](#raw_values)
|
||||
- [递归枚举(Recursive Enumerations)](#recursive_enumerations)
|
||||
|
||||
枚举定义了一个通用类型的一组相关的值,使你可以在你的代码中以一个安全的方式来使用这些值。
|
||||
*枚举*定义了一个通用类型的一组相关值,使你可以在你的代码中以一种安全的方式来使用这些值。
|
||||
|
||||
如果你熟悉 C 语言,你就会知道,在 C 语言中枚举指定相关名称为一组整型值。Swift 中的枚举更加灵活,不必给每一个枚举成员提供一个值。如果一个值(被认为是“原始”值)被提供给每个枚举成员,则该值可以是一个字符串,一个字符,或是一个整型值或浮点值。
|
||||
如果你熟悉 C 语言,你就会知道,在 C 语言中枚举将枚举名和一个整型值相对应。Swift 中的枚举更加灵活,不必给每一个枚举成员提供一个值。如果给枚举成员提供一个值(称为“原始”值),则该值的类型可以是字符串,字符,或是一个整型值或浮点数。
|
||||
|
||||
此外,枚举成员可以指定任何类型的相关值存储到枚举成员值中,就像其他语言中的联合体(unions)和变体(variants)。你可以定义一组通用的相关成员作为枚举的一部分,每一组都有不同的一组与它相关的适当类型的数值。
|
||||
|
||||
在 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>
|
||||
## 枚举语法
|
||||
|
||||
使用`enum`关键词并且把它们的整个定义放在一对大括号内:
|
||||
使用`enum`关键词来创建枚举并且把它们的整个定义放在一对大括号内:
|
||||
|
||||
```swift
|
||||
enum SomeEnumeration {
|
||||
@ -43,10 +48,10 @@ enum CompassPoint {
|
||||
}
|
||||
```
|
||||
|
||||
一个枚举中被定义的值(例如 `North`,`South`,`East`和`West`)是枚举的***成员值***(或者***成员***)。`case`关键词表明新的一行成员值将被定义。
|
||||
一个枚举中被定义的值(例如 `North`,`South`,`East`和`West`)是枚举的*成员值*(或者*成员*)。`case`关键词表明新的一行成员值将被定义。
|
||||
|
||||
> 注意:
|
||||
> 不像 C 和 Objective-C 一样,Swift 的枚举成员在被创建时不会被赋予一个默认的整数值。在上面的`CompassPoints`例子中,`North`,`South`,`East`和`West`不是隐式的等于`0`,`1`,`2`和`3`。相反的,这些不同的枚举成员在`CompassPoint`的一种显示定义中拥有各自不同的值。
|
||||
> 和 C 和 Objective-C 不同,Swift 的枚举成员在被创建时不会被赋予一个默认的整型值。在上面的`CompassPoints`例子中,`North`,`South`,`East`和`West`不会隐式地赋值为了`0`,`1`,`2`和`3`。相反的,这些不同的枚举成员在`CompassPoint`的一种显示定义中拥有各自不同的值。
|
||||
|
||||
多个成员值可以出现在同一行上,用逗号隔开:
|
||||
|
||||
@ -62,51 +67,51 @@ enum Planet {
|
||||
var directionToHead = CompassPoint.West
|
||||
```
|
||||
|
||||
`directionToHead`的类型被推断当它被`CompassPoint`的一个可能值初始化。一旦`directionToHead`被声明为一个`CompassPoint`,你可以使用更短的点(.)语法将其设置为另一个`CompassPoint`的值:
|
||||
`directionToHead`的类型可以在它被`CompassPoint`的一个可能值初始化时推断出来。一旦`directionToHead`被声明为一个`CompassPoint`,你可以使用一个缩写语法(.)将其设置为另一个`CompassPoint`的值:
|
||||
|
||||
```swift
|
||||
directionToHead = .East
|
||||
```
|
||||
|
||||
`directionToHead`的类型已知时,当设定它的值时,你可以不再写类型名。使用显式类型的枚举值可以让代码具有更好的可读性。
|
||||
当`directionToHead`的类型已知时,再次为其赋值可以省略枚举名。使用显式类型的枚举值可以让代码具有更好的可读性。
|
||||
|
||||
<a name="matching_enumeration_values_with_a_switch_statement"></a>
|
||||
## 匹配枚举值和`Switch`语句
|
||||
|
||||
你可以匹配单个枚举值和`switch`语句:
|
||||
你可以使用`switch`语句匹配单个枚举值:
|
||||
|
||||
```swift
|
||||
directionToHead = .South
|
||||
switch directionToHead {
|
||||
case .North:
|
||||
println("Lots of planets have a north")
|
||||
print("Lots of planets have a north")
|
||||
case .South:
|
||||
println("Watch out for penguins")
|
||||
print("Watch out for penguins")
|
||||
case .East:
|
||||
println("Where the sun rises")
|
||||
print("Where the sun rises")
|
||||
case .West:
|
||||
println("Where the skies are blue")
|
||||
print("Where the skies are blue")
|
||||
}
|
||||
// 输出 "Watch out for penguins”
|
||||
```
|
||||
|
||||
你可以如此理解这段代码:
|
||||
你可以这样理解这段代码:
|
||||
|
||||
“考虑`directionToHead`的值。当它等于`.North`,打印`“Lots of planets have a north”`。当它等于`.South`,打印`“Watch out for penguins”`。”
|
||||
“判断`directionToHead`的值。当它等于`.North`,打印`“Lots of planets have a north”`。当它等于`.South`,打印`“Watch out for penguins”`。”
|
||||
|
||||
等等依次类推。
|
||||
等等以此类推。
|
||||
|
||||
正如在[控制流(Control Flow)](05_Control_Flow.html)中介绍,当考虑一个枚举的成员们时,一个`switch`语句必须全面。如果忽略了`.West`这种情况,上面那段代码将无法通过编译,因为它没有考虑到`CompassPoint`的全部成员。全面性的要求确保了枚举成员不会被意外遗漏。
|
||||
正如在[控制流(Control Flow)](./05_Control_Flow.html)中介绍的那样,在判断一个枚举类型的值时,`switch`语句必须穷举所有情况。如果忽略了`.West`这种情况,上面那段代码将无法通过编译,因为它没有考虑到`CompassPoint`的全部成员。强制性全部穷举的要求确保了枚举成员不会被意外遗漏。
|
||||
|
||||
当不需要匹配每个枚举成员的时候,你可以提供一个默认`default`分支来涵盖所有未明确被提出的任何成员:
|
||||
当不需要匹配每个枚举成员的时候,你可以提供一个默认`default`分支来涵盖所有未明确被提出的枚举成员:
|
||||
|
||||
```swift
|
||||
let somePlanet = Planet.Earth
|
||||
switch somePlanet {
|
||||
case .Earth:
|
||||
println("Mostly harmless")
|
||||
print("Mostly harmless")
|
||||
default:
|
||||
println("Not a safe place for humans")
|
||||
print("Not a safe place for humans")
|
||||
}
|
||||
// 输出 "Mostly harmless”
|
||||
```
|
||||
@ -114,11 +119,11 @@ default:
|
||||
<a name="associated_values"></a>
|
||||
## 相关值(Associated Values)
|
||||
|
||||
上一小节的例子演示了一个枚举的成员是如何被定义(分类)的。你可以为`Planet.Earth`设置一个常量或则变量,并且在之后查看这个值。不管怎样,如果有时候能够把其他类型的相关值和成员值一起存储起来会很有用。这能让你存储成员值之外的自定义信息,并且当你每次在代码中使用该成员时允许这个信息产生变化。
|
||||
上一小节的例子演示了如何定义(分类)枚举的成员。你可以为`Planet.Earth`设置一个常量或者变量,并且在赋值之后查看这个值。不管怎样,如果有时候能够把其他类型的*相关值*和成员值一起存储起来会很有用。这能让你存储成员值之外的自定义信息,并且当你每次在代码中使用该成员时允许这个信息产生变化。
|
||||
|
||||
你可以定义 Swift 的枚举存储任何类型的相关值,如果需要的话,每个成员的数据类型可以是各不相同的。枚举的这种特性跟其他语言中的可辨识联合(discriminated unions),标签联合(tagged unions),或者变体(variants)相似。
|
||||
|
||||
例如,假设一个库存跟踪系统需要利用两种不同类型的条形码来跟踪商品。有些商品上标有 UPC-A 格式的一维码,它使用数字 0 到 9。每一个条形码都有一个代表“数字系统”的数字,该数字后接 10 个代表“标识符”的数字。最后一个数字是“检查”位,用来验证代码是否被正确扫描:
|
||||
例如,假设一个库存跟踪系统需要利用两种不同类型的条形码来跟踪商品。有些商品上标有 UPC-A 格式的一维t条形码,它使用数字 0 到 9。每一个条形码都有一个代表“数字系统”的数字,该数字后接 5 个代表“生产代码”的数字,接下来是5位“产品代码”。最后一个数字是“检查”位,用来验证代码是否被正确扫描:
|
||||
|
||||
<img width="252" height="120" alt="" src="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/barcode_UPC_2x.png">
|
||||
|
||||
@ -128,28 +133,28 @@ default:
|
||||
|
||||
对于库存跟踪系统来说,能够把 UPC-A 码作为三个整型值的元组,和把 QR 码作为一个任何长度的字符串存储起来是方便的。
|
||||
|
||||
在 Swift 中,用来定义两种商品条码的枚举是这样子的:
|
||||
在 Swift 中,使用如下方式定义两种商品条码的枚举:
|
||||
|
||||
```swift
|
||||
enum Barcode {
|
||||
case UPCA(Int, Int, Int)
|
||||
case UPCA(Int, Int, Int, Int)
|
||||
case QRCode(String)
|
||||
}
|
||||
```
|
||||
|
||||
以上代码可以这么理解:
|
||||
|
||||
“定义一个名为`Barcode`的枚举类型,它可以是`UPCA`的一个相关值(`Int`,`Int`,`Int`),或者`QRCode`的一个字符串类型(`String`)相关值。”
|
||||
“定义一个名为`Barcode`的枚举类型,它可以是`UPCA`的一个相关值(`Int`,`Int`,`Int`,`Int`),或者是`QRCode`的一个字符串类型(`String`)相关值。”
|
||||
|
||||
这个定义不提供任何`Int`或`String`的实际值,它只是定义了,当`Barcode`常量和变量等于`Barcode.UPCA`或`Barcode.QRCode`时,相关值的类型。
|
||||
|
||||
然后可以使用任何一种条码类型创建新的条码,如:
|
||||
|
||||
```swift
|
||||
var productBarcode = Barcode.UPCA(8, 85909_51226, 3)
|
||||
var productBarcode = Barcode.UPCA(8, 85909, 51226, 3)
|
||||
```
|
||||
|
||||
以上例子创建了一个名为`productBarcode`的新变量,并且赋给它一个`Barcode.UPCA`的相关元组值`(8, 8590951226, 3)`。提供的“标识符”值在整数字中有一个下划线,使其便于阅读条形码。
|
||||
以上例子创建了一个名为`productBarcode`的变量,并且赋给它一个`Barcode.UPCA`的相关元组值`(8, 85909, 51226, 3)`。
|
||||
|
||||
同一个商品可以被分配给一个不同类型的条形码,如:
|
||||
|
||||
@ -163,32 +168,32 @@ productBarcode = .QRCode("ABCDEFGHIJKLMNOP")
|
||||
|
||||
```swift
|
||||
switch productBarcode {
|
||||
case .UPCA(let numberSystem, let identifier, let check):
|
||||
println("UPC-A with value of \(numberSystem), \(identifier), \(check).")
|
||||
case .UPCA(let numberSystem, let manufacturer, let product, let check):
|
||||
print("UPC-A: \(numberSystem), \(manufacturer), \(product), \(check).")
|
||||
case .QRCode(let productCode):
|
||||
println("QR code with value of \(productCode).")
|
||||
print("QR code: \(productCode).")
|
||||
}
|
||||
// 输出 "QR code with value of ABCDEFGHIJKLMNOP.”
|
||||
// 输出 "QR code: ABCDEFGHIJKLMNOP."
|
||||
```
|
||||
|
||||
如果一个枚举成员的所有相关值被提取为常量,或者它们全部被提取为变量,为了简洁,你可以只放置一个`var`或者`let`标注在成员名称前:
|
||||
|
||||
```swift
|
||||
switch productBarcode {
|
||||
case let .UPCA(numberSystem, identifier, check):
|
||||
println("UPC-A with value of \(numberSystem), \(identifier), \(check).")
|
||||
case let .UPCA(numberSystem, manufacturer, product, check):
|
||||
print("UPC-A: \(numberSystem), \(manufacturer), \(product), \(check).")
|
||||
case let .QRCode(productCode):
|
||||
println("QR code with value of \(productCode).")
|
||||
print("QR code: \(productCode).")
|
||||
}
|
||||
// 输出 "QR code with value of ABCDEFGHIJKLMNOP."
|
||||
// 输出 "QR code: ABCDEFGHIJKLMNOP."
|
||||
```
|
||||
|
||||
<a name="raw_values"></a>
|
||||
## 原始值(Raw Values)
|
||||
|
||||
在[Associated Values](#raw_values)小节的条形码例子中演示了一个枚举的成员如何声明它们存储不同类型的相关值。作为相关值的替代,枚举成员可以被默认值(称为原始值)预先填充,其中这些原始值具有相同的类型。
|
||||
在[相关值](#raw_values)小节的条形码例子中演示了一个枚举的成员如何声明它们存储不同类型的相关值。作为相关值的另一种选择,枚举成员可以被默认值(称为原始值)赋值,其中这些原始值具有相同的类型。
|
||||
|
||||
这里是一个枚举成员存储原始 ASCII 值的例子:
|
||||
这里是一个枚举成员存储 ASCII 码的例子:
|
||||
|
||||
```swift
|
||||
enum ASCIIControlCharacter: Character {
|
||||
@ -198,11 +203,19 @@ enum ASCIIControlCharacter: Character {
|
||||
}
|
||||
```
|
||||
|
||||
在这里,称为`ASCIIControlCharacter`的枚举的原始值类型被定义为字符型`Character`,并被设置了一些比较常见的 ASCII 控制字符。字符值的描述请详见字符串和字符[`Strings and Characters`](03_Strings_and_Characters.html)部分。
|
||||
在这里,`ASCIIControlCharacter`的枚举类型的原始值类型被定义为字符型`Character`,并被设置了一些比较常见的 ASCII 控制字符。字符值的描述请详见[字符串和字符](./03_Strings_and_Characters.html)部分。
|
||||
|
||||
注意,原始值和相关值是不相同的。当你开始在你的代码中定义枚举的时候原始值是被预先填充的值,像上述三个 ASCII 码。对于一个特定的枚举成员,它的原始值始终是相同的。相关值是当你在创建一个基于枚举成员的新常量或变量时才会被设置,并且每次当你这么做得时候,它的值可以是不同的。
|
||||
|
||||
原始值可以是字符串,字符,或者任何整型值或浮点型值。每个原始值在它的枚举声明中必须是唯一的。当整型值被用于原始值,如果其他枚举成员没有值时,它们会自动递增。
|
||||
原始值可以是字符串,字符,或者任何整型值或浮点型值。每个原始值在它的枚举声明中必须是唯一的。
|
||||
|
||||
>注意:
|
||||
>原始值和相关值是不相同的。当你开始在你的代码中定义枚举的时候原始值是被预先填充的值,像上述三个 ASCII 码。对于一个特定的枚举成员,它的原始值始终是相同的。相关值是当你在创建一个基于枚举成员的新常量或变量时才会被设置,并且每次当你这么做得时候,它的值可以是不同的。
|
||||
|
||||
### 原始值的隐式赋值(Implicitly Assigned Raw Values)
|
||||
|
||||
在使用原始值为整数或者字符串类型的枚举时,不需要显式的为每一个成员赋值,这时,Swift将会自动为你赋值。
|
||||
|
||||
例如,当使用整数作为原始值时,隐式赋值的值依次递增1。如果第一个值没有被赋初值,将会被自动置为0。
|
||||
|
||||
下面的枚举是对之前`Planet`这个枚举的一个细化,利用原始整型值来表示每个 planet 在太阳系中的顺序:
|
||||
|
||||
@ -212,23 +225,46 @@ enum Planet: Int {
|
||||
}
|
||||
```
|
||||
|
||||
自动递增意味着`Planet.Venus`的原始值是`2`,依次类推。
|
||||
在上面的例子中,`Plant.Mercury`赋了初值`1`,`Planet.Venus`会拥有隐式赋值`2`,依次类推。
|
||||
|
||||
当使用字符串作为枚举类型的初值时,每个枚举成员的隐式初值则为该成员的名称。
|
||||
|
||||
下面的例子是`CompassPoint`枚举类型的精简版,使用字符串作为初值类型,隐式初始化为咩个方向的名称:
|
||||
|
||||
```swift
|
||||
enum CompassPoint: String {
|
||||
case North, South, East, West
|
||||
}
|
||||
```
|
||||
|
||||
上面例子中,`CompassPoint.South`拥有隐式初值`South`,依次类推。
|
||||
|
||||
使用枚举成员的`rawValue`属性可以访问该枚举成员的原始值:
|
||||
|
||||
```swift
|
||||
let earthsOrder = Planet.Earth.rawValue
|
||||
// earthsOrder is 3
|
||||
```
|
||||
// earthsOrder 值为 3
|
||||
|
||||
通过参数为`rawValue`构造函数创建特定原始值的枚举。这个例子通过原始值`7`识别`Uranus`:
|
||||
let sunsetDirection = CompassPoint.West.rawValue
|
||||
// sunsetDirection 值为 "West"
|
||||
```
|
||||
### 使用原始值来初始化(Initializing from a Raw Value)
|
||||
|
||||
### 使用原始值初始化枚举变量(Initializing from a Raw Value)
|
||||
|
||||
如果在定义枚举类型的时候使用了原始值,那么将会自动获得一个初始化方法,这个方法将原始值类型作为参数,返回枚举成员或者`nil`。你可以使用这种初始化方法来创建一个新的枚举变量。
|
||||
|
||||
这个例子通过原始值`7`从而创建枚举成员:
|
||||
|
||||
```swift
|
||||
let possiblePlanet = Planet(rawValue: 7)
|
||||
// possiblePlanet is of type Planet? and equals Planet.Uranus
|
||||
// possiblePlanet 类型为 Planet? 值为 Planet.Uranus
|
||||
```
|
||||
|
||||
然而,并非所有可能的`Int`值都可以找到一个匹配的行星。正因为如此,构造函数可以返回一个***可选***的枚举成员。在上面的例子中,`possiblePlanet`是`Planet?`类型,或“可选的`Planet`”。
|
||||
然而,并非所有可能的`Int`值都可以找到一个匹配的行星。正因为如此,构造函数可以返回一个*可选*的枚举成员。在上面的例子中,`possiblePlanet`是`Planet?`类型,或“可选的`Planet`”。
|
||||
|
||||
>注意:
|
||||
>原始值构造器是一个可失败构造器,因为并不是每一个原始值都有与之对应的枚举成员。更多信息请参见[可失败构造器](../chapter3/05_Declarations#failable_initializers)
|
||||
|
||||
如果你试图寻找一个位置为9的行星,通过参数为`rawValue`构造函数返回的可选`Planet`值将是`nil`:
|
||||
|
||||
@ -237,15 +273,71 @@ let positionToFind = 9
|
||||
if let somePlanet = Planet(rawValue: positionToFind) {
|
||||
switch somePlanet {
|
||||
case .Earth:
|
||||
println("Mostly harmless")
|
||||
print("Mostly harmless")
|
||||
default:
|
||||
println("Not a safe place for humans")
|
||||
print("Not a safe place for humans")
|
||||
}
|
||||
} else {
|
||||
println("There isn't a planet at position \(positionToFind)")
|
||||
print("There isn't a planet at position \(positionToFind)")
|
||||
}
|
||||
// 输出 "There isn't a planet at position 9
|
||||
```
|
||||
|
||||
这个范例使用可选绑定(optional binding),通过原始值`9`试图访问一个行星。`if let somePlanet = Planet(rawValue: 9)`语句获得一个可选`Planet`,如果可选`Planet`可以被获得,把`somePlanet`设置成该可选`Planet`的内容。在这个范例中,无法检索到位置为`9`的行星,所以`else`分支被执行。
|
||||
|
||||
<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"
|
||||
```
|
||||
|
||||
该函数如果遇到纯数字,就直接返回该数字的值。如果遇到的是加法或乘法元算,则分别计算左边表达式和右边表达式的值,然后相加或相乘。
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
# 类和结构体(Classes and Structures)
|
||||
|
||||
> 1.0
|
||||
> 翻译:[JaySurplus](https://github.com/JaySurplus)
|
||||
> 校对:[sg552](https://github.com/sg552)
|
||||
|
||||
# 类和结构体
|
||||
> 2.0
|
||||
> 翻译+校对:[SkyJean](https://github.com/SkyJean)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
@ -9,9 +13,10 @@
|
||||
- [结构体和枚举是值类型](#structures_and_enumerations_are_value_types)
|
||||
- [类是引用类型](#classes_are_reference_types)
|
||||
- [类和结构体的选择](#choosing_between_classes_and_structures)
|
||||
- [集合(collection)类型的赋值与复制行为](#assignment_and_copy_behavior_for_collection_types)
|
||||
- [字符串(String)、数组(Array)、和字典(Dictionary)类型的赋值与复制行为](#assignment_and_copy_behavior_for_strings_arrays_and_dictionaries)
|
||||
|
||||
类和结构体是人们构建代码所用的一种通用且灵活的构造体。为了在类和结构体中实现各种功能,我们必须要严格按照常量、变量以及函数所规定的语法规则来定义属性和添加方法。
|
||||
|
||||
类和结构体是人们构建代码所用的一种通用且灵活的构造体。我们可以使用完全相同的语法规则来为类和结构体定义属性(常量、变量)和添加方法,从而扩展类和结构体的功能。
|
||||
|
||||
与其他编程语言所不同的是,Swift 并不要求你为自定义类和结构去创建独立的接口和实现文件。你所要做的是在一个单一文件中定义一个类或者结构体,系统将会自动生成面向其它代码的外部接口。
|
||||
|
||||
@ -28,9 +33,9 @@ Swift 中类和结构体有很多共同点。共同处在于:
|
||||
* 定义附属脚本用于访问值
|
||||
* 定义构造器用于生成初始化值
|
||||
* 通过扩展以增加默认实现的功能
|
||||
* 符合协议以对某类提供标准功能
|
||||
* 实现协议以提供某种标准功能
|
||||
|
||||
更多信息请参见 [属性](10_Properties.html),[方法](11_Methods.html),[下标脚本](12_Subscripts.html),[初始过程](14_Initialization.html),[扩展](20_Extensions.html),和[协议](21_Protocols.html)。
|
||||
更多信息请参见 [属性](./10_Properties.html),[方法](./11_Methods.html),[下标脚本](./12_Subscripts.html),[初始过程](./14_Initialization.html),[扩展](./20_Extensions.html),和[协议](./21_Protocols.html)。
|
||||
|
||||
与结构体相比,类还有如下的附加功能:
|
||||
|
||||
@ -39,7 +44,7 @@ Swift 中类和结构体有很多共同点。共同处在于:
|
||||
* 解构器允许一个类实例释放任何其所被分配的资源
|
||||
* 引用计数允许对一个类的多次引用
|
||||
|
||||
更多信息请参见[继承](http://),[类型转换](http://),[初始化](http://),和[自动引用计数](http://)。
|
||||
更多信息请参见[继承](./13_Inheritance.html),[类型转换](./20_Type_Casting.html),[析构过程](./15_Deinitialization),和[自动引用计数](./16_Automatic_Reference_Counting)。
|
||||
|
||||
> 注意:
|
||||
结构体总是通过被复制的方式在代码中传递,因此请不要使用引用计数。
|
||||
@ -77,7 +82,7 @@ class VideoMode {
|
||||
|
||||
在上面的示例中我们定义了一个名为`Resolution`的结构体,用来描述一个显示器的像素分辨率。这个结构体包含了两个名为`width`和`height`的存储属性。存储属性是捆绑和存储在类或结构体中的常量或变量。当这两个属性被初始化为整数`0`的时候,它们会被推断为`Int`类型。
|
||||
|
||||
在上面的示例中我们还定义了一个名为`VideoMode`的类,用来描述一个视频显示器的特定模式。这个类包含了四个储存属性变量。第一个是`分辨率`,它被初始化为一个新的`Resolution`结构体的实例,具有`Resolution`的属性类型。新`VideoMode`实例同时还会初始化其它三个属性,它们分别是,初始值为`false`(意为“non-interlaced video”)的`interlaced`,回放帧率初始值为`0.0`的`frameRate`和值为可选`String`的`name`。`name`属性会被自动赋予一个默认值`nil`,意为“没有`name`值”,因为它是一个可选类型。
|
||||
在上面的示例中我们还定义了一个名为`VideoMode`的类,用来描述一个视频显示器的特定模式。这个类包含了四个储存属性变量。第一个是`分辨率`,它被初始化为一个新的`Resolution`结构体的实例,具有`Resolution`的属性类型。新`VideoMode`实例同时还会初始化其它三个属性,它们分别是,初始值为`false`(意为“非隔行扫描视频”)的`interlaced`,回放帧率初始值为`0.0`的`frameRate`和值为可选`String`的`name`。`name`属性会被自动赋予一个默认值`nil`,意为“没有`name`值”,因为它是一个可选类型。
|
||||
|
||||
### 类和结构体实例
|
||||
|
||||
@ -90,14 +95,14 @@ let someResolution = Resolution()
|
||||
let someVideoMode = VideoMode()
|
||||
```
|
||||
|
||||
结构体和类都使用构造器语法来生成新的实例。构造器语法的最简单形式是在结构体或者类的类型名称后跟随一个空括弧,如`Resolution()`或`VideoMode()`。通过这种方式所创建的类或者结构体实例,其属性均会被初始化为默认值。[构造过程](14_Initialization.html)章节会对类和结构体的初始化进行更详细的讨论。
|
||||
结构体和类都使用构造器语法来生成新的实例。构造器语法的最简单形式是在结构体或者类的类型名称后跟随一对空括号,如`Resolution()`或`VideoMode()`。通过这种方式所创建的类或者结构体实例,其属性均会被初始化为默认值。[构造过程](./14_Initialization.html)章节会对类和结构体的初始化进行更详细的讨论。
|
||||
|
||||
### 属性访问
|
||||
|
||||
通过使用*点语法*(*dot syntax*),你可以访问实例中所含有的属性。其语法规则是,实例名后面紧跟属性名,两者通过点号(.)连接:
|
||||
|
||||
```swift
|
||||
println("The width of someResolution is \(someResolution.width)")
|
||||
print("The width of someResolution is \(someResolution.width)")
|
||||
// 输出 "The width of someResolution is 0"
|
||||
```
|
||||
|
||||
@ -106,7 +111,7 @@ println("The width of someResolution is \(someResolution.width)")
|
||||
你也可以访问子属性,如`VideoMode`中`Resolution`属性的`width`属性:
|
||||
|
||||
```swift
|
||||
println("The width of someVideoMode is \(someVideoMode.resolution.width)")
|
||||
print("The width of someVideoMode is \(someVideoMode.resolution.width)")
|
||||
// 输出 "The width of someVideoMode is 0"
|
||||
```
|
||||
|
||||
@ -114,31 +119,31 @@ println("The width of someVideoMode is \(someVideoMode.resolution.width)")
|
||||
|
||||
```swift
|
||||
someVideoMode.resolution.width = 1280
|
||||
println("The width of someVideoMode is now \(someVideoMode.resolution.width)")
|
||||
print("The width of someVideoMode is now \(someVideoMode.resolution.width)")
|
||||
// 输出 "The width of someVideoMode is now 1280"
|
||||
```
|
||||
|
||||
> 注意:
|
||||
与 Objective-C 语言不同的是,Swift 允许直接设置结构体属性的子属性。上面的最后一个例子,就是直接设置了`someVideoMode`中`resolution`属性的`width`这个子属性,以上操作并不需要重新设置`resolution`属性。
|
||||
|
||||
### 结构体类型的成员逐一构造器(Memberwise Initializers for structure Types)
|
||||
### 结构体类型的成员逐一构造器(Memberwise Initializers for Structure Types)
|
||||
|
||||
所有结构体都有一个自动生成的成员逐一构造器,用于初始化新结构体实例中成员的属性。新实例中各个属性的初始值可以通过属性的名称传递到成员逐一构造器之中:
|
||||
所有结构体都有一个自动生成的*成员逐一构造器*,用于初始化新结构体实例中成员的属性。新实例中各个属性的初始值可以通过属性的名称传递到成员逐一构造器之中:
|
||||
|
||||
```swift
|
||||
let vga = Resolution(width:640, height: 480)
|
||||
```
|
||||
|
||||
与结构体不同,类实例没有默认的成员逐一构造器。[构造过程](14_Initialization.html)章节会对构造器进行更详细的讨论。
|
||||
与结构体不同,类实例没有默认的成员逐一构造器。[构造过程](./14_Initialization.html)章节会对构造器进行更详细的讨论。
|
||||
|
||||
<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`结构体:
|
||||
|
||||
@ -160,18 +165,18 @@ cinema.width = 2048
|
||||
这里,将会显示`cinema`的`width`属性确已改为了`2048`:
|
||||
|
||||
```swift
|
||||
println("cinema is now \(cinema.width) pixels wide")
|
||||
print("cinema is now \(cinema.width) pixels wide")
|
||||
// 输出 "cinema is now 2048 pixels wide"
|
||||
```
|
||||
|
||||
然而,初始的`hd`实例中`width`属性还是`1920`:
|
||||
|
||||
```swift
|
||||
println("hd is still \(hd.width ) pixels wide")
|
||||
print("hd is still \(hd.width ) pixels wide")
|
||||
// 输出 "hd is still 1920 pixels wide"
|
||||
```
|
||||
|
||||
在将`hd`赋予给`cinema`的时候,实际上是将`hd`中所存储的`值(values)`进行拷贝,然后将拷贝的数据存储到新的`cinema`实例中。结果就是两个完全独立的实例碰巧包含有相同的数值。由于两者相互独立,因此将`cinema`的`width`修改为`2048`并不会影响`hd`中的宽(width)。
|
||||
在将`hd`赋予给`cinema`的时候,实际上是将`hd`中所存储的`值(values)`进行拷贝,然后将拷贝的数据存储到新的`cinema`实例中。结果就是两个完全独立的实例碰巧包含有相同的数值。由于两者相互独立,因此将`cinema`的`width`修改为`2048`并不会影响`hd`中的`width`的值。
|
||||
|
||||
枚举也遵循相同的行为准则:
|
||||
|
||||
@ -183,7 +188,7 @@ var currentDirection = CompassPoint.West
|
||||
let rememberedDirection = currentDirection
|
||||
currentDirection = .East
|
||||
if rememberedDirection == .West {
|
||||
println("The remembered direction is still .West")
|
||||
print("The remembered direction is still .West")
|
||||
}
|
||||
// 输出 "The remembered direction is still .West"
|
||||
```
|
||||
@ -219,7 +224,7 @@ alsoTenEighty.frameRate = 30.0
|
||||
下面,通过查看`tenEighty`的`frameRate`属性,我们会发现它正确的显示了基本`VideoMode`实例的新帧率,其值为`30.0`:
|
||||
|
||||
```swift
|
||||
println("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
|
||||
print("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
|
||||
// 输出 "The frameRate property of theEighty is now 30.0"
|
||||
```
|
||||
|
||||
@ -238,7 +243,7 @@ println("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
|
||||
|
||||
```swift
|
||||
if tenEighty === alsoTenEighty {
|
||||
println("tenEighty and alsoTenEighty refer to the same Resolution instance.")
|
||||
print("tenEighty and alsoTenEighty refer to the same Resolution instance.")
|
||||
}
|
||||
//输出 "tenEighty and alsoTenEighty refer to the same Resolution instance."
|
||||
```
|
||||
@ -248,11 +253,11 @@ if tenEighty === alsoTenEighty {
|
||||
* “等价于”表示两个类类型(class type)的常量或者变量引用同一个类实例。
|
||||
* “等于”表示两个实例的值“相等”或“相同”,判定时要遵照类设计者定义定义的评判标准,因此相比于“相等”,这是一种更加合适的叫法。
|
||||
|
||||
当你在定义你的自定义类和结构体的时候,你有义务来决定判定两个实例“相等”的标准。在章节[运算符函数(Operator Functions)](24_Advanced_Operators.html#operator_functions)中将会详细介绍实现自定义“等于”和“不等于”运算符的流程。
|
||||
当你在定义你的自定义类和结构体的时候,你有义务来决定判定两个实例“相等”的标准。在章节[等价操作符](./24_Advanced_Operators.html#equivalence_operators)中将会详细介绍实现自定义“等于”和“不等于”运算符的流程。
|
||||
|
||||
### 指针
|
||||
|
||||
如果你有 C,C++ 或者 Objective-C 语言的经验,那么你也许会知道这些语言使用指针来引用内存中的地址。一个 Swift 常量或者变量引用一个引用类型的实例与 C 语言中的指针类似,不同的是并不直接指向内存中的某个地址,而且也不要求你使用星号(*)来表明你在创建一个引用。Swift 中这些引用与其它的常量或变量的定义方式相同。
|
||||
如果你有 C,C++ 或者 Objective-C 语言的经验,那么你也许会知道这些语言使用*指针*来引用内存中的地址。一个 Swift 常量或者变量引用一个引用类型的实例与 C 语言中的指针类似,不同的是并不直接指向内存中的某个地址,而且也不要求你使用星号(*)来表明你在创建一个引用。Swift 中这些引用与其它的常量或变量的定义方式相同。
|
||||
|
||||
<a name="choosing_between_classes_and_structures"></a>
|
||||
## 类和结构体的选择
|
||||
@ -268,7 +273,7 @@ if tenEighty === alsoTenEighty {
|
||||
* 任何在结构体中储存的值类型属性,也将会被拷贝,而不是被引用。
|
||||
* 结构体不需要去继承另一个已存在类型的属性或者行为。
|
||||
|
||||
合适的结构体候选者包括:
|
||||
举例来说,以下情境中适合使用结构体:
|
||||
|
||||
* 几何形状的大小,封装一个`width`属性和`height`属性,两者均为`Double`类型。
|
||||
* 一定范围内的路径,封装一个`start`属性和`length`属性,两者均为`Int`类型。
|
||||
@ -276,15 +281,14 @@ if tenEighty === alsoTenEighty {
|
||||
|
||||
在所有其它案例中,定义一个类,生成一个它的实例,并通过引用来管理和传递。实际中,这意味着绝大部分的自定义数据构造都应该是类,而非结构体。
|
||||
|
||||
<a name="assignment_and_copy_behavior_for_collection_types"></a>
|
||||
## 集合(Collection)类型的赋值和拷贝行为
|
||||
<a name="assignment_and_copy_behavior_for_strings_arrays_and_dictionaries"></a>
|
||||
## 字符串(String)、数组(Array)、和字典(Dictionary)类型的赋值与复制行为
|
||||
|
||||
Swift 中`字符串(String)`,`数组(Array)`和`字典(Dictionary)`类型均以结构体的形式实现。这意味着String,Array,Dictionary类型数据被赋值给新的常量(或变量),或者被传入函数(或方法)中时,它们的值会发生拷贝行为(值传递方式)。
|
||||
Swift 中`字符串(String)`,`数组(Array)`和`字典(Dictionary)`类型均以结构体的形式实现。这意味着String,Array,Dictionary类型数据被赋值给新的常量或变量,或者被传入函数或方法中时,它们的值会发生拷贝行为(值传递方式)。
|
||||
|
||||
Objective-C中`字符串(NSString)`,`数组(NSArray)`和`字典(NSDictionary)`类型均以类的形式实现,这与Swfit中以值传递方式是不同的。NSString,NSArray,NSDictionary在发生赋值或者传入函数(或方法)时,不会发生值拷贝,而是传递已存在实例的引用。
|
||||
|
||||
|
||||
> 注意:
|
||||
以上是对于数组,字典,字符串和其它值的`拷贝`的描述。
|
||||
以上是对于字符串、数组、字典和其它值的`拷贝`的描述。
|
||||
在你的代码中,拷贝好像是确实是在有拷贝行为的地方产生过。然而,在 Swift 的后台中,只有确有必要,`实际(actual)`拷贝才会被执行。Swift 管理所有的值拷贝以确保性能最优化的性能,所以你也没有必要去避免赋值以保证最优性能。(实际赋值由系统管理优化)
|
||||
|
||||
|
||||
@ -1,8 +1,12 @@
|
||||
> 翻译:[shinyzhu](https://github.com/shinyzhu)
|
||||
> 校对:[pp-prog](https://github.com/pp-prog)
|
||||
|
||||
# 属性 (Properties)
|
||||
---
|
||||
---
|
||||
|
||||
> 1.0
|
||||
> 翻译:[shinyzhu](https://github.com/shinyzhu)
|
||||
> 校对:[pp-prog](https://github.com/pp-prog) [yangsiy](https://github.com/yangsiy)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[yangsiy](https://github.com/yangsiy)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
@ -12,18 +16,18 @@
|
||||
- [全局变量和局部变量(Global and Local Variables)](#global_and_local_variables)
|
||||
- [类型属性(Type Properties)](#type_properties)
|
||||
|
||||
**属性**将值跟特定的类、结构或枚举关联。存储属性存储常量或变量作为实例的一部分,计算属性计算(而不是存储)一个值。计算属性可以用于类、结构体和枚举里,存储属性只能用于类和结构体。
|
||||
*属性*将值跟特定的类、结构或枚举关联。存储属性存储常量或变量作为实例的一部分,而计算属性计算(不是存储)一个值。计算属性可以用于类、结构体和枚举,存储属性只能用于类和结构体。
|
||||
|
||||
存储属性和计算属性通常用于特定类型的实例,但是,属性也可以直接用于类型本身,这种属性称为类型属性。
|
||||
存储属性和计算属性通常与特定类型的实例关联。但是,属性也可以直接作用于类型本身,这种属性称为类型属性。
|
||||
|
||||
另外,还可以定义属性观察器来监控属性值的变化,以此来触发一个自定义的操作。属性观察器可以添加到自己写的存储属性上,也可以添加到从父类继承的属性上。
|
||||
另外,还可以定义属性观察器来监控属性值的变化,以此来触发一个自定义的操作。属性观察器可以添加到自己定义的存储属性上,也可以添加到从父类继承的属性上。
|
||||
|
||||
<a name="stored_properties"></a>
|
||||
## 存储属性
|
||||
|
||||
简单来说,一个存储属性就是存储在特定类或结构体的实例里的一个常量或变量,存储属性可以是*变量存储属性*(用关键字`var`定义),也可以是*常量存储属性*(用关键字`let`定义)。
|
||||
简单来说,一个存储属性就是存储在特定类或结构体的实例里的一个常量或变量。存储属性可以是*变量存储属性*(用关键字`var`定义),也可以是*常量存储属性*(用关键字`let`定义)。
|
||||
|
||||
可以在定义存储属性的时候指定默认值,请参考[构造过程](../chapter2/14_Initialization.html)一章的[默认属性值](../chapter2/14_Initialization.html#default_property_values)一节。也可以在构造过程中设置或修改存储属性的值,甚至修改常量存储属性的值,请参考[构造过程](../chapter2/14_Initialization.html)一章的[在初始化阶段修改常量存储属性](../chapter2/14_Initialization.html#modifying_constant_properties_during_initialization)一节。
|
||||
可以在定义存储属性的时候指定默认值,请参考[默认属性值](./14_Initialization.html#default_property_values)一节。也可以在构造过程中设置或修改存储属性的值,甚至修改常量存储属性的值,请参考[在初始化阶段修改常量存储属性](./14_Initialization.html#assigning_constant_properties_during_initialization)一节。
|
||||
|
||||
下面的例子定义了一个名为`FixedLengthRange`的结构体,它描述了一个在创建后无法修改值域宽度的区间:
|
||||
|
||||
@ -38,12 +42,12 @@ rangeOfThreeItems.firstValue = 6
|
||||
// 该区间现在表示整数6,7,8
|
||||
```
|
||||
|
||||
`FixedLengthRange`的实例包含一个名为`firstValue`的变量存储属性和一个名为`length`的常量存储属性。在上面的例子中,`length`在创建实例的时候被赋值,因为它是一个常量存储属性,所以之后无法修改它的值。
|
||||
`FixedLengthRange`的实例包含一个名为`firstValue`的变量存储属性和一个名为`length`的常量存储属性。在上面的例子中,`length`在创建实例的时候被初始化,因为它是一个常量存储属性,所以之后无法修改它的值。
|
||||
|
||||
<a name="stored_properties_of_constant_structure_instances"></a>
|
||||
### 常量和存储属性
|
||||
### 常量结构体的存储属性
|
||||
|
||||
如果创建了一个结构体的实例并赋值给一个常量,则无法修改实例的任何属性,即使定义了变量存储属性:
|
||||
如果创建了一个结构体的实例并将其赋值给一个常量,则无法修改该实例的任何属性,即使定义了变量存储属性:
|
||||
|
||||
```swift
|
||||
let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
|
||||
@ -52,11 +56,11 @@ rangeOfFourItems.firstValue = 6
|
||||
// 尽管 firstValue 是个变量属性,这里还是会报错
|
||||
```
|
||||
|
||||
因为`rangeOfFourItems`声明成了常量(用`let`关键字),即使`firstValue`是一个变量属性,也无法再修改它了。
|
||||
因为`rangeOfFourItems`被声明成了常量(用`let`关键字),即使`firstValue`是一个变量属性,也无法再修改它了。
|
||||
|
||||
这种行为是由于结构体(struct)属于*值类型*。当值类型的实例被声明为常量的时候,它的所有属性也就成了常量。
|
||||
|
||||
属于*引用类型*的类(class)则不一样,把一个引用类型的实例赋给一个常量后,仍然可以修改实例的变量属性。
|
||||
属于*引用类型*的类(class)则不一样。把一个引用类型的实例赋给一个常量后,仍然可以修改该实例的变量属性。
|
||||
|
||||
<a name="lazy_stored_properties"></a>
|
||||
### 延迟存储属性
|
||||
@ -64,11 +68,11 @@ rangeOfFourItems.firstValue = 6
|
||||
延迟存储属性是指当第一次被调用的时候才会计算其初始值的属性。在属性声明前使用`lazy`来标示一个延迟存储属性。
|
||||
|
||||
> 注意:
|
||||
> 必须将延迟存储属性声明成变量(使用`var`关键字),因为属性的值在实例构造完成之前可能无法得到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟属性。
|
||||
> 必须将延迟存储属性声明成变量(使用`var`关键字),因为属性的初始值可能在实例构造完成之后才会得到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟属性。
|
||||
|
||||
延迟属性很有用,当属性的值依赖于在实例的构造过程结束前无法知道具体值的外部因素时,或者当属性的值需要复杂或大量计算时,可以只在需要的时候来计算它。
|
||||
延迟属性很有用,当属性的值依赖于在实例的构造过程结束后才会知道具体值的外部因素时,或者当获得属性的初始值需要复杂或大量计算时,可以只在需要的时候计算它。
|
||||
|
||||
下面的例子使用了延迟存储属性来避免复杂类的不必要的初始化。例子中定义了`DataImporter`和`DataManager`两个类,下面是部分代码:
|
||||
下面的例子使用了延迟存储属性来避免复杂类中不必要的初始化。例子中定义了`DataImporter`和`DataManager`两个类,下面是部分代码:
|
||||
|
||||
```swift
|
||||
class DataImporter {
|
||||
@ -87,37 +91,39 @@ class DataManager {
|
||||
}
|
||||
|
||||
let manager = DataManager()
|
||||
manager.data.append("Some data")
|
||||
manager.data.append("Some data")
|
||||
manager.data.append("Some more data")
|
||||
// DataImporter 实例的 importer 属性还没有被创建
|
||||
```
|
||||
|
||||
`DataManager`类包含一个名为`data`的存储属性,初始值是一个空的字符串(`String`)数组。虽然没有写出全部代码,`DataManager`类的目的是管理和提供对这个字符串数组的访问。
|
||||
|
||||
`DataManager`的一个功能是从文件导入数据,该功能由`DataImporter`类提供,`DataImporter`需要消耗不少时间完成初始化:因为它的实例在初始化时可能要打开文件,还要读取文件内容到内存。
|
||||
`DataManager`的一个功能是从文件导入数据。该功能由`DataImporter`类提供,`DataImporter`完成初始化需要消耗不少时间:因为它的实例在初始化时可能要打开文件,还要读取文件内容到内存。
|
||||
|
||||
`DataManager`也可能不从文件中导入数据。所以当`DataManager`的实例被创建时,没必要创建一个`DataImporter`的实例,更明智的是当用到`DataImporter`的时候才去创建它。
|
||||
`DataManager`也可能不从文件中导入数据就完成了管理数据的功能。所以当`DataManager`的实例被创建时,没必要创建一个`DataImporter`的实例,更明智的是当第一次用到`DataImporter`的时候才去创建它。
|
||||
|
||||
由于使用了`lazy`,`importer`属性只有在第一次被访问的时候才被创建。比如访问它的属性`fileName`时:
|
||||
|
||||
```swift
|
||||
println(manager.importer.fileName)
|
||||
print(manager.importer.fileName)
|
||||
// DataImporter 实例的 importer 属性现在被创建了
|
||||
// 输出 "data.txt”
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> 如果一个被标记为`lazy`的属性在没有初始化时就同时被多个线程访问,则无法保证该属性只会被初始化一次。
|
||||
|
||||
<a name="stored_properties_and_instance_variables"></a>
|
||||
### 存储属性和实例变量
|
||||
|
||||
如果您有过 Objective-C 经验,应该知道Objective-C为类实例存储值和引用提供两种方法。对于属性来说,也可以使用实例变量作为属性值的后端存储。
|
||||
如果您有过 Objective-C 经验,应该知道 Objective-C 为类实例存储值和引用提供两种方法。对于属性来说,也可以使用实例变量作为属性值的后端存储。
|
||||
|
||||
Swift 编程语言中把这些理论统一用属性来实现。Swift 中的属性没有对应的实例变量,属性的后端存储也无法直接访问。这就避免了不同场景下访问方式的困扰,同时也将属性的定义简化成一个语句。
|
||||
一个类型中属性的全部信息——包括命名、类型和内存管理特征——都在唯一一个地方(类型定义中)定义。
|
||||
Swift 编程语言中把这些理论统一用属性来实现。Swift 中的属性没有对应的实例变量,属性的后端存储也无法直接访问。这就避免了不同场景下访问方式的困扰,同时也将属性的定义简化成一个语句。一个类型中属性的全部信息——包括命名、类型和内存管理特征——都在唯一一个地方(类型定义中)定义。
|
||||
|
||||
<a name="computed_properties"></a>
|
||||
## 计算属性
|
||||
|
||||
除存储属性外,类、结构体和枚举可以定义*计算属性*,计算属性不直接存储值,而是提供一个 getter 来获取值,一个可选的 setter 来间接设置其他属性或变量的值。
|
||||
除存储属性外,类、结构体和枚举可以定义*计算属性*。计算属性不直接存储值,而是提供一个 getter 和一个可选的 setter,来间接获取和设置其他属性或变量的值。
|
||||
|
||||
```swift
|
||||
struct Point {
|
||||
@ -130,38 +136,38 @@ struct Rect {
|
||||
var origin = Point()
|
||||
var size = Size()
|
||||
var center: Point {
|
||||
get {
|
||||
let centerX = origin.x + (size.width / 2)
|
||||
let centerY = origin.y + (size.height / 2)
|
||||
return Point(x: centerX, y: centerY)
|
||||
}
|
||||
set(newCenter) {
|
||||
origin.x = newCenter.x - (size.width / 2)
|
||||
origin.y = newCenter.y - (size.height / 2)
|
||||
}
|
||||
get {
|
||||
let centerX = origin.x + (size.width / 2)
|
||||
let centerY = origin.y + (size.height / 2)
|
||||
return Point(x: centerX, y: centerY)
|
||||
}
|
||||
set(newCenter) {
|
||||
origin.x = newCenter.x - (size.width / 2)
|
||||
origin.y = newCenter.y - (size.height / 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
var square = Rect(origin: Point(x: 0.0, y: 0.0),
|
||||
size: Size(width: 10.0, height: 10.0))
|
||||
let initialSquareCenter = square.center
|
||||
square.center = Point(x: 15.0, y: 15.0)
|
||||
println("square.origin is now at (\(square.origin.x), \(square.origin.y))")
|
||||
print("square.origin is now at (\(square.origin.x), \(square.origin.y))")
|
||||
// 输出 "square.origin is now at (10.0, 10.0)”
|
||||
```
|
||||
|
||||
这个例子定义了 3 个几何形状的结构体:
|
||||
这个例子定义了 3 个结构体来描述几何形状:
|
||||
|
||||
- `Point`封装了一个`(x, y)`的坐标
|
||||
- `Size`封装了一个`width`和`height`
|
||||
- `Size`封装了一个`width`和一个`height`
|
||||
- `Rect`表示一个有原点和尺寸的矩形
|
||||
|
||||
`Rect`也提供了一个名为`center`的计算属性。一个矩形的中心点可以从原点和尺寸来算出,所以不需要将它以显式声明的`Point`来保存。`Rect`的计算属性`center`提供了自定义的 getter 和 setter 来获取和设置矩形的中心点,就像它有一个存储属性一样。
|
||||
`Rect`也提供了一个名为`center`的计算属性。一个矩形的中心点可以从原点(`origin`)和尺寸(`size`)算出,所以不需要将它以显式声明的`Point`来保存。`Rect`的计算属性`center`提供了自定义的 getter 和 setter 来获取和设置矩形的中心点,就像它有一个存储属性一样。
|
||||
|
||||
例子中接下来创建了一个名为`square`的`Rect`实例,初始值原点是`(0, 0)`,宽度高度都是`10`。如图所示蓝色正方形。
|
||||
上述例子中创建了一个名为`square`的`Rect`实例,初始值原点是`(0, 0)`,宽度高度都是`10`。如下图中蓝色正方形所示。
|
||||
|
||||
`square`的`center`属性可以通过点运算符(`square.center`)来访问,这会调用 getter 来获取属性的值。跟直接返回已经存在的值不同,getter 实际上通过计算然后返回一个新的`Point`来表示`square`的中心点。如代码所示,它正确返回了中心点`(5, 5)`。
|
||||
`square`的`center`属性可以通过点运算符(`square.center`)来访问,这会调用该属性的 getter 来获取它的值。跟直接返回已经存在的值不同,getter 实际上通过计算然后返回一个新的`Point`来表示`square`的中心点。如代码所示,它正确返回了中心点`(5, 5)`。
|
||||
|
||||
`center`属性之后被设置了一个新的值`(15, 15)`,表示向右上方移动正方形到如图所示橙色正方形的位置。设置属性`center`的值会调用 setter 来修改属性`origin`的`x`和`y`的值,从而实现移动正方形到新的位置。
|
||||
`center`属性之后被设置了一个新的值`(15, 15)`,表示向右上方移动正方形到如下图橙色正方形所示的位置。设置属性`center`的值会调用它的 setter 来修改属性`origin`的`x`和`y`的值,从而实现移动正方形到新的位置。
|
||||
|
||||
<img src="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/computedProperties_2x.png" alt="Computed Properties sample" width="388" height="387" />
|
||||
|
||||
@ -175,15 +181,15 @@ struct AlternativeRect {
|
||||
var origin = Point()
|
||||
var size = Size()
|
||||
var center: Point {
|
||||
get {
|
||||
let centerX = origin.x + (size.width / 2)
|
||||
let centerY = origin.y + (size.height / 2)
|
||||
return Point(x: centerX, y: centerY)
|
||||
}
|
||||
set {
|
||||
origin.x = newValue.x - (size.width / 2)
|
||||
origin.y = newValue.y - (size.height / 2)
|
||||
}
|
||||
get {
|
||||
let centerX = origin.x + (size.width / 2)
|
||||
let centerY = origin.y + (size.height / 2)
|
||||
return Point(x: centerX, y: centerY)
|
||||
}
|
||||
set {
|
||||
origin.x = newValue.x - (size.width / 2)
|
||||
origin.y = newValue.y - (size.height / 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -193,63 +199,61 @@ struct AlternativeRect {
|
||||
|
||||
只有 getter 没有 setter 的计算属性就是*只读计算属性*。只读计算属性总是返回一个值,可以通过点运算符访问,但不能设置新的值。
|
||||
|
||||
> 注意:
|
||||
>
|
||||
> 注意:
|
||||
> 必须使用`var`关键字定义计算属性,包括只读计算属性,因为它们的值不是固定的。`let`关键字只用来声明常量属性,表示初始化后再也无法修改的值。
|
||||
|
||||
|
||||
只读计算属性的声明可以去掉`get`关键字和花括号:
|
||||
|
||||
```swift
|
||||
struct Cuboid {
|
||||
var width = 0.0, height = 0.0, depth = 0.0
|
||||
var volume: Double {
|
||||
return width * height * depth
|
||||
return width * height * depth
|
||||
}
|
||||
}
|
||||
let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
|
||||
println("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
|
||||
print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
|
||||
// 输出 "the volume of fourByFiveByTwo is 40.0"
|
||||
```
|
||||
|
||||
这个例子定义了一个名为`Cuboid`的结构体,表示三维空间的立方体,包含`width`、`height`和`depth`属性,还有一个名为`volume`的只读计算属性用来返回立方体的体积。设置`volume`的值毫无意义,因为通过`width`、`height`和`depth`就能算出`volume`。然而,`Cuboid`提供一个只读计算属性来让外部用户直接获取体积是很有用的。
|
||||
这个例子定义了一个名为`Cuboid`的结构体,表示三维空间的立方体,包含`width`、`height`和`depth`属性。结构体还有一个名为`volume`的只读计算属性用来返回立方体的体积。设置`volume`的值毫无意义,因为无法确定修改`width`、`height`和`depth`三者中的哪些值来匹配新的`volume`,从而造成歧义。然而,`Cuboid`提供一个只读计算属性来让外部用户直接获取体积是很有用的。
|
||||
|
||||
<a name="property_observers"></a>
|
||||
## 属性观察器
|
||||
|
||||
*属性观察器*监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,甚至新的值和现在的值相同的时候也不例外。
|
||||
|
||||
可以为除了延迟存储属性之外的其他存储属性添加属性观察器,也可以通过重载属性的方式为继承的属性(包括存储属性和计算属性)添加属性观察器。属性重载请参考[继承](chapter/13_Inheritance.html)一章的[重载](chapter/13_Inheritance.html#overriding)。
|
||||
可以为除了延迟存储属性之外的其他存储属性添加属性观察器,也可以通过重载属性的方式为继承的属性(包括存储属性和计算属性)添加属性观察器。属性重载请参考[重载](./13_Inheritance.html#overriding)。
|
||||
|
||||
> 注意:
|
||||
> 不需要为无法重载的计算属性添加属性观察器,因为可以通过 setter 直接监控和响应值的变化。
|
||||
> 不需要为非重载的计算属性添加属性观察器,因为可以通过它的 setter 直接监控和响应值的变化。
|
||||
|
||||
可以为属性添加如下的一个或全部观察器:
|
||||
|
||||
- `willSet`在设置新的值之前调用
|
||||
- `willSet`在新的值被设置之前调用
|
||||
- `didSet`在新的值被设置之后立即调用
|
||||
|
||||
`willSet`观察器会将新的属性值作为固定参数传入,在`willSet`的实现代码中可以为这个参数指定一个名称,如果不指定则参数仍然可用,这时使用默认名称`newValue`表示。
|
||||
`willSet`观察器会将新的属性值作为常量参数传入,在`willSet`的实现代码中可以为这个参数指定一个名称,如果不指定则参数仍然可用,这时使用默认名称`newValue`表示。
|
||||
|
||||
类似地,`didSet`观察器会将旧的属性值作为参数传入,可以为该参数命名或者使用默认参数名`oldValue`。
|
||||
|
||||
> 注意:
|
||||
>
|
||||
> `willSet`和`didSet`观察器在属性初始化过程中不会被调用,它们只会当属性的值在初始化之外的地方被设置时被调用。
|
||||
> 注意:
|
||||
> 父类的属性在子类的构造器中被赋值时,它在父类中的`willSet`和`didSet`观察器会被调用。
|
||||
> 有关构造器代理的更多信息,请参考[值类型的构造器代理](./14_Initialization.html#initializer_delegation_for_value_types)和[类的构造器代理规则](./14_Initialization.html#initializer_delegation_for_class_types)。
|
||||
|
||||
这里是一个`willSet`和`didSet`的实际例子,其中定义了一个名为`StepCounter`的类,用来统计当人步行时的总步数,可以跟计步器或其他日常锻炼的统计装置的输入数据配合使用。
|
||||
这里是一个`willSet`和`didSet`的实际例子,其中定义了一个名为`StepCounter`的类,用来统计当人步行时的总步数。这个类可以跟计步器或其他日常锻炼的统计装置的输入数据配合使用。
|
||||
|
||||
```swift
|
||||
class StepCounter {
|
||||
var totalSteps: Int = 0 {
|
||||
willSet(newTotalSteps) {
|
||||
println("About to set totalSteps to \(newTotalSteps)")
|
||||
}
|
||||
didSet {
|
||||
if totalSteps > oldValue {
|
||||
println("Added \(totalSteps - oldValue) steps")
|
||||
willSet(newTotalSteps) {
|
||||
print("About to set totalSteps to \(newTotalSteps)")
|
||||
}
|
||||
didSet {
|
||||
if totalSteps > oldValue {
|
||||
print("Added \(totalSteps - oldValue) steps")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let stepCounter = StepCounter()
|
||||
@ -270,19 +274,19 @@ stepCounter.totalSteps = 896
|
||||
|
||||
例子中的`willSet`观察器将表示新值的参数自定义为`newTotalSteps`,这个观察器只是简单的将新的值输出。
|
||||
|
||||
`didSet`观察器在`totalSteps`的值改变后被调用,它把新的值和旧的值进行对比,如果总的步数增加了,就输出一个消息表示增加了多少步。`didSet`没有提供自定义名称,所以默认值`oldValue`表示旧值的参数名。
|
||||
`didSet`观察器在`totalSteps`的值改变后被调用,它把新的值和旧的值进行对比,如果总的步数增加了,就输出一个消息表示增加了多少步。`didSet`没有为旧的值提供自定义名称,所以默认值`oldValue`表示旧值的参数名。
|
||||
|
||||
> 注意:
|
||||
> 如果在`didSet`观察器里为属性赋值,这个值会替换观察器之前设置的值。
|
||||
> 如果在一个属性的`didSet`观察器里为它赋值,这个值会替换该观察器之前设置的值。
|
||||
|
||||
<a name="global_and_local_variables"></a>
|
||||
##全局变量和局部变量
|
||||
|
||||
计算属性和属性观察器所描述的模式也可以用于*全局变量*和*局部变量*,全局变量是在函数、方法、闭包或任何类型之外定义的变量,局部变量是在函数、方法或闭包内部定义的变量。
|
||||
计算属性和属性观察器所描述的模式也可以用于*全局变量*和*局部变量*。全局变量是在函数、方法、闭包或任何类型之外定义的变量。局部变量是在函数、方法或闭包内部定义的变量。
|
||||
|
||||
前面章节提到的全局或局部变量都属于存储型变量,跟存储属性类似,它提供特定类型的存储空间,并允许读取和写入。
|
||||
|
||||
另外,在全局或局部范围都可以定义计算型变量和为存储型变量定义观察器,计算型变量跟计算属性一样,返回一个计算的值而不是存储值,声明格式也完全一样。
|
||||
另外,在全局或局部范围都可以定义计算型变量和为存储型变量定义观察器。计算型变量跟计算属性一样,返回一个计算的值而不是存储值,声明格式也完全一样。
|
||||
|
||||
> 注意:
|
||||
> 全局的常量或变量都是延迟计算的,跟[延迟存储属性](#lazy_stored_properties)相似,不同的地方在于,全局的常量或变量不需要标记`lazy`特性。
|
||||
@ -297,9 +301,7 @@ stepCounter.totalSteps = 896
|
||||
|
||||
类型属性用于定义特定类型所有实例共享的数据,比如所有实例都能用的一个常量(就像 C 语言中的静态常量),或者所有实例都能访问的一个变量(就像 C 语言中的静态变量)。
|
||||
|
||||
对于值类型(指结构体和枚举)可以定义存储型和计算型类型属性,对于类(class)则只能定义计算型类型属性。
|
||||
|
||||
值类型的存储型类型属性可以是变量或常量,计算型类型属性跟实例的计算属性一样定义成变量属性。
|
||||
值类型的存储型类型属性可以是变量或常量,计算型类型属性跟实例的计算属性一样只能定义成变量属性。
|
||||
|
||||
> 注意:
|
||||
> 跟实例的存储属性不同,必须给存储型类型属性指定默认值,因为类型本身无法在初始化过程中使用构造器给类型属性赋值。
|
||||
@ -307,26 +309,30 @@ stepCounter.totalSteps = 896
|
||||
<a name="type_property_syntax"></a>
|
||||
###类型属性语法
|
||||
|
||||
在 C 或 Objective-C 中,静态常量和静态变量的定义是通过特定类型加上`global`关键字。在 Swift 编程语言中,类型属性是作为类型定义的一部分写在类型最外层的花括号内,因此它的作用范围也就在类型支持的范围内。
|
||||
在 C 或 Objective-C 中,与某个类型关联的静态常量和静态变量,是作为全局(*global*)静态变量定义的。但是在 Swift 编程语言中,类型属性是作为类型定义的一部分写在类型最外层的花括号内,因此它的作用范围也就在类型支持的范围内。
|
||||
|
||||
使用关键字`static`来定义值类型的类型属性,关键字`class`来为类(class)定义类型属性。下面的例子演示了存储型和计算型类型属性的语法:
|
||||
使用关键字`static`来定义类型属性。在为类(class)定义计算型类型属性时,可以使用关键字`class`来支持子类对父类的实现进行重写。下面的例子演示了存储型和计算型类型属性的语法:
|
||||
|
||||
```swift
|
||||
struct SomeStructure {
|
||||
static var storedTypeProperty = "Some value."
|
||||
static var computedTypeProperty: Int {
|
||||
// 这里返回一个 Int 值
|
||||
return 1
|
||||
}
|
||||
}
|
||||
enum SomeEnumeration {
|
||||
static var storedTypeProperty = "Some value."
|
||||
static var computedTypeProperty: Int {
|
||||
// 这里返回一个 Int 值
|
||||
return 6
|
||||
}
|
||||
}
|
||||
class SomeClass {
|
||||
class var computedTypeProperty: Int {
|
||||
// 这里返回一个 Int 值
|
||||
static var storedTypeProperty = "Some value."
|
||||
static var computedTypeProperty: Int {
|
||||
return 27
|
||||
}
|
||||
class var overrideableComputedTypeProperty: Int {
|
||||
return 107
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -337,17 +343,18 @@ class SomeClass {
|
||||
<a name="querying_and_setting_type_properties"></a>
|
||||
###获取和设置类型属性的值
|
||||
|
||||
跟实例的属性一样,类型属性的访问也是通过点运算符来进行,但是,类型属性是通过类型本身来获取和设置,而不是通过实例。比如:
|
||||
跟实例的属性一样,类型属性的访问也是通过点运算符来进行。但是,类型属性是通过类型本身来获取和设置,而不是通过实例。比如:
|
||||
|
||||
```swift
|
||||
println(SomeClass.computedTypeProperty)
|
||||
// 输出 "42"
|
||||
|
||||
println(SomeStructure.storedTypeProperty)
|
||||
print(SomeStructure.storedTypeProperty)
|
||||
// 输出 "Some value."
|
||||
SomeStructure.storedTypeProperty = "Another value."
|
||||
println(SomeStructure.storedTypeProperty)
|
||||
print(SomeStructure.storedTypeProperty)
|
||||
// 输出 "Another value.”
|
||||
print(SomeEnumeration.computedTypeProperty)
|
||||
// 输出 "6"
|
||||
print(SomeClass.computedTypeProperty)
|
||||
// 输出 "27"
|
||||
```
|
||||
|
||||
下面的例子定义了一个结构体,使用两个存储型类型属性来表示多个声道的声音电平值,每个声道有一个 0 到 10 之间的整数表示声音电平值。
|
||||
@ -356,23 +363,23 @@ println(SomeStructure.storedTypeProperty)
|
||||
|
||||
<img src="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/staticPropertiesVUMeter_2x.png" alt="Static Properties VUMeter" width="243" height="357" />
|
||||
|
||||
上面所描述的声道模型使用`AudioChannel`结构体来表示:
|
||||
上面所描述的声道模型使用`AudioChannel`结构体的实例来表示:
|
||||
|
||||
```swift
|
||||
struct AudioChannel {
|
||||
static let thresholdLevel = 10
|
||||
static var maxInputLevelForAllChannels = 0
|
||||
var currentLevel: Int = 0 {
|
||||
didSet {
|
||||
if currentLevel > AudioChannel.thresholdLevel {
|
||||
// 将新电平值设置为阀值
|
||||
currentLevel = AudioChannel.thresholdLevel
|
||||
didSet {
|
||||
if currentLevel > AudioChannel.thresholdLevel {
|
||||
// 将新电平值设置为阀值
|
||||
currentLevel = AudioChannel.thresholdLevel
|
||||
}
|
||||
if currentLevel > AudioChannel.maxInputLevelForAllChannels {
|
||||
// 存储当前电平值作为新的最大输入电平
|
||||
AudioChannel.maxInputLevelForAllChannels = currentLevel
|
||||
}
|
||||
}
|
||||
if currentLevel > AudioChannel.maxInputLevelForAllChannels {
|
||||
// 存储当前电平值作为新的最大输入电平
|
||||
AudioChannel.maxInputLevelForAllChannels = currentLevel
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -383,10 +390,10 @@ struct AudioChannel {
|
||||
|
||||
`AudioChannel`也定义了一个名为`currentLevel`的实例存储属性,表示当前声道现在的电平值,取值为 0 到 10。
|
||||
|
||||
属性`currentLevel`包含`didSet`属性观察器来检查每次新设置后的属性值,有如下两个检查:
|
||||
属性`currentLevel`包含`didSet`属性观察器来检查每次新设置后的属性值,它有如下两个检查:
|
||||
|
||||
- 如果`currentLevel`的新值大于允许的阈值`thresholdLevel`,属性观察器将`currentLevel`的值限定为阈值`thresholdLevel`。
|
||||
- 如果修正后的`currentLevel`值大于任何之前任意`AudioChannel`实例中的值,属性观察器将新值保存在静态属性`maxInputLevelForAllChannels`中。
|
||||
- 如果前一个修正后的`currentLevel`值大于任何之前任意`AudioChannel`实例中的值,属性观察器将新值保存在静态类型属性`maxInputLevelForAllChannels`中。
|
||||
|
||||
> 注意:
|
||||
> 在第一个检查过程中,`didSet`属性观察器将`currentLevel`设置成了不同的值,但这时不会再次调用属性观察器。
|
||||
@ -402,9 +409,9 @@ var rightChannel = AudioChannel()
|
||||
|
||||
```swift
|
||||
leftChannel.currentLevel = 7
|
||||
println(leftChannel.currentLevel)
|
||||
print(leftChannel.currentLevel)
|
||||
// 输出 "7"
|
||||
println(AudioChannel.maxInputLevelForAllChannels)
|
||||
print(AudioChannel.maxInputLevelForAllChannels)
|
||||
// 输出 "7"
|
||||
```
|
||||
|
||||
@ -412,8 +419,8 @@ println(AudioChannel.maxInputLevelForAllChannels)
|
||||
|
||||
```swift
|
||||
rightChannel.currentLevel = 11
|
||||
println(rightChannel.currentLevel)
|
||||
print(rightChannel.currentLevel)
|
||||
// 输出 "10"
|
||||
println(AudioChannel.maxInputLevelForAllChannels)
|
||||
print(AudioChannel.maxInputLevelForAllChannels)
|
||||
// 输出 "10"
|
||||
```
|
||||
|
||||
@ -1,9 +1,13 @@
|
||||
> 翻译:[pp-prog](https://github.com/pp-prog)
|
||||
> 校对:[zqp](https://github.com/zqp)
|
||||
|
||||
# 方法(Methods)
|
||||
-----------------
|
||||
|
||||
> 1.0
|
||||
> 翻译:[pp-prog](https://github.com/pp-prog)
|
||||
> 校对:[zqp](https://github.com/zqp)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[DianQK](https://github.com/DianQK)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [实例方法(Instance Methods)](#instance_methods)
|
||||
@ -14,19 +18,19 @@
|
||||
结构体和枚举能够定义方法是 Swift 与 C/Objective-C 的主要区别之一。在 Objective-C 中,类是唯一能定义方法的类型。但在 Swift 中,你不仅能选择是否要定义一个类/结构体/枚举,还能灵活的在你创建的类型(类/结构体/枚举)上定义方法。
|
||||
|
||||
<a name="instance_methods"></a>
|
||||
## 实例方法(Instance Methods)
|
||||
## 实例方法 (Instance Methods)
|
||||
|
||||
**实例方法**是属于某个特定类、结构体或者枚举类型实例的方法。实例方法提供访问和修改实例属性的方法或提供与实例目的相关的功能,并以此来支撑实例的功能。实例方法的语法与函数完全一致,详情参见[函数](../charpter2/06_Functions.md)。
|
||||
**实例方法**是属于某个特定类、结构体或者枚举类型实例的方法。实例方法提供访问和修改实例属性的方法或提供与实例目的相关的功能,并以此来支撑实例的功能。实例方法的语法与函数完全一致,详情参见[函数](./06_Functions.md)。
|
||||
|
||||
实例方法要写在它所属的类型的前后大括号之间。实例方法能够隐式访问它所属类型的所有的其他实例方法和属性。实例方法只能被它所属的类的某个特定实例调用。实例方法不能脱离于现存的实例而被调用。
|
||||
|
||||
下面的例子,定义一个很简单的类`Counter`,`Counter`能被用来对一个动作发生的次数进行计数:
|
||||
下面的例子,定义一个很简单的`Counter`类,`Counter`能被用来对一个动作发生的次数进行计数:
|
||||
|
||||
```swift
|
||||
class Counter {
|
||||
var count = 0
|
||||
func increment() {
|
||||
count++
|
||||
++count
|
||||
}
|
||||
func incrementBy(amount: Int) {
|
||||
count += amount
|
||||
@ -60,13 +64,13 @@ class Counter {
|
||||
<a name="local_and_external_parameter"></a>
|
||||
### 方法的局部参数名称和外部参数名称(Local and External Parameter Names for Methods)
|
||||
|
||||
函数参数可以同时有一个局部名称(在函数体内部使用)和一个外部名称(在调用函数时使用),详情参见[函数的外部参数名](06_Functions.html)。方法参数也一样(因为方法就是函数,只是这个函数与某个类型相关联了)。但是,方法和函数的局部名称和外部名称的默认行为是不一样的。
|
||||
函数参数可以同时有一个局部名称(在函数体内部使用)和一个外部名称(在调用函数时使用),详情参见[指定外部参数名](./06_Functions.html#specifying_external_parameter_names)。方法参数也一样(因为方法就是函数,只是这个函数与某个类型相关联了)。
|
||||
|
||||
Swift 中的方法和 Objective-C 中的方法极其相似。像在 Objective-C 中一样,Swift 中方法的名称通常用一个介词指向方法的第一个参数,比如:`with`,`for`,`by`等等。前面的`Counter`类的例子中`incrementBy`方法就是这样的。介词的使用让方法在被调用时能像一个句子一样被解读。和函数参数不同,对于方法的参数,Swift 使用不同的默认处理方式,这可以让方法命名规范更容易写。
|
||||
Swift 中的方法和 Objective-C 中的方法极其相似。像在 Objective-C 中一样,Swift 中方法的名称通常用一个介词指向方法的第一个参数,比如:`with`,`for`,`by`等等。前面的`Counter`类的例子中`incrementBy(_:)`方法就是这样的。介词的使用让方法在被调用时能像一个句子一样被解读。
|
||||
|
||||
具体来说,Swift 默认仅给方法的第一个参数名称一个局部参数名称;默认同时给第二个和后续的参数名称局部参数名称和外部参数名称。这个约定与典型的命名和调用约定相适应,与你在写 Objective-C 的方法时很相似。这个约定还让表达式方法在调用时不需要再限定参数名称。
|
||||
具体来说,Swift 默认仅给方法的第一个参数名称一个局部参数名称;默认同时给第二个和后续的参数名称局部参数名称和外部参数名称。这个约定与典型的命名和调用约定相适应,与你在写 Objective-C 的方法时很相似。这个约定还让表达式方法在调用时不需要再限定参数名称。
|
||||
|
||||
看看下面这个`Counter`的另一个版本(它定义了一个更复杂的`incrementBy`方法):
|
||||
看看下面这个`Counter`的另一个版本(它定义了一个更复杂的`incrementBy(_:)`方法):
|
||||
|
||||
```swift
|
||||
class Counter {
|
||||
@ -77,35 +81,28 @@ class Counter {
|
||||
}
|
||||
```
|
||||
|
||||
`incrementBy`方法有两个参数: `amount`和`numberOfTimes`。默认情况下,Swift 只把`amount`当作一个局部名称,但是把`numberOfTimes`即看作局部名称又看作外部名称。下面调用这个方法:
|
||||
`incrementBy(_:numverOfTimes:)`方法有两个参数: `amount`和`numberOfTimes`。默认情况下,Swift 只把`amount`当作一个局部名称,但是把`numberOfTimes`即看作局部名称又看作外部名称。下面调用这个方法:
|
||||
|
||||
```swift
|
||||
let counter = Counter()
|
||||
counter.incrementBy(5, numberOfTimes: 3)
|
||||
// counter value is now 15
|
||||
```
|
||||
|
||||
你不必为第一个参数值再定义一个外部变量名:因为从函数名`incrementBy`已经能很清楚地看出它的作用。但是第二个参数,就要被一个外部参数名称所限定,以便在方法被调用时明确它的作用。
|
||||
|
||||
这种默认的行为能够有效的处理方法(method),类似于在参数`numberOfTimes`前写一个井号(`#`):
|
||||
|
||||
```swift
|
||||
func incrementBy(amount: Int, #numberOfTimes: Int) {
|
||||
count += amount * numberOfTimes
|
||||
}
|
||||
// counter 的值现在是 15
|
||||
```
|
||||
|
||||
你不必为第一个参数值再定义一个外部变量名:因为从函数名`incrementBy(_numberOfTimes:)`已经能很清楚地看出它的作用。但是第二个参数,就要被一个外部参数名称所限定,以便在方法被调用时明确它的作用。
|
||||
这种默认行为使上面代码意味着:在 Swift 中定义方法使用了与 Objective-C 同样的语法风格,并且方法将以自然表达式的方式被调用。
|
||||
|
||||
<a name="modifying_external_parameter"></a>
|
||||
|
||||
<a name="modifying_external_parameter_name_behavior_for_methods"></a>
|
||||
### 修改方法的外部参数名称(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`属性来引用当前实例。
|
||||
|
||||
@ -132,14 +129,14 @@ struct Point {
|
||||
}
|
||||
let somePoint = Point(x: 4.0, y: 5.0)
|
||||
if somePoint.isToTheRightOfX(1.0) {
|
||||
println("This point is to the right of the line where x == 1.0")
|
||||
print("This point is to the right of the line where x == 1.0")
|
||||
}
|
||||
// 输出 "This point is to the right of the line where x == 1.0"(这个点在x等于1.0这条线的右边)
|
||||
// 打印输出: This point is to the right of the line where x == 1.0
|
||||
```
|
||||
|
||||
如果不使用`self`前缀,Swift 就认为两次使用的`x`都指的是名称为`x`的函数参数。
|
||||
|
||||
<a name="modifying_value_types"></a>
|
||||
<a name="modifying_value_types_from_within_instance_methods"></a>
|
||||
### 在实例方法中修改值类型(Modifying Value Types from Within Instance Methods)
|
||||
|
||||
结构体和枚举是**值类型**。一般情况下,值类型的属性不能在它的实例方法中被修改。
|
||||
@ -158,21 +155,21 @@ struct Point {
|
||||
}
|
||||
var somePoint = Point(x: 1.0, y: 1.0)
|
||||
somePoint.moveByX(2.0, y: 3.0)
|
||||
println("The point is now at (\(somePoint.x), \(somePoint.y))")
|
||||
// 输出 "The point is now at (3.0, 4.0)"
|
||||
print("The point is now at (\(somePoint.x), \(somePoint.y))")
|
||||
// 打印输出: "The point is now at (3.0, 4.0)"
|
||||
```
|
||||
|
||||
上面的`Point`结构体定义了一个变异方法(mutating method)`moveByX`,`moveByX`用来移动点。`moveByX`方法在被调用时修改了这个点,而不是返回一个新的点。方法定义时加上`mutating`关键字,这才让方法可以修改值类型的属性。
|
||||
上面的`Point`结构体定义了一个变异方法(mutating method)`moveByX(_:y:)`用来移动点。`moveByX`方法在被调用时修改了这个点,而不是返回一个新的点。方法定义时加上`mutating`关键字,这才让方法可以修改值类型的属性。
|
||||
|
||||
注意:不能在结构体类型常量上调用变异方法,因为常量的属性不能被改变,即使想改变的是常量的变量属性也不行,详情参见[存储属性和实例变量](10_Properties.html#global_and_local_variables)
|
||||
注意:不能在结构体类型常量上调用变异方法,因为常量的属性不能被改变,即使想改变的是常量的变量属性也不行,详情参见[存储属性和实例变量](./10_Properties.html#global_and_local_variables):
|
||||
|
||||
```swift
|
||||
let fixedPoint = Point(x: 3.0, y: 3.0)
|
||||
fixedPoint.moveByX(2.0, y: 3.0)
|
||||
// this will report an error
|
||||
// 这里将会抛出一个错误
|
||||
```
|
||||
|
||||
<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`一个全新的实例。上面`Point`的例子可以用下面的方式改写:
|
||||
@ -186,7 +183,7 @@ struct Point {
|
||||
}
|
||||
```
|
||||
|
||||
新版的变异方法`moveByX`创建了一个新的结构(它的 x 和 y 的值都被设定为目标值)。调用这个版本的方法和调用上个版本的最终结果是一样的。
|
||||
新版的变异方法`moveByX(_:y:)`创建了一个新的结构(它的 x 和 y 的值都被设定为目标值)。调用这个版本的方法和调用上个版本的最终结果是一样的。
|
||||
|
||||
枚举的变异方法可以把`self`设置为相同的枚举类型中不同的成员:
|
||||
|
||||
@ -214,9 +211,9 @@ ovenLight.next()
|
||||
上面的例子中定义了一个三态开关的枚举。每次调用`next`方法时,开关在不同的电源状态(`Off`,`Low`,`High`)之前循环切换。
|
||||
|
||||
<a name="type_methods"></a>
|
||||
## 类型方法(Type Methods)
|
||||
## 类型方法 (Type Methods)
|
||||
|
||||
实例方法是被类型的某个实例调用的方法。你也可以定义类型本身调用的方法,这种方法就叫做**类型方法**。声明类的类型方法,在方法的`func`关键字之前加上关键字`class`;声明结构体和枚举的类型方法,在方法的`func`关键字之前加上关键字`static`。
|
||||
实例方法是被类型的某个实例调用的方法。你也可以定义类型本身调用的方法,这种方法就叫做**类型方法**。声明结构体和枚举的类型方法,在方法的`func`关键字之前加上关键字`static`。类可能会用关键字`class`来允许子类重写父类的实现方法。
|
||||
|
||||
> 注意:
|
||||
> 在 Objective-C 里面,你只能为 Objective-C 的类定义类型方法(type-level methods)。在 Swift 中,你可以为所有的类、结构体和枚举定义类型方法:每一个类型方法都被它所支持的类型显式包含。
|
||||
@ -225,7 +222,7 @@ ovenLight.next()
|
||||
|
||||
```swift
|
||||
class SomeClass {
|
||||
class func someTypeMethod() {
|
||||
static func someTypeMethod() {
|
||||
// type method implementation goes here
|
||||
}
|
||||
}
|
||||
@ -242,22 +239,22 @@ SomeClass.someTypeMethod()
|
||||
|
||||
```swift
|
||||
struct LevelTracker {
|
||||
static var highestUnlockedLevel = 1
|
||||
static func unlockLevel(level: Int) {
|
||||
if level > highestUnlockedLevel { highestUnlockedLevel = level }
|
||||
}
|
||||
static func levelIsUnlocked(level: Int) -> Bool {
|
||||
return level <= highestUnlockedLevel
|
||||
}
|
||||
var currentLevel = 1
|
||||
mutating func advanceToLevel(level: Int) -> Bool {
|
||||
if LevelTracker.levelIsUnlocked(level) {
|
||||
currentLevel = level
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
static var highestUnlockedLevel = 1
|
||||
static func unlockLevel(level: Int) {
|
||||
if level > highestUnlockedLevel { highestUnlockedLevel = level }
|
||||
}
|
||||
static func levelIsUnlocked(level: Int) -> Bool {
|
||||
return level <= highestUnlockedLevel
|
||||
}
|
||||
var currentLevel = 1
|
||||
mutating func advanceToLevel(level: Int) -> Bool {
|
||||
if LevelTracker.levelIsUnlocked(level) {
|
||||
currentLevel = level
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -285,25 +282,25 @@ class Player {
|
||||
}
|
||||
```
|
||||
|
||||
`Player`类创建一个新的`LevelTracker`实例来监测这个用户的发展进度。它提供了`completedLevel`方法:一旦玩家完成某个指定等级就调用它。这个方法为所有玩家解锁下一等级,并且将当前玩家的进度更新为下一等级。(我们忽略了`advanceToLevel`返回的布尔值,因为之前调用`LevelTracker.unlockLevel`时就知道了这个等级已经被解锁了)。
|
||||
`Player`类创建一个新的`LevelTracker`实例来监测这个用户的进度。它提供了`completedLevel`方法:一旦玩家完成某个指定等级就调用它。这个方法为所有玩家解锁下一等级,并且将当前玩家的进度更新为下一等级。(我们忽略了`advanceToLevel`返回的布尔值,因为之前调用`LevelTracker.unlockLevel`时就知道了这个等级已经被解锁了)。
|
||||
|
||||
你还可以为一个新的玩家创建一个`Player`的实例,然后看这个玩家完成等级一时发生了什么:
|
||||
|
||||
```swift
|
||||
var player = Player(name: "Argyrios")
|
||||
player.completedLevel(1)
|
||||
println("highest unlocked level is now \(LevelTracker.highestUnlockedLevel)")
|
||||
// 输出 "highest unlocked level is now 2"(最高等级现在是2)
|
||||
print("highest unlocked level is now \(LevelTracker.highestUnlockedLevel)")
|
||||
// 打印输出:highest unlocked level is now 2
|
||||
```
|
||||
|
||||
如果你创建了第二个玩家,并尝试让它开始一个没有被任何玩家解锁的等级,那么这次设置玩家当前等级的尝试将会失败:
|
||||
如果你创建了第二个玩家,并尝试让他开始一个没有被任何玩家解锁的等级,那么这次设置玩家当前等级的尝试将会失败:
|
||||
|
||||
```swift
|
||||
player = Player(name: "Beto")
|
||||
if player.tracker.advanceToLevel(6) {
|
||||
println("player is now on level 6")
|
||||
print("player is now on level 6")
|
||||
} else {
|
||||
println("level 6 has not yet been unlocked")
|
||||
print("level 6 has not yet been unlocked")
|
||||
}
|
||||
// 输出 "level 6 has not yet been unlocked"(等级6还没被解锁)
|
||||
// 打印输出:level 6 has not yet been unlocked
|
||||
```
|
||||
|
||||
@ -1,19 +1,22 @@
|
||||
> 翻译:[siemenliu](https://github.com/siemenliu)
|
||||
> 校对:[zq54zquan](https://github.com/zq54zquan)
|
||||
|
||||
|
||||
# 下标脚本(Subscripts)
|
||||
-----------------
|
||||
|
||||
> 1.0
|
||||
> 翻译:[siemenliu](https://github.com/siemenliu)
|
||||
> 校对:[zq54zquan](https://github.com/zq54zquan)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[shanksyang](https://github.com/shanksyang)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [下标脚本语法](#subscript_syntax)
|
||||
- [下标脚本用法](#subscript_usage)
|
||||
- [下标脚本选项](#subscript_options)
|
||||
|
||||
*下标脚本* 可以定义在类(Class)、结构体(structure)和枚举(enumeration)这些目标中,可以认为是访问对象、集合或序列的快捷方式,不需要再调用实例的特定的赋值和访问方法。举例来说,用下标脚本访问一个数组(Array)实例中的元素可以这样写 `someArray[index]` ,访问字典(Dictionary)实例中的元素可以这样写 `someDictionary[key]`。
|
||||
*下标脚本* 可以定义在类(Class)、结构体(structure)和枚举(enumeration)这些目标中,可以认为是访问集合(collection),列表(list)或序列(sequence的快捷方式,使用下标脚本的索引设置和获取值,不需要再调用实例的特定的赋值和访问方法。举例来说,用下标脚本访问一个数组(Array)实例中的元素可以这样写 `someArray[index]` ,访问字典(Dictionary)实例中的元素可以这样写 `someDictionary[key]`。
|
||||
|
||||
对于同一个目标可以定义多个下标脚本,通过索引值类型的不同来进行重载,而且索引值的个数可以是多个。
|
||||
对于同一个目标可以定义多个下标脚本,通过索引值类型的不同来进行重载,下标脚本不限于单个纬度,你可以定义多个入参的下标脚本满足自定义类型的需求。
|
||||
|
||||
> 译者:这里附属脚本重载在本小节中原文并没有任何演示
|
||||
|
||||
@ -54,7 +57,7 @@ struct TimesTable {
|
||||
}
|
||||
}
|
||||
let threeTimesTable = TimesTable(multiplier: 3)
|
||||
println("3的6倍是\(threeTimesTable[6])")
|
||||
print("3的6倍是\(threeTimesTable[6])")
|
||||
// 输出 "3的6倍是18"
|
||||
```
|
||||
|
||||
@ -63,7 +66,7 @@ println("3的6倍是\(threeTimesTable[6])")
|
||||
你可以通过下标脚本来得到结果,比如`threeTimesTable[6]`。这条语句访问了`threeTimesTable`的第六个元素,返回`6`的`3`倍即`18`。
|
||||
|
||||
>注意:
|
||||
> `TimesTable`例子是基于一个固定的数学公式。它并不适合开放写权限来对`threeTimesTable[someIndex]`进行赋值操作,这也是为什么附属脚本只定义为只读的原因。
|
||||
> `TimesTable`例子是基于一个固定的数学公式。它并不适合对`threeTimesTable[someIndex]`进行赋值操作,这也是为什么附属脚本只定义为只读的原因。
|
||||
|
||||
<a name="subscript_usage"></a>
|
||||
## 下标脚本用法
|
||||
@ -77,9 +80,9 @@ var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
|
||||
numberOfLegs["bird"] = 2
|
||||
```
|
||||
|
||||
上例定义一个名为`numberOfLegs`的变量并用一个字典字面量初始化出了包含三对键值的字典实例。`numberOfLegs`的字典存放值类型推断为`Dictionary<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`即可。
|
||||
@ -89,7 +92,7 @@ numberOfLegs["bird"] = 2
|
||||
|
||||
下标脚本允许任意数量的入参索引,并且每个入参类型也没有限制。下标脚本的返回值也可以是任何类型。下标脚本可以使用变量参数和可变参数,但使用写入读出(in-out)参数或给参数设置默认值都是不允许的。
|
||||
|
||||
一个类或结构体可以根据自身需要提供多个下标脚本实现,在定义下标脚本时通过入参个类型进行区分,使用下标脚本时会自动匹配合适的下标脚本实现运行,这就是*下标脚本的重载*。
|
||||
一个类或结构体可以根据自身需要提供多个下标脚本实现,在定义下标脚本时通过入参的类型进行区分,使用下标脚本时会自动匹配合适的下标脚本实现运行,这就是*下标脚本的重载*。
|
||||
|
||||
一个下标脚本入参是最常见的情况,但只要有合适的场景也可以定义多个下标脚本入参。如下例定义了一个`Matrix`结构体,将呈现一个`Double`类型的二维矩阵。`Matrix`结构体的下标脚本需要两个整型参数:
|
||||
|
||||
@ -118,7 +121,7 @@ struct Matrix {
|
||||
}
|
||||
```
|
||||
|
||||
`Matrix`提供了一个两个入参的构造方法,入参分别是`rows`和`columns`,创建了一个足够容纳`rows * columns`个数的`Double`类型数组。为了存储,将数组的大小和数组每个元素初始值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`实例:
|
||||
|
||||
@ -151,7 +154,7 @@ matrix[1, 0] = 3.2
|
||||
3.2, 0.0]
|
||||
```
|
||||
|
||||
`Matrix`下标脚本的`getter`和`setter`中同时调用了下标脚本入参的`row`和`column`是否有效的判断。为了方便进行断言,`Matrix`包含了一个名为`indexIsValid`的成员方法,用来确认入参的`row`或`column`值是否会造成数组越界:
|
||||
`Matrix`下标脚本的`getter`和`setter`中同时调用了下标脚本入参的`row`和`column`是否有效的判断。为了方便进行断言,`Matrix`包含了一个名为`indexIsValidForRow(_:column:)`的成员方法,用来确认入参的`row`或`column`值是否会造成数组越界:
|
||||
|
||||
```swift
|
||||
func indexIsValidForRow(row: Int, column: Int) -> Bool {
|
||||
|
||||
@ -1,8 +1,12 @@
|
||||
> 翻译:[Hawstein](https://github.com/Hawstein)
|
||||
# 继承(Inheritance)
|
||||
-------------------
|
||||
|
||||
> 1.0
|
||||
> 翻译:[Hawstein](https://github.com/Hawstein)
|
||||
> 校对:[menlongsheng](https://github.com/menlongsheng)
|
||||
|
||||
# 继承(Inheritance)
|
||||
-------------------
|
||||
> 2.0
|
||||
> 翻译+校对:[shanksyang](https://github.com/shanksyang)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
@ -11,11 +15,11 @@
|
||||
- [重写(Overriding)](#overriding)
|
||||
- [防止重写](#preventing_overrides)
|
||||
|
||||
一个类可以*继承(inherit)*另一个类的方法(methods),属性(property)和其它特性。当一个类继承其它类时,继承类叫*子类(subclass)*,被继承类叫*超类(或父类,superclass)*。在 Swift 中,继承是区分「类」与其它类型的一个基本特征。
|
||||
一个类可以*继承(inherit)*另一个类的方法(methods),属性(properties)和其它特性。当一个类继承其它类时,继承类叫*子类(subclass)*,被继承类叫*超类(或父类,superclass)*。在 Swift 中,继承是区分「类」与其它类型的一个基本特征。
|
||||
|
||||
在 Swift 中,类可以调用和访问超类的方法,属性和下标脚本(subscripts),并且可以重写(override)这些方法,属性和下标脚本来优化或修改它们的行为。Swift 会检查你的重写定义在超类中是否有匹配的定义,以此确保你的重写行为是正确的。
|
||||
|
||||
可以为类中继承来的属性添加属性观察器(property observer),这样一来,当属性值改变时,类就会被通知到。可以为任何属性添加属性观察器,无论它原本被定义为存储型属性(stored property)还是计算型属性(computed property)。
|
||||
可以为类中继承来的属性添加属性观察器(property observers),这样一来,当属性值改变时,类就会被通知到。可以为任何属性添加属性观察器,无论它原本被定义为存储型属性(stored property)还是计算型属性(computed property)。
|
||||
|
||||
<a name="defining_a_base_class"></a>
|
||||
## 定义一个基类(Base class)
|
||||
@ -25,34 +29,36 @@
|
||||
> 注意:
|
||||
Swift 中的类并不是从一个通用的基类继承而来。如果你不为你定义的类指定一个超类的话,这个类就自动成为基类。
|
||||
|
||||
下面的例子定义了一个叫`Vehicle`的基类。这个基类声明了一个名为`currentSpeed `,默认值是0.0的存储属性(属性类型推断为`Double `)。`currentSpeed `属性的值被一个`String` 类型的只读计算型属性`description`使用,用来创建车辆的描述。
|
||||
|
||||
`Vehicle`基类也定义了一个名为`makeNoise`的方法。这个方法实际上不为`Vehicle`实例做任何事,但之后将会被`Vehicle`的子类定制
|
||||
下面的例子定义了一个叫`Vehicle`的基类。这个基类声明了一个名为`currentSpeed `,默认值是0.0的存储属性(属性类型推断为`Double `)。`currentSpeed `属性的值被一个`String` 类型的只读计算型属性`description`使用,用来创建车辆的描述。
|
||||
|
||||
`Vehicle`基类也定义了一个名为`makeNoise`的方法。这个方法实际上不为`Vehicle`实例做任何事,但之后将会被`Vehicle`的子类定制:
|
||||
|
||||
```swift
|
||||
class Vehicle {
|
||||
var currentSpeed = 0.0
|
||||
var description: String {
|
||||
return "traveling at \(currentSpeed) miles per hour"
|
||||
}
|
||||
func makeNoise() {
|
||||
// 什么也不做-因为车辆不一定会有噪音
|
||||
}
|
||||
class Vehicle {
|
||||
var currentSpeed = 0.0
|
||||
var description: String {
|
||||
return "traveling at \(currentSpeed) miles per hour"
|
||||
}
|
||||
func makeNoise() {
|
||||
// 什么也不做-因为车辆不一定会有噪音
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
您可以用初始化语法创建一个`Vehicle `的新实例,即 `TypeName`后面跟一个空括号:
|
||||
```swift
|
||||
let someVehicle = Vehicle()
|
||||
```
|
||||
|
||||
现在已经创建了一个`Vehicle`的新实例,你可以访问它的`description`属性来打印车辆的当前速度。
|
||||
|
||||
```swift
|
||||
println("Vehicle: \(someVehicle.description)")
|
||||
// Vehicle: traveling at 0.0 miles per hour
|
||||
|
||||
您可以用初始化语法创建一个`Vehicle `的新实例,即类名后面跟一个空括号:
|
||||
```swift
|
||||
let someVehicle = Vehicle()
|
||||
```
|
||||
|
||||
现在已经创建了一个`Vehicle`的新实例,你可以访问它的`description`属性来打印车辆的当前速度。
|
||||
|
||||
```swift
|
||||
print("Vehicle: \(someVehicle.description)")
|
||||
// Vehicle: traveling at 0.0 miles per hour
|
||||
```
|
||||
|
||||
`Vehicle`类定义了一个通用特性的车辆类,实际上没什么用处。为了让它变得更加有用,需要改进它能够描述一个更加具体的车辆类。
|
||||
|
||||
<a name="subclassing"></a>
|
||||
## 子类生成(Subclassing)
|
||||
|
||||
@ -66,55 +72,51 @@ class SomeClass: SomeSuperclass {
|
||||
}
|
||||
```
|
||||
|
||||
下一个例子,定义一个更具体的车辆类叫`Bicycle`。这个新类是在 `Vehicle`类的基础上创建起来。因此你需要将`Vehicle`类放在 `Bicycle`类后面,用冒号分隔。
|
||||
|
||||
我们可以将这读作:
|
||||
|
||||
“定义一个新的类叫`Bicycle `,它继承了`Vehicle`的特性”;
|
||||
下一个例子,定义一个叫`Bicycle`的子类,继承成父类`Vehicle`
|
||||
|
||||
```swift
|
||||
class Bicycle: Vehicle {
|
||||
var hasBasket = false
|
||||
class Bicycle: Vehicle {
|
||||
var hasBasket = false
|
||||
}
|
||||
```
|
||||
|
||||
新的`Bicycle`类自动获得`Vehicle`类的所有特性,比如 `currentSpeed `和`description`属性,还有它的`makeNoise`方法。
|
||||
|
||||
除了它所继承的特性,`Bicycle`类还定义了一个默认值为`false`的存储型属性`hasBasket`(属性推断为`Bool`)。
|
||||
|
||||
默认情况下,你创建任何新的`Bicycle`实例将不会有一个篮子,创建该实例之后,你可以为特定的`Bicycle`实例设置`hasBasket `属性为`ture`:
|
||||
|
||||
```swift
|
||||
let bicycle = Bicycle()
|
||||
bicycle.hasBasket = true
|
||||
```
|
||||
|
||||
你还可以修改`Bicycle `实例所继承的`currentSpeed `属性,和查询实例所继承的`description `属性:
|
||||
|
||||
```swift
|
||||
bicycle.currentSpeed = 15.0
|
||||
println("Bicycle: \(bicycle.description)")
|
||||
// Bicycle: traveling at 15.0 miles per hour
|
||||
新的`Bicycle`类自动获得`Vehicle`类的所有特性,比如 `currentSpeed `和`description`属性,还有它的`makeNoise`方法。
|
||||
|
||||
除了它所继承的特性,`Bicycle`类还定义了一个默认值为`false`的存储型属性`hasBasket`(属性推断为`Bool`)。
|
||||
|
||||
默认情况下,你创建任何新的`Bicycle`实例将不会有一个篮子,创建该实例之后,你可以为特定的`Bicycle`实例设置`hasBasket `属性为`ture`:
|
||||
|
||||
```swift
|
||||
let bicycle = Bicycle()
|
||||
bicycle.hasBasket = true
|
||||
```
|
||||
|
||||
你还可以修改`Bicycle `实例所继承的`currentSpeed `属性,和查询实例所继承的`description `属性:
|
||||
|
||||
```swift
|
||||
bicycle.currentSpeed = 15.0
|
||||
print("Bicycle: \(bicycle.description)")
|
||||
// Bicycle: traveling at 15.0 miles per hour
|
||||
```
|
||||
|
||||
子类还可以继续被其它类继承,下面的示例为`Bicycle `创建了一个名为`Tandem `(双人自行车)的子类:
|
||||
|
||||
```swift
|
||||
class Tandem: Bicycle {
|
||||
var currentNumberOfPassengers = 0
|
||||
class Tandem: Bicycle {
|
||||
var currentNumberOfPassengers = 0
|
||||
}
|
||||
```
|
||||
|
||||
`Tandem`从`Bicycle`继承了所有的属性与方法,这又使它同时继承了`Vehicle`的所有属性与方法。`Tandem`也增加了一个新的叫做`currentNumberOfPassengers`的存储型属性,默认值为0。
|
||||
|
||||
`Tandem`从`Bicycle`继承了所有的属性与方法,这又使它同时继承了`Vehicle`的所有属性与方法。`Tandem`也增加了一个新的叫做`currentNumberOfPassengers`的存储型属性,默认值为0。
|
||||
|
||||
如果你创建了一个`Tandem`的实例,你可以使用它所有的新属性和继承的属性,还能查询从`Vehicle`继承来的只读属性`description `:
|
||||
|
||||
```swift
|
||||
let tandem = Tandem()
|
||||
tandem.hasBasket = true
|
||||
tandem.currentNumberOfPassengers = 2
|
||||
tandem.currentSpeed = 22.0
|
||||
println("Tandem: \(tandem.description)")
|
||||
let tandem = Tandem()
|
||||
tandem.hasBasket = true
|
||||
tandem.currentNumberOfPassengers = 2
|
||||
tandem.currentSpeed = 22.0
|
||||
print("Tandem: \(tandem.description)")
|
||||
// Tandem: traveling at 22.0 miles per hour
|
||||
```
|
||||
|
||||
@ -144,24 +146,24 @@ println("Tandem: \(tandem.description)")
|
||||
下面的例子定义了`Vehicle`的一个新的子类,叫`Train `,它重写了从`Vehicle`类继承来的`makeNoise `方法:
|
||||
|
||||
```swift
|
||||
class Train: Vehicle {
|
||||
override func makeNoise() {
|
||||
println("Choo Choo")
|
||||
}
|
||||
class Train: Vehicle {
|
||||
override func makeNoise() {
|
||||
print("Choo Choo")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
如果你创建一个`Train `的新实例,并调用了它的`makeNoise `方法,你就会发现`Train `版本的方法被调用:
|
||||
|
||||
```swift
|
||||
let train = Train()
|
||||
train.makeNoise()
|
||||
let train = Train()
|
||||
train.makeNoise()
|
||||
// prints "Choo Choo"
|
||||
```
|
||||
|
||||
### 重写属性
|
||||
|
||||
你可以重写继承来的实例属性或类属性,提供自己定制的getter和setter,或添加属性观察器使重写的属性观察属性值什么时候发生改变。
|
||||
你可以重写继承来的实例属性或类属性,提供自己定制的getter和setter,或添加属性观察器使重写的属性可以观察属性值什么时候发生改变。
|
||||
|
||||
#### 重写属性的Getters和Setters
|
||||
|
||||
@ -175,23 +177,23 @@ train.makeNoise()
|
||||
以下的例子定义了一个新类,叫`Car`,它是`Vehicle `的子类。这个类引入了一个新的存储型属性叫做`gear `,默认为整数1。`Car`类重写了继承自`Vehicle `的description属性,提供自定义的,包含当前档位的描述:
|
||||
|
||||
```swift
|
||||
class Car: Vehicle {
|
||||
var gear = 1
|
||||
override var description: String {
|
||||
return super.description + " in gear \(gear)"
|
||||
}
|
||||
class Car: Vehicle {
|
||||
var gear = 1
|
||||
override var description: String {
|
||||
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`:
|
||||
|
||||
```swift
|
||||
let car = Car()
|
||||
car.currentSpeed = 25.0
|
||||
car.gear = 3
|
||||
println("Car: \(car.description)")
|
||||
let car = Car()
|
||||
car.currentSpeed = 25.0
|
||||
car.gear = 3
|
||||
print("Car: \(car.description)")
|
||||
// Car: traveling at 25.0 miles per hour in gear 3
|
||||
```
|
||||
|
||||
@ -205,21 +207,21 @@ println("Car: \(car.description)")
|
||||
下面的例子定义了一个新类叫`AutomaticCar`,它是`Car`的子类。`AutomaticCar`表示自动挡汽车,它可以根据当前的速度自动选择合适的挡位:
|
||||
|
||||
```swift
|
||||
class AutomaticCar: Car {
|
||||
override var currentSpeed: Double {
|
||||
didSet {
|
||||
gear = Int(currentSpeed / 10.0) + 1
|
||||
}
|
||||
}
|
||||
class AutomaticCar: Car {
|
||||
override var currentSpeed: Double {
|
||||
didSet {
|
||||
gear = Int(currentSpeed / 10.0) + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
当你设置`AutomaticCar`的`currentSpeed `属性,属性的`didSet`观察器就会自动地设置`gear`属性,为新的速度选择一个合适的挡位。具体来说就是,属性观察器将新的速度值除以10,然后向下取得最接近的整数值,最后加1来得到档位`gear`的值。例如,速度为10.0时,挡位为1;速度为35.0时,挡位为4:
|
||||
|
||||
```swift
|
||||
let automatic = AutomaticCar()
|
||||
automatic.currentSpeed = 35.0
|
||||
println("AutomaticCar: \(automatic.description)")
|
||||
let automatic = AutomaticCar()
|
||||
automatic.currentSpeed = 35.0
|
||||
print("AutomaticCar: \(automatic.description)")
|
||||
// AutomaticCar: traveling at 35.0 miles per hour in gear 4
|
||||
```
|
||||
|
||||
@ -228,7 +230,6 @@ println("AutomaticCar: \(automatic.description)")
|
||||
|
||||
你可以通过把方法,属性或下标脚本标记为*`final`*来防止它们被重写,只需要在声明关键字前加上`final`特性即可。(例如:`final var`, `final func`, `final class func`, 以及 `final subscript`)
|
||||
|
||||
如果你重写了`final`方法,属性或下标脚本,在编译时会报错。在扩展中,你添加到类里的方法,属性或下标脚本也可以在扩展的定义里标记为 final。
|
||||
|
||||
你可以通过在关键字`class`前添加`final`特性(`final class`)来将整个类标记为 final 的,这样的类是不可被继承的,否则会报编译错误。
|
||||
如果你重写了`final`方法,属性或下标脚本,在编译时会报错。在类扩展中的方法,属性或下标脚本也可以在扩展的定义里标记为 final。
|
||||
|
||||
你可以通过在关键字`class`前添加`final`特性(`final class`)来将整个类标记为 final 的,这样的类是不可被继承的,任何子类试图继承此类时,在编译时会报错。
|
||||
|
||||
@ -1,108 +1,112 @@
|
||||
> 翻译:[bruce0505](https://github.com/bruce0505)
|
||||
> 校对:[fd5788](https://github.com/fd5788)
|
||||
|
||||
# 析构过程(Deinitialization)
|
||||
---------------------------
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [析构过程原理](#how_deinitialization_works)
|
||||
- [析构函数操作](#deinitializers_in_action)
|
||||
|
||||
在一个类的实例被释放之前,析构函数被立即调用。用关键字`deinit`来标示析构函数,类似于初始化函数用`init`来标示。析构函数只适用于类类型。
|
||||
|
||||
<a name="how_deinitialization_works"></a>
|
||||
##析构过程原理
|
||||
|
||||
Swift 会自动释放不再需要的实例以释放资源。如[自动引用计数](16_Automatic_Reference_Counting.html)那一章描述,Swift 通过_自动引用计数_(ARC)处理实例的内存管理。通常当你的实例被释放时不需要手动地去清理。但是,当使用自己的资源时,你可能需要进行一些额外的清理。例如,如果创建了一个自定义的类来打开一个文件,并写入一些数据,你可能需要在类实例被释放之前关闭该文件。
|
||||
|
||||
在类的定义中,每个类最多只能有一个析构函数。析构函数不带任何参数,在写法上不带括号:
|
||||
|
||||
```swift
|
||||
deinit {
|
||||
// 执行析构过程
|
||||
}
|
||||
```
|
||||
|
||||
析构函数是在实例释放发生前一步被自动调用。不允许主动调用自己的析构函数。子类继承了父类的析构函数,并且在子类析构函数实现的最后,父类的析构函数被自动调用。即使子类没有提供自己的析构函数,父类的析构函数也总是被调用。
|
||||
|
||||
因为直到实例的析构函数被调用时,实例才会被释放,所以析构函数可以访问所有请求实例的属性,并且根据那些属性可以修改它的行为(比如查找一个需要被关闭的文件的名称)。
|
||||
|
||||
<a name="deinitializers_in_action"></a>
|
||||
##析构函数操作
|
||||
|
||||
这里是一个析构函数操作的例子。这个例子是一个简单的游戏,定义了两种新类型,`Bank`和`Player`。`Bank`结构体管理一个虚拟货币的流通,在这个流通中`Bank`永远不可能拥有超过 10,000 的硬币。在这个游戏中有且只能有一个`Bank`存在,因此`Bank`由带有静态属性和静态方法的结构体实现,从而存储和管理其当前的状态。
|
||||
|
||||
```swift
|
||||
struct Bank {
|
||||
static var coinsInBank = 10_000
|
||||
static func vendCoins(var numberOfCoinsToVend: Int) -> Int {
|
||||
numberOfCoinsToVend = min(numberOfCoinsToVend, coinsInBank)
|
||||
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。
|
||||
|
||||
`Player`类描述了游戏中的一个玩家。每一个 player 在任何时刻都有一定数量的硬币存储在他们的钱包中。这通过 player 的`coinsInPurse`属性来体现:
|
||||
|
||||
```swift
|
||||
class Player {
|
||||
var coinsInPurse: Int
|
||||
init(coins: Int) {
|
||||
coinsInPurse = Bank.vendCoins(coins)
|
||||
}
|
||||
func winCoins(coins: Int) {
|
||||
coinsInPurse += Bank.vendCoins(coins)
|
||||
}
|
||||
deinit {
|
||||
Bank.receiveCoins(coinsInPurse)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
每个`Player`实例都由一个指定数目硬币组成的启动额度初始化,这些硬币在 bank 初始化的过程中得到。如果没有足够的硬币可用,`Player`实例可能收到比指定数目少的硬币。
|
||||
|
||||
`Player`类定义了一个`winCoins`方法,该方法从银行获取一定数量的硬币,并把它们添加到玩家的钱包。`Player`类还实现了一个析构函数,这个析构函数在`Player`实例释放前一步被调用。这里析构函数只是将玩家的所有硬币都返回给银行:
|
||||
|
||||
```swift
|
||||
var playerOne: Player? = Player(coins: 100)
|
||||
println("A new player has joined the game with \(playerOne!.coinsInPurse) coins")
|
||||
// 输出 "A new player has joined the game with 100 coins"
|
||||
println("There are now \(Bank.coinsInBank) coins left in the bank")
|
||||
// 输出 "There are now 9900 coins left in the bank"
|
||||
```
|
||||
|
||||
一个新的`Player`实例随着一个 100 个硬币(如果有)的请求而被创建。这`个Player`实例存储在一个名为`playerOne`的可选`Player`变量中。这里使用一个可选变量,是因为玩家可以随时离开游戏。设置为可选使得你可以跟踪当前是否有玩家在游戏中。
|
||||
|
||||
因为`playerOne`是可选的,所以由一个感叹号(`!`)来修饰,每当其`winCoins`方法被调用时,`coinsInPurse`属性被访问并打印出它的默认硬币数目。
|
||||
|
||||
```swift
|
||||
playerOne!.winCoins(2_000)
|
||||
println("PlayerOne won 2000 coins & now has \(playerOne!.coinsInPurse) coins")
|
||||
// 输出 "PlayerOne won 2000 coins & now has 2100 coins"
|
||||
println("The bank now only has \(Bank.coinsInBank) coins left")
|
||||
// 输出 "The bank now only has 7900 coins left"
|
||||
```
|
||||
|
||||
这里,player 已经赢得了 2,000 硬币。player 的钱包现在有 2,100 硬币,bank 只剩余 7,900 硬币。
|
||||
|
||||
```swift
|
||||
playerOne = nil
|
||||
println("PlayerOne has left the game")
|
||||
// 输出 "PlayerOne has left the game"
|
||||
println("The bank now has \(Bank.coinsInBank) coins")
|
||||
// 输出 "The bank now has 10000 coins"
|
||||
```
|
||||
|
||||
玩家现在已经离开了游戏。这表明是要将可选的`playerOne`变量设置为`nil`,意思是“没有`Player`实例”。当这种情况发生的时候,`playerOne`变量对`Player`实例的引用被破坏了。没有其它属性或者变量引用`Player`实例,因此为了清空它占用的内存从而释放它。在这发生前一步,其析构函数被自动调用,其硬币被返回到银行。
|
||||
# 析构过程(Deinitialization)
|
||||
---------------------------
|
||||
|
||||
> 1.0
|
||||
> 翻译:[bruce0505](https://github.com/bruce0505)
|
||||
> 校对:[fd5788](https://github.com/fd5788)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[chenmingbiao](https://github.com/chenmingbiao)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [析构过程原理](#how_deinitialization_works)
|
||||
- [析构器操作](#deinitializers_in_action)
|
||||
|
||||
析构器只适用于类类型,当一个类的实例被释放之前,析构器会被立即调用。析构器用关键字`deinit`来标示,类似于构造器要用`init`来标示。
|
||||
|
||||
<a name="how_deinitialization_works"></a>
|
||||
##析构过程原理
|
||||
|
||||
Swift 会自动释放不再需要的实例以释放资源。如[自动引用计数](./16_Automatic_Reference_Counting.html)章节中所讲述,Swift 通过`自动引用计数(ARC)`处理实例的内存管理。通常当你的实例被释放时不需要手动地去清理。但是,当使用自己的资源时,你可能需要进行一些额外的清理。例如,如果创建了一个自定义的类来打开一个文件,并写入一些数据,你可能需要在类实例被释放之前手动去关闭该文件。
|
||||
|
||||
在类的定义中,每个类最多只能有一个析构器,而且析构器不带任何参数,如下所示:
|
||||
|
||||
```swift
|
||||
deinit {
|
||||
// 执行析构过程
|
||||
}
|
||||
```
|
||||
|
||||
析构器是在实例释放发生前被自动调用。析构器是不允许被主动调用的。子类继承了父类的析构器,并且在子类析构器实现的最后,父类的析构器会被自动调用。即使子类没有提供自己的析构器,父类的析构器也同样会被调用。
|
||||
|
||||
因为直到实例的析构器被调用时,实例才会被释放,所以析构器可以访问所有请求实例的属性,并且根据那些属性可以修改它的行为(比如查找一个需要被关闭的文件)。
|
||||
|
||||
<a name="deinitializers_in_action"></a>
|
||||
##析构器操作
|
||||
|
||||
这是一个析构器操作的例子。这个例子描述了一个简单的游戏,这里定义了两种新类型,分别是`Bank`和`Player`。`Bank`结构体管理一个虚拟货币的流通,在这个流通中我们设定`Bank`永远不可能拥有超过 10,000 的硬币,而且在游戏中有且只能有一个`Bank`存在,因此`Bank`结构体在实现时会带有静态属性和静态方法来存储和管理其当前的状态。
|
||||
|
||||
```swift
|
||||
struct Bank {
|
||||
static var coinsInBank = 10_000
|
||||
static func vendCoins(var numberOfCoinsToVend: Int) -> Int {
|
||||
numberOfCoinsToVend = min(numberOfCoinsToVend, coinsInBank)
|
||||
coinsInBank -= numberOfCoinsToVend
|
||||
return numberOfCoinsToVend
|
||||
}
|
||||
static func receiveCoins(coins: Int) {
|
||||
coinsInBank += coins
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`Bank`根据它的`coinsInBank`属性来跟踪当前它拥有的硬币数量。`Bank`还提供两个方法——`vendCoins(_:)`和`receiveCoins(_:)`,分别用来处理硬币的分发和收集。
|
||||
|
||||
`vendCoins(_:)`方法在bank对象分发硬币之前检查是否有足够的硬币。如果没有足够多的硬币,`Bank`会返回一个比请求时要小的数字(如果没有硬币留在bank对象中就返回 0)。`vendCoins`方法声明`numberOfCoinsToVend`为一个变量参数,这样就可以在方法体的内部修改数字,而不需要定义一个新的变量。`vendCoins`方法返回一个整型值,表明了提供的硬币的实际数目。
|
||||
|
||||
`receiveCoins`方法只是将bank对象的硬币存储和接收到的硬币数目相加,再保存回bank对象。
|
||||
|
||||
`Player`类描述了游戏中的一个玩家。每一个 player 在任何时刻都有一定数量的硬币存储在他们的钱包中。这通过 player 的`coinsInPurse`属性来体现:
|
||||
|
||||
```swift
|
||||
class Player {
|
||||
var coinsInPurse: Int
|
||||
init(coins: Int) {
|
||||
coinsInPurse = Bank.vendCoins(coins)
|
||||
}
|
||||
func winCoins(coins: Int) {
|
||||
coinsInPurse += Bank.vendCoins(coins)
|
||||
}
|
||||
deinit {
|
||||
Bank.receiveCoins(coinsInPurse)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
每个`Player`实例构造时都会设定由硬币组成的启动额度值,这些硬币在bank对象初始化的过程中得到。如果在bank对象中没有足够的硬币可用,`Player`实例可能收到比指定数目少的硬币。
|
||||
|
||||
`Player`类定义了一个`winCoins(_:)`方法,该方法从bank对象获取一定数量的硬币,并把它们添加到玩家的钱包。`Player`类还实现了一个析构器,这个析构器在`Player`实例释放前被调用。在这里,析构器的作用只是将玩家的所有硬币都返回给bank对象:
|
||||
|
||||
```swift
|
||||
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"
|
||||
print("There are now \(Bank.coinsInBank) coins left in the bank")
|
||||
// 输出 "There are now 9900 coins left in the bank"
|
||||
```
|
||||
|
||||
一个新的`Player`实例被创建时会设定有 100 个硬币(如果bank对象中硬币的数目足够)。这`个Player`实例存储在一个名为`playerOne`的可选`Player`变量中。这里使用一个可选变量,是因为玩家可以随时离开游戏。设置为可选使得你可以跟踪当前是否有玩家在游戏中。
|
||||
|
||||
因为`playerOne`是可选的,所以用一个感叹号(`!`)作为修饰符,每当其`winCoins(_:)`方法被调用时,`coinsInPurse`属性就会被访问并打印出它的默认硬币数目。
|
||||
|
||||
```swift
|
||||
playerOne!.winCoins(2_000)
|
||||
print("PlayerOne won 2000 coins & now has \(playerOne!.coinsInPurse) coins")
|
||||
// 输出 "PlayerOne won 2000 coins & now has 2100 coins"
|
||||
print("The bank now only has \(Bank.coinsInBank) coins left")
|
||||
// 输出 "The bank now only has 7900 coins left"
|
||||
```
|
||||
|
||||
这里,player 已经赢得了 2,000 硬币,所以player 的钱包现在有 2,100 硬币,而bank对象只剩余 7,900 硬币。
|
||||
|
||||
```swift
|
||||
playerOne = nil
|
||||
print("PlayerOne has left the game")
|
||||
// 输出 "PlayerOne has left the game"
|
||||
print("The bank now has \(Bank.coinsInBank) coins")
|
||||
// 输出 "The bank now has 10000 coins"
|
||||
```
|
||||
|
||||
玩家现在已经离开了游戏。这表明是要将可选的`playerOne`变量设置为`nil`,意思是“不存在`Player`实例”。当这种情况发生的时候,`playerOne`变量对`Player`实例的引用被破坏了。没有其它属性或者变量引用`Player`实例,因此为了清空它占用的内存从而释放它。在这发生前,其析构器会被自动调用,从而使其硬币被返回到bank对象中。
|
||||
|
||||
@ -1,8 +1,12 @@
|
||||
> 翻译:[TimothyYe](https://github.com/TimothyYe)
|
||||
# 自动引用计数(Automatic Reference Counting)
|
||||
-----------------
|
||||
|
||||
> 1.0
|
||||
> 翻译:[TimothyYe](https://github.com/TimothyYe)
|
||||
> 校对:[Hawstein](https://github.com/Hawstein)
|
||||
|
||||
# 自动引用计数
|
||||
-----------------
|
||||
> 2.0
|
||||
> 翻译+校对:[Channe](https://github.com/Channe)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
@ -13,7 +17,7 @@
|
||||
- [闭包引起的循环强引用](#strong_reference_cycles_for_closures)
|
||||
- [解决闭包引起的循环强引用](#resolving_strong_reference_cycles_for_closures)
|
||||
|
||||
Swift 使用自动引用计数(ARC)这一机制来跟踪和管理你的应用程序的内存。通常情况下,Swift 的内存管理机制会一直起着作用,你无须自己来考虑内存的管理。ARC 会在类的实例不再被使用时,自动释放其占用的内存。
|
||||
Swift 使用自动引用计数(ARC)机制来跟踪和管理你的应用程序的内存。通常情况下,Swift 的内存管理机制会一直起着作用,你无须自己来考虑内存的管理。ARC 会在类的实例不再被使用时,自动释放其占用的内存。
|
||||
|
||||
然而,在少数情况下,ARC 为了能帮助你管理内存,需要更多的关于你的代码之间关系的信息。本章描述了这些情况,并且为你示范怎样启用 ARC 来管理你的应用程序的内存。
|
||||
|
||||
@ -23,13 +27,15 @@ Swift 使用自动引用计数(ARC)这一机制来跟踪和管理你的应
|
||||
<a name="how_arc_works"></a>
|
||||
## 自动引用计数的工作机制
|
||||
|
||||
当你每次创建一个类的新的实例的时候,ARC 会分配一大块内存用来储存实例的信息。内存中会包含实例的类型信息,以及这个实例所有相关属性的值。此外,当实例不再被使用时,ARC 释放实例所占用的内存,并让释放的内存能挪作他用。这确保了不再被使用的实例,不会一直占用内存空间。
|
||||
当你每次创建一个类的新的实例的时候,ARC 会分配一大块内存用来储存实例的信息。内存中会包含实例的类型信息,以及这个实例所有相关属性的值。
|
||||
|
||||
此外,当实例不再被使用时,ARC 释放实例所占用的内存,并让释放的内存能挪作他用。这确保了不再被使用的实例,不会一直占用内存空间。
|
||||
|
||||
然而,当 ARC 收回和释放了正在被使用中的实例,该实例的属性和方法将不能再被访问和调用。实际上,如果你试图访问这个实例,你的应用程序很可能会崩溃。
|
||||
|
||||
为了确保使用中的实例不会被销毁,ARC 会跟踪和计算每一个实例正在被多少属性,常量和变量所引用。哪怕实例的引用数为一,ARC都不会销毁这个实例。
|
||||
为了确保使用中的实例不会被销毁,ARC 会跟踪和计算每一个实例正在被多少属性,常量和变量所引用。哪怕实例的引用数为1,ARC都不会销毁这个实例。
|
||||
|
||||
为了使之成为可能,无论你将实例赋值给属性,常量或者是变量,属性,常量或者变量,都会对此实例创建强引用。之所以称之为强引用,是因为它会将实例牢牢的保持住,只要强引用还在,实例是不允许被销毁的。
|
||||
为了使上述成为可能,无论你将实例赋值给属性、常量或变量,它们都会创建此实例的强引用。之所以称之为“强”引用,是因为它会将实例牢牢的保持住,只要强引用还在,实例是不允许被销毁的。
|
||||
|
||||
<a name="arc_in_action"></a>
|
||||
## 自动引用计数实践
|
||||
@ -41,17 +47,17 @@ class Person {
|
||||
let name: String
|
||||
init(name: String) {
|
||||
self.name = name
|
||||
println("\(name) is being initialized")
|
||||
print("\(name) is being initialized")
|
||||
}
|
||||
deinit {
|
||||
println("\(name) is being deinitialized")
|
||||
print("\(name) is being deinitialized")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`Person`类有一个构造函数,此构造函数为实例的`name`属性赋值并打印出信息,以表明初始化过程生效。`Person`类同时也拥有析构函数,同样会在实例被销毁的时候打印出信息。
|
||||
`Person`类有一个构造函数,此构造函数为实例的`name`属性赋值,并打印一条消息以表明初始化过程生效。`Person`类也拥有一个析构函数,这个析构函数会在实例被销毁时打印一条消息。
|
||||
|
||||
接下来的代码片段定义了三个类型为`Person?`的变量,用来按照代码片段中的顺序,为新的`Person`实例建立多个引用。由于这些变量是被定义为可选类型(Person?,而不是Person),它们的值会被自动初始化为`nil`,目前还不会引用到`Person`类的实例。
|
||||
接下来的代码片段定义了三个类型为`Person?`的变量,用来按照代码片段中的顺序,为新的`Person`实例建立多个引用。由于这些变量是被定义为可选类型(`Person?`,而不是`Person`),它们的值会被自动初始化为`nil`,目前还不会引用到`Person`类的实例。
|
||||
|
||||
```swift
|
||||
var reference1: Person?
|
||||
@ -59,7 +65,7 @@ var reference2: Person?
|
||||
var reference3: Person?
|
||||
```
|
||||
|
||||
现在你可以创建`Person`类的新实例,并且将它赋值给三个变量其中的一个:
|
||||
现在你可以创建`Person`类的新实例,并且将它赋值给三个变量中的一个:
|
||||
|
||||
```swift
|
||||
reference1 = Person(name: "John Appleseed")
|
||||
@ -68,25 +74,25 @@ reference1 = Person(name: "John Appleseed")
|
||||
|
||||
应当注意到当你调用`Person`类的构造函数的时候,"John Appleseed is being initialized”会被打印出来。由此可以确定构造函数被执行。
|
||||
|
||||
由于`Person`类的新实例被赋值给了`reference1`变量,所以`reference1`到`Person`类的新实例之间建立了一个强引用。正是因为这个强引用,ARC 会保证`Person`实例被保持在内存中不被销毁。
|
||||
由于`Person`类的新实例被赋值给了`reference1`变量,所以`reference1`到`Person`类的新实例之间建立了一个强引用。正是因为这一个强引用,ARC 会保证`Person`实例被保持在内存中不被销毁。
|
||||
|
||||
如果你将同样的`Person`实例也赋值给其他两个变量,该实例又会多出两个强引用:
|
||||
如果你将同一个`Person`实例也赋值给其他两个变量,该实例又会多出两个强引用:
|
||||
|
||||
```swift
|
||||
reference2 = reference1
|
||||
reference3 = reference1
|
||||
```
|
||||
|
||||
现在这个`Person`实例已经有三个强引用了。
|
||||
现在这一个`Person`实例已经有三个强引用了。
|
||||
|
||||
如果你通过给两个变量赋值`nil`的方式断开两个强引用()包括最先的那个强引用),只留下一个强引用,`Person`实例不会被销毁:
|
||||
如果你通过给其中两个变量赋值`nil`的方式断开两个强引用(包括最先的那个强引用),只留下一个强引用,`Person`实例不会被销毁:
|
||||
|
||||
```swift
|
||||
reference1 = nil
|
||||
reference2 = nil
|
||||
```
|
||||
|
||||
ARC 会在第三个,也即最后一个强引用被断开的时候,销毁`Person`实例,这也意味着你不再使用这个`Person`实例:
|
||||
在你清楚地表明不再使用这个`Person`实例时,即第三个也就是最后一个强引用被断开时,ARC 会销毁它。
|
||||
|
||||
```swift
|
||||
reference3 = nil
|
||||
@ -98,9 +104,9 @@ reference3 = nil
|
||||
|
||||
在上面的例子中,ARC 会跟踪你所新创建的`Person`实例的引用数量,并且会在`Person`实例不再被需要时销毁它。
|
||||
|
||||
然而,我们可能会写出这样的代码,一个类永远不会有0个强引用。这种情况发生在两个类实例互相保持对方的强引用,并让对方不被销毁。这就是所谓的循环强引用。
|
||||
然而,我们可能会写出一个类实例的强引用数永远不能变成0的代码。如果两个类实例互相持有对方的强引用,因而每个实例都让对方一直存在,就是这种情况。这就是所谓的循环强引用。
|
||||
|
||||
你可以通过定义类之间的关系为弱引用或者无主引用,以此替代强引用,从而解决循环强引用的问题。具体的过程在[解决类实例之间的循环强引用](#resolving_strong_reference_cycles_between_class_instances)中有描述。不管怎样,在你学习怎样解决循环强引用之前,很有必要了解一下它是怎样产生的。
|
||||
你可以通过定义类之间的关系为弱引用或无主引用,以替代强引用,从而解决循环强引用的问题。具体的过程在[解决类实例之间的循环强引用](#resolving_strong_reference_cycles_between_class_instances)中有描述。不管怎样,在你学习怎样解决循环强引用之前,很有必要了解一下它是怎样产生的。
|
||||
|
||||
下面展示了一个不经意产生循环强引用的例子。例子定义了两个类:`Person`和`Apartment`,用来建模公寓和它其中的居民:
|
||||
|
||||
@ -109,16 +115,16 @@ class Person {
|
||||
let name: String
|
||||
init(name: String) { self.name = name }
|
||||
var apartment: Apartment?
|
||||
deinit { println("\(name) is being deinitialized") }
|
||||
deinit { print("\(name) is being deinitialized") }
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
class Apartment {
|
||||
let number: Int
|
||||
init(number: Int) { self.number = number }
|
||||
let unit: String
|
||||
init(unit: String) { self.unit = unit }
|
||||
var tenant: Person?
|
||||
deinit { println("Apartment #\(number) is being deinitialized") }
|
||||
deinit { print("Apartment \(unit) is being deinitialized") }
|
||||
}
|
||||
```
|
||||
|
||||
@ -128,45 +134,45 @@ class Apartment {
|
||||
|
||||
这两个类都定义了析构函数,用以在类实例被析构的时候输出信息。这让你能够知晓`Person`和`Apartment`的实例是否像预期的那样被销毁。
|
||||
|
||||
接下来的代码片段定义了两个可选类型的变量`john`和`number73`,并分别被设定为下面的`Apartment`和`Person`的实例。这两个变量都被初始化为`nil`,并为可选的:
|
||||
接下来的代码片段定义了两个可选类型的变量`john`和`unit4A`,并分别被设定为下面的`Apartment`和`Person`的实例。这两个变量都被初始化为`nil`,这正是可选的优点:
|
||||
|
||||
```swift
|
||||
var john: Person?
|
||||
var number73: Apartment?
|
||||
var unit4A: Apartment?
|
||||
```
|
||||
|
||||
现在你可以创建特定的`Person`和`Apartment`实例并将类实例赋值给`john`和`number73`变量:
|
||||
现在你可以创建特定的`Person`和`Apartment`实例并将赋值给`john`和`unit4A`变量:
|
||||
|
||||
```swift
|
||||
john = Person(name: "John Appleseed")
|
||||
number73 = Apartment(number: 73)
|
||||
unit4A = Apartment(unit: "4A")
|
||||
```
|
||||
|
||||
在两个实例被创建和赋值后,下图表现了强引用的关系。变量`john`现在有一个指向`Person`实例的强引用,而变量`number73`有一个指向`Apartment`实例的强引用:
|
||||
在两个实例被创建和赋值后,下图表现了强引用的关系。变量`john`现在有一个指向`Person`实例的强引用,而变量`unit4A`有一个指向`Apartment`实例的强引用:
|
||||
|
||||

|
||||
|
||||
现在你能够将这两个实例关联在一起,这样人就能有公寓住了,而公寓也有了房客。注意感叹号是用来展开和访问可选变量`john`和`number73`中的实例,这样实例的属性才能被赋值:
|
||||
现在你能够将这两个实例关联在一起,这样人就能有公寓住了,而公寓也有了房客。注意感叹号是用来展开和访问可选变量`john`和`unit4A`中的实例,这样实例的属性才能被赋值:
|
||||
|
||||
```swift
|
||||
john!.apartment = number73
|
||||
number73!.tenant = john
|
||||
john!.apartment = unit4A
|
||||
unit4A!.tenant = john
|
||||
```
|
||||
|
||||
在将两个实例联系在一起之后,强引用的关系如图所示:
|
||||
|
||||

|
||||
|
||||
不幸的是,将这两个实例关联在一起之后,一个循环强引用被创建了。`Person`实例现在有了一个指向`Apartment`实例的强引用,而`Apartment`实例也有了一个指向`Person`实例的强引用。因此,当你断开`john`和`number73`变量所持有的强引用时,引用计数并不会降为 0,实例也不会被 ARC 销毁:
|
||||
不幸的是,这两个实例关联后会产生一个循环强引用。`Person`实例现在有了一个指向`Apartment`实例的强引用,而`Apartment`实例也有了一个指向`Person`实例的强引用。因此,当你断开`john`和`unit4A`变量所持有的强引用时,引用计数并不会降为 0,实例也不会被 ARC 销毁:
|
||||
|
||||
```swift
|
||||
john = nil
|
||||
number73 = nil
|
||||
unit4A = nil
|
||||
```
|
||||
|
||||
注意,当你把这两个变量设为`nil`时,没有任何一个析构函数被调用。强引用循环阻止了`Person`和`Apartment`类实例的销毁,并在你的应用程序中造成了内存泄漏。
|
||||
注意,当你把这两个变量设为`nil`时,没有任何一个析构函数被调用。循环强引用会一直阻止`Person`和`Apartment`类实例的销毁,这就在你的应用程序中造成了内存泄漏。
|
||||
|
||||
在你将`john`和`number73`赋值为`nil`后,强引用关系如下图:
|
||||
在你将`john`和`unit4A`赋值为`nil`后,强引用关系如下图:
|
||||
|
||||

|
||||
|
||||
@ -179,20 +185,20 @@ Swift 提供了两种办法用来解决你在使用类的属性时所遇到的
|
||||
|
||||
弱引用和无主引用允许循环引用中的一个实例引用另外一个实例而不保持强引用。这样实例能够互相引用而不产生循环强引用。
|
||||
|
||||
对于生命周期中会变为`nil`的实例使用弱引用。相反的,对于初始化赋值后再也不会被赋值为`nil`的实例,使用无主引用。
|
||||
对于生命周期中会变为`nil`的实例使用弱引用。相反地,对于初始化赋值后再也不会被赋值为`nil`的实例,使用无主引用。
|
||||
|
||||
### 弱引用
|
||||
|
||||
弱引用不会牢牢保持住引用的实例,并且不会阻止 ARC 销毁被引用的实例。这种行为阻止了引用变为循环强引用。声明属性或者变量时,在前面加上`weak`关键字表明这是一个弱引用。
|
||||
弱引用不会对其引用的实例保持强引用,因而不会阻止 ARC 销毁被引用的实例。这个特性阻止了引用变为循环强引用。声明属性或者变量时,在前面加上`weak`关键字表明这是一个弱引用。
|
||||
|
||||
在实例的生命周期中,如果某些时候引用没有值,那么弱引用可以阻止循环强引用。如果引用总是有值,则可以使用无主引用,在[无主引用](#2)中有描述。在上面`Apartment`的例子中,一个公寓的生命周期中,有时是没有“居民”的,因此适合使用弱引用来解决循环强引用。
|
||||
在实例的生命周期中,如果某些时候引用没有值,那么弱引用可以避免循环强引用。如果引用总是有值,则可以使用无主引用,在[无主引用](#2)中有描述。在上面`Apartment`的例子中,一个公寓的生命周期中,有时是没有“居民”的,因此适合使用弱引用来解决循环强引用。
|
||||
|
||||
> 注意:
|
||||
> 弱引用必须被声明为变量,表明其值能在运行时被修改。弱引用不能被声明为常量。
|
||||
|
||||
因为弱引用可以没有值,你必须将每一个弱引用声明为可选类型。可选类型是在 Swift 语言中推荐的用来表示可能没有值的类型。
|
||||
因为弱引用可以没有值,你必须将每一个弱引用声明为可选类型。在 Swift 中,推荐使用可选类型描述可能没有值的类型。
|
||||
|
||||
因为弱引用不会保持所引用的实例,即使引用存在,实例也有可能被销毁。因此,ARC 会在引用的实例被销毁后自动将其赋值为`nil`。你可以像其他可选值一样,检查弱引用的值是否存在,你永远也不会遇到被销毁了而不存在的实例。
|
||||
因为弱引用不会保持所引用的实例,即使引用存在,实例也有可能被销毁。因此,ARC 会在引用的实例被销毁后自动将其赋值为`nil`。你可以像其他可选值一样,检查弱引用的值是否存在,你将永远不会访问已销毁的实例的引用。
|
||||
|
||||
下面的例子跟上面`Person`和`Apartment`的例子一致,但是有一个重要的区别。这一次,`Apartment`的`tenant`属性被声明为弱引用:
|
||||
|
||||
@ -201,30 +207,30 @@ class Person {
|
||||
let name: String
|
||||
init(name: String) { self.name = name }
|
||||
var apartment: Apartment?
|
||||
deinit { println("\(name) is being deinitialized") }
|
||||
deinit { print("\(name) is being deinitialized") }
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
class Apartment {
|
||||
let number: Int
|
||||
init(number: Int) { self.number = number }
|
||||
let unit: String
|
||||
init(unit: String) { self.unit = unit }
|
||||
weak var tenant: Person?
|
||||
deinit { println("Apartment #\(number) is being deinitialized") }
|
||||
deinit { print("Apartment \(unit) is being deinitialized") }
|
||||
}
|
||||
```
|
||||
|
||||
然后跟之前一样,建立两个变量(john和number73)之间的强引用,并关联两个实例:
|
||||
然后跟之前一样,建立两个变量(`john`和`unit4A`)之间的强引用,并关联两个实例:
|
||||
|
||||
```swift
|
||||
var john: Person?
|
||||
var number73: Apartment?
|
||||
var unit4A: Apartment?
|
||||
|
||||
john = Person(name: "John Appleseed")
|
||||
number73 = Apartment(number: 73)
|
||||
unit4A = Apartment(unit: "4A")
|
||||
|
||||
john!.apartment = number73
|
||||
number73!.tenant = john
|
||||
john!.apartment = unit4A
|
||||
unit4A!.tenant = john
|
||||
```
|
||||
|
||||
现在,两个关联在一起的实例的引用关系如下图所示:
|
||||
@ -242,18 +248,18 @@ john = nil
|
||||
// prints "John Appleseed is being deinitialized"
|
||||
```
|
||||
|
||||
唯一剩下的指向`Apartment`实例的强引用来自于变量`number73`。如果你断开这个强引用,再也没有指向`Apartment`实例的强引用了:
|
||||
唯一剩下的指向`Apartment`实例的强引用来自于变量`unit4A`。如果你断开这个强引用,再也没有指向`Apartment`实例的强引用了:
|
||||
|
||||

|
||||
|
||||
由于再也没有指向`Apartment`实例的强引用,该实例也会被销毁:
|
||||
|
||||
```swift
|
||||
number73 = nil
|
||||
// prints "Apartment #73 is being deinitialized"
|
||||
unit4A = nil
|
||||
// prints "Apartment 4A is being deinitialized"
|
||||
```
|
||||
|
||||
上面的两段代码展示了变量`john`和`number73`在被赋值为`nil`后,`Person`实例和`Apartment`实例的析构函数都打印出“销毁”的信息。这证明了引用循环被打破了。
|
||||
上面的两段代码展示了变量`john`和`unit4A`在被赋值为`nil`后,`Person`实例和`Apartment`实例的析构函数都打印出“销毁”的信息。这证明了引用循环被打破了。
|
||||
|
||||
<a name="2"></a>
|
||||
### 无主引用
|
||||
@ -264,11 +270,11 @@ number73 = nil
|
||||
|
||||
> 注意:
|
||||
>如果你试图在实例被销毁后,访问该实例的无主引用,会触发运行时错误。使用无主引用,你必须确保引用始终指向一个未销毁的实例。
|
||||
> 还需要注意的是如果你试图访问实例已经被销毁的无主引用,程序会直接崩溃,而不会发生无法预期的行为。所以你应当避免这样的事情发生。
|
||||
> 还需要注意的是如果你试图访问实例已经被销毁的无主引用,Swift 确保程序会直接崩溃,而不会发生无法预期的行为。所以你应当避免这样的事情发生。
|
||||
|
||||
下面的例子定义了两个类,`Customer`和`CreditCard`,模拟了银行客户和客户的信用卡。这两个类中,每一个都将另外一个类的实例作为自身的属性。这种关系会潜在的创造循环强引用。
|
||||
下面的例子定义了两个类,`Customer`和`CreditCard`,模拟了银行客户和客户的信用卡。这两个类中,每一个都将另外一个类的实例作为自身的属性。这种关系可能会造成循环强引用。
|
||||
|
||||
`Customer`和`CreditCard`之间的关系与前面弱引用例子中`Apartment`和`Person`的关系截然不同。在这个数据模型中,一个客户可能有或者没有信用卡,但是一张信用卡总是关联着一个客户。为了表示这种关系,`Customer`类有一个可选类型的`card`属性,但是`CreditCard`类有一个非可选类型的`customer`属性。
|
||||
`Customer`和`CreditCard`之间的关系与前面弱引用例子中`Apartment`和`Person`的关系略微不同。在这个数据模型中,一个客户可能有或者没有信用卡,但是一张信用卡总是关联着一个客户。为了表示这种关系,`Customer`类有一个可选类型的`card`属性,但是`CreditCard`类有一个非可选类型的`customer`属性。
|
||||
|
||||
此外,只能通过将一个`number`值和`customer`实例传递给`CreditCard`构造函数的方式来创建`CreditCard`实例。这样可以确保当创建`CreditCard`实例时总是有一个`customer`实例与之关联。
|
||||
|
||||
@ -281,22 +287,25 @@ class Customer {
|
||||
init(name: String) {
|
||||
self.name = name
|
||||
}
|
||||
deinit { println("\(name) is being deinitialized") }
|
||||
deinit { print("\(name) is being deinitialized") }
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
class CreditCard {
|
||||
let number: Int
|
||||
let number: UInt64
|
||||
unowned let customer: Customer
|
||||
init(number: Int, customer: Customer) {
|
||||
init(number: UInt64, customer: Customer) {
|
||||
self.number = number
|
||||
self.customer = customer
|
||||
}
|
||||
deinit { println("Card #\(number) is being deinitialized") }
|
||||
deinit { print("Card #\(number) is being deinitialized") }
|
||||
}
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> `CreditCard`类的`number`属性被定义为`UInt64`类型而不是`Int`类型,以确保`number`属性的存储量在32位和64位系统上都能足够容纳16位的卡号。
|
||||
|
||||
下面的代码片段定义了一个叫`john`的可选类型`Customer`变量,用来保存某个特定客户的引用。由于是可选类型,所以变量被初始化为`nil`。
|
||||
|
||||
```swift
|
||||
@ -330,24 +339,26 @@ john = nil
|
||||
|
||||
最后的代码展示了在`john`变量被设为`nil`后`Customer`实例和`CreditCard`实例的构造函数都打印出了“销毁”的信息。
|
||||
|
||||
|
||||
<a name="unowned_references_and_implicitly_unwrapped_optional_properties"></a>
|
||||
###无主引用以及隐式解析可选属性
|
||||
|
||||
上面弱引用和无主引用的例子涵盖了两种常用的需要打破循环强引用的场景。
|
||||
|
||||
`Person`和`Apartment`的例子展示了两个属性的值都允许为`nil`,并会潜在的产生循环强引用。这种场景最适合用弱引用来解决。
|
||||
|
||||
`Customer`和`CreditCard`的例子展示了一个属性的值允许为`nil`,而另一个属性的值不允许为`nil`,并会潜在的产生循环强引用。这种场景最适合通过无主引用来解决。
|
||||
`Customer`和`CreditCard`的例子展示了一个属性的值允许为`nil`,而另一个属性的值不允许为`nil`,这也可能会产生循环强引用。这种场景最适合通过无主引用来解决。
|
||||
|
||||
然而,存在着第三种场景,在这种场景中,两个属性都必须有值,并且初始化完成后不能为`nil`。在这种场景中,需要一个类使用无主属性,而另外一个类使用隐式解析可选属性。
|
||||
然而,存在着第三种场景,在这种场景中,两个属性都必须有值,并且初始化完成后永远不会为`nil`。在这种场景中,需要一个类使用无主属性,而另外一个类使用隐式解析可选属性。
|
||||
|
||||
这使两个属性在初始化完成后能被直接访问(不需要可选展开),同时避免了循环引用。这一节将为你展示如何建立这种关系。
|
||||
|
||||
下面的例子定义了两个类,`Country`和`City`,每个类将另外一个类的实例保存为属性。在这个模型中,每个国家必须有首都,而每一个城市必须属于一个国家。为了实现这种关系,`Country`类拥有一个`capitalCity`属性,而`City`类有一个`country`属性:
|
||||
下面的例子定义了两个类,`Country`和`City`,每个类将另外一个类的实例保存为属性。在这个模型中,每个国家必须有首都,每个城市必须属于一个国家。为了实现这种关系,`Country`类拥有一个`capitalCity`属性,而`City`类有一个`country`属性:
|
||||
|
||||
```swift
|
||||
class Country {
|
||||
let name: String
|
||||
let capitalCity: City!
|
||||
var capitalCity: City!
|
||||
init(name: String, capitalName: String) {
|
||||
self.name = name
|
||||
self.capitalCity = City(name: capitalName, country: self)
|
||||
@ -368,9 +379,9 @@ class City {
|
||||
|
||||
为了建立两个类的依赖关系,`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`的构造函数。
|
||||
|
||||
@ -378,7 +389,7 @@ class City {
|
||||
|
||||
```swift
|
||||
var country = Country(name: "Canada", capitalName: "Ottawa")
|
||||
println("\(country.name)'s capital city is called \(country.capitalCity.name)")
|
||||
print("\(country.name)'s capital city is called \(country.capitalCity.name)")
|
||||
// prints "Canada's capital city is called Ottawa"
|
||||
```
|
||||
|
||||
@ -387,13 +398,13 @@ println("\(country.name)'s capital city is called \(country.capitalCity.name)")
|
||||
<a name="strong_reference_cycles_for_closures"></a>
|
||||
##闭包引起的循环强引用
|
||||
|
||||
前面我们看到了循环强引用环是在两个类实例属性互相保持对方的强引用时产生的,还知道了如何用弱引用和无主引用来打破循环强引用。
|
||||
前面我们看到了循环强引用是在两个类实例属性互相保持对方的强引用时产生的,还知道了如何用弱引用和无主引用来打破这些循环强引用。
|
||||
|
||||
循环强引用还会发生在当你将一个闭包赋值给类实例的某个属性,并且这个闭包体中又使用了实例。这个闭包体中可能访问了实例的某个属性,例如`self.someProperty`,或者闭包中调用了实例的某个方法,例如`self.someMethod`。这两种情况都导致了闭包 “捕获" `self`,从而产生了循环强引用。
|
||||
循环强引用还会发生在当你将一个闭包赋值给类实例的某个属性,并且这个闭包体中又使用了这个类实例。这个闭包体中可能访问了实例的某个属性,例如`self.someProperty`,或者闭包中调用了实例的某个方法,例如`self.someMethod`。这两种情况都导致了闭包 “捕获" `self`,从而产生了循环强引用。
|
||||
|
||||
循环强引用的产生,是因为闭包和类相似,都是引用类型。当你把一个闭包赋值给某个属性时,你也把一个引用赋值给了这个闭包。实质上,这跟之前的问题是一样的-两个强引用让彼此一直有效。但是,和两个类实例不同,这次一个是类实例,另一个是闭包。
|
||||
|
||||
Swift 提供了一种优雅的方法来解决这个问题,称之为闭包占用列表(closuer capture list)。同样的,在学习如何用闭包占用列表破坏循环强引用之前,先来了解一下循环强引用是如何产生的,这对我们是很有帮助的。
|
||||
Swift 提供了一种优雅的方法来解决这个问题,称之为闭包捕获列表(closuer capture list)。同样的,在学习如何用闭包捕获列表破坏循环强引用之前,先来了解一下这里的循环强引用是如何产生的,这对我们很有帮助。
|
||||
|
||||
下面的例子为你展示了当一个闭包引用了`self`后是如何产生一个循环强引用的。例子中定义了一个叫`HTMLElement`的类,用一种简单的模型表示 HTML 中的一个单独的元素:
|
||||
|
||||
@ -403,7 +414,7 @@ class HTMLElement {
|
||||
let name: String
|
||||
let text: String?
|
||||
|
||||
lazy var asHTML: () -> String = {
|
||||
lazy var asHTML: Void -> String = {
|
||||
if let text = self.text {
|
||||
return "<\(self.name)>\(text)</\(self.name)>"
|
||||
} else {
|
||||
@ -417,7 +428,7 @@ class HTMLElement {
|
||||
}
|
||||
|
||||
deinit {
|
||||
println("\(name) is being deinitialized")
|
||||
print("\(name) is being deinitialized")
|
||||
}
|
||||
|
||||
}
|
||||
@ -425,9 +436,9 @@ class HTMLElement {
|
||||
|
||||
`HTMLElement`类定义了一个`name`属性来表示这个元素的名称,例如代表段落的"p",或者代表换行的"br"。`HTMLElement`还定义了一个可选属性`text`,用来设置和展现 HTML 元素的文本。
|
||||
|
||||
除了上面的两个属性,`HTMLElement`还定义了一个`lazy`属性`asHTML`。这个属性引用了一个闭包,将`name`和`text`组合成 HTML 字符串片段。该属性是`() -> String`类型,或者可以理解为“一个没有参数,返回`String`的函数”。
|
||||
除了上面的两个属性,`HTMLElement`还定义了一个`lazy`属性`asHTML`。这个属性引用了一个将`name`和`text`组合成 HTML 字符串片段的闭包。该属性是`Void -> String`类型,或者可以理解为“一个没有参数,返回`String`的函数”。
|
||||
|
||||
默认情况下,闭包赋值给了`asHTML`属性,这个闭包返回一个代表 HTML 标签的字符串。如果`text`值存在,该标签就包含可选值`text`;如果`text`不存在,该标签就不包含文本。对于段落元素,根据`text`是"some text"还是`nil`,闭包会返回"`<p>some text</p>`"或者"`<p />`"。
|
||||
默认情况下,闭包赋值给了`asHTML`属性,这个闭包返回一个代表 HTML 标签的字符串。如果`text`值存在,该标签就包含可选值`text`;如果`text`不存在,该标签就不包含文本。对于段落元素,根据`text`是`"some text"`还是`nil`,闭包会返回"`<p>some text</p>`"或者"`<p />`"。
|
||||
|
||||
可以像实例方法那样去命名、使用`asHTML`属性。然而,由于`asHTML`是闭包而不是实例方法,如果你想改变特定元素的 HTML 处理的话,可以用自定义的闭包来取代默认值。
|
||||
|
||||
@ -440,7 +451,7 @@ class HTMLElement {
|
||||
|
||||
```swift
|
||||
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
|
||||
println(paragraph!.asHTML())
|
||||
print(paragraph!.asHTML())
|
||||
// prints"hello, world"
|
||||
```
|
||||
|
||||
@ -451,7 +462,7 @@ println(paragraph!.asHTML())
|
||||
|
||||

|
||||
|
||||
实例的`asHTML`属性持有闭包的强引用。但是,闭包在其闭包体内使用了`self`(引用了`self.name`和`self.text`),因此闭包捕获了`self`,这意味着闭包又反过来持有了`HTMLElement`实例的强引用。这样两个对象就产生了循环强引用。(更多关于闭包捕获值的信息,请参考[值捕获](07_Closures.html))。
|
||||
实例的`asHTML`属性持有闭包的强引用。但是,闭包在其闭包体内使用了`self`(引用了`self.name`和`self.text`),因此闭包捕获了`self`,这意味着闭包又反过来持有了`HTMLElement`实例的强引用。这样两个对象就产生了循环强引用。(更多关于闭包捕获值的信息,请参考[值捕获](./07_Closures.html#capturing_values))。
|
||||
|
||||
>注意:
|
||||
虽然闭包多次使用了`self`,它只捕获`HTMLElement`实例的一个强引用。
|
||||
@ -470,38 +481,38 @@ paragraph = nil
|
||||
在定义闭包时同时定义捕获列表作为闭包的一部分,通过这种方式可以解决闭包和类实例之间的循环强引用。捕获列表定义了闭包体内捕获一个或者多个引用类型的规则。跟解决两个类实例间的循环强引用一样,声明每个捕获的引用为弱引用或无主引用,而不是强引用。应当根据代码关系来决定使用弱引用还是无主引用。
|
||||
|
||||
>注意:
|
||||
Swift 有如下要求:只要在闭包内使用`self`的成员,就要用`self.someProperty`或者`self.someMethod`(而不只是`someProperty`或`someMethod`)。这提醒你可能会不小心就捕获了`self`。
|
||||
Swift 有如下要求:只要在闭包内使用`self`的成员,就要用`self.someProperty`或者`self.someMethod`(而不只是`someProperty`或`someMethod`)。这提醒你可能会一不小心就捕获了`self`。
|
||||
|
||||
###定义捕获列表
|
||||
|
||||
捕获列表中的每个元素都是由`weak`或者`unowned`关键字和实例的引用(如`self`或`someInstance`)成对组成。每一对都在方括号中,通过逗号分开。
|
||||
捕获列表中的每一项都由一对元素组成,一个元素是`weak`或`unowned`关键字,另一个元素是类实例的引用(如`self`)或初始化过的变量(如`delegate = self.delegate!`)。这些项在方括号中用逗号分开。
|
||||
|
||||
捕获列表放置在闭包参数列表和返回类型之前:
|
||||
如果闭包有参数列表和返回类型,把捕获列表放在它们前面:
|
||||
|
||||
```swift
|
||||
lazy var someClosure: (Int, String) -> String = {
|
||||
[unowned self] (index: Int, stringToProcess: String) -> String in
|
||||
[unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
|
||||
// closure body goes here
|
||||
}
|
||||
```
|
||||
|
||||
如果闭包没有指定参数列表或者返回类型,则可以通过上下文推断,那么可以捕获列表放在闭包开始的地方,跟着是关键字`in`:
|
||||
如果闭包没有指明参数列表或者返回类型,即它们会通过上下文推断,那么可以把捕获列表和关键字`in`放在闭包最开始的地方:
|
||||
|
||||
```swift
|
||||
lazy var someClosure: () -> String = {
|
||||
[unowned self] in
|
||||
lazy var someClosure: Void -> String = {
|
||||
[unowned self, weak delegate = self.delegate!] in
|
||||
// closure body goes here
|
||||
}
|
||||
```
|
||||
|
||||
###弱引用和无主引用
|
||||
|
||||
当闭包和捕获的实例总是互相引用时并且总是同时销毁时,将闭包内的捕获定义为无主引用。
|
||||
在闭包和捕获的实例总是互相引用时并且总是同时销毁时,将闭包内的捕获定义为无主引用。
|
||||
|
||||
相反的,当捕获引用有时可能会是`nil`时,将闭包内的捕获定义为弱引用。弱引用总是可选类型,并且当引用的实例被销毁后,弱引用的值会自动置为`nil`。这使我们可以在闭包内检查它们是否存在。
|
||||
相反的,在被捕获的引用可能会变为`nil`时,将闭包内的捕获定义为弱引用。弱引用总是可选类型,并且当引用的实例被销毁后,弱引用的值会自动置为`nil`。这使我们可以在闭包体内检查它们是否存在。
|
||||
|
||||
>注意:
|
||||
如果捕获的引用绝对不会置为`nil`,应该用无主引用,而不是弱引用。
|
||||
如果被捕获的引用绝对不会变为`nil`,应该用无主引用,而不是弱引用。
|
||||
|
||||
前面的`HTMLElement`例子中,无主引用是正确的解决循环强引用的方法。这样编写`HTMLElement`类来避免循环强引用:
|
||||
|
||||
@ -511,7 +522,7 @@ class HTMLElement {
|
||||
let name: String
|
||||
let text: String?
|
||||
|
||||
lazy var asHTML: () -> String = {
|
||||
lazy var asHTML: Void -> String = {
|
||||
[unowned self] in
|
||||
if let text = self.text {
|
||||
return "<\(self.name)>\(text)</\(self.name)>"
|
||||
@ -526,19 +537,19 @@ class HTMLElement {
|
||||
}
|
||||
|
||||
deinit {
|
||||
println("\(name) is being deinitialized")
|
||||
print("\(name) is being deinitialized")
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
上面的`HTMLElement`实现和之前的实现一致,只是在`asHTML`闭包中多了一个捕获列表。这里,捕获列表是`[unowned self]`,表示“用无主引用而不是强引用来捕获`self`”。
|
||||
上面的`HTMLElement`实现和之前的实现一致,除了在`asHTML`闭包中多了一个捕获列表。这里,捕获列表是`[unowned self]`,表示“用无主引用而不是强引用来捕获`self`”。
|
||||
|
||||
和之前一样,我们可以创建并打印`HTMLElement`实例:
|
||||
|
||||
```swift
|
||||
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
|
||||
println(paragraph!.asHTML())
|
||||
print(paragraph!.asHTML())
|
||||
// prints "<p>hello, world</p>"
|
||||
```
|
||||
|
||||
@ -552,4 +563,3 @@ println(paragraph!.asHTML())
|
||||
paragraph = nil
|
||||
// prints "p is being deinitialized"
|
||||
```
|
||||
|
||||
|
||||
@ -1,105 +1,99 @@
|
||||
> 翻译:[Jasonbroker](https://github.com/Jasonbroker)
|
||||
> 校对:[numbbbbb](https://github.com/numbbbbb), [stanzhai](https://github.com/stanzhai)
|
||||
# 可空链式调用(Optional Chaining)
|
||||
|
||||
# Optional Chaining
|
||||
-----------------
|
||||
|
||||
本页包含内容:
|
||||
> 1.0
|
||||
> 翻译:[Jasonbroker](https://github.com/Jasonbroker)
|
||||
> 校对:[numbbbbb](https://github.com/numbbbbb), [stanzhai](https://github.com/stanzhai)
|
||||
|
||||
- [可选链可替代强制解析](#optional_chaining_as_an_alternative_to_forced_unwrapping)
|
||||
- [为可选链定义模型类](#defining_model_classes_for_optional_chaining)
|
||||
- [通过可选链调用属性](#calling_properties_through_optional_chaining)
|
||||
- [通过可选链调用方法](#calling_methods_through_optional_chaining)
|
||||
- [使用可选链调用下标脚本](#calling_subscripts_through_optional_chaining)
|
||||
- [连接多层链接](#linking_multiple_levels_of_chaining)
|
||||
- [链接可选返回值的方法](#chaining_on_methods_with_optional_return_values)
|
||||
> 2.0
|
||||
> 翻译+校对:[lyojo](https://github.com/lyojo)
|
||||
|
||||
可选链(Optional Chaining)是一种可以请求和调用属性、方法及下标脚本的过程,它的可选性体现于请求或调用的目标当前可能为空(`nil`)。如果可选的目标有值,那么调用就会成功;相反,如果选择的目标为空(`nil`),则这种调用将返回空(`nil`)。多次请求或调用可以被链接在一起形成一个链,如果任何一个节点为空(`nil`)将导致整个链失效。
|
||||
|
||||
> 注意:
|
||||
Swift 的可选链和 Objective-C 中的消息为空有些相像,但是 Swift 可以使用在任意类型中,并且失败与否可以被检测到。
|
||||
可空链式调用(Optional Chaining)是一种可以请求和调用属性、方法及下标的过程,它的可空性体现于请求或调用的目标当前可能为空(nil)。如果可空的目标有值,那么调用就会成功;如果选择的目标为空(nil),那么这种调用将返回空(nil)。多个连续的调用可以被链接在一起形成一个调用链,如果其中任何一个节点为空(nil)将导致整个链调用失败。
|
||||
|
||||
>
|
||||
注意:
|
||||
Swift 的可空链式调用和 Objective-C 中的消息为空有些相像,但是 Swift 可以使用在任意类型中,并且能够检查调用是否成功。
|
||||
|
||||
<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`。
|
||||
|
||||
```swift
|
||||
class Person {
|
||||
var residence: Residence?
|
||||
var residence: Residence?
|
||||
}
|
||||
|
||||
class Residence {
|
||||
var numberOfRooms = 1
|
||||
var numberOfRooms = 1
|
||||
}
|
||||
```
|
||||
|
||||
`Residence`具有一个`Int`类型的`numberOfRooms`,其值为 1。`Person`具有一个可选`residence`属性,它的类型是`Residence?`。
|
||||
`Residence`有一个`Int`类型的属性`numberOfRooms`,其默认值为1。`Person`具有一个可空的`residence`属性,其类型为`Residence?`。
|
||||
|
||||
如果你创建一个新的`Person`实例,它的`residence`属性由于是被定义为可选型的,此属性将默认初始化为空:
|
||||
如果创建一个新的`Person`实例,因为它的`residence`属性是可空的,`john`属性将初始化为`nil`:
|
||||
|
||||
```swift
|
||||
let john = Person()
|
||||
```
|
||||
|
||||
如果你想使用感叹号(`!`)强制解析获得这个人`residence`属性`numberOfRooms`属性值,将会引发运行时错误,因为这时没有可以供解析的`residence`值。
|
||||
如果使用叹号(!)强制展开获得这个`john`的`residence`属性中的`numberOfRooms`值,会触发运行时错误,因为这时没有可以展开的`residence`:
|
||||
|
||||
```swift
|
||||
let roomCount = john.residence!.numberOfRooms
|
||||
//将导致运行时错误
|
||||
// this triggers a runtime error
|
||||
```
|
||||
当`john.residence`不是`nil`时,会运行通过,且会将`roomCount` 设置为一个`int`类型的合理值。然而,如上所述,当`residence`为空时,这个代码将会导致运行时错误。
|
||||
|
||||
可选链提供了一种另一种获得`numberOfRooms`的方法。利用可选链,使用问号来代替原来`!`的位置:
|
||||
`john.residence`非空的时候,上面的调用成功,并且把`roomCount`设置为`Int`类型的房间数量。正如上面说到的,当`residence`为空的时候上面这段代码会触发运行时错误。
|
||||
|
||||
可空链式调用提供了一种另一种访问`numberOfRooms`的方法,使用问号(?)来代替原来叹号(!)的位置:
|
||||
|
||||
```swift
|
||||
if let roomCount = john.residence?.numberOfRooms {
|
||||
println("John's residence has \(roomCount) room(s).")
|
||||
print("John's residence has \(roomCount) room(s).")
|
||||
} else {
|
||||
println("Unable to retrieve the number of rooms.")
|
||||
print("Unable to retrieve the number of rooms.")
|
||||
}
|
||||
// 打印 "Unable to retrieve the number of rooms.
|
||||
// prints "Unable to retrieve the number of rooms."
|
||||
```
|
||||
|
||||
这告诉 Swift 来链接可选`residence?`属性,如果`residence`存在则取回`numberOfRooms`的值。
|
||||
在`residence`后面添加问号之后,Swift就会在`residence`不为空的情况下访问`numberOfRooms`。
|
||||
|
||||
因为这种尝试获得`numberOfRooms`的操作有可能失败,可选链会返回`Int?`类型值,或者称作“可选`Int`”。当`residence`是空的时候(上例),选择`Int`将会为空,因此会出现无法访问`numberOfRooms`的情况。
|
||||
因为访问`numberOfRooms`有可能失败,可空链式调用会返回`Int?`类型,或称为“可空的Int”。如上例所示,当`residence`为`nil`的时候,可空的`Int`将会为`nil`,表明无法访问`numberOfRooms`。
|
||||
|
||||
要注意的是,即使numberOfRooms是非可选`Int`(`Int?`)时这一点也成立。只要是通过可选链的请求就意味着最后`numberOfRooms`总是返回一个`Int?`而不是`Int`。
|
||||
要注意的是,即使`numberOfRooms`是不可空的`Int`时,这一点也成立。只要是通过可空链式调用就意味着最后`numberOfRooms`返回一个`Int?`而不是`Int`。
|
||||
|
||||
你可以自己定义一个`Residence`实例给`john.residence`,这样它就不再为空了:
|
||||
通过赋给`john.residence`一个`Residence`的实例变量:
|
||||
|
||||
```swift
|
||||
john.residence = Residence()
|
||||
```
|
||||
|
||||
`john.residence` 现在有了实际存在的实例而不是nil了。如果你想使用和前面一样的可选链来获得`numberOfRoooms`,它将返回一个包含默认值 1 的`Int?`:
|
||||
这样`john.residence`不为`nil`了。现在就可以正常访问`john.residence.numberOfRooms`,其值为默认的1,类型为`Int?`:
|
||||
|
||||
```swift
|
||||
if let roomCount = john.residence?.numberOfRooms {
|
||||
println("John's residence has \(roomCount) room(s).")
|
||||
print("John's residence has \(roomCount) room(s).")
|
||||
} else {
|
||||
println("Unable to retrieve the number of rooms.")
|
||||
print("Unable to retrieve the number of rooms.")
|
||||
}
|
||||
// 打印 "John's residence has 1 room(s)"。
|
||||
// prints "John's residence has 1 room(s)."
|
||||
```
|
||||
|
||||
<a name="defining_model_classes_for_optional_chaining"></a>
|
||||
##为可选链定义模型类
|
||||
##为可空链式调用定义模型类
|
||||
通过使用可空链式调用可以调用多层属性,方法,和下标。这样可以通过各种模型向下访问各种子属性。并且判断能否访问子属性的属性,方法或下标。
|
||||
|
||||
你可以使用可选链来多层调用属性,方法,和下标脚本。这让你可以利用它们之间的复杂模型来获取更底层的属性,并检查是否可以成功获取此类底层属性。
|
||||
下面这段代码定义了四个模型类,这些例子包括多层可空链式调用。为了方便说明,在`Person`和`Residence`的基础上增加了`Room`和`Address`,以及相关的属性,方法以及下标。
|
||||
|
||||
后面的代码定义了四个将在后面使用的模型类,其中包括多层可选链。这些类是由上面的`Person`和`Residence`模型通过添加一个`Room`和一个`Address`类拓展来。
|
||||
|
||||
`Person`类定义与之前相同。
|
||||
Person类定义基本保持不变:
|
||||
|
||||
```swift
|
||||
class Person {
|
||||
@ -107,32 +101,32 @@ class Person {
|
||||
}
|
||||
```
|
||||
|
||||
`Residence`类比之前复杂些。这次,它定义了一个变量 `rooms`,它被初始化为一个`Room[]`类型的空数组:
|
||||
`Residence`类比之前复杂些,增加了一个`Room`类型的空数组`room`:
|
||||
|
||||
```swift
|
||||
class Residence {
|
||||
var rooms = [Room]()
|
||||
var numberOfRooms: Int {
|
||||
return rooms.count
|
||||
return rooms.count
|
||||
}
|
||||
subscript(i: Int) -> Room {
|
||||
return rooms[i]
|
||||
get {
|
||||
return rooms[i]
|
||||
}
|
||||
set {
|
||||
rooms[i] = newValue
|
||||
}
|
||||
}
|
||||
func printNumberOfRooms() {
|
||||
println("The number of rooms is \(numberOfRooms)")
|
||||
print("The number of rooms is \(numberOfRooms)")
|
||||
}
|
||||
var address: Address?
|
||||
}
|
||||
```
|
||||
|
||||
因为`Residence`存储了一个`Room`实例的数组,它的`numberOfRooms`属性值不是一个固定的存储值,而是通过计算而来的。`numberOfRooms`属性值是由返回`rooms`数组的`count`属性值得到的。
|
||||
现在`Residence`有了一个存储`Room`类型的数组,`numberOfRooms`属性需要计算,而不是作为单纯的变量。计算后的`numberOfRooms`返回`rooms`数组的`count`属性值。现在的`Residence`还提供访问`rooms`数组的快捷方式, 通过可读写的下标来访问指定位置的数组元素。此外,还提供`printNumberOfRooms`方法,这个方法的作用就是输出这个房子中房间的数量。最后,`Residence`定义了一个可空属性`address`,其类型为`Address?`。`Address`类的定义在下面会说明。
|
||||
|
||||
为了能快速访问`rooms`数组,`Residence`定义了一个只读的下标脚本,通过插入数组的元素角标就可以成功调用。如果该角标存在,下标脚本则将该元素返回。
|
||||
|
||||
`Residence`中也提供了一个`printNumberOfRooms`的方法,即简单的打印房间个数。
|
||||
|
||||
最后,`Residence`定义了一个可选属性叫`address`(`address?`)。`Address`类的属性将在后面定义。
|
||||
用于`rooms`数组的`Room`类是一个很简单的类,它只有一个`name`属性和一个设定`room`名的初始化器。
|
||||
类`Room`是一个简单类,只包含一个属性`name`,以及一个初始化函数:
|
||||
|
||||
```swift
|
||||
class Room {
|
||||
@ -141,8 +135,7 @@ class Room {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
这个模型中的最终类叫做`Address`。它有三个类型是`String?`的可选属性。前面两个可选属性`buildingName`和 `buildingNumber`作为地址的一部分,是定义某个建筑物的两种方式。第三个属性`street`,用于命名地址的街道名:
|
||||
最后一个类是`Address`,这个类有三个`String?`类型的可空属性。`buildingName`以及`buildingNumber`属性表示建筑的名称和号码,用来表示某个特定的建筑。第三个属性表示建筑所在街道的名称:
|
||||
|
||||
```swift
|
||||
class Address {
|
||||
@ -150,9 +143,9 @@ class Address {
|
||||
var buildingNumber: String?
|
||||
var street: String?
|
||||
func buildingIdentifier() -> String? {
|
||||
if buildingName {
|
||||
if buildingName != nil {
|
||||
return buildingName
|
||||
} else if buildingNumber {
|
||||
} else if buildingNumber != nil {
|
||||
return buildingNumber
|
||||
} else {
|
||||
return nil
|
||||
@ -161,161 +154,201 @@ class Address {
|
||||
}
|
||||
```
|
||||
|
||||
`Address`类还提供了一个`buildingIdentifier`的方法,它的返回值类型为`String?`。这个方法检查`buildingName`和`buildingNumber`的属性,如果`buildingName`有值则将其返回,或者如果`buildingNumber`有值则将其返回,再或如果没有一个属性有值,返回空。
|
||||
类`Address`提供`buildingIdentifier()`方法,返回值为`String?`。 如果`buildingName`不为空则返回`buildingName`, 如果`buildingNumber`不为空则返回`buildingNumber`。如果这两个属性都为空则返回`nil`。
|
||||
|
||||
<a name="calling_properties_through_optional_chaining"></a>
|
||||
##通过可选链调用属性
|
||||
##通过可空链式调用访问属性
|
||||
正如[使用可空链式调用来强制展开](#optional_chaining_as_an_alternative_to_forced_unwrapping)中所述,可以通过可空链式调用访问属性的可空值,并且判断访问是否成功。
|
||||
|
||||
正如上面“ [可选链可替代强制解析](#optional_chaining_as_an_alternative_to_forced_unwrapping)”中所述,你可以利用可选链的可选值获取属性,并且检查属性是否获取成功。然而,你不能使用可选链为属性赋值。
|
||||
|
||||
使用上述定义的类来创建一个人实例,并再次尝试后去它的`numberOfRooms`属性:
|
||||
下面的代码创建了一个`Person`实例,然后访问`numberOfRooms`属性:
|
||||
|
||||
```swift
|
||||
let john = Person()
|
||||
if let roomCount = john.residence?.numberOfRooms {
|
||||
println("John's residence has \(roomCount) room(s).")
|
||||
print("John's residence has \(roomCount) room(s).")
|
||||
} else {
|
||||
println("Unable to retrieve the number of rooms.")
|
||||
print("Unable to retrieve the number of rooms.")
|
||||
}
|
||||
// 打印 "Unable to retrieve the number of rooms。
|
||||
// prints "Unable to retrieve the number of rooms."
|
||||
```
|
||||
|
||||
由于`john.residence`是空,所以这个可选链和之前一样失败了,但是没有运行时错误。
|
||||
因为`john.residence`为`nil`,所以毫无疑问这个可空链式调用失败。
|
||||
|
||||
<a name="calling_methods_through_optional_chaining"></a>
|
||||
##通过可选链调用方法
|
||||
|
||||
你可以使用可选链的来调用可选值的方法并检查方法调用是否成功。即使这个方法没有返回值,你依然可以使用可选链来达成这一目的。
|
||||
|
||||
`Residence`的`printNumberOfRooms`方法会打印`numberOfRooms`的当前值。方法如下:
|
||||
通过可空链式调用来设定属性值:
|
||||
|
||||
```swift
|
||||
func printNumberOfRooms(){
|
||||
println(“The number of rooms is \(numberOfRooms)”)
|
||||
}
|
||||
let someAddress = Address()
|
||||
someAddress.buildingNumber = "29"
|
||||
someAddress.street = "Acacia Road"
|
||||
john.residence?.address = someAddress
|
||||
```
|
||||
|
||||
这个方法没有返回值。但是,没有返回值类型的函数和方法有一个隐式的返回值类型`Void`(参见Function Without Return Values)。
|
||||
在这个例子中,通过`john.residence`来设定`address`属性也是不行的,因为`john.residence`为`nil`。
|
||||
|
||||
如果你利用可选链调用此方法,这个方法的返回值类型将是`Void?`,而不是`Void`,因为当通过可选链调用方法时返回值总是可选类型(optional type)。即使这个方法本身没有定义返回值,你也可以使用`if`语句来检查是否能成功调用`printNumberOfRooms`方法:如果方法通过可选链调用成功,`printNumberOfRooms`的隐式返回值将会是`Void`,如果没有成功,将返回`nil`:
|
||||
##通过可空链式调用来调用方法
|
||||
可以通过可空链式调用来调用方法,并判断是否调用成功,即使这个方法没有返回值。
|
||||
`Residence`中的`printNumberOfRooms()`方法输出当前的`numberOfRooms`值:
|
||||
|
||||
```swift
|
||||
if john.residence?.printNumberOfRooms?() {
|
||||
println("It was possible to print the number of rooms.")
|
||||
} else {
|
||||
println("It was not possible to print the number of rooms.")
|
||||
func printNumberOfRooms() {
|
||||
print("The number of rooms is \(numberOfRooms)")
|
||||
}
|
||||
// 打印 "It was not possible to print the number of rooms."。
|
||||
```
|
||||
|
||||
<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
|
||||
if let firstRoomName = john.residence?[0].name {
|
||||
println("The first room name is \(firstRoomName).")
|
||||
print("The first room name is \(firstRoomName).")
|
||||
} else {
|
||||
println("Unable to retrieve the first room name.")
|
||||
print("Unable to retrieve the first room name.")
|
||||
}
|
||||
// 打印 "Unable to retrieve the first room name."。
|
||||
// prints "Unable to retrieve the first room name."
|
||||
```
|
||||
|
||||
在下标脚本调用中可选链的问号直接跟在`john.residence`的后面,在下标脚本括号的前面,因为`john.residence`是可选链试图获得的可选值。
|
||||
|
||||
如果你创建一个`Residence`实例给`john.residence`,且在他的`rooms`数组中有一个或多个`Room`实例,那么你可以使用可选链通过`Residence`下标脚本来获取在`rooms`数组中的实例了:
|
||||
在这个例子中,问号直接放在`john.residence`的后面,并且在方括号的前面,因为`john.residence`是可空值。
|
||||
|
||||
类似的,可以通过下标,用可空链式调用来赋值:
|
||||
|
||||
```swift
|
||||
john.residence?[0] = Room(name: "Bathroom")
|
||||
```
|
||||
|
||||
|
||||
这次赋值同样会失败,因为`residence`目前是`nil`。
|
||||
|
||||
如果你创建一个`Residence`实例,添加一些`Room`实例并赋值给`john.residence`,那就可以通过可选链和下标来访问数组中的元素:
|
||||
|
||||
```swift
|
||||
let johnsHouse = Residence()
|
||||
johnsHouse.rooms += Room(name: "Living Room")
|
||||
johnsHouse.rooms += Room(name: "Kitchen")
|
||||
johnsHouse.rooms.append(Room(name: "Living Room"))
|
||||
johnsHouse.rooms.append(Room(name: "Kitchen"))
|
||||
john.residence = johnsHouse
|
||||
|
||||
if let firstRoomName = john.residence?[0].name {
|
||||
println("The first room name is \(firstRoomName).")
|
||||
print("The first room name is \(firstRoomName).")
|
||||
} else {
|
||||
println("Unable to retrieve the first room name.")
|
||||
print("Unable to retrieve the first room name.")
|
||||
}
|
||||
// 打印 "The first room name is Living Room."。
|
||||
// prints "The first room name is Living Room."
|
||||
```
|
||||
|
||||
<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
|
||||
if let johnsStreet = john.residence?.address?.street {
|
||||
println("John's street name is \(johnsStreet).")
|
||||
print("John's street name is \(johnsStreet).")
|
||||
} else {
|
||||
println("Unable to retrieve the address.")
|
||||
print("Unable to retrieve the address.")
|
||||
}
|
||||
// 打印 "Unable to retrieve the address.”。
|
||||
// prints "Unable to retrieve the address."
|
||||
```
|
||||
|
||||
`john.residence`的值现在包含一个`Residence`实例,然而`john.residence.address`现在是`nil`,因此`john.residence?.address?.street`调用失败。
|
||||
`john.residence`包含`Residence`实例,但是`john.residence.address`为`nil`。因此,不能访问`john.residence?.address?.street`。
|
||||
|
||||
从上面的例子发现,你试图获得`street`属性值。这个属性的类型是`String?`。因此尽管在可选类型属性前使用了两层可选链,`john.residence?.address?.street`的返回值类型也是`String?`。
|
||||
需要注意的是,上面的例子中,`street`的属性为`String?`。`john.residence?.address?.street`的返回值也依然是`String?`,即使已经进行了两次可空的链式调用。
|
||||
|
||||
如果你为`Address`设定一个实例来作为`john.residence.address`的值,并为`address`的`street`属性设定一个实际值,你可以通过多层可选链来得到这个属性值。
|
||||
如果把`john.residence.address`指向一个实例,并且为`address`中的`street`属性赋值,我们就能过通过可空链式调用来访问`street`属性。
|
||||
|
||||
```swift
|
||||
let johnsAddress = Address()
|
||||
johnsAddress.buildingName = "The Larches"
|
||||
johnsAddress.street = "Laurel Street"
|
||||
john.residence!.address = johnsAddress
|
||||
```
|
||||
|
||||
```swift
|
||||
john.residence?.address = johnsAddress
|
||||
|
||||
if let johnsStreet = john.residence?.address?.street {
|
||||
println("John's street name is \(johnsStreet).")
|
||||
print("John's street name is \(johnsStreet).")
|
||||
} else {
|
||||
println("Unable to retrieve the address.")
|
||||
print("Unable to retrieve the address.")
|
||||
}
|
||||
// 打印 "John's street name is Laurel Street."。
|
||||
// prints "John's street name is Laurel Street."
|
||||
```
|
||||
|
||||
值得注意的是,“`!`”符号在给`john.residence.address`分配`address`实例时的使用。`john.residence`属性是一个可选类型,因此你需要在它获取`address`属性之前使用`!`解析以获得它的实际值。
|
||||
在上面的例子中,因为`john.residence`是一个可用的`Residence`实例,所以对`john.residence`的`address`属性赋值成功。
|
||||
|
||||
<a name="chaining_on_methods_with_optional_return_values"></a>
|
||||
##链接可选返回值的方法
|
||||
|
||||
前面的例子解释了如何通过可选链来获得可选类型属性值。你也可以通过可选链调用一个返回可选类型值的方法并按需链接该方法的返回值。
|
||||
|
||||
下面的例子通过可选链调用了`Address`类中的`buildingIdentifier` 方法。这个方法的返回值类型是`String?`。如上所述,这个方法在可选链调用后最终的返回值类型依然是`String?`:
|
||||
##对返回可空值的函数进行链接
|
||||
上面的例子说明了如何通过可空链式调用来获取可空属性值。我们还可以通过可空链式调用来调用返回可空值的方法,并且可以继续对可空值进行链接。
|
||||
|
||||
在下面的例子中,通过可空链式调用来调用`Address`的`buildingIdentifier()`方法。这个方法返回`String?`类型。正如上面所说,通过可空链式调用的方法的最终返回值还是`String?`:
|
||||
|
||||
```swift
|
||||
if let buildingIdentifier = john.residence?.address?.buildingIdentifier() {
|
||||
println("John's building identifier is \(buildingIdentifier).")
|
||||
print("John's building identifier is \(buildingIdentifier).")
|
||||
}
|
||||
// 打印 "John's building identifier is The Larches."。
|
||||
// prints "John's building identifier is The Larches."
|
||||
```
|
||||
|
||||
如果你还想进一步对方法返回值执行可选链,将可选链问号符放在方法括号的后面:
|
||||
如果要进一步对方法的返回值进行可空链式调用,在方法`buildingIdentifier()`的圆括号后面加上问号:
|
||||
|
||||
```swift
|
||||
if let upper = john.residence?.address?.buildingIdentifier()?.uppercaseString {
|
||||
println("John's uppercase building identifier is \(upper).")
|
||||
if let beginsWithThe =
|
||||
john.residence?.address?.buildingIdentifier()?.hasPrefix("The") {
|
||||
if beginsWithThe {
|
||||
print("John's building identifier begins with \"The\".")
|
||||
} else {
|
||||
print("John's building identifier does not begin with \"The\".")
|
||||
}
|
||||
}
|
||||
// 打印 "John's uppercase building identifier is THE LARCHES."。
|
||||
// prints "John's building identifier begins with "The"."
|
||||
```
|
||||
|
||||
> 注意:
|
||||
在上面的例子中,你将可选链问号符放在括号后面是因为你想要链接的可选值是`buildingIdentifier`方法的返回值,不是`buildingIdentifier`方法本身。
|
||||
>
|
||||
注意:
|
||||
在上面的例子中在,在方法的圆括号后面加上问号是因为`buildingIdentifier()`的返回值是可空值,而不是方法本身是可空的。
|
||||
|
||||
@ -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`。这个调用不管是否有抛出都会执行。
|
||||
|
||||
23
source/chapter2/19_Nested_Types.md
Executable file → Normal file
@ -1,6 +1,10 @@
|
||||
> 1.0
|
||||
> 翻译:[Lin-H](https://github.com/Lin-H)
|
||||
> 校对:[shinyzhu](https://github.com/shinyzhu)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[SergioChan](https://github.com/SergioChan)
|
||||
|
||||
# 嵌套类型
|
||||
-----------------
|
||||
|
||||
@ -41,15 +45,15 @@ struct BlackjackCard {
|
||||
case .Jack, .Queen, .King:
|
||||
return Values(first: 10, second: nil)
|
||||
default:
|
||||
return Values(first: self.toRaw(), second: nil)
|
||||
return Values(first: self.rawValue, second: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// BlackjackCard 的属性和方法
|
||||
let rank: Rank, suit: Suit
|
||||
var description: String {
|
||||
var output = "suit is \(suit.toRaw()),"
|
||||
var output = "suit is \(suit.rawValue),"
|
||||
output += " value is \(rank.values.first)"
|
||||
if let second = rank.values.second {
|
||||
output += " or \(second)"
|
||||
@ -63,20 +67,20 @@ struct BlackjackCard {
|
||||
|
||||
枚举型的`Rank`用来描述扑克牌从`Ace`~10,`J`,`Q`,`K`,13张牌,并分别用一个`Int`类型的值表示牌的面值。(这个`Int`类型的值不适用于`Ace`,`J`,`Q`,`K`的牌)。
|
||||
|
||||
如上文所提到的,枚举型`Rank`在自己内部定义了一个嵌套结构体`Values`。这个结构体包含两个变量,只有`Ace`有两个数值,其余牌都只有一个数值。结构体`Values`中定义的两个属性:
|
||||
如上文所提到的,枚举型`Rank`在自己内部定义了一个嵌套结构体`Values`。在这个结构体中,只有`Ace`有两个数值,其余牌都只有一个数值。结构体`Values`中定义的两个属性:
|
||||
|
||||
`first`, 为` Int`
|
||||
`second`, 为 `Int?`, 或 “optional `Int`”
|
||||
|
||||
`Rank`定义了一个计算属性`values`,这个计算属性会根据牌的面值,用适当的数值去初始化`Values`实例,并赋值给`values`。对于`J`,`Q`,`K`,`Ace`会使用特殊数值,对于数字面值的牌使用`Int`类型的值。
|
||||
`Rank`定义了一个计算属性`values`,它将会返回一个结构体`Values`的实例。这个计算属性会根据牌的面值,用适当的数值去初始化`Values`实例,并赋值给`values`。对于`J`,`Q`,`K`,`Ace`会使用特殊数值,对于数字面值的牌使用`Int`类型的值。
|
||||
|
||||
`BlackjackCard`结构体自身有两个属性—`rank`与`suit`,也同样定义了一个计算属性`description`,`description`属性用`rank`和`suit`的中内容来构建对这张扑克牌名字和数值的描述,并用可选类型`second`来检查是否存在第二个值,若存在,则在原有的描述中增加对第二数值的描述。
|
||||
|
||||
因为`BlackjackCard`是一个没有自定义构造函数的结构体,在[Memberwise Initializers for Structure Types](https://github.com/CocoaChina-editors/Welcome-to-Swift/blob/master/The%20Swift%20Programming%20Language/02Language%20Guide/14Initialization.md)中知道结构体有默认的成员构造函数,所以你可以用默认的`initializer`去初始化新的常量`theAceOfSpades`:
|
||||
因为`BlackjackCard`是一个没有自定义构造函数的结构体,在[结构体的逐一成员构造器](./14_Initialization.html#memberwise_initializers_for_structure_types)中知道结构体有默认的成员构造函数,所以你可以用默认的`initializer`去初始化新的常量`theAceOfSpades`:
|
||||
|
||||
```swift
|
||||
let theAceOfSpades = BlackjackCard(rank: .Ace, suit: .Spades)
|
||||
println("theAceOfSpades: \(theAceOfSpades.description)")
|
||||
print("theAceOfSpades: \(theAceOfSpades.description)")
|
||||
// 打印出 "theAceOfSpades: suit is ♠, value is 1 or 11"
|
||||
```
|
||||
|
||||
@ -88,9 +92,8 @@ println("theAceOfSpades: \(theAceOfSpades.description)")
|
||||
在外部对嵌套类型的引用,以被嵌套类型的名字为前缀,加上所要引用的属性名:
|
||||
|
||||
```swift
|
||||
let heartsSymbol = BlackjackCard.Suit.Hearts.toRaw()
|
||||
let heartsSymbol = BlackjackCard.Suit.Hearts.rawValue
|
||||
// 红心的符号 为 "♡"
|
||||
```
|
||||
|
||||
对于上面这个例子,这样可以使`Suit`, `Rank`, 和 `Values`的名字尽可能的短,因为它们的名字会自然的由被定义的上下文来限定。
|
||||
|
||||
对于上面这个例子,这样可以使`Suit`, `Rank`, 和 `Values`的名字尽可能的短,因为它们的名字会自然的由定义它们的上下文来限定。
|
||||
|
||||
261
source/chapter2/19_Type_Casting.md
Normal file
@ -0,0 +1,261 @@
|
||||
# 类型转换(Type Casting)
|
||||
-----------------
|
||||
|
||||
> 1.0
|
||||
> 翻译:[xiehurricane](https://github.com/xiehurricane)
|
||||
> 校对:[happyming](https://github.com/happyming)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[yangsiy](https://github.com/yangsiy)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [定义一个类层次作为例子](#defining_a_class_hierarchy_for_type_casting)
|
||||
- [检查类型](#checking_type)
|
||||
- [向下转型(Downcasting)](#downcasting)
|
||||
- [`Any`和`AnyObject`的类型转换](#type_casting_for_any_and_anyobject)
|
||||
|
||||
|
||||
_类型转换_ 可以判断实例的类型,也可以将实例看做是其父类或者子类的实例。
|
||||
|
||||
类型转换在 Swift 中使用 `is` 和 `as` 操作符实现。这两个操作符提供了一种简单达意的方式去检查值的类型或者转换它的类型。
|
||||
|
||||
你也可以用它来检查一个类是否实现了某个协议,就像在 [检验协议的一致性](./22_Protocols.html#checking_for_protocol_conformance)部分讲述的一样。
|
||||
|
||||
<a name="defining_a_class_hierarchy_for_type_casting"></a>
|
||||
## 定义一个类层次作为例子
|
||||
|
||||
你可以将类型转换用在类和子类的层次结构上,检查特定类实例的类型并且转换这个类实例的类型成为这个层次结构中的其他类型。下面的三个代码段定义了一个类层次和一个包含了几个这些类实例的数组,作为类型转换的例子。
|
||||
|
||||
第一个代码片段定义了一个新的基础类 `MediaItem`。这个类为任何出现在数字媒体库的媒体项提供基础功能。特别的,它声明了一个 `String` 类型的 `name` 属性,和一个 `init name` 初始化器。(假定所有的媒体项都有个名称。)
|
||||
|
||||
```swift
|
||||
class MediaItem {
|
||||
var name: String
|
||||
init(name: String) {
|
||||
self.name = name
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
下一个代码段定义了 `MediaItem` 的两个子类。第一个子类 `Movie` 封装了与电影相关的额外信息,在父类(或者说基类)的基础上增加了一个 `director`(导演)属性,和相应的初始化器。第二个子类 `Song`,在父类的基础上增加了一个 `artist`(艺术家)属性,和相应的初始化器:
|
||||
|
||||
```swift
|
||||
class Movie: MediaItem {
|
||||
var director: String
|
||||
init(name: String, director: String) {
|
||||
self.director = director
|
||||
super.init(name: name)
|
||||
}
|
||||
}
|
||||
|
||||
class Song: MediaItem {
|
||||
var artist: String
|
||||
init(name: String, artist: String) {
|
||||
self.artist = artist
|
||||
super.init(name: name)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
最后一个代码段创建了一个数组常量 `library`,包含两个 `Movie` 实例和三个 `Song` 实例。`library` 的类型是在它被初始化时根据它数组中所包含的内容推断来的。Swift的类型检测器能够推理出 `Movie` 和 `Song` 有共同的父类 `MediaItem`,所以它推断出 `[MediaItem]` 类作为 `library` 的类型。
|
||||
|
||||
```swift
|
||||
let library = [
|
||||
Movie(name: "Casablanca", director: "Michael Curtiz"),
|
||||
Song(name: "Blue Suede Shoes", artist: "Elvis Presley"),
|
||||
Movie(name: "Citizen Kane", director: "Orson Welles"),
|
||||
Song(name: "The One And Only", artist: "Chesney Hawkes"),
|
||||
Song(name: "Never Gonna Give You Up", artist: "Rick Astley")
|
||||
]
|
||||
// the type of "library" is inferred to be [MediaItem]
|
||||
```
|
||||
|
||||
在幕后 `library` 里存储的媒体项依然是 `Movie` 和 `Song` 类型的。但是,若你迭代它,依次取出的实例会是 `MediaItem` 类型的,而不是 `Movie` 和 `Song` 类型。为了让它们作为原本的类型工作,你需要检查它们的类型或者向下转换它们到其它类型,就像下面描述的一样。
|
||||
|
||||
<a name="checking_type"></a>
|
||||
## 检查类型(Checking Type)
|
||||
|
||||
用类型检查操作符(`is`)来检查一个实例是否属于特定子类型。若实例属于那个子类型,类型检查操作符返回 `true`,否则返回 `false`。
|
||||
|
||||
下面的例子定义了两个变量,`movieCount` 和 `songCount`,用来计算数组 `library` 中 `Movie` 和 `Song` 类型的实例数量。
|
||||
|
||||
```swift
|
||||
var movieCount = 0
|
||||
var songCount = 0
|
||||
|
||||
for item in library {
|
||||
if item is Movie {
|
||||
++movieCount
|
||||
} else if item is Song {
|
||||
++songCount
|
||||
}
|
||||
}
|
||||
|
||||
print("Media library contains \(movieCount) movies and \(songCount) songs")
|
||||
// prints "Media library contains 2 movies and 3 songs"
|
||||
```
|
||||
|
||||
示例迭代了数组 `library` 中的所有项。每一次,`for`-`in` 循环设置
|
||||
`item` 为数组中的下一个 `MediaItem`。
|
||||
|
||||
若当前 `MediaItem` 是一个 `Movie` 类型的实例,`item is Movie` 返回
|
||||
`true`,相反返回 `false`。同样的,`item is
|
||||
Song` 检查item是否为 `Song` 类型的实例。在循环结束后,`movieCount` 和 `songCount` 的值就是被找到属于各自的类型的实例数量。
|
||||
|
||||
<a name="downcasting"></a>
|
||||
## 向下转型(Downcasting)
|
||||
|
||||
某类型的一个常量或变量可能在幕后实际上属于一个子类。当确定是这种情况时,你可以尝试向下转到它的子类型,用类型转换操作符(`as?` 或 `as!`)
|
||||
|
||||
因为向下转型可能会失败,类型转型操作符带有两种不同形式。条件形式(conditional form) `as?` 返回一个你试图向下转成的类型的可选值(optional value)。强制形式 `as!` 把试图向下转型和强制解包(force-unwraps)结果作为一个混合动作。
|
||||
|
||||
当你不确定向下转型可以成功时,用类型转换的条件形式(`as?`)。条件形式的类型转换总是返回一个可选值(optional value),并且若下转是不可能的,可选值将是 `nil`。这使你能够检查向下转型是否成功。
|
||||
|
||||
只有你可以确定向下转型一定会成功时,才使用强制形式(`as!`)。当你试图向下转型为一个不正确的类型时,强制形式的类型转换会触发一个运行时错误。
|
||||
|
||||
下面的例子,迭代了 `library` 里的每一个 `MediaItem`,并打印出适当的描述。要这样做,`item` 需要真正作为 `Movie` 或 `Song` 的类型来使用,不仅仅是作为 `MediaItem`。为了能够在描述中使用 `Movie` 或 `Song` 的 `director` 或 `artist` 属性,这是必要的。
|
||||
|
||||
在这个示例中,数组中的每一个 `item` 可能是 `Movie` 或 `Song`。事前你不知道每个 `item` 的真实类型,所以这里使用条件形式的类型转换(`as?`)去检查循环里的每次下转。
|
||||
|
||||
```swift
|
||||
for item in library {
|
||||
if let movie = item as? Movie {
|
||||
print("Movie: '\(movie.name)', dir. \(movie.director)")
|
||||
} else if let song = item as? Song {
|
||||
print("Song: '\(song.name)', by \(song.artist)")
|
||||
}
|
||||
}
|
||||
|
||||
// Movie: 'Casablanca', dir. Michael Curtiz
|
||||
// Song: 'Blue Suede Shoes', by Elvis Presley
|
||||
// Movie: 'Citizen Kane', dir. Orson Welles
|
||||
// Song: 'The One And Only', by Chesney Hawkes
|
||||
// Song: 'Never Gonna Give You Up', by Rick Astley
|
||||
```
|
||||
|
||||
示例首先试图将 `item` 下转为 `Movie`。因为 `item` 是一个 `MediaItem`
|
||||
类型的实例,它可能是一个 `Movie`;同样,它也可能是一个 `Song`,或者仅仅是基类
|
||||
`MediaItem`。因为不确定,`as?`形式在试图下转时将返回一个可选值。`item as? Movie` 的返回值是 `Movie?` 或 “可选 `Movie`”类型。
|
||||
|
||||
当向下转型为 `Movie` 应用在两个 `Song`
|
||||
实例时将会失败。为了处理这种情况,上面的例子使用了可选绑定(optional binding)来检查可选 `Movie` 真的包含一个值(这个是为了判断下转是否成功。)可选绑定是这样写的“`if let movie = item as? Movie`”,可以这样解读:
|
||||
|
||||
“尝试将 `item` 转为 `Movie` 类型。若成功,设置一个新的临时常量 `movie` 来存储返回的可选 `Movie`”
|
||||
|
||||
若向下转型成功,然后 `movie` 的属性将用于打印一个 `Movie` 实例的描述,包括它的导演的名字 `director` 。相近的原理被用来检测 `Song` 实例,当 `Song` 被找到时则打印它的描述(包含 `artist` 的名字)。
|
||||
|
||||
> 注意:
|
||||
> 转换没有真的改变实例或它的值。潜在的根本的实例保持不变;只是简单地把它作为它被转换成的类来使用。
|
||||
|
||||
<a name="type_casting_for_any_and_anyobject"></a>
|
||||
## `Any`和`AnyObject`的类型转换
|
||||
|
||||
Swift为不确定类型提供了两种特殊类型别名:
|
||||
|
||||
* `AnyObject`可以代表任何class类型的实例。
|
||||
* `Any`可以表示任何类型,包括方法类型(function types)。
|
||||
|
||||
> 注意:
|
||||
> 只有当你明确的需要它的行为和功能时才使用`Any`和`AnyObject`。在你的代码里使用你期望的明确的类型总是更好的。
|
||||
|
||||
<a name="anyobject"></a>
|
||||
### `AnyObject`类型
|
||||
|
||||
当在工作中使用 Cocoa APIs,我们一般会接收一个`[AnyObject]`类型的数组,或者说“一个任何对象类型的数组”。这是因为 Objective-C 没有明确的类型化数组。但是,你常常可以从 API 提供的信息中清晰地确定数组中对象的类型。
|
||||
|
||||
在这些情况下,你可以使用强制形式的类型转换(`as`)来下转在数组中的每一项到比 `AnyObject` 更明确的类型,不需要可选解析(optional unwrapping)。
|
||||
|
||||
下面的示例定义了一个 `[AnyObject]` 类型的数组并填入三个`Movie`类型的实例:
|
||||
|
||||
```swift
|
||||
let someObjects: [AnyObject] = [
|
||||
Movie(name: "2001: A Space Odyssey", director: "Stanley Kubrick"),
|
||||
Movie(name: "Moon", director: "Duncan Jones"),
|
||||
Movie(name: "Alien", director: "Ridley Scott")
|
||||
]
|
||||
```
|
||||
|
||||
因为知道这个数组只包含 `Movie` 实例,你可以直接用(`as!`)下转并解包到不可选的`Movie`类型:
|
||||
|
||||
```swift
|
||||
for object in someObjects {
|
||||
let movie = object as! Movie
|
||||
print("Movie: '\(movie.name)', dir. \(movie.director)")
|
||||
}
|
||||
// Movie: '2001: A Space Odyssey', dir. Stanley Kubrick
|
||||
// Movie: 'Moon', dir. Duncan Jones
|
||||
// Movie: 'Alien', dir. Ridley Scott
|
||||
```
|
||||
|
||||
为了变为一个更短的形式,下转`someObjects`数组为`[Movie]`类型来代替下转数组中每一项的方式。
|
||||
|
||||
```swift
|
||||
for movie in someObjects as! [Movie] {
|
||||
print("Movie: '\(movie.name)', dir. \(movie.director)")
|
||||
}
|
||||
// Movie: '2001: A Space Odyssey', dir. Stanley Kubrick
|
||||
// Movie: 'Moon', dir. Duncan Jones
|
||||
// Movie: 'Alien', dir. Ridley Scott
|
||||
```
|
||||
|
||||
### `Any`类型
|
||||
|
||||
这里有个示例,使用 `Any` 类型来和混合的不同类型一起工作,包括方法类型和非 `class` 类型。它创建了一个可以存储`Any`类型的数组 `things`。
|
||||
|
||||
```swift
|
||||
var things = [Any]()
|
||||
|
||||
things.append(0)
|
||||
things.append(0.0)
|
||||
things.append(42)
|
||||
things.append(3.14159)
|
||||
things.append("hello")
|
||||
things.append((3.0, 5.0))
|
||||
things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman"))
|
||||
things.append({ (name: String) -> String in "Hello, \(name)" })
|
||||
```
|
||||
|
||||
`things` 数组包含两个 `Int` 值,2个 `Double` 值,1个 `String` 值,一个元组 `(Double, Double)` ,电影“Ghostbusters”,和一个获取 `String` 值并返回另一个 `String` 值的闭包表达式。
|
||||
|
||||
你可以在 `switch` 表达式的cases中使用 `is` 和 `as` 操作符来发觉只知道是 `Any` 或 `AnyObject` 的常量或变量的类型。下面的示例迭代 `things` 数组中的每一项的并用`switch`语句查找每一项的类型。这几种 `switch` 语句的情形绑定它们匹配的值到一个规定类型的常量,让它们的值可以被打印:
|
||||
|
||||
```swift
|
||||
for thing in things {
|
||||
switch thing {
|
||||
case 0 as Int:
|
||||
print("zero as an Int")
|
||||
case 0 as Double:
|
||||
print("zero as a Double")
|
||||
case let someInt as Int:
|
||||
print("an integer value of \(someInt)")
|
||||
case let someDouble as Double where someDouble > 0:
|
||||
print("a positive double value of \(someDouble)")
|
||||
case is Double:
|
||||
print("some other double value that I don't want to print")
|
||||
case let someString as String:
|
||||
print("a string value of \"\(someString)\"")
|
||||
case let (x, y) as (Double, Double):
|
||||
print("an (x, y) point at \(x), \(y)")
|
||||
case let movie as Movie:
|
||||
print("a movie called '\(movie.name)', dir. \(movie.director)")
|
||||
case let stringConverter as String -> String:
|
||||
print(stringConverter("Michael"))
|
||||
default:
|
||||
print("something else")
|
||||
}
|
||||
}
|
||||
|
||||
// zero as an Int
|
||||
// zero as a Double
|
||||
// an integer value of 42
|
||||
// a positive double value of 3.14159
|
||||
// a string value of "hello"
|
||||
// an (x, y) point at 3.0, 5.0
|
||||
// a movie called 'Ghostbusters', dir. Ivan Reitman
|
||||
// Hello, Michael
|
||||
```
|
||||
|
||||
|
||||
> 注意:
|
||||
> 在一个switch语句的case中使用强制形式的类型转换操作符(as, 而不是 as?)来检查和转换到一个明确的类型。在 `switch` case 语句的内容中这种检查总是安全的。
|
||||
99
source/chapter2/20_Nested_Types.md
Executable file
@ -0,0 +1,99 @@
|
||||
# 嵌套类型(Nested Types)
|
||||
-----------------
|
||||
|
||||
> 1.0
|
||||
> 翻译:[Lin-H](https://github.com/Lin-H)
|
||||
> 校对:[shinyzhu](https://github.com/shinyzhu)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[SergioChan](https://github.com/SergioChan)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [嵌套类型实例](#nested_types_in_action)
|
||||
- [嵌套类型的引用](#referring_to_nested_types)
|
||||
|
||||
枚举类型常被用于实现特定类或结构体的功能。也能够在有多种变量类型的环境中,方便地定义通用类或结构体来使用,为了实现这种功能,Swift允许你定义嵌套类型,可以在枚举类型、类和结构体中定义支持嵌套的类型。
|
||||
|
||||
要在一个类型中嵌套另一个类型,将需要嵌套的类型的定义写在被嵌套类型的区域{}内,而且可以根据需要定义多级嵌套。
|
||||
|
||||
<a name="nested_types_in_action"></a>
|
||||
##嵌套类型实例
|
||||
|
||||
下面这个例子定义了一个结构体`BlackjackCard`(二十一点),用来模拟`BlackjackCard`中的扑克牌点数。`BlackjackCard`结构体包含2个嵌套定义的枚举类型`Suit` 和 `Rank`。
|
||||
|
||||
在`BlackjackCard`规则中,`Ace`牌可以表示1或者11,`Ace`牌的这一特征用一个嵌套在枚举型`Rank`的结构体`Values`来表示。
|
||||
|
||||
```swift
|
||||
struct BlackjackCard {
|
||||
// 嵌套定义枚举型Suit
|
||||
enum Suit: Character {
|
||||
case Spades = "♠", Hearts = "♡", Diamonds = "♢", Clubs = "♣"
|
||||
}
|
||||
|
||||
// 嵌套定义枚举型Rank
|
||||
enum Rank: Int {
|
||||
case Two = 2, Three, Four, Five, Six, Seven, Eight, Nine, Ten
|
||||
case Jack, Queen, King, Ace
|
||||
struct Values {
|
||||
let first: Int, second: Int?
|
||||
}
|
||||
var values: Values {
|
||||
switch self {
|
||||
case .Ace:
|
||||
return Values(first: 1, second: 11)
|
||||
case .Jack, .Queen, .King:
|
||||
return Values(first: 10, second: nil)
|
||||
default:
|
||||
return Values(first: self.rawValue, second: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BlackjackCard 的属性和方法
|
||||
let rank: Rank, suit: Suit
|
||||
var description: String {
|
||||
var output = "suit is \(suit.rawValue),"
|
||||
output += " value is \(rank.values.first)"
|
||||
if let second = rank.values.second {
|
||||
output += " or \(second)"
|
||||
}
|
||||
return output
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
枚举型的`Suit`用来描述扑克牌的四种花色,并分别用一个`Character`类型的值代表花色符号。
|
||||
|
||||
枚举型的`Rank`用来描述扑克牌从`Ace`~10,`J`,`Q`,`K`,13张牌,并分别用一个`Int`类型的值表示牌的面值。(这个`Int`类型的值不适用于`Ace`,`J`,`Q`,`K`的牌)。
|
||||
|
||||
如上文所提到的,枚举型`Rank`在自己内部定义了一个嵌套结构体`Values`。在这个结构体中,只有`Ace`有两个数值,其余牌都只有一个数值。结构体`Values`中定义的两个属性:
|
||||
|
||||
- `first`为` Int`
|
||||
- `second`为 `Int?` 或 “optional `Int`”
|
||||
|
||||
`Rank`定义了一个计算属性`values`,它将会返回一个结构体`Values`的实例。这个计算属性会根据牌的面值,用适当的数值去初始化`Values`实例,并赋值给`values`。对于`J`,`Q`,`K`,`Ace`会使用特殊数值,对于数字面值的牌使用`Int`类型的值。
|
||||
|
||||
`BlackjackCard`结构体自身有两个属性—`rank`与`suit`,也同样定义了一个计算属性`description`,`description`属性用`rank`和`suit`的中内容来构建对这张扑克牌名字和数值的描述,并用可选类型`second`来检查是否存在第二个值,若存在,则在原有的描述中增加对第二数值的描述。
|
||||
|
||||
因为`BlackjackCard`是一个没有自定义构造函数的结构体,在[结构体的逐一成员构造器](./14_Initialization.html#memberwise_initializers_for_structure_types)中知道结构体有默认的成员构造函数,所以你可以用默认的`initializer`去初始化新的常量`theAceOfSpades`:
|
||||
|
||||
```swift
|
||||
let theAceOfSpades = BlackjackCard(rank: .Ace, suit: .Spades)
|
||||
print("theAceOfSpades: \(theAceOfSpades.description)")
|
||||
// 打印出 "theAceOfSpades: suit is ♠, value is 1 or 11"
|
||||
```
|
||||
|
||||
尽管`Rank`和`Suit`嵌套在`BlackjackCard`中,但仍可被引用,所以在初始化实例时能够通过枚举类型中的成员名称单独引用。在上面的例子中`description`属性能正确得输出对`Ace`牌有1和11两个值。
|
||||
|
||||
<a name="referring_to_nested_types"></a>
|
||||
##嵌套类型的引用
|
||||
|
||||
在外部对嵌套类型的引用,以被嵌套类型的名字为前缀,加上所要引用的属性名:
|
||||
|
||||
```swift
|
||||
let heartsSymbol = BlackjackCard.Suit.Hearts.rawValue
|
||||
// 红心的符号 为 "♡"
|
||||
```
|
||||
|
||||
对于上面这个例子,这样可以使`Suit`, `Rank`, 和 `Values`的名字尽可能的短,因为它们的名字会自然的由定义它们的上下文来限定。
|
||||
@ -1,6 +1,10 @@
|
||||
> 1.0
|
||||
> 翻译:[xiehurricane](https://github.com/xiehurricane)
|
||||
> 校对:[happyming](https://github.com/happyming)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[yangsiy](https://github.com/yangsiy)
|
||||
|
||||
# 类型转换(Type Casting)
|
||||
-----------------
|
||||
|
||||
@ -14,16 +18,16 @@
|
||||
|
||||
_类型转换_可以判断实例的类型,也可以将实例看做是其父类或者子类的实例。
|
||||
|
||||
类型转换在 Swift 中使用`is` 和 `as`操作符实现。这两个操作符提供了一种简单达意的方式去检查值的类型或者转换它的类型。
|
||||
类型转换在 Swift 中使用 `is` 和 `as` 操作符实现。这两个操作符提供了一种简单达意的方式去检查值的类型或者转换它的类型。
|
||||
|
||||
你也可以用来检查一个类是否实现了某个协议,就像在 [Checking for Protocol Conformance](Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-XID_363)部分讲述的一样。
|
||||
你也可以用它来检查一个类是否实现了某个协议,就像在 [检验协议的一致性](./22_Protocols.html#checking_for_protocol_conformance)部分讲述的一样。
|
||||
|
||||
<a name="defining_a_class_hierarchy_for_type_casting"></a>
|
||||
## 定义一个类层次作为例子
|
||||
|
||||
你可以将它用在类和子类的层次结构上,检查特定类实例的类型并且转换这个类实例的类型成为这个层次结构中的其他类型。这下面的三个代码段定义了一个类层次和一个包含了几个这些类实例的数组,作为类型转换的例子。
|
||||
你可以将类型转换用在类和子类的层次结构上,检查特定类实例的类型并且转换这个类实例的类型成为这个层次结构中的其他类型。下面的三个代码段定义了一个类层次和一个包含了几个这些类实例的数组,作为类型转换的例子。
|
||||
|
||||
第一个代码片段定义了一个新的基础类`MediaItem`。这个类为任何出现在数字媒体库的媒体项提供基础功能。特别的,它声明了一个 `String` 类型的 `name` 属性,和一个`init name`初始化器。(它假定所有的媒体项都有个名称。)
|
||||
第一个代码片段定义了一个新的基础类 `MediaItem`。这个类为任何出现在数字媒体库的媒体项提供基础功能。特别的,它声明了一个 `String` 类型的 `name` 属性,和一个 `init name` 初始化器。(假定所有的媒体项都有个名称。)
|
||||
|
||||
```swift
|
||||
class MediaItem {
|
||||
@ -34,7 +38,7 @@ class MediaItem {
|
||||
}
|
||||
```
|
||||
|
||||
下一个代码段定义了 `MediaItem` 的两个子类。第一个子类`Movie`,在父类(或者说基类)的基础上增加了一个 `director`(导演) 属性,和相应的初始化器。第二个类在父类的基础上增加了一个 `artist`(艺术家) 属性,和相应的初始化器:
|
||||
下一个代码段定义了 `MediaItem` 的两个子类。第一个子类 `Movie` 封装了与电影相关的额外信息,在父类(或者说基类)的基础上增加了一个 `director`(导演)属性,和相应的初始化器。第二个子类 `Song`,在父类的基础上增加了一个 `artist`(艺术家)属性,和相应的初始化器:
|
||||
|
||||
```swift
|
||||
class Movie: MediaItem {
|
||||
@ -54,7 +58,7 @@ class Song: MediaItem {
|
||||
}
|
||||
```
|
||||
|
||||
最后一个代码段创建了一个数组常量 `library`,包含两个`Movie`实例和三个`Song`实例。`library`的类型是在它被初始化时根据它数组中所包含的内容推断来的。Swift 的类型检测器能够演绎出`Movie` 和 `Song` 有共同的父类 `MediaItem` ,所以它推断出 `[MediaItem]` 类作为 `library` 的类型。
|
||||
最后一个代码段创建了一个数组常量 `library`,包含两个 `Movie` 实例和三个 `Song` 实例。`library` 的类型是在它被初始化时根据它数组中所包含的内容推断来的。Swift的类型检测器能够推理出 `Movie` 和 `Song` 有共同的父类 `MediaItem`,所以它推断出 `[MediaItem]` 类作为 `library` 的类型。
|
||||
|
||||
```swift
|
||||
let library = [
|
||||
@ -67,14 +71,14 @@ let library = [
|
||||
// the type of "library" is inferred to be [MediaItem]
|
||||
```
|
||||
|
||||
在幕后`library` 里存储的媒体项依然是 `Movie` 和 `Song` 类型的,但是,若你迭代它,取出的实例会是 `MediaItem` 类型的,而不是 `Movie` 和 `Song` 类型的。为了让它们作为它们本来的类型工作,你需要检查它们的类型或者向下转换它们的类型到其它类型,就像下面描述的一样。
|
||||
在幕后 `library` 里存储的媒体项依然是 `Movie` 和 `Song` 类型的。但是,若你迭代它,依次取出的实例会是 `MediaItem` 类型的,而不是 `Movie` 和 `Song` 类型。为了让它们作为原本的类型工作,你需要检查它们的类型或者向下转换它们到其它类型,就像下面描述的一样。
|
||||
|
||||
<a name="checking_type"></a>
|
||||
## 检查类型(Checking Type)
|
||||
|
||||
用类型检查操作符(`is`)来检查一个实例是否属于特定子类型。若实例属于那个子类型,类型检查操作符返回 `true` ,否则返回 `false` 。
|
||||
用类型检查操作符(`is`)来检查一个实例是否属于特定子类型。若实例属于那个子类型,类型检查操作符返回 `true`,否则返回 `false`。
|
||||
|
||||
下面的例子定义了两个变量,`movieCount` 和 `songCount`,用来计算数组`library` 中 `Movie` 和 `Song` 类型的实例数量。
|
||||
下面的例子定义了两个变量,`movieCount` 和 `songCount`,用来计算数组 `library` 中 `Movie` 和 `Song` 类型的实例数量。
|
||||
|
||||
```swift
|
||||
var movieCount = 0
|
||||
@ -88,38 +92,38 @@ for item in library {
|
||||
}
|
||||
}
|
||||
|
||||
println("Media library contains \(movieCount) movies and \(songCount) songs")
|
||||
print("Media library contains \(movieCount) movies and \(songCount) songs")
|
||||
// prints "Media library contains 2 movies and 3 songs"
|
||||
```
|
||||
|
||||
示例迭代了数组 `library` 中的所有项。每一次, `for`-`in` 循环设置
|
||||
示例迭代了数组 `library` 中的所有项。每一次,`for`-`in` 循环设置
|
||||
`item` 为数组中的下一个 `MediaItem`。
|
||||
|
||||
若当前 `MediaItem` 是一个 `Movie` 类型的实例, `item is Movie` 返回
|
||||
若当前 `MediaItem` 是一个 `Movie` 类型的实例,`item is Movie` 返回
|
||||
`true`,相反返回 `false`。同样的,`item is
|
||||
Song`检查item是否为`Song`类型的实例。在循环结束后,`movieCount` 和 `songCount`的值就是被找到属于各自的类型的实例数量。
|
||||
Song` 检查item是否为 `Song` 类型的实例。在循环结束后,`movieCount` 和 `songCount` 的值就是被找到属于各自的类型的实例数量。
|
||||
|
||||
<a name="downcasting"></a>
|
||||
## 向下转型(Downcasting)
|
||||
|
||||
某类型的一个常量或变量可能在幕后实际上属于一个子类。你可以相信,上面就是这种情况。你可以尝试向下转到它的子类型,用类型转换操作符(`as`)
|
||||
某类型的一个常量或变量可能在幕后实际上属于一个子类。当确定是这种情况时,你可以尝试向下转到它的子类型,用类型转换操作符(`as?` 或 `as!`)
|
||||
|
||||
因为向下转型可能会失败,类型转型操作符带有两种不同形式。可选形式( optional form) `as?` 返回一个你试图下转成的类型的可选值(optional value)。强制形式 `as` 把试图向下转型和强制解包(force-unwraps)结果作为一个混合动作。
|
||||
因为向下转型可能会失败,类型转型操作符带有两种不同形式。条件形式(conditional form) `as?` 返回一个你试图向下转成的类型的可选值(optional value)。强制形式 `as!` 把试图向下转型和强制解包(force-unwraps)结果作为一个混合动作。
|
||||
|
||||
当你不确定向下转型可以成功时,用类型转换的可选形式(`as?`)。可选形式的类型转换总是返回一个可选值(optional value),并且若下转是不可能的,可选值将是 `nil` 。这使你能够检查向下转型是否成功。
|
||||
当你不确定向下转型可以成功时,用类型转换的条件形式(`as?`)。条件形式的类型转换总是返回一个可选值(optional value),并且若下转是不可能的,可选值将是 `nil`。这使你能够检查向下转型是否成功。
|
||||
|
||||
只有你可以确定向下转型一定会成功时,才使用强制形式。当你试图向下转型为一个不正确的类型时,强制形式的类型转换会触发一个运行时错误。
|
||||
只有你可以确定向下转型一定会成功时,才使用强制形式(`as!`)。当你试图向下转型为一个不正确的类型时,强制形式的类型转换会触发一个运行时错误。
|
||||
|
||||
下面的例子,迭代了`library`里的每一个 `MediaItem` ,并打印出适当的描述。要这样做,`item`需要真正作为`Movie` 或 `Song`的类型来使用。不仅仅是作为 `MediaItem`。为了能够使用`Movie` 或 `Song`的 `director` 或 `artist`属性,这是必要的。
|
||||
下面的例子,迭代了 `library` 里的每一个 `MediaItem`,并打印出适当的描述。要这样做,`item` 需要真正作为 `Movie` 或 `Song` 的类型来使用,不仅仅是作为 `MediaItem`。为了能够在描述中使用 `Movie` 或 `Song` 的 `director` 或 `artist` 属性,这是必要的。
|
||||
|
||||
在这个示例中,数组中的每一个`item`可能是 `Movie` 或 `Song`。 事前你不知道每个`item`的真实类型,所以这里使用可选形式的类型转换 (`as?`)去检查循环里的每次下转。
|
||||
在这个示例中,数组中的每一个 `item` 可能是 `Movie` 或 `Song`。事前你不知道每个 `item` 的真实类型,所以这里使用条件形式的类型转换(`as?`)去检查循环里的每次下转。
|
||||
|
||||
```swift
|
||||
for item in library {
|
||||
if let movie = item as? Movie {
|
||||
println("Movie: '\(movie.name)', dir. \(movie.director)")
|
||||
print("Movie: '\(movie.name)', dir. \(movie.director)")
|
||||
} else if let song = item as? Song {
|
||||
println("Song: '\(song.name)', by \(song.artist)")
|
||||
print("Song: '\(song.name)', by \(song.artist)")
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,18 +135,18 @@ for item in library {
|
||||
```
|
||||
|
||||
示例首先试图将 `item` 下转为 `Movie`。因为 `item` 是一个 `MediaItem`
|
||||
类型的实例,它可能是一个`Movie`;同样,它可能是一个 `Song`,或者仅仅是基类
|
||||
`MediaItem`。因为不确定,`as?`形式在试图下转时将返还一个可选值。 `item as Movie` 的返回值是`Movie?`类型或 “optional `Movie`”。
|
||||
类型的实例,它可能是一个 `Movie`;同样,它也可能是一个 `Song`,或者仅仅是基类
|
||||
`MediaItem`。因为不确定,`as?`形式在试图下转时将返回一个可选值。`item as? Movie` 的返回值是 `Movie?` 或 “可选 `Movie`”类型。
|
||||
|
||||
当向下转型为 `Movie` 应用在两个 `Song`
|
||||
实例时将会失败。为了处理这种情况,上面的例子使用了可选绑定(optional binding)来检查可选 `Movie`真的包含一个值(这个是为了判断下转是否成功。)可选绑定是这样写的“`if let movie = item as? Movie`”,可以这样解读:
|
||||
实例时将会失败。为了处理这种情况,上面的例子使用了可选绑定(optional binding)来检查可选 `Movie` 真的包含一个值(这个是为了判断下转是否成功。)可选绑定是这样写的“`if let movie = item as? Movie`”,可以这样解读:
|
||||
|
||||
“尝试将 `item` 转为 `Movie`类型。若成功,设置一个新的临时常量 `movie` 来存储返回的可选`Movie`”
|
||||
“尝试将 `item` 转为 `Movie` 类型。若成功,设置一个新的临时常量 `movie` 来存储返回的可选 `Movie`”
|
||||
|
||||
若向下转型成功,然后`movie`的属性将用于打印一个`Movie`实例的描述,包括它的导演的名字`director`。当`Song`被找到时,一个相近的原理被用来检测 `Song` 实例和打印它的描述。
|
||||
若向下转型成功,然后 `movie` 的属性将用于打印一个 `Movie` 实例的描述,包括它的导演的名字 `director` 。相近的原理被用来检测 `Song` 实例,当 `Song` 被找到时则打印它的描述(包含 `artist` 的名字)。
|
||||
|
||||
> 注意:
|
||||
转换没有真的改变实例或它的值。潜在的根本的实例保持不变;只是简单地把它作为它被转换成的类来使用。
|
||||
> 注意:
|
||||
> 转换没有真的改变实例或它的值。潜在的根本的实例保持不变;只是简单地把它作为它被转换成的类来使用。
|
||||
|
||||
<a name="type_casting_for_any_and_anyobject"></a>
|
||||
## `Any`和`AnyObject`的类型转换
|
||||
@ -152,12 +156,13 @@ Swift为不确定类型提供了两种特殊类型别名:
|
||||
* `AnyObject`可以代表任何class类型的实例。
|
||||
* `Any`可以表示任何类型,包括方法类型(function types)。
|
||||
|
||||
> 注意:
|
||||
只有当你明确的需要它的行为和功能时才使用`Any`和`AnyObject`。在你的代码里使用你期望的明确的类型总是更好的。
|
||||
> 注意:
|
||||
> 只有当你明确的需要它的行为和功能时才使用`Any`和`AnyObject`。在你的代码里使用你期望的明确的类型总是更好的。
|
||||
|
||||
<a name="anyobject"></a>
|
||||
### `AnyObject`类型
|
||||
|
||||
当需要在工作中使用 Cocoa APIs,它一般接收一个`[AnyObject]`类型的数组,或者说“一个任何对象类型的数组”。这是因为 Objective-C 没有明确的类型化数组。但是,你常常可以确定包含在仅从你知道的 API 信息提供的这样一个数组中的对象的类型。
|
||||
当在工作中使用 Cocoa APIs,我们一般会接收一个`[AnyObject]`类型的数组,或者说“一个任何对象类型的数组”。这是因为 Objective-C 没有明确的类型化数组。但是,你常常可以从 API 提供的信息中清晰地确定数组中对象的类型。
|
||||
|
||||
在这些情况下,你可以使用强制形式的类型转换(`as`)来下转在数组中的每一项到比 `AnyObject` 更明确的类型,不需要可选解析(optional unwrapping)。
|
||||
|
||||
@ -171,23 +176,23 @@ let someObjects: [AnyObject] = [
|
||||
]
|
||||
```
|
||||
|
||||
因为知道这个数组只包含 `Movie` 实例,你可以直接用(`as`)下转并解包到不可选的`Movie`类型(ps:其实就是我们常用的正常类型,这里是为了和可选类型相对比)。
|
||||
因为知道这个数组只包含 `Movie` 实例,你可以直接用(`as!`)下转并解包到不可选的`Movie`类型:
|
||||
|
||||
```swift
|
||||
for object in someObjects {
|
||||
let movie = object as Movie
|
||||
println("Movie: '\(movie.name)', dir. \(movie.director)")
|
||||
let movie = object as! Movie
|
||||
print("Movie: '\(movie.name)', dir. \(movie.director)")
|
||||
}
|
||||
// Movie: '2001: A Space Odyssey', dir. Stanley Kubrick
|
||||
// Movie: 'Moon', dir. Duncan Jones
|
||||
// Movie: 'Alien', dir. Ridley Scott
|
||||
```
|
||||
|
||||
为了变为一个更短的形式,下转`someObjects`数组为`[Movie]`类型来代替下转每一项方式。
|
||||
为了变为一个更短的形式,下转`someObjects`数组为`[Movie]`类型来代替下转数组中每一项的方式。
|
||||
|
||||
```swift
|
||||
for movie in someObjects as! [Movie] {
|
||||
println("Movie: '\(movie.name)', dir. \(movie.director)")
|
||||
print("Movie: '\(movie.name)', dir. \(movie.director)")
|
||||
}
|
||||
// Movie: '2001: A Space Odyssey', dir. Stanley Kubrick
|
||||
// Movie: 'Moon', dir. Duncan Jones
|
||||
@ -196,7 +201,7 @@ for movie in someObjects as! [Movie] {
|
||||
|
||||
### `Any`类型
|
||||
|
||||
这里有个示例,使用 `Any` 类型来和混合的不同类型一起工作,包括非`class`类型。它创建了一个可以存储`Any`类型的数组 `things`。
|
||||
这里有个示例,使用 `Any` 类型来和混合的不同类型一起工作,包括方法类型和非 `class` 类型。它创建了一个可以存储`Any`类型的数组 `things`。
|
||||
|
||||
```swift
|
||||
var things = [Any]()
|
||||
@ -211,33 +216,33 @@ things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman"))
|
||||
things.append({ (name: String) -> String in "Hello, \(name)" })
|
||||
```
|
||||
|
||||
`things` 数组包含两个 `Int` 值,2个 `Double` 值,1个 `String` 值,一个元组 `(Double, Double)` ,Ivan Reitman 导演的电影“Ghostbusters”。
|
||||
`things` 数组包含两个 `Int` 值,2个 `Double` 值,1个 `String` 值,一个元组 `(Double, Double)` ,电影“Ghostbusters”,和一个获取 `String` 值并返回另一个 `String` 值的闭包表达式。
|
||||
|
||||
你可以在 `switch` `cases`里用`is` 和 `as` 操作符来发觉只知道是 `Any` 或 `AnyObject`的常量或变量的类型。 下面的示例迭代 `things`数组中的每一项的并用`switch`语句查找每一项的类型。这几种`switch`语句的情形绑定它们匹配的值到一个规定类型的常量,让它们可以打印它们的值:
|
||||
你可以在 `switch` 表达式的cases中使用 `is` 和 `as` 操作符来发觉只知道是 `Any` 或 `AnyObject` 的常量或变量的类型。下面的示例迭代 `things` 数组中的每一项的并用`switch`语句查找每一项的类型。这几种 `switch` 语句的情形绑定它们匹配的值到一个规定类型的常量,让它们的值可以被打印:
|
||||
|
||||
```swift
|
||||
for thing in things {
|
||||
switch thing {
|
||||
case 0 as Int:
|
||||
println("zero as an Int")
|
||||
print("zero as an Int")
|
||||
case 0 as Double:
|
||||
println("zero as a Double")
|
||||
print("zero as a Double")
|
||||
case let someInt as Int:
|
||||
println("an integer value of \(someInt)")
|
||||
print("an integer value of \(someInt)")
|
||||
case let someDouble as Double where someDouble > 0:
|
||||
println("a positive double value of \(someDouble)")
|
||||
print("a positive double value of \(someDouble)")
|
||||
case is Double:
|
||||
println("some other double value that I don't want to print")
|
||||
print("some other double value that I don't want to print")
|
||||
case let someString as String:
|
||||
println("a string value of \"\(someString)\"")
|
||||
print("a string value of \"\(someString)\"")
|
||||
case let (x, y) as (Double, Double):
|
||||
println("an (x, y) point at \(x), \(y)")
|
||||
print("an (x, y) point at \(x), \(y)")
|
||||
case let movie as Movie:
|
||||
println("a movie called '\(movie.name)', dir. \(movie.director)")
|
||||
print("a movie called '\(movie.name)', dir. \(movie.director)")
|
||||
case let stringConverter as String -> String:
|
||||
println(stringConverter("Michael"))
|
||||
print(stringConverter("Michael"))
|
||||
default:
|
||||
println("something else")
|
||||
print("something else")
|
||||
}
|
||||
}
|
||||
|
||||
@ -252,5 +257,5 @@ for thing in things {
|
||||
```
|
||||
|
||||
|
||||
> 注意:
|
||||
在一个switch语句的case中使用强制形式的类型转换操作符(as, 而不是 as?)来检查和转换到一个明确的类型。在 switch case 语句的内容中这种检查总是安全的。
|
||||
> 注意:
|
||||
> 在一个switch语句的case中使用强制形式的类型转换操作符(as, 而不是 as?)来检查和转换到一个明确的类型。在 `switch` case 语句的内容中这种检查总是安全的。
|
||||
|
||||
@ -1,8 +1,12 @@
|
||||
> 翻译:[lyuka](https://github.com/lyuka)
|
||||
# 扩展(Extensions)
|
||||
----
|
||||
|
||||
> 1.0
|
||||
> 翻译:[lyuka](https://github.com/lyuka)
|
||||
> 校对:[Hawstein](https://github.com/Hawstein)
|
||||
|
||||
#扩展(Extensions)
|
||||
----
|
||||
> 2.0
|
||||
> 翻译+校对:[shanksyang](https://github.com/shanksyang)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
@ -13,22 +17,23 @@
|
||||
- [下标](#subscripts)
|
||||
- [嵌套类型](#nested_types)
|
||||
|
||||
*扩展*就是向一个已有的类、结构体或枚举类型添加新功能(functionality)。这包括在没有权限获取原始源代码的情况下扩展类型的能力(即*逆向建模*)。扩展和 Objective-C 中的分类(categories)类似。(不过与Objective-C不同的是,Swift 的扩展没有名字。)
|
||||
*扩展*就是向一个已有的类、结构体、枚举类型或者协议类型添加新功能(functionality)。这包括在没有权限获取原始源代码的情况下扩展类型的能力(即*逆向建模*)。扩展和 Objective-C 中的分类(categories)类似。(不过与 Objective-C 不同的是,Swift 的扩展没有名字。)
|
||||
|
||||
Swift 中的扩展可以:
|
||||
|
||||
- 添加计算型属性和计算静态属性
|
||||
- 添加计算型属性和计算型静态属性
|
||||
- 定义实例方法和类型方法
|
||||
- 提供新的构造器
|
||||
- 定义下标
|
||||
- 定义和使用新的嵌套类型
|
||||
- 使一个已有类型符合某个协议
|
||||
|
||||
在 Swift 中,你甚至可以对一个协议(Protocol)进行扩展,提供协议需要的实现,或者添加额外的功能能够对合适的类型带来额外的好处。你可以从[协议扩展](./22_Protocols.html#protocol_extensions)获取更多的细节。
|
||||
|
||||
>注意:
|
||||
如果你定义了一个扩展向一个已有类型添加新功能,那么这个新功能对该类型的所有已有实例中都是可用的,即使它们是在你的这个扩展的前面定义的。
|
||||
|
||||
扩展可以对一个类型添加新的功能,但是不能重写已有的功能。
|
||||
<a name="extension_syntax"></a>
|
||||
|
||||
## 扩展语法(Extension Syntax)
|
||||
|
||||
声明一个扩展使用关键字`extension`:
|
||||
@ -38,7 +43,6 @@ extension SomeType {
|
||||
// 加到SomeType的新功能写到这里
|
||||
}
|
||||
```
|
||||
|
||||
一个扩展可以扩展一个已有类型,使其能够适配一个或多个协议(protocol)。当这种情况发生时,协议的名字应该完全按照类或结构体的名字的方式进行书写:
|
||||
|
||||
```swift
|
||||
@ -47,12 +51,15 @@ extension SomeType: SomeProtocol, AnotherProctocol {
|
||||
}
|
||||
```
|
||||
|
||||
按照这种方式添加的协议遵循者(protocol conformance)被称之为[在扩展中添加协议遵循者](21_Protocols.html#adding_protocol_conformance_with_an_extension)
|
||||
按照这种方式添加的协议遵循者(protocol conformance)被称之为[在扩展中添加协议遵循者](./22_Protocols.html#adding_protocol_conformance_with_an_extension)
|
||||
|
||||
>注意:
|
||||
如果你定义了一个扩展向一个已有类型添加新功能,那么这个新功能对该类型的所有已有实例中都是可用的,即使它们是在你的这个扩展的前面定义的。
|
||||
|
||||
<a name="computed_properties"></a>
|
||||
## 计算型属性(Computed Properties)
|
||||
|
||||
扩展可以向已有类型添加计算型实例属性和计算型类型属性。下面的例子向 Swift 的内建`Double`类型添加了5个计算型实例属性,从而提供与距离单位协作的基本支持。
|
||||
扩展可以向已有类型添加计算型实例属性和计算型类型属性。下面的例子向 Swift 的内建`Double`类型添加了5个计算型实例属性,从而提供与距离单位协作的基本支持:
|
||||
|
||||
```swift
|
||||
extension Double {
|
||||
@ -63,10 +70,10 @@ extension Double {
|
||||
var ft: Double { return self / 3.28084 }
|
||||
}
|
||||
let oneInch = 25.4.mm
|
||||
println("One inch is \(oneInch) meters")
|
||||
print("One inch is \(oneInch) meters")
|
||||
// 打印输出:"One inch is 0.0254 meters"
|
||||
let threeFeet = 3.ft
|
||||
println("Three feet is \(threeFeet) meters")
|
||||
print("Three feet is \(threeFeet) meters")
|
||||
// 打印输出:"Three feet is 0.914399970739201 meters"
|
||||
```
|
||||
|
||||
@ -80,11 +87,10 @@ println("Three feet is \(threeFeet) meters")
|
||||
|
||||
```swift
|
||||
let aMarathon = 42.km + 195.m
|
||||
println("A marathon is \(aMarathon) meters long")
|
||||
print("A marathon is \(aMarathon) meters long")
|
||||
// 打印输出:"A marathon is 42195.0 meters long"
|
||||
```
|
||||
|
||||
|
||||
>注意:
|
||||
扩展可以添加新的计算属性,但是不可以添加存储属性,也不可以向已有属性添加属性观测器(property observers)。
|
||||
|
||||
@ -93,13 +99,12 @@ println("A marathon is \(aMarathon) meters long")
|
||||
|
||||
扩展可以向已有类型添加新的构造器。这可以让你扩展其它类型,将你自己的定制类型作为构造器参数,或者提供该类型的原始实现中没有包含的额外初始化选项。
|
||||
|
||||
扩展能向类中添加新的便利构造器,但是它们不能向类中添加新的指定构造器或析构函数。指定构造器和析构函数必须总是由原始的类实现来提供。
|
||||
扩展能向类中添加新的便利构造器,但是它们不能向类中添加新的指定构造器或析构器。指定构造器和析构器必须总是由原始的类实现来提供。
|
||||
|
||||
> 注意:
|
||||
如果你使用扩展向一个值类型添加一个构造器,在该值类型已经向所有的存储属性提供默认值,而且没有定义任何定制构造器(custom initializers)时,你可以在值类型的扩展构造器中调用默认构造器(default initializers)和逐一成员构造器(memberwise initializers)。
|
||||
>
|
||||
正如在[值类型的构造器代理](14_Initialization.html#initializer_delegation_for_value_types)中描述的,如果你已经把构造器写成值类型原始实现的一部分,上述规则不再适用。
|
||||
|
||||
正如在[值类型的构造器代理](./14_Initialization.html#initializer_delegation_for_value_types)中描述的,如果你已经把构造器写成值类型原始实现的一部分,上述规则不再适用。
|
||||
|
||||
下面的例子定义了一个用于描述几何矩形的定制结构体`Rect`。这个例子同时定义了两个辅助结构体`Size`和`Point`,它们都把`0.0`作为所有属性的默认值:
|
||||
|
||||
@ -115,8 +120,7 @@ struct Rect {
|
||||
var size = Size()
|
||||
}
|
||||
```
|
||||
|
||||
因为结构体`Rect`提供了其所有属性的默认值,所以正如默认构造器中描述的,它可以自动接受一个默认的构造器和一个成员级构造器。这些构造器可以用于构造新的`Rect`实例:
|
||||
因为结构体`Rect`提供了其所有属性的默认值,所以正如[默认构造器](./14_Initialization.html#default_initializers)中描述的,它可以自动接受一个默认构造器和一个逐一成员构造器。这些构造器可以用于构造新的`Rect`实例:
|
||||
|
||||
```swift
|
||||
let defaultRect = Rect()
|
||||
@ -135,8 +139,7 @@ extension Rect {
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
这个新的构造器首先根据提供的`center`和`size`值计算一个合适的原点。然后调用该结构体自动的成员构造器`init(origin:size:)`,该构造器将新的原点和大小存到了合适的属性中:
|
||||
这个新的构造器首先根据提供的`center`和`size`值计算一个合适的原点。然后调用该结构体自动的逐一成员构造器`init(origin:size:)`,该构造器将新的原点和大小存到了合适的属性中:
|
||||
|
||||
```swift
|
||||
let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
|
||||
@ -169,7 +172,7 @@ extension Int {
|
||||
|
||||
```swift
|
||||
3.repetitions({
|
||||
println("Hello!")
|
||||
print("Hello!")
|
||||
})
|
||||
// Hello!
|
||||
// Hello!
|
||||
@ -180,7 +183,7 @@ extension Int {
|
||||
|
||||
```swift
|
||||
3.repetitions{
|
||||
println("Goodbye!")
|
||||
print("Goodbye!")
|
||||
}
|
||||
// Goodbye!
|
||||
// Goodbye!
|
||||
@ -250,52 +253,49 @@ extension Int {
|
||||
扩展可以向已有的类、结构体和枚举添加新的嵌套类型:
|
||||
|
||||
```swift
|
||||
extension Character {
|
||||
extension Int {
|
||||
enum Kind {
|
||||
case Vowel, Consonant, Other
|
||||
case Negative, Zero, Positive
|
||||
}
|
||||
var kind: Kind {
|
||||
switch String(self).lowercaseString {
|
||||
case "a", "e", "i", "o", "u":
|
||||
return .Vowel
|
||||
case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
|
||||
"n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
|
||||
return .Consonant
|
||||
switch self {
|
||||
case 0:
|
||||
return .Zero
|
||||
case let x where x > 0:
|
||||
return .Positive
|
||||
default:
|
||||
return .Other
|
||||
return .Negative
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
该例子向`Character`添加了新的嵌套枚举。这个名为`Kind`的枚举表示特定字符的类型。具体来说,就是表示一个标准的拉丁脚本中的字符是元音还是辅音(不考虑口语和地方变种),或者是其它类型。
|
||||
该例子向`Int`添加了新的嵌套枚举。这个名为`Kind`的枚举表示特定整数的类型。具体来说,就是表示整数是正数,零或者负数。
|
||||
|
||||
这个例子还向`Character`添加了一个新的计算实例属性,即`kind`,用来返回合适的`Kind`枚举成员。
|
||||
这个例子还向`Int`添加了一个新的计算实例属性,即`kind`,用来返回合适的`Kind`枚举成员。
|
||||
|
||||
现在,这个嵌套枚举可以和一个`Int`值联合使用了:
|
||||
|
||||
现在,这个嵌套枚举可以和一个`Character`值联合使用了:
|
||||
|
||||
```swift
|
||||
func printLetterKinds(word: String) {
|
||||
println("'\(word)' is made up of the following kinds of letters:")
|
||||
for character in word {
|
||||
switch character.kind {
|
||||
case .Vowel:
|
||||
print("vowel ")
|
||||
case .Consonant:
|
||||
print("consonant ")
|
||||
case .Other:
|
||||
print("other ")
|
||||
func printIntegerKinds(numbers: [Int]) {
|
||||
for number in numbers {
|
||||
switch number.kind {
|
||||
case .Negative:
|
||||
print("- ", appendNewline: false)
|
||||
case .Zero:
|
||||
print("0 ", appendNewline: false)
|
||||
case .Positive:
|
||||
print("+ ", appendNewline: false)
|
||||
}
|
||||
}
|
||||
print("\n")
|
||||
print("")
|
||||
}
|
||||
printLetterKinds("Hello")
|
||||
// 'Hello' is made up of the following kinds of letters:
|
||||
// consonant vowel consonant consonant vowel
|
||||
printIntegerKinds([3, 19, -27, 0, -6, 0, 7])
|
||||
// prints "+ + - 0 - 0 +"
|
||||
```
|
||||
|
||||
函数`printLetterKinds`的输入是一个`String`值并对其字符进行迭代。在每次迭代过程中,考虑当前字符的`kind`计算属性,并打印出合适的类别描述。所以`printLetterKinds`就可以用来打印一个完整单词中所有字母的类型,正如上述单词`"hello"`所展示的。
|
||||
|
||||
>注意:
|
||||
由于已知`character.kind`是`Character.Kind`型,所以`Character.Kind`中的所有成员值都可以使用`switch`语句里的形式简写,比如使用 `.Vowel`代替`Character.Kind.Vowel`
|
||||
函数`printIntegerKinds`的输入是一个`Int`数组值并对其字符进行迭代。在每次迭代过程中,考虑当前字符的`kind`计算属性,并打印出合适的类别描述。
|
||||
|
||||
>注意:
|
||||
由于已知`number.kind `是`Int.Kind`型,所以`Int.Kind`中的所有成员值都可以使用`switch`语句里的形式简写,比如使用 `. Negative`代替`Int.Kind.Negative`。
|
||||
|
||||
@ -1,15 +1,19 @@
|
||||
> 翻译:[geek5nan](https://github.com/geek5nan)
|
||||
# 协议(Protocols)
|
||||
-----------------
|
||||
|
||||
> 1.0
|
||||
> 翻译:[geek5nan](https://github.com/geek5nan)
|
||||
> 校对:[dabing1022](https://github.com/dabing1022)
|
||||
|
||||
# 协议
|
||||
-----------------
|
||||
> 2.0
|
||||
> 翻译+校对:[futantan](https://github.com/futantan)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [协议的语法(Protocol Syntax)](#protocol_syntax)
|
||||
- [对属性的规定(Property Requirements)](#property_requirements)
|
||||
- [对方法的规定(Method Requirements)](#method_requirements)
|
||||
- [对突变方法的规定(Mutating Method Requirements)](#mutating_method_requirements)
|
||||
- [对Mutating方法的规定(Mutating Method Requirements)](#mutating_method_requirements)
|
||||
- [对构造器的规定(Initializer Requirements)](#initializer_requirements)
|
||||
- [协议类型(Protocols as Types)](#protocols_as_types)
|
||||
- [委托(代理)模式(Delegation)](#delegation)
|
||||
@ -21,15 +25,15 @@
|
||||
- [协议合成(Protocol Composition)](#protocol_composition)
|
||||
- [检验协议的一致性(Checking for Protocol Conformance)](#checking_for_protocol_conformance)
|
||||
- [对可选协议的规定(Optional Protocol Requirements)](#optional_protocol_requirements)
|
||||
- [协议扩展(Protocol Extensions)](#protocol_extensions)
|
||||
|
||||
`协议(Protocol)`用于定义完成某项任务或功能所必须的方法和属性,协议实际上并不提供这些功能或任务的具体`实现(Implementation)`--而只用来描述这些实现应该是什么样的。类,结构体,枚举通过提供协议所要求的方法,属性的具体实现来`采用(adopt)`协议。任意能够满足协议要求的类型被称为协议的`遵循者`。
|
||||
|
||||
`协议`可以要求其`遵循者`提供特定的实例属性,实例方法,类方法,操作符或下标脚本等。
|
||||
`协议`定义了一个蓝图,规定了用来实现某一特定工作或者功能所必需的方法和属性。类,结构体或枚举类型都可以遵循协议,并提供具体实现来完成协议定义的方法和功能。任意能够满足协议要求的类型被称为`遵循(conform)`这个协议。
|
||||
|
||||
<a name="protocol_syntax"></a>
|
||||
## 协议的语法
|
||||
|
||||
`协议`的定义方式与`类,结构体,枚举`的定义都非常相似,如下所示:
|
||||
协议的定义方式与类,结构体,枚举的定义非常相似。
|
||||
|
||||
```swift
|
||||
protocol SomeProtocol {
|
||||
@ -37,7 +41,7 @@ protocol SomeProtocol {
|
||||
}
|
||||
```
|
||||
|
||||
在类型名称后加上`协议名称`,中间以冒号`:`分隔即可实现协议;实现多个协议时,各协议之间用逗号`,`分隔,如下所示:
|
||||
要使类遵循某个协议,需要在类型名称后加上协议名称,中间以冒号`:`分隔,作为类型定义的一部分。遵循多个协议时,各协议之间用逗号`,`分隔。
|
||||
|
||||
```swift
|
||||
struct SomeStructure: FirstProtocol, AnotherProtocol {
|
||||
@ -45,7 +49,7 @@ struct SomeStructure: FirstProtocol, AnotherProtocol {
|
||||
}
|
||||
```
|
||||
|
||||
如果一个类在含有`父类`的同时也采用了协议,应当把`父类`放在所有的`协议`之前,如下所示:
|
||||
如果类在遵循协议的同时拥有父类,应该将父类名放在协议名之前,以逗号分隔。
|
||||
|
||||
```swift
|
||||
class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
|
||||
@ -56,11 +60,11 @@ class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
|
||||
<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
|
||||
protocol SomeProtocol {
|
||||
@ -69,15 +73,15 @@ protocol SomeProtocol {
|
||||
}
|
||||
```
|
||||
|
||||
如下所示,通常在协议的定义中使用`class`前缀表示该属性为类成员;在枚举和结构体实现协议时中,需要使用`static`关键字作为前缀。
|
||||
在协议中定义类属性(type property)时,总是使用`static`关键字作为前缀。当协议的遵循者是类时,可以使用`class`或`static`关键字来声明类属性,但是在协议的定义中,仍然要使用`static`关键字。
|
||||
|
||||
```swift
|
||||
protocol AnotherProtocol {
|
||||
class var someTypeProperty: Int { get set }
|
||||
static var someTypeProperty: Int { get set }
|
||||
}
|
||||
```
|
||||
|
||||
如下所示,这是一个含有一个实例属性要求的协议:
|
||||
如下所示,这是一个含有一个实例属性要求的协议。
|
||||
|
||||
```swift
|
||||
protocol FullyNamed {
|
||||
@ -85,9 +89,9 @@ protocol FullyNamed {
|
||||
}
|
||||
```
|
||||
|
||||
`FullyNamed`协议定义了任何拥有`fullName`的类型。它并不指定具体类型,而只是要求类型必须提供一个`fullName`。任何`FullyNamed`类型都得有一个只读的`fullName`属性,类型为`String`。
|
||||
`FullyNamed`协议除了要求协议的遵循者提供fullName属性外,对协议对遵循者的类型并没有特别的要求。这个协议表示,任何遵循`FullyNamed`协议的类型,都具有一个可读的`String`类型实例属性`fullName`。
|
||||
|
||||
如下所示,这是一个实现了`FullyNamed`协议的简单结构体:
|
||||
下面是一个遵循`FullyNamed`协议的简单结构体。
|
||||
|
||||
```swift
|
||||
struct Person: FullyNamed{
|
||||
@ -97,47 +101,44 @@ let john = Person(fullName: "John Appleseed")
|
||||
//john.fullName 为 "John Appleseed"
|
||||
```
|
||||
|
||||
这个例子中定义了一个叫做`Person`的结构体,用来表示具有指定名字的人。从第一行代码中可以看出,它采用了`FullyNamed`协议。
|
||||
这个例子中定义了一个叫做`Person`的结构体,用来表示具有名字的人。从第一行代码中可以看出,它遵循了`FullyNamed`协议。
|
||||
|
||||
`Person`结构体的每一个实例都有一个叫做`fullName`,`String`类型的存储型属性,这正好匹配了`FullyNamed`协议的要求,也就意味着,`Person`结构体完整的`遵循`了协议。(如果协议要求未被完全满足,在编译时会报错)
|
||||
`Person`结构体的每一个实例都有一个叫做`fullName`,`String`类型的存储型属性。这正好满足了`FullyNamed`协议的要求,也就意味着,`Person`结构体完整的`遵循`了协议。(如果协议要求未被完全满足,在编译时会报错)
|
||||
|
||||
这有一个更为复杂的类,它采用并实现了`FullyNamed`协议,如下所示:
|
||||
下面是一个更为复杂的类,它采用并遵循了`FullyNamed`协议:
|
||||
|
||||
```swift
|
||||
class Starship: FullyNamed {
|
||||
var prefix: String?
|
||||
var name: String
|
||||
init(name: String, prefix: String? = nil ) {
|
||||
self.name = name
|
||||
self.prefix = prefix
|
||||
}
|
||||
var fullName: String {
|
||||
return (prefix != nil ? prefix! + " " : " ") + name
|
||||
}
|
||||
var prefix: String?
|
||||
var name: String
|
||||
init(name: String, prefix: String? = nil) {
|
||||
self.name = name
|
||||
self.prefix = prefix
|
||||
}
|
||||
var fullName: String {
|
||||
return (prefix != nil ? prefix! + " " : "") + name
|
||||
}
|
||||
}
|
||||
var ncc1701 = Starship(name: "Enterprise", prefix: "USS")
|
||||
// ncc1701.fullName == "USS Enterprise"
|
||||
// ncc1701.fullName is "USS Enterprise"
|
||||
```
|
||||
|
||||
`Starship`类把`fullName`属性实现为只读的`计算型属性`。每一个`Starship`类的实例都有一个名为`name`的必备属性和一个名为`prefix`的可选属性。 当`prefix`存在时,将`prefix`插入到`name`之前来为`Starship`构建`fullName`,`prefix`不存在时,则将直接用`name`构建`fullName`
|
||||
Starship类把`fullName`属性实现为只读的计算型属性。每一个`Starship`类的实例都有一个名为`name`的属性和一个名为`prefix`的可选属性。 当`prefix`存在时,将`prefix`插入到`name`之前来为Starship构建`fullName`,`prefix`不存在时,则将直接用`name`构建`fullName`。
|
||||
|
||||
<a name="method_requirements"></a>
|
||||
## 对方法的规定
|
||||
|
||||
`协议`可以要求其`遵循者`实现某些指定的`实例方法`或`类方法`。这些方法作为协议的一部分,像普通的方法一样清晰的放在协议的定义中,而不需要大括号和方法体。
|
||||
协议可以要求其遵循者实现某些指定的实例方法或类方法。这些方法作为协议的一部分,像普通的方法一样放在协议的定义中,但是不需要大括号和方法体。可以在协议中定义具有可变参数的方法,和普通方法的定义方式相同。但是在协议的方法定义中,不支持参数默认值。
|
||||
|
||||
>注意:
|
||||
>协议中的方法支持`变长参数(variadic parameter)`,不支持`参数默认值(default value)`。
|
||||
|
||||
如下所示,协议中类方法的定义与类属性的定义相似,在协议定义的方法前置`class`关键字来表示。当在`枚举`或`结构体`实现类方法时,需要使用`static`关键字来代替。
|
||||
正如对属性的规定中所说的,在协议中定义类方法的时候,总是使用`static`关键字作为前缀。当协议的遵循者是类的时候,虽然你可以在类的实现中使用`class`或者`static`来实现类方法,但是在协议中声明类方法,仍然要使用`static`关键字。
|
||||
|
||||
```swift
|
||||
protocol SomeProtocol {
|
||||
class func someTypeMethod()
|
||||
static func someTypeMethod()
|
||||
}
|
||||
```
|
||||
|
||||
如下所示,定义了含有一个实例方法的的协议。
|
||||
下面的例子定义了含有一个实例方法的协议。
|
||||
|
||||
```swift
|
||||
protocol RandomNumberGenerator {
|
||||
@ -145,7 +146,7 @@ protocol RandomNumberGenerator {
|
||||
}
|
||||
```
|
||||
|
||||
`RandomNumberGenerator`协议要求其`遵循者`必须拥有一个名为`random`, 返回值类型为`Double`的实例方法。 (尽管这里并未指明,但是我们假设返回值在[0,1]区间内)。
|
||||
`RandomNumberGenerator`协议要求其遵循者必须拥有一个名为`random`, 返回值类型为`Double`的实例方法。尽管这里并未指明,但是我们假设返回值在[0,1)区间内。
|
||||
|
||||
`RandomNumberGenerator`协议并不在意每一个随机数是怎样生成的,它只强调这里有一个随机数生成器。
|
||||
|
||||
@ -164,24 +165,26 @@ class LinearCongruentialGenerator: RandomNumberGenerator {
|
||||
}
|
||||
}
|
||||
let generator = LinearCongruentialGenerator()
|
||||
println("Here's a random number: \(generator.random())")
|
||||
print("Here's a random number: \(generator.random())")
|
||||
// 输出 : "Here's a random number: 0.37464991998171"
|
||||
println("And another one: \(generator.random())")
|
||||
print("And another one: \(generator.random())")
|
||||
// 输出 : "And another one: 0.729023776863283"
|
||||
```
|
||||
|
||||
<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
|
||||
protocol Togglable {
|
||||
@ -189,9 +192,9 @@ protocol Togglable {
|
||||
}
|
||||
```
|
||||
|
||||
当使用`枚举`或`结构体`来实现`Togglabl`协议时,需要提供一个带有`mutating`前缀的`toggle`方法。
|
||||
当使用`枚举`或`结构体`来实现`Togglable`协议时,需要提供一个带有`mutating`前缀的`toggle`方法。
|
||||
|
||||
如下所示,`OnOffSwitch`枚举`遵循`了`Togglable`协议,`On`,`Off`两个成员用于表示当前状态。枚举的`toggle`方法被标记为`mutating`,用以匹配`Togglabel`协议的规定。
|
||||
下面定义了一个名为`OnOffSwitch`的枚举类型。这个枚举类型在两种状态之间进行切换,用枚举成员`On`和`Off`表示。枚举类型的`toggle`方法被标记为`mutating`以满足`Togglable`协议的要求。
|
||||
|
||||
```swift
|
||||
enum OnOffSwitch: Togglable {
|
||||
@ -213,7 +216,7 @@ lightSwitch.toggle()
|
||||
<a name="initializer_requirements"></a>
|
||||
## 对构造器的规定
|
||||
|
||||
协议可以要求它的遵循类型实现特定的构造器。你可以像书写普通的构造器那样,在协议的定义里写下构造器的需求,但不需要写花括号和构造器的实体:
|
||||
协议可以要求它的遵循者实现指定的构造器。你可以像书写普通的构造器那样,在协议的定义里写下构造器的声明,但不需要写花括号和构造器的实体:
|
||||
|
||||
```swift
|
||||
protocol SomeProtocol {
|
||||
@ -221,9 +224,9 @@ protocol SomeProtocol {
|
||||
}
|
||||
```
|
||||
|
||||
**协议构造器规定在类中的实现**
|
||||
### 协议构造器规定在类中的实现
|
||||
|
||||
你可以在遵循该协议的类中实现构造器,并指定其为类的特定构造器或者便捷构造器。在这两种情况下,你都必须给构造器实现标上"required"修饰符:
|
||||
你可以在遵循该协议的类中实现构造器,并指定其为类的指定构造器(designated initializer)或者便利构造器(convenience initializer)。在这两种情况下,你都必须给构造器实现标上"required"修饰符:
|
||||
|
||||
```swift
|
||||
class SomeClass: SomeProtocol {
|
||||
@ -235,11 +238,10 @@ class SomeClass: SomeProtocol {
|
||||
|
||||
使用`required`修饰符可以保证:所有的遵循该协议的子类,同样能为构造器规定提供一个显式的实现或继承实现。
|
||||
|
||||
关于`required`构造器的更多内容,请参考<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`修饰符的更多内容,请参见<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>
|
||||
>注意
|
||||
>如果类已经被标记为`final`,那么不需要在协议构造器的实现中使用`required`修饰符。因为final类不能有子类。关于`final`修饰符的更多内容,请参见[防止重写](./13_Inheritance.html#preventing_overrides)。
|
||||
|
||||
如果一个子类重写了父类的指定构造器,并且该构造器遵循了某个协议的规定,那么该构造器的实现需要被同时标示`required`和`override`修饰符
|
||||
|
||||
@ -251,39 +253,39 @@ protocol SomeProtocol {
|
||||
|
||||
class SomeSuperClass {
|
||||
init() {
|
||||
//协议定义
|
||||
// 构造器的实现
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class SomeSubClass: SomeSuperClass, SomeProtocol {
|
||||
// "required" from SomeProtocol conformance; "override" from SomeSuperClass
|
||||
// 因为遵循协议,需要加上"required"; 因为继承自父类,需要加上"override"
|
||||
required override init() {
|
||||
// 构造器实现
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**可失败构造器的规定**
|
||||
### 可失败构造器的规定
|
||||
|
||||
可以通过给协议```Protocols```中添加可失败构造器来使遵循该协议的类型必须实现该可失败构造器。
|
||||
可以通过给协议`Protocols`中添加[可失败构造器](./14_Initialization.html#failable_initializers)来使遵循该协议的类型必须实现该可失败构造器。
|
||||
|
||||
如果在协议中定义一个可失败构造器,则在遵顼该协议的类型中必须添加同名同参数的可失败构造器或非可失败构造器。
|
||||
如果在协议中定义一个非可失败构造器,则在遵循该协议的类型中必须添加同名同参数的非可失败构造器或隐式解析类型的可失败构造器(`init!`)。
|
||||
如果在协议中定义一个可失败构造器,则在遵顼该协议的类型中必须添加同名同参数的可失败构造器或非可失败构造器。如果在协议中定义一个非可失败构造器,则在遵循该协议的类型中必须添加同名同参数的非可失败构造器或隐式解析类型的可失败构造器(`init!`)。
|
||||
|
||||
|
||||
<a name="protocols_as_types"></a>
|
||||
## 协议类型
|
||||
|
||||
尽管`协议`本身并不实现任何功能,但是`协议`可以被当做类型来使用。
|
||||
尽管协议本身并不实现任何功能,但是协议可以被当做类型来使用。
|
||||
|
||||
使用场景:
|
||||
协议可以像其他普通类型一样使用,使用场景:
|
||||
|
||||
* `协议类型`作为函数、方法或构造器中的参数类型或返回值类型
|
||||
* `协议类型`作为常量、变量或属性的类型
|
||||
* `协议类型`作为数组、字典或其他容器中的元素类型
|
||||
* 作为函数、方法或构造器中的参数类型或返回值类型
|
||||
* 作为常量、变量或属性的类型
|
||||
* 作为数组、字典或其他容器中的元素类型
|
||||
|
||||
> 注意: 协议是一种类型,因此协议类型的名称应与其他类型(Int,Double,String)的写法相同,使用驼峰式写法
|
||||
> 注意
|
||||
> 协议是一种类型,因此协议类型的名称应与其他类型(Int,Double,String)的写法相同,使用大写字母开头的驼峰式写法,例如(`FullyNamed`和`RandomNumberGenerator`)
|
||||
|
||||
如下所示,这个示例中将协议当做类型来使用
|
||||
|
||||
@ -301,20 +303,20 @@ class Dice {
|
||||
}
|
||||
```
|
||||
|
||||
例子中又一个`Dice`类,用来代表桌游中的拥有N个面的骰子。`Dice`的实例含有`sides`和`generator`两个属性,前者是整型,用来表示骰子有几个面,后者为骰子提供一个随机数生成器。
|
||||
例子中定义了一个`Dice`类,用来代表桌游中的拥有N个面的骰子。`Dice`的实例含有`sides`和`generator`两个属性,前者是整型,用来表示骰子有几个面,后者为骰子提供一个随机数生成器。
|
||||
|
||||
`generator`属性的类型为`RandomNumberGenerator`,因此任何遵循了`RandomNumberGenerator`协议的类型的实例都可以赋值给`generator`,除此之外,无其他要求。
|
||||
|
||||
`Dice`类中也有一个`构造器(initializer)`,用来进行初始化操作。构造器中含有一个名为`generator`,类型为`RandomNumberGenerator`的形参。在调用构造方法时创建`Dice`的实例时,可以传入任何遵循`RandomNumberGenerator`协议的实例给generator。
|
||||
`Dice`类中也有一个构造器(initializer),用来进行初始化操作。构造器中含有一个名为`generator`,类型为`RandomNumberGenerator`的形参。在调用构造方法时创建`Dice`的实例时,可以传入任何遵循`RandomNumberGenerator`协议的实例给generator。
|
||||
|
||||
`Dice`类也提供了一个名为`roll`的实例方法用来模拟骰子的面值。它先使用`generator`的`random`方法来创建一个[0-1]区间内的随机数种子,然后加工这个随机数种子生成骰子的面值。generator被认为是遵循了`RandomNumberGenerator`的类型,因而保证了`random`方法可以被调用。
|
||||
`Dice`类也提供了一个名为`roll`的实例方法用来模拟骰子的面值。它先使用`generator`的`random()`方法来创建一个[0,1)区间内的随机数,然后使用这个随机数生成正确的骰子面值。因为generator遵循了`RandomNumberGenerator`协议,因而保证了`random`方法可以被调用。
|
||||
|
||||
如下所示,这里展示了如何使用`LinearCongruentialGenerator`的实例作为随机数生成器创建一个六面骰子:
|
||||
下面的例子展示了如何使用`LinearCongruentialGenerator`的实例作为随机数生成器创建一个六面骰子:
|
||||
|
||||
```swift
|
||||
var d6 = Dice(sides: 6,generator: LinearCongruentialGenerator())
|
||||
var d6 = Dice(sides: 6, generator: LinearCongruentialGenerator())
|
||||
for _ in 1...5 {
|
||||
println("Random dice roll is \(d6.roll())")
|
||||
print("Random dice roll is \(d6.roll())")
|
||||
}
|
||||
//输出结果
|
||||
//Random dice roll is 3
|
||||
@ -327,13 +329,9 @@ for _ in 1...5 {
|
||||
<a name="delegation"></a>
|
||||
## 委托(代理)模式
|
||||
|
||||
委托是一种设计模式(*译者注: 想起了那年 UITableViewDelegate 中的奔跑,那是我逝去的Objective-C。。。*),它允许`类`或`结构体`将一些需要它们负责的功能`交由(委托)`给其他的类型的实例。
|
||||
委托是一种设计模式,它允许`类`或`结构体`将一些需要它们负责的功能`交由(委托)`给其他的类型的实例。委托模式的实现很简单: 定义协议来封装那些需要被委托的函数和方法, 使其`遵循者`拥有这些被委托的`函数和方法`。委托模式可以用来响应特定的动作或接收外部数据源提供的数据,而无需要知道外部数据源的类型信息。
|
||||
|
||||
委托模式的实现很简单: 定义`协议`来`封装`那些需要被委托的`函数和方法`, 使其`遵循者`拥有这些被委托的`函数和方法`。
|
||||
|
||||
委托模式可以用来响应特定的动作或接收外部数据源提供的数据,而无需要知道外部数据源的所属类型(*译者注:只要求外部数据源`遵循`某协议*)。
|
||||
|
||||
下文是两个基于骰子游戏的协议:
|
||||
下面的例子是两个基于骰子游戏的协议:
|
||||
|
||||
```swift
|
||||
protocol DiceGame {
|
||||
@ -348,9 +346,9 @@ protocol DiceGameDelegate {
|
||||
}
|
||||
```
|
||||
|
||||
`DiceGame`协议可以在任意含有骰子的游戏中实现,`DiceGameDelegate`协议可以用来追踪`DiceGame`的游戏过程
|
||||
|
||||
如下所示,`SnakesAndLadders`是`Snakes and Ladders`(译者注:[Control Flow](2)章节有该游戏的详细介绍)游戏的新版本。新版本使用`Dice`作为骰子,并且实现了`DiceGame`和`DiceGameDelegate`协议,后者用来记录游戏的过程:
|
||||
`DiceGame`协议可以在任意含有骰子的游戏中实现。`DiceGameDelegate`协议可以用来追踪`DiceGame`的游戏过程
|
||||
|
||||
如下所示,`SnakesAndLadders`是`Snakes and Ladders`([Control Flow](./05_Control_Flow.html)章节有该游戏的详细介绍)游戏的新版本。新版本使用`Dice`作为骰子,并且实现了`DiceGame`和`DiceGameDelegate`协议,后者用来记录游戏的过程:
|
||||
|
||||
```swift
|
||||
class SnakesAndLadders: DiceGame {
|
||||
@ -385,15 +383,15 @@ class SnakesAndLadders: DiceGame {
|
||||
}
|
||||
```
|
||||
|
||||
这个版本的游戏封装到了`SnakesAndLadders`类中,该类采用了`DiceGame`协议,并且提供了`dice`属性和`play`实例方法用来`遵循`协议。(`dice`属性在构造之后就不在改变,且协议只要求`dice`为只读的,因此将`dice`声明为常量属性。)
|
||||
这个版本的游戏封装到了`SnakesAndLadders`类中,该类遵循了`DiceGame`协议,并且提供了相应的可读的`dice`属性和`play`实例方法。(`dice`属性在构造之后就不再改变,且协议只要求`dice`为只读的,因此将`dice`声明为常量属性。)
|
||||
|
||||
在`SnakesAndLadders`类的`构造器(initializer)`初始化游戏。所有的游戏逻辑被转移到了`play`方法中,`play`方法使用协议规定的`dice`属性提供骰子摇出的值。
|
||||
游戏使用`SnakesAndLadders`类的`构造器(initializer)`初始化游戏。所有的游戏逻辑被转移到了协议中的`play`方法,`play`方法使用协议规定的`dice`属性提供骰子摇出的值。
|
||||
|
||||
> 注意:`delegate`并不是游戏的必备条件,因此`delegate`被定义为遵循`DiceGameDelegate`协议的可选属性,`delegate`使用`nil`作为初始值。
|
||||
注意:`delegate`并不是游戏的必备条件,因此`delegate`被定义为遵循`DiceGameDelegate`协议的可选属性。因为`delegate`是可选值,因此在初始化的时候被自动赋值为`nil`。随后,可以在游戏中为`delegate`设置适当的值。
|
||||
|
||||
`DicegameDelegate`协议提供了三个方法用来追踪游戏过程。被放置于游戏的逻辑中,即`play()`方法内。分别在游戏开始时,新一轮开始时,游戏结束时被调用。
|
||||
|
||||
因为`delegate`是一个遵循`DiceGameDelegate`的可选属性,因此在`play()`方法中使用了`可选链`来调用委托方法。 若`delegate`属性为`nil`, 则delegate所调用的方法失效。若`delegate`不为`nil`,则方法能够被调用
|
||||
因为`delegate`是一个遵循`DiceGameDelegate`的可选属性,因此在`play()`方法中使用了`可选链`来调用委托方法。 若`delegate`属性为`nil`, 则delegate所调用的方法失效,并不会产生错误。若`delegate`不为`nil`,则方法能够被调用
|
||||
|
||||
如下所示,`DiceGameTracker`遵循了`DiceGameDelegate`协议
|
||||
|
||||
@ -403,25 +401,25 @@ class DiceGameTracker: DiceGameDelegate {
|
||||
func gameDidStart(game: DiceGame) {
|
||||
numberOfTurns = 0
|
||||
if game is SnakesAndLadders {
|
||||
println("Started a new game of Snakes and Ladders")
|
||||
print("Started a new game of Snakes and Ladders")
|
||||
}
|
||||
println("The game is using a \(game.dice.sides)-sided dice")
|
||||
print("The game is using a \(game.dice.sides)-sided dice")
|
||||
}
|
||||
func game(game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) {
|
||||
++numberOfTurns
|
||||
println("Rolled a \(diceRoll)")
|
||||
print("Rolled a \(diceRoll)")
|
||||
}
|
||||
func gameDidEnd(game: DiceGame) {
|
||||
println("The game lasted for \(numberOfTurns) turns")
|
||||
print("The game lasted for \(numberOfTurns) turns")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`DiceGameTracker`实现了`DiceGameDelegate`协议规定的三个方法,用来记录游戏已经进行的轮数。 当游戏开始时,`numberOfTurns`属性被赋值为0; 在每新一轮中递加; 游戏结束后,输出打印游戏的总轮数。
|
||||
`DiceGameTracker`实现了`DiceGameDelegate`协议规定的三个方法,用来记录游戏已经进行的轮数。 当游戏开始时,`numberOfTurns`属性被赋值为0; 在每新一轮中递增; 游戏结束后,输出打印游戏的总轮数。
|
||||
|
||||
`gameDidStart`方法从`game`参数获取游戏信息并输出。`game`在方法中被当做`DiceGame`类型而不是`SnakeAndLadders`类型,所以方法中只能访问`DiceGame`协议中的成员。当然了,这些方法也可以在类型转换之后调用。在上例代码中,通过`is`操作符检查`game`是否为 `SnakesAndLadders`类型的实例,如果是,则打印出相应的内容。
|
||||
|
||||
无论当前进行的是何种游戏,`game`都遵循`DiceGame`协议以确保`game`含有`dice`属性,因此在`gameDidStart`方法中可以通过传入的`game`参数来访问`dice`属性,进而打印出`dice`的`sides`属性的值。
|
||||
无论当前进行的是何种游戏,`game`都遵循`DiceGame`协议以确保`game`含有`dice`属性,因此在`gameDidStart(_:)`方法中可以通过传入的`game`参数来访问`dice`属性,进而打印出`dice`的`sides`属性的值。
|
||||
|
||||
`DiceGameTracker`的运行情况,如下所示:
|
||||
|
||||
@ -442,11 +440,12 @@ game.play()
|
||||
<a name="adding_protocol_conformance_with_an_extension"></a>
|
||||
## 在扩展中添加协议成员
|
||||
|
||||
即便无法修改源代码,依然可以通过`扩展(Extension)`来扩充已存在类型(*译者注: 类,结构体,枚举等*)。`扩展`可以为已存在的类型添加`属性`,`方法`,`下标脚本`,`协议`等成员。详情请在[扩展](4)章节中查看。
|
||||
即便无法修改源代码,依然可以通过扩展(Extension)来扩充已存在类型(*译者注: 类,结构体,枚举等*)。扩展可以为已存在的类型添加属性,方法,下标脚本,协议等成员。详情请在[扩展](./21_Extensions.html)章节中查看。
|
||||
|
||||
> 注意: 通过`扩展`为已存在的类型`遵循`协议时,该类型的所有实例也会随之添加协议中的方法
|
||||
> 注意
|
||||
> 通过扩展为已存在的类型遵循协议时,该类型的所有实例也会随之添加协议中的方法
|
||||
|
||||
`TextRepresentable`协议含有一个`asText`,如下所示:
|
||||
例如`TextRepresentable`协议,任何想要表示一些文本内容的类型都可以遵循该协议。这些想要表示的内容可以是类型本身的描述,也可以是当前内容的版本:
|
||||
|
||||
```swift
|
||||
protocol TextRepresentable {
|
||||
@ -454,7 +453,7 @@ protocol TextRepresentable {
|
||||
}
|
||||
```
|
||||
|
||||
通过`扩展`为上一节中提到的`Dice`类遵循`TextRepresentable`协议
|
||||
可以通过扩展,为上一节中提到的`Dice`增加类遵循`TextRepresentable`协议的功能
|
||||
|
||||
```swift
|
||||
extension Dice: TextRepresentable {
|
||||
@ -463,16 +462,17 @@ extension Dice: TextRepresentable {
|
||||
}
|
||||
}
|
||||
```
|
||||
现在,通过扩展使得`Dice`类型遵循了一个新的协议,这和`Dice`类型在定义的时候声明为遵循`TextRepresentable`协议的效果相同。在扩展的时候,协议名称写在类型名之后,以冒号隔开,在大括号内写明新添加的协议内容。
|
||||
|
||||
从现在起,`Dice`类型的实例可被当作`TextRepresentable`类型:
|
||||
现在所有`Dice`的实例都遵循了`TextRepresentable`协议:
|
||||
|
||||
```swift
|
||||
let d12 = Dice(sides: 12,generator: LinearCongruentialGenerator())
|
||||
println(d12.asText())
|
||||
print(d12.asText())
|
||||
// 输出 "A 12-sided dice"
|
||||
```
|
||||
|
||||
`SnakesAndLadders`类也可以通过`扩展`的方式来遵循协议:
|
||||
同样`SnakesAndLadders`类也可以通过`扩展`的方式来遵循`TextRepresentable`协议:
|
||||
|
||||
```swift
|
||||
extension SnakesAndLadders: TextRepresentable {
|
||||
@ -480,14 +480,14 @@ extension SnakesAndLadders: TextRepresentable {
|
||||
return "A game of Snakes and Ladders with \(finalSquare) squares"
|
||||
}
|
||||
}
|
||||
println(game.asText())
|
||||
print(game.asText())
|
||||
// 输出 "A game of Snakes and Ladders with 25 squares"
|
||||
```
|
||||
|
||||
<a name="declaring_protocol_adoption_with_an_extension"></a>
|
||||
## 通过扩展补充协议声明
|
||||
|
||||
当一个类型已经实现了协议中的所有要求,却没有声明时,可以通过`扩展`来补充协议声明:
|
||||
当一个类型已经实现了协议中的所有要求,却没有声明为遵循该协议时,可以通过扩展(空的扩展体)来补充协议声明:
|
||||
|
||||
```swift
|
||||
struct Hamster {
|
||||
@ -504,26 +504,27 @@ extension Hamster: TextRepresentable {}
|
||||
```swift
|
||||
let simonTheHamster = Hamster(name: "Simon")
|
||||
let somethingTextRepresentable: TextRepresentable = simonTheHamster
|
||||
println(somethingTextRepresentable.asText())
|
||||
print(somethingTextRepresentable.asText())
|
||||
// 输出 "A hamster named Simon"
|
||||
```
|
||||
|
||||
> 注意: 即使满足了协议的所有要求,类型也不会自动转变,因此你必须为它做出明显的协议声明
|
||||
> 注意
|
||||
> 即使满足了协议的所有要求,类型也不会自动转变,因此你必须为它做出显式的协议声明
|
||||
|
||||
<a name="collections_of_protocol_types"></a>
|
||||
## 集合中的协议类型
|
||||
|
||||
协议类型可以被集合使用,表示集合中的元素均为协议类型:
|
||||
协议类型可以在集合使用,表示集合中的元素均为协议类型,下面的例子创建了一个类型为`TextRepresentable`的数组:
|
||||
|
||||
```swift
|
||||
let things: [TextRepresentable] = [game,d12,simonTheHamster]
|
||||
```
|
||||
|
||||
如下所示,`things`数组可以被直接遍历,并调用其中元素的`asText()`函数:
|
||||
如下所示,`things`数组可以被直接遍历,并打印每个元素的文本表示:
|
||||
|
||||
```swift
|
||||
for thing in things {
|
||||
println(thing.asText())
|
||||
print(thing.asText())
|
||||
}
|
||||
// A game of Snakes and Ladders with 25 squares
|
||||
// A 12-sided dice
|
||||
@ -535,7 +536,7 @@ for thing in things {
|
||||
<a name="protocol_inheritance"></a>
|
||||
## 协议的继承
|
||||
|
||||
协议能够继承一到多个其他协议。语法与类的继承相似,多个协议间用逗号`,`分隔
|
||||
协议能够继承一个或多个其他协议,可以在继承的协议基础上增加新的内容要求。协议的继承语法与类的继承相似,多个被继承的协议间用逗号分隔:
|
||||
|
||||
```swift
|
||||
protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
|
||||
@ -551,9 +552,9 @@ protocol PrettyTextRepresentable: TextRepresentable {
|
||||
}
|
||||
```
|
||||
|
||||
遵循`PrettyTextRepresentable`协议的同时,也需要遵循`TextRepresentable`协议。
|
||||
例子中定义了一个新的协议`PrettyTextRepresentable`,它继承自`TextRepresentable`协议。任何遵循`PrettyTextRepresentable`协议的类型在满足该协议的要求时,也必须满足`TextRepresentable`协议的要求。在这个例子中,`PrettyTextRepresentable`协议要求其遵循者提供一个返回值为`String`类型的`asPrettyText`方法。
|
||||
|
||||
如下所示,用`扩展`为`SnakesAndLadders`遵循`PrettyTextRepresentable`协议:
|
||||
如下所示,扩展`SnakesAndLadders`,让其遵循`PrettyTextRepresentable`协议:
|
||||
|
||||
```swift
|
||||
extension SnakesAndLadders: PrettyTextRepresentable {
|
||||
@ -574,43 +575,44 @@ extension SnakesAndLadders: PrettyTextRepresentable {
|
||||
}
|
||||
```
|
||||
|
||||
在`for in`中迭代出了`board`数组中的每一个元素:
|
||||
上述扩展使得`SnakesAndLadders`遵循了`PrettyTextRepresentable`协议,并为每个`SnakesAndLadders`类型提供了了协议要求的`asPrettyText()`方法。每个`PrettyTextRepresentable`类型同时也是`TextRepresentable`类型,所以在`asPrettyText`的实现中,可以调用`asText()`方法。之后在每一行加上换行符,作为输出的开始。然后遍历数组中的元素,输出一个几何图形来表示遍历的结果:
|
||||
|
||||
* 当从数组中迭代出的元素的值大于0时,用`▲`表示
|
||||
* 当从数组中迭代出的元素的值小于0时,用`▼`表示
|
||||
* 当从数组中迭代出的元素的值等于0时,用`○`表示
|
||||
* 当从数组中取出的元素的值大于0时,用`▲`表示
|
||||
* 当从数组中取出的元素的值小于0时,用`▼`表示
|
||||
* 当从数组中取出的元素的值等于0时,用`○`表示
|
||||
|
||||
任意`SankesAndLadders`的实例都可以使用`asPrettyText()`方法。
|
||||
|
||||
```swift
|
||||
println(game.asPrettyText())
|
||||
print(game.asPrettyText())
|
||||
// A game of Snakes and Ladders with 25 squares:
|
||||
// ○ ○ ▲ ○ ○ ▲ ○ ○ ▲ ▲ ○ ○ ○ ▼ ○ ○ ○ ○ ▼ ○ ○ ▼ ○ ▼ ○
|
||||
```
|
||||
|
||||
<a name="class_only_protocol"></a>
|
||||
## 类专属协议
|
||||
你可以在协议的继承列表中,通过添加“class”关键字,限制协议只能适配到类(class)类型。(结构体或枚举不能遵循该协议)。该“class”关键字必须是第一个出现在协议的继承列表中,其后,才是其他继承协议。
|
||||
你可以在协议的继承列表中,通过添加`class`关键字,限制协议只能适配到类(class)类型。(结构体或枚举不能遵循该协议)。该`class`关键字必须是第一个出现在协议的继承列表中,其后,才是其他继承协议。
|
||||
|
||||
```swift
|
||||
protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
|
||||
// class-only protocol definition goes here
|
||||
}
|
||||
```
|
||||
在以上例子中,协议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>
|
||||
在以上例子中,协议`SomeClassOnlyProtocol`只能被类(class)类型适配。如果尝试让结构体或枚举类型适配该协议,则会出现编译错误。
|
||||
|
||||
<!--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>
|
||||
## 协议合成
|
||||
|
||||
一个协议可由多个协议采用`protocol<SomeProtocol, AnotherProtocol>`这样的格式进行组合,称为`协议合成(protocol composition)`。
|
||||
有时候需要同时遵循多个协议。你可以将多个协议采用`protocol<SomeProtocol, AnotherProtocol>`这样的格式进行组合,称为`协议合成(protocol composition)`。你可以在`<>`中罗列任意多个你想要遵循的协议,以逗号分隔。
|
||||
|
||||
举个例子:
|
||||
下面的例子中,将`Named`和`Aged`两个协议按照上述的语法组合成一个协议:
|
||||
|
||||
```swift
|
||||
protocol Named {
|
||||
@ -624,7 +626,7 @@ struct Person: Named, Aged {
|
||||
var age: Int
|
||||
}
|
||||
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)
|
||||
wishHappyBirthday(birthdayPerson)
|
||||
@ -633,27 +635,31 @@ wishHappyBirthday(birthdayPerson)
|
||||
|
||||
`Named`协议包含`String`类型的`name`属性;`Aged`协议包含`Int`类型的`age`属性。`Person`结构体`遵循`了这两个协议。
|
||||
|
||||
`wishHappyBirthday`函数的形参`celebrator`的类型为`protocol<Named,Aged>`。可以传入任意`遵循`这两个协议的类型的实例
|
||||
`wishHappyBirthday`函数的形参`celebrator`的类型为`protocol<Named,Aged>`。可以传入任意`遵循`这两个协议的类型的实例。
|
||||
|
||||
> 注意: `协议合成`并不会生成一个新协议类型,而是将多个协议合成为一个临时的协议,超出范围后立即失效。
|
||||
上面的例子创建了一个名为`birthdayPerson`的`Person`实例,作为参数传递给了`wishHappyBirthday(_:)`函数。因为`Person`同时遵循这两个协议,所以这个参数合法,函数将输出生日问候语。
|
||||
|
||||
> 注意
|
||||
> `协议合成`并不会生成一个新协议类型,而是将多个协议合成为一个临时的协议,超出范围后立即失效。
|
||||
|
||||
<a name="checking_for_protocol_conformance"></a>
|
||||
## 检验协议的一致性
|
||||
|
||||
使用`is`和`as`操作符来检查协议的一致性或转化协议类型。检查和转化的语法和之前相同(*详情查看[Typy Casting章节](5)*):
|
||||
<!--TODO 链接-->
|
||||
你可以使用`is`和`as`操作符来检查是否遵循某一协议或强制转化为某一类型。检查和转化的语法和之前相同(*详情查看[类型转换](./20_Type_Casting.html)*):
|
||||
|
||||
* `is`操作符用来检查实例是否`遵循`了某个`协议`。
|
||||
* `is`操作符用来检查实例是否`遵循`了某个`协议`
|
||||
* `as?`返回一个可选值,当实例`遵循`协议时,返回该协议类型;否则返回`nil`
|
||||
* `as`用以强制向下转型。
|
||||
* `as`用以强制向下转型,如果强转失败,会引起运行时错误。
|
||||
|
||||
下面的例子定义了一个`HasArea`的协议,要求有一个`Double`类型可读的`area`:
|
||||
|
||||
```swift
|
||||
@objc protocol HasArea {
|
||||
protocol HasArea {
|
||||
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`协议
|
||||
|
||||
```swift
|
||||
@ -680,7 +686,7 @@ class Animal {
|
||||
}
|
||||
```
|
||||
|
||||
`Circle,Country,Animal`并没有一个相同的基类,因而采用`AnyObject`类型的数组来装载在他们的实例,如下所示:
|
||||
`Circle`,`Country`,`Animal`并没有一个相同的基类,然而,它们都是类,它们的实例都可以作为`AnyObject`类型的变量,存储在同一个数组中:
|
||||
|
||||
```swift
|
||||
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`协议:
|
||||
|
||||
```swift
|
||||
for object in objects {
|
||||
if let objectWithArea = object as? HasArea {
|
||||
println("Area is \(objectWithArea.area)")
|
||||
print("Area is \(objectWithArea.area)")
|
||||
} else {
|
||||
println("Something that doesn't have an area")
|
||||
print("Something that doesn't have an area")
|
||||
}
|
||||
}
|
||||
// Area is 12.5663708
|
||||
@ -709,20 +715,23 @@ for object in objects {
|
||||
|
||||
当迭代出的元素遵循`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>
|
||||
## 对可选协议的规定
|
||||
|
||||
可选协议含有可选成员,其`遵循者`可以选择是否实现这些成员。在协议中使用`@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
|
||||
@objc protocol CounterDataSource {
|
||||
@ -731,9 +740,10 @@ for object in objects {
|
||||
}
|
||||
```
|
||||
|
||||
`CounterDataSource`含有`incrementForCount`的`可选方法`和`fiexdIncrement`的`可选属性`,它们使用了不同的方法来从数据源中获取合适的增量值。
|
||||
`CounterDataSource`含有`incrementForCount`可选方法和`fiexdIncrement`可选属性,它们使用了不同的方法来从数据源中获取合适的增量值。
|
||||
|
||||
> 注意: `CounterDataSource`中的属性和方法都是可选的,因此可以在类中声明但不实现这些成员,尽管技术上允许这样做,不过最好不要这样写。
|
||||
> 注意
|
||||
> `CounterDataSource`中的属性和方法都是可选的,因此可以在类中声明都不实现这些成员,尽管技术上允许这样做,不过最好不要这样写。
|
||||
|
||||
`Counter`类含有`CounterDataSource?`类型的可选属性`dataSource`,如下所示:
|
||||
|
||||
@ -751,32 +761,34 @@ for object in objects {
|
||||
}
|
||||
```
|
||||
|
||||
`count`属性用于存储当前的值,`increment`方法用来为`count`赋值。
|
||||
类`Counter`使用`count`来存储当前的值。该类同时定义了一个`increment`方法,每次调用该方法的时候,将会增加`count`的值。
|
||||
|
||||
`increment`方法通过`可选链`,尝试从两种`可选成员`中获取`count`。
|
||||
|
||||
1. 由于`dataSource`可能为`nil`,因此在`dataSource`后边加上了`?`标记来表明只在`dataSource`非空时才去调用`incrementForCount`方法。
|
||||
`increment()`方法首先试图使用`incrementForCount(_:)`方法来得到每次的增量。`increment()`方法使用可选链来尝试调用`incrementForCount(_:)`,并将当前的`count`值作为参数传入。
|
||||
|
||||
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 {
|
||||
let fixedIncrement = 3
|
||||
}
|
||||
`ThreeSource`实现了`CounterDataSource`协议,它实现来可选属性`fixedIncrement`,每次返回值`3`:
|
||||
|
||||
使用`ThreeSource`作为数据源开实例化一个`Counter`:
|
||||
```swift
|
||||
@objc class ThreeSource: CounterDataSource {
|
||||
let fixedIncrement = 3
|
||||
}
|
||||
```
|
||||
|
||||
可以使用`ThreeSource`的实例作为`Counter`实例的数据源:
|
||||
|
||||
```swift
|
||||
var counter = Counter()
|
||||
counter.dataSource = ThreeSource()
|
||||
for _ in 1...4 {
|
||||
counter.increment()
|
||||
println(counter.count)
|
||||
print(counter.count)
|
||||
}
|
||||
// 3
|
||||
// 6
|
||||
@ -784,7 +796,9 @@ for _ in 1...4 {
|
||||
// 12
|
||||
```
|
||||
|
||||
`TowardsZeroSource`实现了`CounterDataSource`协议中的`incrementForCount`方法,如下所示:
|
||||
上述代码新建了一个`Counter`实例;将它的数据源设置为`TreeSource`实例;调用`increment()`4次。和你预想的一样,每次在调用的时候,`count`的值增加3.
|
||||
|
||||
下面是一个更为复杂的数据源`TowardsZeroSource`,它将使得最后的值变为0:
|
||||
|
||||
```swift
|
||||
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
|
||||
counter.count = -4
|
||||
counter.dataSource = TowardsZeroSource()
|
||||
for _ in 1...5 {
|
||||
counter.increment()
|
||||
println(counter.count)
|
||||
print(counter.count)
|
||||
}
|
||||
// -3
|
||||
// -2
|
||||
@ -815,3 +833,80 @@ for _ in 1...5 {
|
||||
// 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)"
|
||||
```
|
||||
|
||||
> 注意
|
||||
> 如果有多个协议扩展,而一个协议的遵循者又同时满足它们的限制,那么将会使用所满足限制最多的那个扩展。
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
# 泛型(Generics)
|
||||
|
||||
------
|
||||
|
||||
> 1.0
|
||||
> 翻译:[takalard](https://github.com/takalard)
|
||||
> 校对:[lifedim](https://github.com/lifedim)
|
||||
|
||||
# 泛型
|
||||
|
||||
------
|
||||
> 2.0
|
||||
> 翻译+校对: [SergioChan](https://github.com/SergioChan)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
@ -13,6 +16,7 @@
|
||||
- [类型参数](#type_parameters)
|
||||
- [命名类型参数](#naming_type_parameters)
|
||||
- [泛型类型](#generic_types)
|
||||
- [扩展一个泛型类型](#extending_a_generic_type)
|
||||
- [类型约束](#type_constraints)
|
||||
- [关联类型](#associated_types)
|
||||
- [`Where`语句](#where_clauses)
|
||||
@ -27,43 +31,43 @@
|
||||
这里是一个标准的,非泛型函数`swapTwoInts`,用来交换两个Int值:
|
||||
|
||||
```swift
|
||||
func swapTwoInts(inout a: Int, inout b: Int) {
|
||||
func swapTwoInts(inout a: Int, inout _ b: Int) {
|
||||
let temporaryA = a
|
||||
a = b
|
||||
b = temporaryA
|
||||
}
|
||||
```
|
||||
|
||||
这个函数使用写入读出(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`变量值:
|
||||
|
||||
```swift
|
||||
var someInt = 3
|
||||
var anotherInt = 107
|
||||
swapTwoInts(&someInt, &anotherInt)
|
||||
println("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
|
||||
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
|
||||
// 输出 "someInt is now 107, and anotherInt is now 3"
|
||||
```
|
||||
|
||||
|
||||
`swapTwoInts`函数是非常有用的,但是它只能交换`Int`值,如果你想要交换两个`String`或者`Double`,就不得不写更多的函数,如 `swapTwoStrings`和`swapTwoDoublesfunctions `,如同如下所示:
|
||||
`swapTwoInts(_:_:)`函数是非常有用的,但是它只能交换`Int`值,如果你想要交换两个`String`或者`Double`,就不得不写更多的函数,如 `swapTwoStrings`和`swapTwoDoubles(_:_:)`,如同如下所示:
|
||||
|
||||
```swift
|
||||
func swapTwoStrings(inout a: String, inout b: String) {
|
||||
func swapTwoStrings(inout a: String, inout _ b: String) {
|
||||
let temporaryA = a
|
||||
a = b
|
||||
b = temporaryA
|
||||
}
|
||||
|
||||
func swapTwoDoubles(inout a: Double, inout b: Double) {
|
||||
func swapTwoDoubles(inout a: Double, inout _ b: Double) {
|
||||
let temporaryA = a
|
||||
a = b
|
||||
b = temporaryA
|
||||
}
|
||||
```
|
||||
|
||||
你可能注意到 `swapTwoInts`、 `swapTwoStrings`和`swapTwoDoubles`函数功能都是相同的,唯一不同之处就在于传入的变量类型不同,分别是`Int`、`String`和`Double`。
|
||||
你可能注意到 `swapTwoInts`、 `swapTwoStrings`和`swapTwoDoubles(_:_:)`函数功能都是相同的,唯一不同之处就在于传入的变量类型不同,分别是`Int`、`String`和`Double`。
|
||||
|
||||
但实际应用中通常需要一个用处更强大并且尽可能的考虑到更多的灵活性单个函数,可以用来交换两个任何类型值,很幸运的是,泛型代码帮你解决了这种问题。(一个这种泛型函数后面已经定义好了。)
|
||||
|
||||
@ -73,29 +77,29 @@ func swapTwoDoubles(inout a: Double, inout b: Double) {
|
||||
<a name="generic_functions"></a>
|
||||
## 泛型函数
|
||||
|
||||
`泛型函数`可以工作于任何类型,这里是一个上面`swapTwoInts`函数的泛型版本,用于交换两个值:
|
||||
`泛型函数`可以工作于任何类型,这里是一个上面`swapTwoInts(_:_:)`函数的泛型版本,用于交换两个值:
|
||||
|
||||
```swift
|
||||
func swapTwoValues<T>(inout a: T, inout b: T) {
|
||||
func swapTwoValues<T>(inout a: T, inout _ b: T) {
|
||||
let temporaryA = a
|
||||
a = b
|
||||
b = temporaryA
|
||||
}
|
||||
```
|
||||
|
||||
`swapTwoValues`函数主体和`swapTwoInts`函数是一样的,它只在第一行稍微有那么一点点不同于`swapTwoInts`,如下所示:
|
||||
`swapTwoValues(_:_:)`函数主体和`swapTwoInts(_:_:)`函数是一样的,它只在第一行稍微有那么一点点不同于`swapTwoInts`,如下所示:
|
||||
|
||||
```swift
|
||||
func swapTwoInts(inout a: Int, inout b: Int)
|
||||
func swapTwoValues<T>(inout a: T, inout b: T)
|
||||
func swapTwoInts(inout a: Int, inout _ b: Int)
|
||||
func swapTwoValues<T>(inout a: T, inout _ b: T)
|
||||
```
|
||||
|
||||
|
||||
这个函数的泛型版本使用了占位类型名字(通常此情况下用字母`T`来表示)来代替实际类型名(如`Int`、`String`或`Double`)。占位类型名没有提示`T`必须是什么类型,但是它提示了`a`和`b`必须是同一类型`T`,而不管`T`表示什么类型。只有`swapTwoValues`函数在每次调用时所传入的实际类型才能决定`T`所代表的类型。
|
||||
这个函数的泛型版本使用了占位类型名字(通常此情况下用字母`T`来表示)来代替实际类型名(如`Int`、`String`或`Double`)。占位类型名没有提示`T`必须是什么类型,但是它提示了`a`和`b`必须是同一类型`T`,而不管`T`表示什么类型。只有`swapTwoValues(_:_:)`函数在每次调用时所传入的实际类型才能决定`T`所代表的类型。
|
||||
|
||||
另外一个不同之处在于这个泛型函数名后面跟着的占位类型名字(T)是用尖括号括起来的(`<T>`)。这个尖括号告诉 Swift 那个`T`是`swapTwoValues`函数所定义的一个类型。因为`T`是一个占位命名类型,Swift 不会去查找命名为T的实际类型。
|
||||
另外一个不同之处在于这个泛型函数名后面跟着的占位类型名字(T)是用尖括号括起来的(`<T>`)。这个尖括号告诉 Swift 那个`T`是`swapTwoValues(_:_:)`函数所定义的一个类型。因为`T`是一个占位命名类型,Swift 不会去查找命名为T的实际类型。
|
||||
|
||||
`swapTwoValues`函数除了要求传入的两个任何类型值是同一类型外,也可以作为`swapTwoInts`函数被调用。每次`swapTwoValues`被调用,T所代表的类型值都会传给函数。
|
||||
`swapTwoValues(_:_:)`函数除了要求传入的两个任何类型值是同一类型外,也可以作为`swapTwoInts`函数被调用。每次`swapTwoValues`被调用,T所代表的类型值都会传给函数。
|
||||
|
||||
在下面的两个例子中,`T`分别代表`Int`和`String`:
|
||||
|
||||
@ -103,26 +107,26 @@ func swapTwoValues<T>(inout a: T, inout b: T)
|
||||
var someInt = 3
|
||||
var anotherInt = 107
|
||||
swapTwoValues(&someInt, &anotherInt)
|
||||
// someInt is now 107, and anotherInt is now 3
|
||||
// someInt 现在等于 107, anotherInt 现在等于 3
|
||||
```
|
||||
|
||||
```swift
|
||||
var someString = "hello"
|
||||
var anotherString = "world"
|
||||
swapTwoValues(&someString, &anotherString)
|
||||
// someString is now "world", and anotherString is now "hello"
|
||||
// someString 现在等于 "world", anotherString 现在等于 "hello"
|
||||
```
|
||||
|
||||
|
||||
>注意
|
||||
上面定义的函数`swapTwoValues`是受`swap`函数启发而实现的。`swap`函数存在于 Swift 标准库,并可以在其它类中任意使用。如果你在自己代码中需要类似`swapTwoValues`函数的功能,你可以使用已存在的交换函数`swap`函数。
|
||||
上面定义的函数`swapTwoValues(_:_:)`是受`swap`函数启发而实现的。`swap`函数存在于 Swift 标准库,并可以在其它类中任意使用。如果你在自己代码中需要类似`swapTwoValues(_:_:)`函数的功能,你可以使用已存在的交换函数`swap(_:_:)`函数。
|
||||
|
||||
<a name="type_parameters"></a>
|
||||
## 类型参数
|
||||
|
||||
在上面的`swapTwoValues`例子中,占位类型`T`是一种类型参数的示例。类型参数指定并命名为一个占位类型,并且紧随在函数名后面,使用一对尖括号括起来(如`<T>`)。
|
||||
|
||||
一旦一个类型参数被指定,那么其可以被使用来定义一个函数的参数类型(如`swapTwoValues`函数中的参数`a`和`b`),或作为一个函数返回类型,或用作函数主体中的注释类型。在这种情况下,被类型参数所代表的占位类型不管函数任何时候被调用,都会被实际类型所替换(在上面`swapTwoValues`例子中,当函数第一次被调用时,`T`被`Int`替换,第二次调用时,被`String`替换。)。
|
||||
一旦一个类型参数被指定,那么其可以被使用来定义一个函数的参数类型(如`swapTwoValues(_:_:)`函数中的参数`a`和`b`),或作为一个函数返回类型,或用作函数主体中的注释类型。在这种情况下,被类型参数所代表的占位类型不管函数任何时候被调用,都会被实际类型所替换(在上面`swapTwoValues`例子中,当函数第一次被调用时,`T`被`Int`替换,第二次调用时,被`String`替换。)。
|
||||
|
||||
你可支持多个类型参数,命名在尖括号中,用逗号分开。
|
||||
|
||||
@ -131,10 +135,10 @@ swapTwoValues(&someString, &anotherString)
|
||||
|
||||
在简单的情况下,泛型函数或泛型类型需要指定一个占位类型(如上面的`swapTwoValues`泛型函数,或一个存储单一类型的泛型集,如数组),通常用一单个字母`T`来命名类型参数。不过,你可以使用任何有效的标识符来作为类型参数名。
|
||||
|
||||
如果你使用多个参数定义更复杂的泛型函数或泛型类型,那么使用更多的描述类型参数是非常有用的。例如,Swift 字典(Dictionary)类型有两个类型参数,一个是键,另外一个是值。如果你自己写字典,你或许会定义这两个类型参数为`KeyType`和`ValueType`,用来记住它们在你的泛型代码中的作用。
|
||||
如果你使用多个参数定义更复杂的泛型函数或泛型类型,那么使用更多的描述类型参数是非常有用的。例如,Swift 字典(Dictionary)类型有两个类型参数,一个是键,另外一个是值。如果你自己写字典,你或许会定义这两个类型参数为`Key`和`Value`,用来记住它们在你的泛型代码中的作用。
|
||||
|
||||
>注意
|
||||
请始终使用大写字母开头的驼峰式命名法(例如`T`和`KeyType`)来给类型参数命名,以表明它们是类型的占位符,而非类型值。
|
||||
请始终使用大写字母开头的驼峰式命名法(例如`T`和`Key`)来给类型参数命名,以表明它们是类型的占位符,而非类型值。
|
||||
|
||||
<a name="generic_types"></a>
|
||||
## 泛型类型
|
||||
@ -145,7 +149,7 @@ swapTwoValues(&someString, &anotherString)
|
||||
这部分向你展示如何写一个泛型集类型--`Stack`(栈)。一个栈是一系列值域的集合,和`Array`(数组)类似,但其是一个比 Swift 的`Array`类型更多限制的集合。一个数组可以允许其里面任何位置的插入/删除操作,而栈,只允许在集合的末端添加新的项(如同*push*一个新值进栈)。同样的一个栈也只能从末端移除项(如同*pop*一个值出栈)。
|
||||
|
||||
>注意
|
||||
栈的概念已被`UINavigationController`类使用来模拟试图控制器的导航结构。你通过调用`UINavigationController`的`pushViewController:animated:`方法来为导航栈添加(add)新的试图控制器;而通过`popViewControllerAnimated:`的方法来从导航栈中移除(pop)某个试图控制器。每当你需要一个严格的`后进先出`方式来管理集合,堆栈都是最实用的模型。
|
||||
栈的概念已被`UINavigationController`类使用来模拟试图控制器的导航结构。你通过调用`UINavigationController`的`pushViewController(_:animated:)`方法来为导航栈添加(add)新的试图控制器;而通过`popViewControllerAnimated(_:)`的方法来从导航栈中移除(pop)某个试图控制器。每当你需要一个严格的`后进先出`方式来管理集合,堆栈都是最实用的模型。
|
||||
|
||||
下图展示了一个栈的压栈(push)/出栈(pop)的行为:
|
||||
|
||||
@ -161,7 +165,7 @@ swapTwoValues(&someString, &anotherString)
|
||||
|
||||
```swift
|
||||
struct IntStack {
|
||||
var items = Int[]()
|
||||
var items = [Int]()
|
||||
mutating func push(item: Int) {
|
||||
items.append(item)
|
||||
}
|
||||
@ -196,10 +200,12 @@ struct Stack<T> {
|
||||
`T`定义了一个名为“某种类型T”的节点提供给后来用。这种将来类型可以在结构体的定义里任何地方表示为“T”。在这种情况下,`T`在如下三个地方被用作节点:
|
||||
|
||||
- 创建一个名为`items`的属性,使用空的T类型值数组对其进行初始化;
|
||||
- 指定一个包含一个参数名为`item`的`push`方法,该参数必须是T类型;
|
||||
- 指定一个包含一个参数名为`item`的`push(_:)`方法,该参数必须是T类型;
|
||||
- 指定一个`pop`方法的返回值,该返回值将是一个T类型值。
|
||||
|
||||
当创建一个新单例并初始化时, 通过用一对紧随在类型名后的尖括号里写出实际指定栈用到类型,创建一个`Stack`实例,同创建`Array`和`Dictionary`一样:
|
||||
由于`Stack`是泛型类型,所以在 Swift 中其可以用来创建任何有效类型的栈,这种方式如同`Array`和`Dictionary`。
|
||||
|
||||
你可以通过在尖括号里写出栈中需要存储的数据类型来创建并初始化一个`Stack`实例。比如,要创建一个`strings`的栈,你可以写成`Stack<String>()`:
|
||||
|
||||
```swift
|
||||
var stackOfStrings = Stack<String>()
|
||||
@ -218,20 +224,46 @@ stackOfStrings.push("cuatro")
|
||||
|
||||
```swift
|
||||
let fromTheTop = stackOfStrings.pop()
|
||||
// fromTheTop is equal to "cuatro", and the stack now contains 3 strings
|
||||
// fromTheTop 等于 "cuatro", 现在栈中还有3个string
|
||||
```
|
||||
|
||||
下图展示了如何从栈中pop一个值的过程:
|
||||

|
||||
|
||||
由于`Stack`是泛型类型,所以在 Swift 中其可以用来创建任何有效类型的栈,这种方式如同`Array`和`Dictionary`。
|
||||
<a name="extending_a_generic_type"></a>
|
||||
## 扩展一个泛型类型
|
||||
|
||||
当你扩展一个泛型类型的时候,你并不需要在扩展的定义中提供类型参数列表。更加方便的是,原始类型定义中声明的类型参数列表在扩展里是可以使用的,并且这些来自原始类型中的参数名称会被用作原始定义中类型参数的引用。
|
||||
|
||||
下面的例子扩展了泛型`Stack`类型,为其添加了一个名为`topItem`的只读计算属性,它将会返回当前栈顶端的元素而不会将其从栈中移除。
|
||||
|
||||
```swift
|
||||
extension Stack {
|
||||
var topItem: T? {
|
||||
return items.isEmpty ? nil : items[items.count - 1]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`topItem`属性会返回一个`T`类型的可选值。当栈为空的时候,`topItem`将会返回`nil`;当栈不为空的时候,`topItem`会返回`items`数组中的最后一个元素。
|
||||
|
||||
注意这里的扩展并没有定义一个类型参数列表。相反的,`Stack`类型已有的类型参数名称,`T`,被用在扩展中当做`topItem`计算属性的可选类型。
|
||||
|
||||
`topItem`计算属性现在可以被用来返回任意`Stack`实例的顶端元素而无需移除它:
|
||||
|
||||
```swift
|
||||
if let topItem = stackOfStrings.topItem {
|
||||
print("The top item on the stack is \(topItem).")
|
||||
}
|
||||
// 输出 "The top item on the stack is tres."
|
||||
```
|
||||
|
||||
<a name="type_constraints"></a>
|
||||
##类型约束
|
||||
|
||||
`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`)默认都是可哈希。
|
||||
|
||||
@ -243,7 +275,7 @@ let fromTheTop = stackOfStrings.pop()
|
||||
|
||||
```swift
|
||||
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
|
||||
// function body goes here
|
||||
// 这里是函数主体
|
||||
}
|
||||
```
|
||||
|
||||
@ -251,11 +283,11 @@ func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
|
||||
|
||||
### 类型约束行为
|
||||
|
||||
这里有个名为`findStringIndex`的非泛型函数,该函数功能是去查找包含一给定`String`值的数组。若查找到匹配的字符串,`findStringIndex`函数返回该字符串在数组中的索引值(`Int`),反之则返回`nil`:
|
||||
这里有个名为`findStringIndex`的非泛型函数,该函数功能是去查找包含一给定`String`值的数组。若查找到匹配的字符串,`findStringIndex(_:_:)`函数返回该字符串在数组中的索引值(`Int`),反之则返回`nil`:
|
||||
|
||||
```swift
|
||||
func findStringIndex(array: [String], valueToFind: String) -> Int? {
|
||||
for (index, value) in enumerate(array) {
|
||||
func findStringIndex(array: [String], _ valueToFind: String) -> Int? {
|
||||
for (index, value) in array.enumerate() {
|
||||
if value == valueToFind {
|
||||
return index
|
||||
}
|
||||
@ -265,12 +297,12 @@ func findStringIndex(array: [String], valueToFind: String) -> Int? {
|
||||
```
|
||||
|
||||
|
||||
`findStringIndex`函数可以作用于查找一字符串数组中的某个字符串:
|
||||
`findStringIndex(_:_:)`函数可以作用于查找一字符串数组中的某个字符串:
|
||||
|
||||
```swift
|
||||
let strings = ["cat", "dog", "llama", "parakeet", "terrapin"]
|
||||
if let foundIndex = findStringIndex(strings, "llama") {
|
||||
println("The index of llama is \(foundIndex)")
|
||||
print("The index of llama is \(foundIndex)")
|
||||
}
|
||||
// 输出 "The index of llama is 2"
|
||||
```
|
||||
@ -280,8 +312,8 @@ if let foundIndex = findStringIndex(strings, "llama") {
|
||||
这里展示如何写一个你或许期望的`findStringIndex`的泛型版本`findIndex`。请注意这个函数仍然返回`Int`,是不是有点迷惑呢,而不是泛型类型?那是因为函数返回的是一个可选的索引数,而不是从数组中得到的一个可选值。需要提醒的是,这个函数不会编译,原因在例子后面会说明:
|
||||
|
||||
```swift
|
||||
func findIndex<T>(array: [T], valueToFind: T) -> Int? {
|
||||
for (index, value) in enumerate(array) {
|
||||
func findIndex<T>(array: [T], _ valueToFind: T) -> Int? {
|
||||
for (index, value) in array.enumerate() {
|
||||
if value == valueToFind {
|
||||
return index
|
||||
}
|
||||
@ -294,11 +326,11 @@ func findIndex<T>(array: [T], valueToFind: T) -> Int? {
|
||||
|
||||
不过,所有的这些并不会让我们无从下手。Swift 标准库中定义了一个`Equatable`协议,该协议要求任何遵循的类型实现等式符(==)和不等符(!=)对任何两个该类型进行比较。所有的 Swift 标准类型自动支持`Equatable`协议。
|
||||
|
||||
任何`Equatable`类型都可以安全的使用在`findIndex`函数中,因为其保证支持等式操作。为了说明这个事实,当你定义一个函数时,你可以写一个`Equatable`类型约束作为类型参数定义的一部分:
|
||||
任何`Equatable`类型都可以安全的使用在`findIndex(_:_:)`函数中,因为其保证支持等式操作。为了说明这个事实,当你定义一个函数时,你可以写一个`Equatable`类型约束作为类型参数定义的一部分:
|
||||
|
||||
```swift
|
||||
func findIndex<T: Equatable>(array: T[], valueToFind: T) -> Int? {
|
||||
for (index, value) in enumerate(array) {
|
||||
func findIndex<T: Equatable>(array: [T], _ valueToFind: T) -> Int? {
|
||||
for (index, value) in array.enumerate() {
|
||||
if value == valueToFind {
|
||||
return index
|
||||
}
|
||||
@ -310,7 +342,7 @@ func findIndex<T: Equatable>(array: T[], valueToFind: T) -> Int? {
|
||||
|
||||
`findIndex`中这个单个类型参数写做:`T: Equatable`,也就意味着“任何T类型都遵循`Equatable`协议”。
|
||||
|
||||
`findIndex`函数现在则可以成功的编译过,并且作用于任何遵循`Equatable`的类型,如`Double`或`String`:
|
||||
`findIndex(_:_:)`函数现在则可以成功的编译过,并且作用于任何遵循`Equatable`的类型,如`Double`或`String`:
|
||||
|
||||
```swift
|
||||
let doubleIndex = findIndex([3.14159, 0.1, 0.25], 9.3)
|
||||
@ -326,7 +358,7 @@ let stringIndex = findIndex(["Mike", "Malcolm", "Andrea"], "Andrea")
|
||||
|
||||
### 关联类型行为
|
||||
|
||||
这里是一个`Container`协议的例子,定义了一个ItemType关联类型:
|
||||
这里是一个`Container`协议的例子,定义了一个`ItemType`关联类型:
|
||||
|
||||
```swift
|
||||
protocol Container {
|
||||
@ -339,19 +371,19 @@ protocol Container {
|
||||
|
||||
`Container`协议定义了三个任何容器必须支持的兼容要求:
|
||||
|
||||
- 必须可以通过`append`方法添加一个新item到容器里;
|
||||
- 必须可以通过使用`count`属性获取容器里items的数量,并返回一个`Int`值;
|
||||
- 必须可以通过容器的`Int`索引值下标可以检索到每一个item。
|
||||
- 必须可以通过`append(_:)`方法添加一个新元素到容器里;
|
||||
- 必须可以通过使用`count`属性获取容器里元素的数量,并返回一个`Int`值;
|
||||
- 必须可以通过容器的`Int`索引值下标可以检索到每一个元素。
|
||||
|
||||
这个协议没有指定容器里item是如何存储的或何种类型是允许的。这个协议只指定三个任何遵循`Container`类型所必须支持的功能点。一个遵循的类型在满足这三个条件的情况下也可以提供其他额外的功能。
|
||||
这个协议没有指定容器里的元素是如何存储的或何种类型是允许的。这个协议只指定三个任何遵循`Container`类型所必须支持的功能点。一个遵循的类型在满足这三个条件的情况下也可以提供其他额外的功能。
|
||||
|
||||
任何遵循`Container`协议的类型必须指定存储在其里面的值类型,必须保证只有正确类型的items可以加进容器里,必须明确可以通过其下标返回item类型。
|
||||
任何遵循`Container`协议的类型必须指定存储在其里面的值类型,必须保证只有正确类型的元素可以加进容器里,必须明确可以通过其下标返回元素类型。
|
||||
|
||||
为了定义这三个条件,`Container`协议需要一个方法指定容器里的元素将会保留,而不需要知道特定容器的类型。`Container`协议需要指定任何通过`append`方法添加到容器里的值和容器里元素是相同类型,并且通过容器下标返回的容器元素类型的值的类型是相同类型。
|
||||
为了定义这三个条件,`Container`协议需要一个方法指定容器里的元素将会保留,而不需要知道特定容器的类型。`Container`协议需要指定任何通过`append(_:)`方法添加到容器里的值和容器里元素是相同类型,并且通过容器下标返回的容器元素类型的值的类型是相同类型。
|
||||
|
||||
为了达到此目的,`Container`协议声明了一个ItemType的关联类型,写作`typealias ItemType`。这个协议不会定义`ItemType`是什么的别名,这个信息将由任何遵循协议的类型来提供。尽管如此,`ItemType`别名提供了一种识别Container中Items类型的方法,并且用于`append`方法和`subscript`方法的类型定义,以便保证任何`Container`期望的行为能够被执行。
|
||||
为了达到此目的,`Container`协议声明了一个`ItemType`的关联类型,写作`typealias ItemType`。这个协议不会定义`ItemType`是什么的别名,这个信息将由任何遵循协议的类型来提供。尽管如此,`ItemType`别名提供了一种识别`Container`中元素类型的方法,并且用于`append(_:)`方法和`subscript`方法的类型定义,以便保证任何`Container`期望的行为能够被执行。
|
||||
|
||||
这里是一个早前IntStack类型的非泛型版本,遵循Container协议:
|
||||
这里是一个早前`IntStack`类型的非泛型版本,遵循`Container`协议:
|
||||
|
||||
```swift
|
||||
struct IntStack: Container {
|
||||
@ -369,7 +401,7 @@ struct IntStack: Container {
|
||||
self.push(item)
|
||||
}
|
||||
var count: Int {
|
||||
return items.count
|
||||
return items.count
|
||||
}
|
||||
subscript(i: Int) -> Int {
|
||||
return items[i]
|
||||
@ -380,9 +412,9 @@ struct IntStack: Container {
|
||||
|
||||
`IntStack`类型实现了`Container`协议的所有三个要求,在`IntStack`类型的每个包含部分的功能都满足这些要求。
|
||||
|
||||
此外,`IntStack`指定了`Container`的实现,适用的ItemType被用作`Int`类型。对于这个`Container`协议实现而言,定义 `typealias ItemType = Int`,将抽象的`ItemType`类型转换为具体的`Int`类型。
|
||||
此外,`IntStack`指定了`Container`的实现,适用的`ItemType`被用作`Int`类型。对于这个`Container`协议实现而言,定义 `typealias ItemType = Int`,将抽象的`ItemType`类型转换为具体的`Int`类型。
|
||||
|
||||
感谢Swift类型参考,你不用在`IntStack`定义部分声明一个具体的`Int`的`ItemType`。由于`IntStack`遵循`Container`协议的所有要求,只要通过简单的查找`append`方法的item参数类型和下标返回的类型,Swift就可以推断出合适的`ItemType`来使用。确实,如果上面的代码中你删除了 `typealias ItemType = Int`这一行,一切仍旧可以工作,因为它清楚的知道ItemType使用的是何种类型。
|
||||
感谢Swift类型参考,你不用在`IntStack`定义部分声明一个具体的`Int`的`ItemType`。由于`IntStack`遵循`Container`协议的所有要求,只要通过简单的查找`append(_:)`方法的`item`参数类型和下标返回的类型,Swift就可以推断出合适的`ItemType`来使用。确实,如果上面的代码中你删除了 `typealias ItemType = Int`这一行,一切仍旧可以工作,因为它清楚的知道`ItemType`使用的是何种类型。
|
||||
|
||||
你也可以生成遵循`Container`协议的泛型`Stack`类型:
|
||||
|
||||
@ -401,7 +433,7 @@ struct Stack<T>: Container {
|
||||
self.push(item)
|
||||
}
|
||||
var count: Int {
|
||||
return items.count
|
||||
return items.count
|
||||
}
|
||||
subscript(i: Int) -> T {
|
||||
return items[i]
|
||||
@ -409,20 +441,20 @@ struct Stack<T>: Container {
|
||||
}
|
||||
```
|
||||
|
||||
这个时候,占位类型参数`T`被用作`append`方法的item参数和下标的返回类型。Swift 因此可以推断出被用作这个特定容器的`ItemType`的`T`的合适类型。
|
||||
这个时候,占位类型参数`T`被用作`append(_:)`方法的`item`参数和下标的返回类型。Swift 因此可以推断出被用作这个特定容器的`ItemType`的`T`的合适类型。
|
||||
|
||||
|
||||
### 扩展一个存在的类型为一指定关联类型
|
||||
|
||||
在[使用扩展来添加协议兼容性](../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
|
||||
extension Array: Container {}
|
||||
```
|
||||
|
||||
如同上面的泛型`Stack`类型一样,`Array的append`方法和下标保证`Swift`可以推断出`ItemType`所使用的适用的类型。定义了这个扩展后,你可以将任何`Array`当作`Container`来使用。
|
||||
如同上面的泛型`Stack`类型一样,`Array`的`append(_:)`方法和下标保证`Swift`可以推断出`ItemType`所使用的适用的类型。定义了这个扩展后,你可以将任何`Array`当作`Container`来使用。
|
||||
|
||||
<a name="where_clauses"></a>
|
||||
## Where 语句
|
||||
@ -480,15 +512,15 @@ func allItemsMatch<
|
||||
|
||||
第三个和第四个要求结合起来的意思是`anotherContainer`中的元素也可以通过 `!=` 操作来检查,因为它们在`someContainer`中元素确实是相同的类型。
|
||||
|
||||
这些要求能够使`allItemsMatch`函数比较两个容器,即便它们是不同的容器类型。
|
||||
这些要求能够使`allItemsMatch(_:_:)`函数比较两个容器,即便它们是不同的容器类型。
|
||||
|
||||
`allItemsMatch`首先检查两个容器是否拥有同样数目的items,如果它们的元素数目不同,没有办法进行匹配,函数就会`false`。
|
||||
`allItemsMatch(_:_:)`首先检查两个容器是否拥有同样数目的items,如果它们的元素数目不同,没有办法进行匹配,函数就会`false`。
|
||||
|
||||
检查完之后,函数通过`for-in`循环和半闭区间操作(..)来迭代`someContainer`中的所有元素。对于每个元素,函数检查是否`someContainer`中的元素不等于对应的`anotherContainer`中的元素,如果这两个元素不等,则这两个容器不匹配,返回`false`。
|
||||
检查完之后,函数通过`for-in`循环和半闭区间操作(`..<`)来迭代`someContainer`中的所有元素。对于每个元素,函数检查是否`someContainer`中的元素不等于对应的`anotherContainer`中的元素,如果这两个元素不等,则这两个容器不匹配,返回`false`。
|
||||
|
||||
如果循环体结束后未发现没有任何的不匹配,那表明两个容器匹配,函数返回`true`。
|
||||
|
||||
这里演示了allItemsMatch函数运算的过程:
|
||||
这里演示了`allItemsMatch(_:_:)`函数运算的过程:
|
||||
|
||||
```swift
|
||||
var stackOfStrings = Stack<String>()
|
||||
@ -499,14 +531,14 @@ stackOfStrings.push("tres")
|
||||
var arrayOfStrings = ["uno", "dos", "tres"]
|
||||
|
||||
if allItemsMatch(stackOfStrings, arrayOfStrings) {
|
||||
println("All items match.")
|
||||
print("All items match.")
|
||||
} else {
|
||||
println("Not all items match.")
|
||||
print("Not all items match.")
|
||||
}
|
||||
// 输出 "All items match."
|
||||
```
|
||||
|
||||
上面的例子创建一个`Stack`单例来存储`String`,然后压了三个字符串进栈。这个例子也创建了一个`Array`单例,并初始化包含三个同栈里一样的原始字符串。即便栈和数组是不同的类型,但它们都遵循`Container`协议,而且它们都包含同样的类型值。因此你可以调用`allItemsMatch`函数,用这两个容器作为它的参数。在上面的例子中,`allItemsMatch`函数正确的显示了所有的这两个容器的`items`匹配。
|
||||
上面的例子创建一个`Stack`单例来存储`String`,然后压了三个字符串进栈。这个例子也创建了一个`Array`单例,并初始化包含三个同栈里一样的原始字符串。即便栈和数组是不同的类型,但它们都遵循`Container`协议,而且它们都包含同样的类型值。因此你可以调用`allItemsMatch(_:_:)`函数,用这两个容器作为它的参数。在上面的例子中,`allItemsMatch(_:_:)`函数正确的显示了这两个容器的所有元素都是相互匹配的。
|
||||
|
||||
[1]: ../chapter2/06_Functions.html
|
||||
[2]: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/stackPushPop_2x.png
|
||||
|
||||
@ -1,8 +1,12 @@
|
||||
> 翻译:[JaceFu](http://www.devtalking.com/)
|
||||
# 访问控制(Access Control)
|
||||
------------------
|
||||
|
||||
> 1.0
|
||||
> 翻译:[JaceFu](http://www.devtalking.com/)
|
||||
> 校对:[ChildhoodAndy](http://childhood.logdown.com)
|
||||
|
||||
# 访问控制
|
||||
------------------
|
||||
> 2.0
|
||||
> 翻译+校对:[mmoaay](https://github.com/mmoaay)
|
||||
|
||||
本页内容包括:
|
||||
|
||||
@ -12,6 +16,7 @@
|
||||
- [默认访问级别](#default_access_levels)
|
||||
- [单目标应用程序的访问级别](#access_levels_for_single-target_apps)
|
||||
- [Framework的访问级别](#access_levels_for_frameworks)
|
||||
- [单元测试目标的访问级别](#access_levels_for_unit_test_targets)
|
||||
- [访问控制语法](#access_control_syntax)
|
||||
- [自定义类型](#custom_types)
|
||||
- [元组类型](#tuple_types)
|
||||
@ -33,45 +38,47 @@
|
||||
- [泛型](#generics)
|
||||
- [类型别名](#type_aliases)
|
||||
|
||||
访问控制可以限定你在源文件或模块中访问代码的级别,也就是说可以控制哪些代码你可以访问,哪些代码你不能访问。这个特性可以让我们隐藏功能实现的一些细节,并且可以明确的指定我们提供给其他人的接口中哪些部分是他们可以使用的,哪些是他们看不到的。
|
||||
*访问控制*可以限定其他源文件或模块中代码对你代码的访问级别。这个特性可以让我们隐藏功能实现的一些细节,并且可以明确的申明我们提供给其他人的接口中哪些部分是他们可以访问和使用的。
|
||||
|
||||
你可以明确的给类、结构体、枚举、设置访问级别,也可以给属性、函数、初始化方法、基本类型、下标索引等设置访问级别。协议也可以被限定在一定的范围内使用,包括协议里的全局常量、变量和函数。
|
||||
你可以明确地给单个类型(类、结构体、枚举)设置访问级别,也可以给这些类型的属性、函数、初始化方法、基本类型、下标索引等设置访问级别。协议也可以被限定在一定的范围内使用,包括协议里的全局常量、变量和函数。
|
||||
|
||||
在提供了不同访问级别的同时,Swift 并没有规定我们要在任何时候都要在代码中明确指定访问级别。其实,如果我们作为独立开发者在开发我们自己的 app,而不是在开发一些`Framework`的时候,我们完全可以不用明确的指定代码的访问级别。
|
||||
在提供了不同访问级别的同时,Swift还为某些典型场景提供了默认的访问级别,这样就不需要我们在每段代码中都申明显式访问级别。其实,如果只是开发一个单目标应用程序,我们完全可以不用申明代码的显式访问级别。
|
||||
|
||||
> 注意:为方便起见,在代码中可以设置访问级别的它们(属性、基本类型、函数等)在下面的章节中我们称之为“实体”。
|
||||
> 注意:简单起见,代码中可以设置访问级别的特性(属性、基本类型、函数等),在下面的章节中我们会以“实体”代替。
|
||||
|
||||
<a name="modules_and_source_files"></a>
|
||||
## 模块和源文件
|
||||
Swift 中的访问控制模型基于模块和源文件这两个概念。
|
||||
|
||||
模块指的是`Framework`或`App bundle`。在 Swift 中,可以用`import`关键字引入自己的工程。
|
||||
*模块*指的是以独立单元构建和发布的`Framework`或`Application`。在Swift 中的一个模块可以使用`import`关键字引入另外一个模块。
|
||||
|
||||
在 Swift 中,`Framework`或`App bundle`被作为模块处理。如果你是为了实现某个通用的功能,或者是为了封装一些常用方法而将代码打包成`Framework`,这个`Framework`在 Swift 中就被称为模块。不论它被引入到某个 App 工程或者其他的`Framework`,它里面的一切(属性、函数等)都属于这个模块。
|
||||
在 Swift 中,Xcode的每个构建目标(比如`Framework`或`app bundle`)都被当作模块处理。如果你是为了实现某个通用的功能,或者是为了封装一些常用方法而将代码打包成独立的`Framework`,这个`Framework`在 Swift 中就被称为模块。当它被引入到某个 app 工程或者另外一个`Framework`时,它里面的一切(属性、函数等)仍然属于这个独立的模块。
|
||||
|
||||
源文件指的是 Swift 中的`Swift File`,就是编写 Swift 代码的文件,它通常属于一个模块。通常一个源文件包含一个`类`,在`类`中又包含`函数`、`属性`等类型。
|
||||
*源文件*指的是 Swift 中的`Swift File`,就是编写 Swift 源代码的文件,它通常属于一个模块。尽管一般我们将不同的`类` 分别定义在不同的源文件中,但是同一个源文件可以包含多个`类`和`函数` 的定义。
|
||||
|
||||
<a name="access_levels"></a>
|
||||
## 访问级别
|
||||
Swift 提供了三种不同的访问级别。这些访问级别相对于源文件中定义的实体,同时也相对于这些源文件所属的模块。
|
||||
Swift 为代码中的实体提供了三种不同的访问级别。这些访问级别不仅与源文件中定义的实体相关,同时也与源文件所属的模块相关。
|
||||
|
||||
- `Public`:可以访问自己模块或应用中源文件里的任何实体,别人也可以访问引入该模块中源文件里的所有实体。通常情况下,某个接口或`Framework`是可以被任何人使用时,你可以将其设置为`public`级别。
|
||||
- `Internal`:可以访问自己模块或应用中源文件里的任何实体,但是别人不能访问该模块中源文件里的实体。通常情况下,某个接口或`Framework`作为内部结构使用时,你可以将其设置为`internal`级别。
|
||||
- `Private`:只能在当前源文件中使用的实体,称为私有实体。使用`private`级别,可以用作隐藏某些功能的实现细节。
|
||||
- `public`:可以访问自己模块中源文件里的任何实体,别人也可以通过引入该模块来访问源文件里的所有实体。通常情况下,`Framework` 中的某个接口是可以被任何人使用时,你可以将其设置为`public`级别。
|
||||
- `internal`:可以访问自己模块中源文件里的任何实体,但是别人不能访问该模块中源文件里的实体。通常情况下,某个接口或`Framework`作为内部结构使用时,你可以将其设置为`internal`级别。
|
||||
- `private`:只能在当前源文件中使用的实体,称为私有实体。使用`private`级别,可以用作隐藏某些功能的实现细节。
|
||||
|
||||
`Public`为最高级访问级别,`Private`为最低级访问级别。
|
||||
`public`为最高级访问级别,`private`为最低级访问级别。
|
||||
|
||||
> 注意:Swift中的`private`访问和其他语言中的`private`访问不太一样,它的范围限于整个源文件,而不是声明。这就意味着,一个`类` 可以访问定义该`类` 的源文件中定义的所有`private`实体,但是如果一个`类` 的扩展是定义在独立的源文件中,那么就不能访问这个`类` 的`private`成员。
|
||||
|
||||
<a name="guiding_principle_of_access_levels"></a>
|
||||
### 访问级别的使用原则
|
||||
在 Swift 中,访问级别有如下使用原则:访问级别统一性。
|
||||
Swift 中的访问级别遵循一个使用原则:访问级别统一性。
|
||||
比如说:
|
||||
|
||||
- 一个`public`访问级别的变量,不能将它的类型定义为`internal`和`private`的类型。因为变量可以被任何人访问,但是定义它的类型不可以,所以这样就会出现错误。
|
||||
- 函数的访问级别不能高于它的参数、返回类型的访问级别。因为如果函数定义为`public`而参数或者返回类型定义为`internal`或`private`,就会出现函数可以被任何人访问,但是它的参数和返回类型不可以,同样会出现错误。
|
||||
- 一个`public`访问级别的变量,不能将它的类型定义为`internal`和`private`。因为变量可以被任何人访问,但是定义它的类型不可以,所以这样就会出现错误。
|
||||
- 函数的访问级别不能高于它的参数、返回类型的访问级别。因为如果函数定义为`public`而参数或者返回类型定义为`internal`或`private`,就会出现函数可以被任何人访问,但是它的参数和返回类型确不可以,同样会出现错误。
|
||||
|
||||
<a name="default_access_levels"></a>
|
||||
### 默认访问级别
|
||||
代码中的所有实体,如果你不明确的定义其访问级别,那么它们默认为`internal`级别。在大多数情况下,我们不需要明确的设置实体的访问级别,因为我们大多数时候都是在开发一个 App bundle。
|
||||
如果你不为代码中的所有实体定义显式访问级别,那么它们默认为`internal`级别。在大多数情况下,我们不需要设置实体的显式访问级别。因为我们一般都是在开发一个`app bundle`。
|
||||
|
||||
<a name="access_levels_for_single-target_apps"></a>
|
||||
### 单目标应用程序的访问级别
|
||||
@ -79,9 +86,14 @@ Swift 提供了三种不同的访问级别。这些访问级别相对于源文
|
||||
|
||||
<a name="access_levels_for_frameworks"></a>
|
||||
### Framework的访问级别
|
||||
当你开发`Framework`时,就需要把一些实体定义为`public`级别,以便其他人导入该`Framework`后可以正常使用其功能。这些被你定义为`public`的实体,就是这个`Framework`的API。
|
||||
当你开发`Framework`时,就需要把一些对外的接口定义为`public`级别,以便其他人导入该`Framework`后可以正常使用其功能。这些被你定义为`public`的接口,就是这个`Framework`的API。
|
||||
|
||||
> 注意:`Framework`的内部实现细节依然可以使用默认的`internal`级别,或者也可以定义为`private`级别。只有你想将它作为 API 的实体,才将其定义为`public`级别。
|
||||
> 注意:`Framework`的内部实现细节依然可以使用默认的`internal`级别,或者也可以定义为`private`级别。只有当你想把它作为 API 的一部分的时候,才将其定义为`public`级别。
|
||||
|
||||
<a name="access_levels_for_unit_test_targets"></a>
|
||||
### 单元测试目标的访问级别
|
||||
|
||||
当你的app有单元测试目标时,为了方便测试,测试模块需要能访问到你app中的代码。默认情况下只有`public`级别的实体才可以被其他模块访问。然而,如果在引入一个生产模块时使用`@testable`注解,然后用带测试的方式编译这个生产模块,单元测试目标就可以访问所有`internal`级别的实体。
|
||||
|
||||
<a name="access_control_syntax"></a>
|
||||
## 访问控制语法
|
||||
@ -97,7 +109,7 @@ internal let someInternalConstant = 0
|
||||
private func somePrivateFunction() {}
|
||||
```
|
||||
|
||||
除非有特殊的说明,否则实体都使用默认的访问级别`internal`,可以查阅**默认访问级别**这一节。这意味着`SomeInternalClass`和`someInternalConstant`不用明确的使用修饰符声明访问级别,但是他们任然拥有隐式的访问级别`internal`:
|
||||
除非有特殊的说明,否则实体都使用默认的访问级别`internal`,可以查阅**[默认访问级别](#default_access_levels)**这一节。这意味着在不使用修饰符声明显式访问级别的情况下,`SomeInternalClass`和`someInternalConstant`仍然拥有隐式的访问级别`internal`:
|
||||
|
||||
```swift
|
||||
class SomeInternalClass {} // 隐式访问级别 internal
|
||||
@ -105,40 +117,40 @@ var someInternalConstant = 0 // 隐式访问级别 internal
|
||||
```
|
||||
<a name="custom_types"></a>
|
||||
## 自定义类型
|
||||
如果你想为一个自定义类型指定一个明确的访问级别,那么你要明确一点。那就是你要确保新类型的访问级别和它实际的作用域相匹配。比如说,如果某个类里的属性、函数、返回值它们的作用域仅在当前的源文件中,那么你就可以将这个类申明为`private`类,而不需要申明为`public`或者`internal`类。
|
||||
如果想为一个自定义类型申明显式访问级别,那么要明确一点。那就是你要确保新类型的访问级别和它实际的作用域相匹配。比如说,如果你定义了一个`private`类,那这个类就只能在定义它的源文件中当作属性类型、函数参数或者返回类型使用。
|
||||
|
||||
类的访问级别也可以影响到类成员(属性、函数、初始化方法等)的默认访问级别。如果你将类申明为`private`类,那么该类的所有成员的默认访问级别也会成为`private`。如果你将类申明为`public`或者`internal`类(或者不明确的指定访问级别,而使用默认的`internal`访问级别),那么该类的所有成员的访问级别是`internal`。
|
||||
类的访问级别也可以影响到类成员(属性、函数、初始化方法等)的默认访问级别。如果你将类申明为`private`类,那么该类的所有成员的默认访问级别也会成为`private`。如果你将类申明为`public`或者`internal`类(或者不明确的申明访问级别,而使用默认的`internal`访问级别),那么该类的所有成员的访问级别是`internal`。
|
||||
|
||||
> 注意:上面提到,一个`public`类的所有成员的访问级别默认为`internal`级别,而不是`public`级别。如果你想将某个成员申明为`public`级别,那么你必须使用修饰符明确的申明该成员。这样做的好处是,在你定义公共接口API的时候,可以明确的选择哪些属性或方法是需要公开的,哪些是内部使用的,可以避免将内部使用的属性方法公开成公共API的错误。
|
||||
> 注意:上面提到,一个`public`类的所有成员的访问级别默认为`internal`级别,而不是`public`级别。如果你想将某个成员申明为`public`级别,那么你必须使用修饰符明确的声明该成员。这样做的好处是,在你定义公共接口API的时候,可以明确的选择哪些属性或方法是需要公开的,哪些是内部使用的,可以避免将内部使用的属性方法公开成公共API的错误。
|
||||
|
||||
```swift
|
||||
public class SomePublicClass { // 显示的 public 类
|
||||
public var somePublicProperty = 0 // 显示的 public 类成员
|
||||
public class SomePublicClass { // 显式的 public 类
|
||||
public var somePublicProperty = 0 // 显式的 public 类成员
|
||||
var someInternalProperty = 0 // 隐式的 internal 类成员
|
||||
private func somePrivateMethod() {} // 显示的 private 类成员
|
||||
private func somePrivateMethod() {} // 显式的 private 类成员
|
||||
}
|
||||
|
||||
class SomeInternalClass { // 隐式的 internal 类
|
||||
var someInternalProperty = 0 // 隐式的 internal 类成员
|
||||
private func somePrivateMethod() {} // 显示的 private 类成员
|
||||
private func somePrivateMethod() {} // 显式的 private 类成员
|
||||
}
|
||||
|
||||
private class SomePrivateClass { // 显示的 private 类
|
||||
private class SomePrivateClass { // 显式的 private 类
|
||||
var somePrivateProperty = 0 // 隐式的 private 类成员
|
||||
func somePrivateMethod() {} // 隐式的 private 类成员
|
||||
}
|
||||
```
|
||||
<a name="tuple_types"></a>
|
||||
### 元组类型
|
||||
元组的访问级别使用是所有类型的访问级别使用中最为严谨的。比如说,如果你构建一个包含两种不同类型元素的元组,其中一个元素类型的访问级别为`internal`,另一个为`private`级别,那么这个元组的访问级别为`private`。也就是说元组的访问级别遵循它里面元组中最低级的访问级别。
|
||||
元组的访问级别使用是所有类型的访问级别使用中最为严谨的。比如说,如果你构建一个包含两种不同类型元素的元组,其中一个元素类型的访问级别为`internal`,另一个为`private`级别,那么这个元组的访问级别为`private`。也就是说元组的访问级别与元组中访问级别最低的类型一致。
|
||||
|
||||
> 注意:元组不同于类、结构体、枚举、函数那样有单独的定义。元组的访问级别是在它被使用时自动推导出的,而不是明确的申明。
|
||||
|
||||
<a name="function_types"></a>
|
||||
### 函数类型
|
||||
函数的访问级别需要根据该函数的参数类型访问级别、返回类型访问级别得出。如果根据参数类型和返回类型得出的函数访问级别不符合上下文,那么就需要明确的申明该函数的访问级别。
|
||||
函数的访问级别需要根据该函数的参数类型和返回类型的访问级别得出。如果根据参数类型和返回类型得出的函数访问级别不符合默认上下文,那么就需要明确地申明该函数的访问级别。
|
||||
|
||||
下面的例子中定义了一个全局函数名为`someFunction`,并且没有明确的申明其访问级别。你也许会认为该函数应该拥有默认的访问级别`internal`,但事实并非如此。事实上,如果按下面这种写法,编译器是无法编译通过的:
|
||||
下面的例子定义了一个名为`someFunction`全局函数,并且没有明确地申明其访问级别。也许你会认为该函数应该拥有默认的访问级别`internal`,但事实并非如此。事实上,如果按下面这种写法,代码是无法编译通过的:
|
||||
|
||||
```swift
|
||||
func someFunction() -> (SomeInternalClass, SomePrivateClass) {
|
||||
@ -146,9 +158,9 @@ func someFunction() -> (SomeInternalClass, SomePrivateClass) {
|
||||
}
|
||||
```
|
||||
|
||||
我们可以看到,这个函数的返回类型是一个元组,该元组中包含两个自定义的类(可查阅**自定义类型**)。其中一个类的访问级别是`internal`,另一个的访问级别是`private`,所以根据元组访问级别的原则,该元组的访问级别是`private`(元组的访问级别遵循它里面元组中最低级的访问级别)。
|
||||
我们可以看到,这个函数的返回类型是一个元组,该元组中包含两个自定义的类(可查阅**[自定义类型](#custom_types)**)。其中一个类的访问级别是`internal`,另一个的访问级别是`private`,所以根据元组访问级别的原则,该元组的访问级别是`private`(元组的访问级别与元组中访问级别最低的类型一致)。
|
||||
|
||||
因为该函数返回类型的访问级别是`private`,所以你必须使用`private`修饰符,明确的申请该函数:
|
||||
因为该函数返回类型的访问级别是`private`,所以你必须使用`private`修饰符,明确的声明该函数:
|
||||
|
||||
```swift
|
||||
private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
|
||||
@ -160,7 +172,7 @@ private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
|
||||
|
||||
<a name="enumeration_types"></a>
|
||||
### 枚举类型
|
||||
枚举中成员的访问级别继承自该枚举,你不能为枚举中的成员指定访问级别。
|
||||
枚举中成员的访问级别继承自该枚举,你不能为枚举中的成员单独申明不同的访问级别。
|
||||
|
||||
比如下面的例子,枚举`CompassPoint`被明确的申明为`public`级别,那么它的成员`North`,`South`,`East`,`West`的访问级别同样也是`public`:
|
||||
|
||||
@ -175,11 +187,11 @@ public enum CompassPoint {
|
||||
|
||||
<a name="raw_values_and_associated_values"></a>
|
||||
### 原始值和关联值
|
||||
用于枚举定义中的任何原始值,或关联的值类型必须有一个访问级别,至少要高于枚举的访问级别。比如说,你不能在一个`internal`访问级别的枚举中定义`private`级别的原始值类型。
|
||||
枚举定义中的任何原始值或关联值的类型都必须有一个访问级别,这个级别至少要不低于枚举的访问级别。比如说,你不能在一个`internal`访问级别的枚举中定义`private`级别的原始值类型。
|
||||
|
||||
<a name="nested_types"></a>
|
||||
### 嵌套类型
|
||||
如果在`private`级别的类型中定义嵌套类型,那么该嵌套类型就自动拥有`private`访问级别。如果在`public`或者`internal`级别的类型中定义嵌套类型,那么该嵌套类型自动拥有`internal`访问级别。如果想让嵌套类型拥有`public`访问级别,那么需要对该嵌套类型进行明确的访问级别申明。
|
||||
如果在`private`级别的类型中定义嵌套类型,那么该嵌套类型就自动拥有`private`访问级别。如果在`public`或者`internal`级别的类型中定义嵌套类型,那么该嵌套类型自动拥有`internal`访问级别。如果想让嵌套类型拥有`public`访问级别,那么需要明确地申明该嵌套类型的访问级别。
|
||||
|
||||
<a name="subclassing"></a>
|
||||
## 子类
|
||||
@ -217,7 +229,7 @@ internal class B: A {
|
||||
|
||||
<a name="constants_variables_properties_subscripts"></a>
|
||||
## 常量、变量、属性、下标
|
||||
常量、变量、属性不能拥有比它们的类型更高的访问级别。比如说,你定义一个`public`级别的属性,但是它的类型是`private`级别的,这是编译器不允许的。同样,下标也不能拥有比索引类型或返回类型更高的访问级别。
|
||||
常量、变量、属性不能拥有比它们的类型更高的访问级别。比如说,你定义一个`public`级别的属性,但是它的类型是`private`级别的,这是编译器所不允许的。同样,下标也不能拥有比索引类型或返回类型更高的访问级别。
|
||||
|
||||
如果常量、变量、属性、下标索引的定义类型是`private`级别的,那么它们必须要明确的申明访问级别为`private`:
|
||||
|
||||
@ -229,11 +241,11 @@ private var privateInstance = SomePrivateClass()
|
||||
### Getter和Setter
|
||||
常量、变量、属性、下标索引的`Getters`和`Setters`的访问级别继承自它们所属成员的访问级别。
|
||||
|
||||
`Setter`的访问级别可以低于对应的`Getter`的访问级别,这样就可以控制变量、属性或下标索引的读写权限。在`var`或`subscript`定义作用域之前,你可以通过`private(set)`或`internal(set)`先为它门的写权限申明一个较低的访问级别。
|
||||
`Setter`的访问级别可以低于对应的`Getter`的访问级别,这样就可以控制变量、属性或下标索引的读写权限。在`var`或`subscript`定义作用域之前,你可以通过`private(set)`或`internal(set)`先为它们的写权限申明一个较低的访问级别。
|
||||
|
||||
> 注意:这个规定适用于用作存储的属性或用作计算的属性。即使你不明确的申明存储属性的`Getter`、`Setter`,Swift也会隐式的为其创建`Getter`和`Setter`,用于对该属性进行读取操作。使用`private(set)`和`internal(set)`可以改变Swift隐式创建的`Setter`的访问级别。在计算属性中也是同样的。
|
||||
> 注意:这个规定适用于用作存储的属性或用作计算的属性。即使你不明确地申明存储属性的`Getter`、`Setter`,Swift也会隐式的为其创建`Getter`和`Setter`,用于对该属性进行读取操作。使用`private(set)`和`internal(set)`可以改变Swift隐式创建的`Setter`的访问级别。这对计算属性也同样适用。
|
||||
|
||||
下面的例子中定义了一个结构体名为`TrackedString`,它记录了`value`属性被修改的次数:
|
||||
下面的例子中定义了一个名为`TrackedString`的结构体,它记录了`value`属性被修改的次数:
|
||||
|
||||
```swift
|
||||
struct TrackedString {
|
||||
@ -247,32 +259,46 @@ struct TrackedString {
|
||||
```
|
||||
|
||||
|
||||
`TrackedString`结构体定义了一个用于存储的属性名为`value`,类型为`String`,并将初始化值设为`""`(即一个空字符串)。该结构体同时也定义了另一个用于存储的属性名为`numberOfEdits`,类型为`Int`,它用于记录属性`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`,这表示该属性只有在当前的源文件中是可读写的,而在当前源文件所属的模块中它只是一个可读的属性。
|
||||
|
||||
如果你实例化`TrackedString`结构体,并且多次对`value`属性的值进行修改,你就会看到`numberOfEdits`的值会随着修改次数更改:
|
||||
如果你实例化`TrackedString`结构体,并且多次对`value`属性的值进行修改,你就会看到`numberOfEdits`的值会随着修改次数进行变化:
|
||||
|
||||
```swift
|
||||
var stringToEdit = TrackedString()
|
||||
stringToEdit.value = "This string will be tracked."
|
||||
stringToEdit.value += " This edit will increment numberOfEdits."
|
||||
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"
|
||||
```
|
||||
|
||||
虽然你可以在其他的源文件中实例化该结构体并且获取到`numberOfEdits`属性的值,但是你不能对其进行赋值。这样就能很好的告诉使用者,你只管使用,而不需要知道其实现细节。
|
||||
|
||||
如果有必要你可以为`Getter`和`Setter`方法设定显式访问级别。下面的例子就是明确申明了`public`访问级别的`TrackedString`结构体。结构体的成员(包括`numberOfEdits`属性)拥有默认的访问级别`internal`。你可以结合`public`和`private(set)`修饰符把结构体中的`numberOfEdits`属性`Getter`方法的访问级别设置为`public`,而`Setter`方法的访问级别设置为`private`:
|
||||
|
||||
```swift
|
||||
public struct TrackedString {
|
||||
public private(set) var numberOfEdits = 0
|
||||
public var value: String = "" {
|
||||
didSet {
|
||||
numberOfEdits++
|
||||
}
|
||||
}
|
||||
public init() {}
|
||||
}
|
||||
```
|
||||
|
||||
<a name="initializers"></a>
|
||||
## 初始化
|
||||
我们可以给自定义的初始化方法指定访问级别,但是必须要低于或等于它所属类的访问级别。但如果该初始化方法是必须要使用的话,那它的访问级别就必须和所属类的访问级别相同。
|
||||
我们可以给自定义的初始化方法申明访问级别,但是要不高于它所属类的访问级别。但[必要构造器](./14_Initialization.html#required_initializers)例外,它的访问级别必须和所属类的访问级别相同。
|
||||
|
||||
如同函数或方法参数,初始化方法参数的访问级别也不能低于初始化方法的访问级别。
|
||||
|
||||
<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`访问级别的无参初始化方法。
|
||||
|
||||
@ -284,7 +310,7 @@ Swift为结构体、类都提供了一个默认的无参初始化方法,用于
|
||||
|
||||
<a name="protocols"></a>
|
||||
## 协议
|
||||
如果你想为一个协议明确的申明访问级别,那么有一点需要注意,就是你要确保该协议只在你申明的访问级别作用域中使用。
|
||||
如果想为一个协议明确的申明访问级别,那么需要注意一点,就是你要确保该协议只在你申明的访问级别作用域中使用。
|
||||
|
||||
协议中的每一个必须要实现的函数都具有和该协议相同的访问级别。这样才能确保该协议的使用者可以实现它所提供的函数。
|
||||
|
||||
@ -298,17 +324,17 @@ Swift为结构体、类都提供了一个默认的无参初始化方法,用于
|
||||
### 协议一致性
|
||||
类可以采用比自身访问级别低的协议。比如说,你可以定义一个`public`级别的类,可以让它在其他模块中使用,同时它也可以采用一个`internal`级别的协议,并且只能在定义了该协议的模块中使用。
|
||||
|
||||
采用了协议的类的访问级别遵循它本身和采用协议中最低的访问级别。也就是说如果一个类是`public`级别,采用的协议是`internal`级别,那个采用了这个协议后,该类的访问级别也是`internal`。
|
||||
采用了协议的类的访问级别取它本身和所采用协议中最低的访问级别。也就是说如果一个类是`public`级别,采用的协议是`internal`级别,那么采用了这个协议后,该类的访问级别也是`internal`。
|
||||
|
||||
如果你采用了协议,那么实现了协议必须的方法后,该方法的访问级别遵循协议的访问级别。比如说,一个`public`级别的类,采用了`internal`级别的协议,那么该类实现协议的方法至少也得是`internal`。
|
||||
如果你采用了协议,那么实现了协议所必须的方法后,该方法的访问级别遵循协议的访问级别。比如说,一个`public`级别的类,采用了`internal`级别的协议,那么该类实现协议的方法至少也得是`internal`。
|
||||
|
||||
> 注意:在Swift中和Objective-C中一样,协议的一致性保证了一个类不可能在同一个程序中用不同的方法采用同一个协议。
|
||||
> 注意:Swift和Objective-C一样,协议的一致性保证了一个类不可能在同一个程序中用不同的方法采用同一个协议。
|
||||
|
||||
<a name="extensions"></a>
|
||||
## 扩展
|
||||
你可以在条件允许的情况下对类、结构体、枚举进行扩展。扩展成员应该具有和原始类成员一致的访问级别。比如你扩展了一个公共类型,那么你新加的成员应该具有和原始成员一样的默认的`internal`访问级别。
|
||||
|
||||
或者,你可以明确申明扩展的访问级别(比如使用`private extension`)给该扩展内所有成员指定一个新的默认访问级别。这个新的默认访问级别仍然可以被单独成员所指定的访问级别所覆盖。
|
||||
或者,你可以明确申明扩展的访问级别(比如使用`private extension`)给该扩展内所有成员申明一个新的默认访问级别。这个新的默认访问级别仍然可以被单独成员所申明的访问级别所覆盖。
|
||||
|
||||
<a name="adding_protocol_conformance_with_an_extension"></a>
|
||||
### 协议的扩展
|
||||
@ -316,12 +342,10 @@ Swift为结构体、类都提供了一个默认的无参初始化方法,用于
|
||||
|
||||
<a name="generics"></a>
|
||||
## 泛型
|
||||
泛型类型或泛型函数的访问级别遵循泛型类型、函数本身、泛型类型参数三者中访问级别最低的级别。
|
||||
泛型类型或泛型函数的访问级别取泛型类型、函数本身、泛型类型参数三者中的最低访问级别。
|
||||
|
||||
<a name="type_aliases"></a>
|
||||
## 类型别名
|
||||
任何被你定义的类型别名都会被视作为不同的类型,这些类型用于访问控制。一个类型别名的访问级别可以低于或等于这个类型的访问级别。比如说,一个`private`级别的类型别名可以设定给一个`public`、`internal`、`private`的类型,但是一个`public`级别的类型别名只能设定给一个`public`级别的类型,不能设定给`internal`或`private`的类类型。
|
||||
|
||||
> 注意:这条规则也适用于为满足协议一致性而给相关类型命名别名。
|
||||
|
||||
任何你定义的类型别名都会被当作不同的类型,以便于进行访问控制。一个类型别名的访问级别不可高于原类型的访问级别。比如说,一个`private`级别的类型别名可以设定给一个`public`、`internal`、`private`的类型,但是一个`public`级别的类型别名只能设定给一个`public`级别的类型,不能设定给`internal`或`private` 级别的类型。
|
||||
|
||||
> 注意:这条规则也适用于为满足协议一致性而给相关类型命名别名的情况。
|
||||
|
||||
@ -1,8 +1,12 @@
|
||||
> 翻译:[xielingwang](https://github.com/xielingwang)
|
||||
# 高级运算符(Advanced Operators)
|
||||
-----------------
|
||||
|
||||
> 1.0
|
||||
> 翻译:[xielingwang](https://github.com/xielingwang)
|
||||
> 校对:[numbbbbb](https://github.com/numbbbbb)
|
||||
|
||||
# 高级运算符
|
||||
-----------------
|
||||
> 2.0
|
||||
> 翻译+校对:[buginux](https://github.com/buginux)
|
||||
|
||||
本页内容包括:
|
||||
|
||||
@ -12,75 +16,75 @@
|
||||
- [运算符函数(Operator Functions)](#operator_functions)
|
||||
- [自定义运算符](#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>
|
||||
## 位运算符
|
||||
|
||||
位操作符可以操作数据结构中原始数据的每个比特位。位操作符通常在诸如图像处理和创建设备驱动等底层开发中使用,位操作符在同外部资源的数据进行交互的时候也很有用,比如在使用用户协议进行通信的时候,运用位运算符来对原始数据进行编码和解码。
|
||||
位运算符(`Bitwise operators`)可以操作一个数据结构中每个独立的位。它们通常被用在底层开发中,比如图形编程和创建设备驱动。位运算符在处理外部资源的原始数据时也十分有用,比如对自定义通信协议传输的数据进行编码和解码。
|
||||
|
||||
Swift支持如下所有C语言的位运算符:
|
||||
Swift 支持C语言中的全部位运算符,具体如下:
|
||||
|
||||
### 按位取反运算符
|
||||
### 按位取反运算符(`bitwise NOT operator`)
|
||||
|
||||
按位取反运算符`~`对一个操作数的每一位都取反。
|
||||
按位取反运算符(`~`) 可以对一个数值的全部位进行取反:
|
||||
|
||||

|
||||

|
||||
|
||||
这个运算符是前置的,所以请不加任何空格地写在操作数之前。
|
||||
按位取反操作符是一个前置运算符,需要直接放在操作数的之前,并且它们之间不能添加任何空格。
|
||||
|
||||
```swift
|
||||
```
|
||||
let initialBits: UInt8 = 0b00001111
|
||||
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`。
|
||||
|
||||

|
||||
|
||||
以下代码,`firstSixBits`和`lastSixBits`中间4个位都为1。对它俩进行按位与运算后,就得到了`00111100`,即十进制的`60`。
|
||||
在下面的示例当中,`firstSixBits` 和 `lastSixBits` 中间 4 个位的值都为 1 。按位与运算符对它们进行了运算,得到二进制数值 `00111100`,等价于无符号十进制数的 `60`:
|
||||
|
||||
```swift
|
||||
```
|
||||
let firstSixBits: UInt8 = 0b11111100
|
||||
let lastSixBits: UInt8 = 0b00111111
|
||||
let middleFourBits = firstSixBits & lastSixBits // 等于 00111100
|
||||
```
|
||||
|
||||
### 按位或运算
|
||||
### 按位或运算符(Bitwise OR Operator)
|
||||
|
||||
按位或运算符`|`比较两个数,然后返回一个新的数,这个数的每一位设置1的条件是两个输入数的同一位都不为0(即任意一个为1,或都为1)。
|
||||
按位或运算符(`|`)可以对两个数的比特位进行比较。它返回一个新的数,只要两个操作数的对应位中有*任意*一个为 `1` 时,该数的对应位就为 `1`。
|
||||
|
||||

|
||||
|
||||
如下代码,`someBits`和`moreBits`在不同位上有`1`。按位或运行的结果是`11111110`,即十进制的`254`。
|
||||
在下面的示例当中,`someBits` 和 `moreBits` 将不同的位设置为 `1`。接位或运算符对它们进行了运算,得到二进制数值 `11111110`,等价于无符号十进制数的 `254`:
|
||||
|
||||
```swift
|
||||
```
|
||||
let someBits: UInt8 = 0b10110010
|
||||
let moreBits: UInt8 = 0b01011110
|
||||
let combinedbits = someBits | moreBits // 等于 11111110
|
||||
```
|
||||
|
||||
### 按位异或运算符
|
||||
### 按位异或运算符(Bitwise XOR Opoerator)
|
||||
|
||||
按位异或运算符`^`比较两个数,然后返回一个数,这个数的每个位设为`1`的条件是两个输入数的同一位不同,如果相同就设为`0`。
|
||||
按位异或运算符(`^`)可以对两个数的比特位进行比较。它返回一个新的数,当两个操作数的对应位不相同时,该数的对应位就为 `1`:
|
||||
|
||||

|
||||
|
||||
以下代码,`firstBits`和`otherBits`都有一个`1`跟另一个数不同的。所以按位异或的结果是把它这些位置为`1`,其他都置为`0`。
|
||||
在下面的示例当中,`firstBits` 和 `otherBits` 都有一个自己设置为 `1` 而对方设置为 `0` 的位。 按位异或运算符将这两个位都设置为 `1`,同时将其它位都设置为 `0`:
|
||||
|
||||
```swift
|
||||
```
|
||||
let firstBits: UInt8 = 0b00010100
|
||||
let otherBits: UInt8 = 0b00000101
|
||||
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 位) 的结果。蓝色的部分是被移位的,灰色的部分是被抛弃的,橙色的部分则是被填充进来的。
|
||||
|
||||

|
||||
|
||||
```swift
|
||||
下面的代码演示了 Swift 中的移位操作:
|
||||
|
||||
```
|
||||
let shiftBits: UInt8 = 4 // 即二进制的00000100
|
||||
shiftBits << 1 // 00001000
|
||||
shiftBits << 2 // 00010000
|
||||
@ -111,221 +121,205 @@ shiftBits << 6 // 00000000
|
||||
shiftBits >> 2 // 00000001
|
||||
```
|
||||
|
||||
你可以使用移位操作进行其他数据类型的编码和解码。
|
||||
可以使用移位操作对其他的数据类型进行编码和解码:
|
||||
|
||||
```swift
|
||||
```
|
||||
let pink: UInt32 = 0xCC6699
|
||||
let redComponent = (pink & 0xFF0000) >> 16 // redComponent 是 0xCC, 即 204
|
||||
let greenComponent = (pink & 0x00FF00) >> 8 // greenComponent 是 0x66, 即 102
|
||||
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` 型整数的二进制位表现形式:
|
||||
|
||||

|
||||
|
||||
符号位为`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` 型整数的二进制位表现形式:
|
||||
|
||||

|
||||
|
||||
现在符号位为`1`,代表负数,7个数值位要表达的二进制值是124,即128 - 4。
|
||||
这次的符号位为 `1`,说明这是一个负数,另外 7 个位则代表了数值 `124`(即 `128 - 4`) 的二进制表示。
|
||||
|
||||

|
||||
|
||||
负数的编码方式称为二进制补码表示。这种表示方式看起来很奇怪,但它有几个优点。
|
||||
负数的表示通常被称为二进制补码(`two's complement`)表示法。用这种方法来表示负数乍看起来有点奇怪,但它有几个优点。
|
||||
|
||||
首先,只需要对全部8个比特位(包括符号)做标准的二进制加法就可以完成 `-1 + -4` 的操作,忽略加法过程产生的超过8个比特位表达的任何信息。
|
||||
首先,如果想对 `-1` 和 `-4` 进行加法操作,我们只需要将这两个数的全部 8 个比特位进行相加,并且将计算结果中超出 8 位的数值丢弃:
|
||||
|
||||

|
||||
|
||||
第二,由于使用二进制补码表示,我们可以和正数一样对负数进行按位左移右移的,同样也是左移1位时乘于`2`,右移1位时除于`2`。要达到此目的,对有符整型的右移有一个特别的要求:
|
||||
其次,使用二进制补码可以使负数的按位左移和右移操作得到跟正数同样的效果,即每向左移一位就将自身的数值乘以 2,每向右一位就将自身的数值除以 2。要达到此目的,对有符号整数的右移有一个额外的规则:
|
||||
|
||||
对有符整型按位右移时,不使用0填充空白位,而是根据符号位(正数为`0`,负数为`1`)填充空白位。
|
||||
* 当对正整数进行按位右移操作时,遵循与无符号整数相同的规则,但是对于移位产生的空白位使用*符号位*进行填充,而不是用 0。
|
||||
|
||||

|
||||
|
||||
这就确保了在右移的过程中,有符整型的符号不会发生变化。这称为算术移位。
|
||||
这个行为可以确保有符号整数的符号位不会因为右移操作而改变,这通常被称为算术移位(`arithmetic shift`)。
|
||||
|
||||
正因为正数和负数特殊的存储方式,向右移位使它接近于`0`。移位过程中保持符号会不变,负数在接近`0`的过程中一直是负数。
|
||||
由于正数和负数的特殊存储方式,在对它们进行右移的时候,会使它们越来越接近 0。在移位的过程中保持符号位不变,意味着负整数在接近 `0` 的过程中会一直保持为负。
|
||||
|
||||
<a name="overflow_operators"></a>
|
||||
## 溢出运算符
|
||||
|
||||
默认情况下,当你往一个整型常量或变量赋于一个它不能承载的大数时,Swift不会让你这么干的,它会报错。这样,在操作过大或过小的数的时候就很安全了。
|
||||
在默认情况下,当向一个整数赋超过它容量的值时,Swift 默认会报错,而不是生成一个无效的数。这个行为给我们操作过大或着过小的数的时候提供了额外的安全性。
|
||||
|
||||
例如,`Int16`整型能承载的整数范围是`-32768`到`32767`,如果给它赋上超过这个范围的数,就会报错:
|
||||
例如,`Int16` 型整数能容纳的有符号整数范围是 `-32768` 到 `32767`,当为一个 `Int16` 型变量赋的值超过这个范围时,系统就会报错:
|
||||
|
||||
```swift
|
||||
```
|
||||
var potentialOverflow = Int16.max
|
||||
// potentialOverflow 等于 32767, 这是 Int16 能承载的最大整数
|
||||
// potentialOverflow 的值是 32767, 这是 Int16 能容纳的最大整数
|
||||
|
||||
potentialOverflow += 1
|
||||
// 噢, 出错了
|
||||
// 这里会报错
|
||||
```
|
||||
|
||||
对过大或过小的数值进行错误处理让你的数值边界条件更灵活。
|
||||
为过大或者过小的数值提供错误处理,能让我们在处理边界值时更加灵活。
|
||||
|
||||
当然,你有意在溢出时对有效位进行截断,你可采用溢出运算,而非错误处理。Swfit为整型计算提供了5个`&`符号开头的溢出运算符。
|
||||
然而,也可以选择让系统在数值溢出的时候采取截断操作,而非报错。可以使用 Swift 提供的三个溢出操作符(`overflow operators`)来让系统支持整数溢出运算。这些操作符都是以 `&` 开头的:
|
||||
|
||||
- 溢出加法 `&+`
|
||||
- 溢出减法 `&-`
|
||||
- 溢出乘法 `&*`
|
||||
- 溢出除法 `&/`
|
||||
- 溢出求余 `&%`
|
||||
* 溢出加法 `&+`
|
||||
* 溢出减法 `&-`
|
||||
* 溢出乘法 `&*`
|
||||
|
||||
### 值的上溢出
|
||||
### 数值溢出
|
||||
|
||||
下面例子使用了溢出加法`&+`来解剖的无符整数的上溢出
|
||||
数值有可能出现上溢或者下溢。
|
||||
|
||||
```swift
|
||||
var willOverflow = UInt8.max
|
||||
// willOverflow 等于UInt8的最大整数 255
|
||||
willOverflow = willOverflow &+ 1
|
||||
// 此时 willOverflow 等于 0
|
||||
这个示例演示了当我们对一个无符号整数使用溢出加法(`&+`)进行上溢运算时会发生什么:
|
||||
```
|
||||
var unsignedOverflow = UInt8.max
|
||||
// unsignedOverflow 等于 UInt8 所能容纳的最大整数 255
|
||||
|
||||
unsignedOverflow = unsignedOverflow &+ 1
|
||||
// 此时 unsignedOverflow 等于 0
|
||||
```
|
||||
|
||||
`willOverflow`用`Int8`所能承载的最大值`255`(二进制`11111111`),然后用`&+`加1。然后`UInt8`就无法表达这个新值的二进制了,也就导致了这个新值上溢出了,大家可以看下图。溢出后,新值在`UInt8`的承载范围内的那部分是`00000000`,也就是`0`。
|
||||
`unsignedOverflow` 被初始化为 `UInt8` 所能容纳的最大整数(`255`,以二进制表示即 `11111111`)。然后使用了溢出加法运算符(`&+`)对其进行加 1 操作。这使得它的二进制表示正好超出 `UInt8` 所能容纳的位数,也就导致了数值的溢出,如下图所示。数值溢出后,留在 `UInt8` 边界内的值是 `00000000`,也就是十进制数值的 0。
|
||||
|
||||

|
||||
|
||||
### 值的下溢出
|
||||
同样地,当我们对一个无符号整数使用溢出减法(`&-`)进行下溢运算时也会产生类似的现象:
|
||||
|
||||
数值也有可能因为太小而越界。举个例子:
|
||||
```
|
||||
var unsignedOverflow = UInt8.min
|
||||
// unsignedOverflow 等于 UInt8 所能容纳的最小整数 0
|
||||
|
||||
`UInt8`的最小值是`0`(二进制为`00000000`)。使用`&-`进行溢出减1,就会得到二进制的`11111111`即十进制的`255`。
|
||||
|
||||

|
||||
|
||||
Swift代码是这样的:
|
||||
|
||||
```swift
|
||||
var willUnderflow = UInt8.min
|
||||
// willUnderflow 等于UInt8的最小值0
|
||||
willUnderflow = willUnderflow &- 1
|
||||
// 此时 willUnderflow 等于 255
|
||||
unsignedOverflow = unsignedOverflow &- 1
|
||||
// 此时 unsignedOverflow 等于 255
|
||||
```
|
||||
|
||||
有符整型也有类似的下溢出,有符整型所有的减法也都是对包括在符号位在内的二进制数进行二进制减法的,这在 "按位左移/右移运算符" 一节提到过。最小的有符整数是`-128`,即二进制的`10000000`。用溢出减法减去去1后,变成了`01111111`,即UInt8所能承载的最大整数`127`。
|
||||
`UInt8` 型整数能容纳的最小值是 0,以二进制表示即 `00000000`。当使用溢出减法运算符对其进行减 1 操作时,数值会产生下溢并被截断为 `11111111`, 也就是十进制数值的 255。
|
||||
|
||||

|
||||
|
||||
溢出也会发生在有符号整型数值上。在对有符号整型数值进行溢出加法或溢出减法运算时,符号位也需要参与计算,正如[按位左移/右移运算符](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` 型整数所能容纳的最大值。
|
||||
|
||||

|
||||
|
||||
来看看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>
|
||||
## 优先级和结合性
|
||||
|
||||
运算符的优先级使得一些运算符优先于其他运算符,高优先级的运算符会先被计算。
|
||||
运算符的优先级(`precedence`)使得一些运算符优先于其他运算符,高优先级的运算符会先被计算。
|
||||
|
||||
结合性定义相同优先级的运算符在一起时是怎么组合或关联的,是和左边的一组呢,还是和右边的一组。意思就是,到底是和左边的表达式结合呢,还是和右边的表达式结合?
|
||||
结合性(`associativity`)定义了相同优先级的运算符是如何结合(或关联)的 —— 是与左边结合为一组,还是与右边结合为一组。可以将这意思理解为“它们是与左边的表达式结合的”或者“它们是与右边的表达式结合的”。
|
||||
|
||||
在混合表达式中,运算符的优先级和结合性是非常重要的。举个例子,为什么下列表达式的结果为`4`?
|
||||
在复合表达式的运算顺序中,运算符的优先级和结合性是非常重要的。举例来说,为什么下面这个表达式的运算结果是 `4`?
|
||||
|
||||
```swift
|
||||
2 + 3 * 4 % 5
|
||||
// 结果是 4
|
||||
```
|
||||
|
||||
如果严格地从左计算到右,计算过程会是这样:
|
||||
|
||||
如果严格地从左到右进行运算,则运算的过程是这样的:
|
||||
|
||||
- 2 + 3 = 5
|
||||
- 5 * 4 = 20
|
||||
- 20 / 5 = 4 余 0
|
||||
- 20 % 5 = 0
|
||||
|
||||
但是正确答案是`4`而不是`0`。优先级高的运算符要先计算,在Swift和C语言中,都是先乘除后加减的。所以,执行完乘法和求余运算才能执行加减运算。
|
||||
但是正确答案是 `4` 而不是 `0`。优先级高的运算符要先于优先级低的运算符进行计算。与C语言类似,在 Swift 当中,乘法运算符(`*`)与取余运算符(`%`)的优先级高于加法运算符(`+`)。因此,它们的计算顺序要先于加法运算。
|
||||
|
||||
乘法和求余拥有相同的优先级,在运算过程中,我们还需要结合性,乘法和求余运算都是左结合的。这相当于在表达式中有隐藏的括号让运算从左开始。
|
||||
而乘法与取余的优先级相同。这时为了得到正确的运算顺序,还需要考虑结合性。乘法与取余运算都是左结合的。可以将这考虑成为这两部分表达式都隐式地加上了括号:
|
||||
|
||||
```swift
|
||||
2 + ((3 * 4) % 5)
|
||||
```
|
||||
|
||||
3 * 4 = 12,所以这相当于:
|
||||
`(3 * 4) = 12`,所以表达式相当于:
|
||||
|
||||
|
||||
```swift
|
||||
2 + (12 % 5)
|
||||
```
|
||||
|
||||
12 % 5 = 2,所这又相当于
|
||||
`12 % 5 = 2`,所以表达式相当于:
|
||||
|
||||
```swift
|
||||
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>
|
||||
## 运算符函数
|
||||
|
||||
让已有的运算符也可以对自定义的类和结构进行运算,这称为运算符重载。
|
||||
类和结构可以为现有的操作符提供自定义的实现,这通常被称为运算符重载(`overloading`)。
|
||||
|
||||
这个例子展示了如何用`+`让一个自定义的结构做加法。算术运算符`+`是一个两目运算符,因为它有两个操作数,而且它必须出现在两个操作数之间。
|
||||
下面的例子展示了如何为自定义的结构实现加法操作符(`+`)。算术加法运算符是一个两目运算符(`binary operator`),因为它可以对两个目标进行操作,同时它还是中缀(`infix`)运算符,因为它出现在两个目标中间。
|
||||
|
||||
例子中定义了一个名为`Vector2D`的二维坐标向量 `(x,y)` 的结构,然后定义了让两个`Vector2D`的对象相加的运算符函数。
|
||||
例子中定义了一个名为 `Vector2D` 的结构体用来表示二维坐标向量`(x, y)`,紧接着定义了一个可以对两个 `Vector2D` 结构体进行相加的运算符函数(`operator function`):
|
||||
|
||||
```swift
|
||||
struct Vector2D {
|
||||
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)
|
||||
}
|
||||
```
|
||||
|
||||
该运算符函数定义了一个全局的`+`函数,这个函数需要两个`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
|
||||
let vector = Vector2D(x: 3.0, y: 1.0)
|
||||
@ -334,151 +328,155 @@ let combinedVector = vector + anotherVector
|
||||
// combinedVector 是一个新的Vector2D, 值为 (5.0, 5.0)
|
||||
```
|
||||
|
||||
这个例子实现两个向量 `(3.0,1.0)` 和 `(2.0,4.0)` 相加,得到向量 `(5.0,5.0)` 的过程。如下图示:
|
||||
这个例子实现两个向量 `(3.0,1.0)` 和 `(2.0,4.0)` 的相加,并得到新的向量 `(5.0,5.0)`。这个过程如下图示:
|
||||
|
||||

|
||||
|
||||
### 前置和后置运算符
|
||||
### 前缀和后缀运算符
|
||||
|
||||
上个例子演示了一个双目中置运算符的自定义实现,同样我们也可以玩标准单目运算符的实现。单目运算符只有一个操作数,在操作数之前就是前置的,如`-a`; 在操作数之后就是后置的,如`i++`。
|
||||
上个例子演示了一个双目中缀运算符的自定义实现。类与结构体也能提供标准单目运算符(`unary operators`)的实现。单目运算符只有一个操作目标。当运算符出现在操作目标之前时,它就是前缀(`prefix`)的(比如 `-a`),而当它出现在操作目标之后时,它就是后缀(`postfix`)的(比如 `i++`)。
|
||||
|
||||
实现一个前置或后置运算符时,在定义该运算符的时候于关键字`func`之前标注 `@prefix` 或 `@postfix` 属性。
|
||||
要实现前缀或者后缀运算符,需要在声明运算符函数的时候在 `func` 关键字之前指定 `prefix` 或者 `postfix` 限定符:
|
||||
|
||||
```swift
|
||||
@prefix func - (vector: Vector2D) -> Vector2D {
|
||||
prefix func - (vector: Vector2D) -> Vector2D {
|
||||
return Vector2D(x: -vector.x, y: -vector.y)
|
||||
}
|
||||
```
|
||||
|
||||
这段代码为`Vector2D`类型提供了单目减运算`-a`,`@prefix`属性表明这是个前置运算符。
|
||||
这段代码为 `Vector2D` 类型实现了单目减运算符(`-a`)。由于单目减运算符是前缀运算符,所以这个函数需要加上 `prefix` 限定符。
|
||||
|
||||
对于数值,单目减运算符可以把正数变负数,把负数变正数。对于`Vector2D`,单目减运算将其`x`和`y`都进进行单目减运算。
|
||||
对于简单数值,单目减运算符可以对它们的正负性进行改变。对于 `Vector2D` 来说,单目减运算将其 `x` 和 `y` 属性的正负性都进行了改变。
|
||||
|
||||
```swift
|
||||
let positive = Vector2D(x: 3.0, y: 4.0)
|
||||
let negative = -positive
|
||||
// negative 为 (-3.0, -4.0)
|
||||
// negative 是一个值为 (-3.0, -4.0) 的 Vector2D 实例
|
||||
|
||||
let alsoPositive = -negative
|
||||
// alsoPositive 为 (3.0, 4.0)
|
||||
// alsoPositive 是一个值为 (3.0, 4.0) 的 Vector2D 实例
|
||||
```
|
||||
|
||||
### 组合赋值运算符
|
||||
### 复合赋值运算符
|
||||
|
||||
组合赋值是其他运算符和赋值运算符一起执行的运算。如`+=`把加运算和赋值运算组合成一个操作。实现一个组合赋值符号需要使用`@assignment`属性,还需要把运算符的左参数设置成`inout`,因为这个参数会在运算符函数内直接修改它的值。
|
||||
复合赋值运算符(`Compound assignment operators`)将赋值运算符(`=`)与其它运算符进行结合。比如,将加法与赋值结合成加法赋值运算符(`+=`)。在实现的时候,需要把运算符的左参数设置成 `inout` 类型,因为这个参数的值会在运算符函数内直接被修改。
|
||||
|
||||
```swift
|
||||
@assignment func += (inout left: Vector2D, right: Vector2D) {
|
||||
func += (inout left: Vector2D, right: Vector2D) {
|
||||
left = left + right
|
||||
}
|
||||
```
|
||||
|
||||
因为加法运算在之前定义过了,这里无需重新定义。所以,加赋运算符函数使用已经存在的高级加法运算符函数来执行左值加右值的运算。
|
||||
因为加法运算在之前已经定义过了,所以在这里无需重新定义。在这里可以直接利用现有的加法运算符函数,用它来对左值和右值进行相加,并再次赋值给左值:
|
||||
|
||||
```swift
|
||||
var original = Vector2D(x: 1.0, y: 2.0)
|
||||
let vectorToAdd = Vector2D(x: 3.0, y: 4.0)
|
||||
original += vectorToAdd
|
||||
// original 现在为 (4.0, 6.0)
|
||||
// original 的值现在为 (4.0, 6.0)
|
||||
```
|
||||
|
||||
你可以将 `@assignment` 属性和 `@prefix` 或 `@postfix` 属性起来组合,实现一个`Vector2D`的前置运算符。
|
||||
还可以将赋值与 `prefix` 或 `postfix` 限定符结合起来,下面的代码为 `Vector2D` 实例实现了前缀自增运算符(`++a`):
|
||||
|
||||
```swift
|
||||
@prefix @assignment func ++ (inout vector: Vector2D) -> Vector2D {
|
||||
prefix func ++ (inout vector: Vector2D) -> Vector2D {
|
||||
vector += Vector2D(x: 1.0, y: 1.0)
|
||||
return vector
|
||||
}
|
||||
```
|
||||
|
||||
这个前置使用了已经定义好的高级加赋运算,将自己加上一个值为 `(1.0,1.0)` 的对象然后赋给自己,然后再将自己返回。
|
||||
这个前缀自增运算符使用了前面定义的加法赋值操作。它对 `Vector2D` 的 `x` 和 `y` 属性都进行了加 `1` 操作,再将结果返回:
|
||||
|
||||
```swift
|
||||
var toIncrement = Vector2D(x: 3.0, y: 4.0)
|
||||
let afterIncrement = ++toIncrement
|
||||
// toIncrement 现在是 (4.0, 5.0)
|
||||
// afterIncrement 现在也是 (4.0, 5.0)
|
||||
// toIncrement 的值现在为 (4.0, 5.0)
|
||||
// afterIncrement 的值同样为 (4.0, 5.0)
|
||||
```
|
||||
|
||||
>注意:
|
||||
默认的赋值符(=)是不可重载的。只有组合赋值符可以重载。三目条件运算符 `a?b:c` 也是不可重载。
|
||||
> 注意:
|
||||
> 不能对默认的赋值运算符(`=`)进行重载。只有组合赋值符可以被重载。同样地,也无法对三目条件运算符 `a ? b : c` 进行重载。
|
||||
|
||||
### 比较运算符
|
||||
<a name="equivalence_operators"></a>
|
||||
### 等价操作符
|
||||
|
||||
Swift无所知道自定义类型是否相等或不等,因为等于或者不等于由你的代码说了算了。所以自定义的类和结构要使用比较符`==`或`!=`就需要重载。
|
||||
自定义的类和结构体没有对等价操作符(`equivalence operators`)进行默认实现,等价操作符通常被称为“相等”操作符(`==`)与“不等”操作符(`!=`)。对于自定义类型,Swift 无法判断其是否“相等”,因为“相等”的含义取决于这些自定义类型在你的代码中所扮演的角色。
|
||||
|
||||
定义相等运算符函数跟定义其他中置运算符雷同:
|
||||
为了使用等价操作符来对自定义的类型进行判等操作,需要为其提供自定义实现,实现的方法与其它中缀运算符一样:
|
||||
|
||||
```swift
|
||||
@infix func == (left: Vector2D, right: Vector2D) -> Bool {
|
||||
func == (left: Vector2D, right: Vector2D) -> Bool {
|
||||
return (left.x == right.x) && (left.y == right.y)
|
||||
}
|
||||
|
||||
@infix func != (left: Vector2D, right: Vector2D) -> Bool {
|
||||
func != (left: Vector2D, right: Vector2D) -> Bool {
|
||||
return !(left == right)
|
||||
}
|
||||
```
|
||||
|
||||
上述代码实现了相等运算符`==`来判断两个`Vector2D`对象是否有相等的值,相等的概念就是它们有相同的`x`值和相同的`y`值,我们就用这个逻辑来实现。接着使用`==`的结果实现了不相等运算符`!=`。
|
||||
上述代码实现了“相等”运算符(`==`)来判断两个 `Vector2D` 对象是否有相等。对于 `Vector2D` 类型来说,“相等”意味“两个实例的 `x`属性 和 `y` 属性都相等”,这也是代码中用来进行判等的逻辑。示例里同时也实现了“不等”操作符(`!=`),它简单地将“相等”操作符进行取反后返回。
|
||||
|
||||
现在我们可以使用这两个运算符来判断两个`Vector2D`对象是否相等。
|
||||
现在我们可以使用这两个运算符来判断两个 `Vector2D` 对象是否相等。
|
||||
|
||||
```swift
|
||||
let twoThree = Vector2D(x: 2.0, y: 3.0)
|
||||
let anotherTwoThree = Vector2D(x: 2.0, y: 3.0)
|
||||
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
|
||||
operator prefix +++ {}
|
||||
prefix operator +++ {}
|
||||
```
|
||||
|
||||
|
||||
这段代码定义了一个新的前置运算符叫`+++`,此前Swift并不存在这个运算符。此处为了演示,我们让`+++`对`Vector2D`对象的操作定义为 `双自增` 这样一个独有的操作,这个操作使用了之前定义的加赋运算实现了自已加上自己然后返回的运算。
|
||||
上面的代码定义了一个新的名为 `+++` 的前缀运算符。对于这个运算符,在 Swift 中并没有意义,因为我们针对 `Vector2D` 的实例来定义它的意义。对这个示例来讲,`+++` 被实现为“前缀双自增”运算符。它使用了前面定义的复合加法操作符来让矩阵对自身进行相加,从而让 `Vector2D` 实例的 `x` 属性和 `y`属性的值翻倍:
|
||||
|
||||
```swift
|
||||
@prefix @assignment func +++ (inout vector: Vector2D) -> Vector2D {
|
||||
prefix func +++ (inout vector: Vector2D) -> Vector2D {
|
||||
vector += vector
|
||||
return vector
|
||||
}
|
||||
```
|
||||
|
||||
`Vector2D` 的 `+++` 的实现和 `++` 的实现很接近, 唯一不同的是前者是加自己, 后者是加值为 `(1.0, 1.0)` 的向量.
|
||||
`Vector2D` 的 `+++` 的实现和 `++` 的实现很相似, 唯一不同的是前者对自身进行相加, 而后者是与另一个值为 `(1.0, 1.0)` 的向量相加.
|
||||
|
||||
```swift
|
||||
var toBeDoubled = Vector2D(x: 1.0, y: 4.0)
|
||||
let afterDoubling = +++toBeDoubled
|
||||
// toBeDoubled 现在是 (2.0, 8.0)
|
||||
// afterDoubling 现在也是 (2.0, 8.0)
|
||||
// toBeDoubled 现在的值为 (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
|
||||
operator infix +- { associativity left precedence 140 }
|
||||
infix operator +- { associativity left precedence 140 }
|
||||
func +- (left: Vector2D, right: Vector2D) -> Vector2D {
|
||||
return Vector2D(x: left.x + right.x, y: left.y - right.y)
|
||||
}
|
||||
let firstVector = Vector2D(x: 1.0, y: 2.0)
|
||||
let secondVector = Vector2D(x: 3.0, y: 4.0)
|
||||
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)。
|
||||
|
||||
> 注意:
|
||||
> 当定义前缀与后缀操作符的时候,我们并没有指定优先级。然而,如果对同一个操作数同时使用前缀与后缀操作符,则后缀操作符会先被执行。
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
> 翻译:[dabing1022](https://github.com/dabing1022)
|
||||
# 关于语言参考(About the Language Reference)
|
||||
-----------------
|
||||
|
||||
> 1.0
|
||||
> 翻译:[dabing1022](https://github.com/dabing1022)
|
||||
> 校对:[numbbbbb](https://github.com/numbbbbb)
|
||||
|
||||
|
||||
# 关于语言附注
|
||||
-----------------
|
||||
> 2.0
|
||||
> 翻译+校对:[KYawn](https://github.com/KYawn)
|
||||
|
||||
本页内容包括:
|
||||
|
||||
@ -11,28 +14,32 @@
|
||||
|
||||
本书的这一节描述了Swift编程语言的形式语法。这里描述的语法是为了帮助您更详细的了解该语言,而不是让您直接实现一个解析器或编译器。
|
||||
|
||||
|
||||
Swift语言相对小点,这是由于在Swift代码中几乎无处不在的许多常见的的类型,函数以及运算符都由Swift标准库来定义。虽然这些类型,函数和运算符不是Swift语言本身的一部分,但是它们被广泛用于这本书的讨论和代码范例。
|
||||
Swift语言相对小一点,这是由于在Swift代码中几乎所有常见的类型、函数以及运算符都已经在Swift标准库中定义了。虽然这些类型、函数和运算符并不是Swift语言自身的一部分,但是它们被广泛应用于本书的讨论和代码范例中。
|
||||
|
||||
<a name="how_to_read_the_grammar"></a>
|
||||
## 如何阅读语法
|
||||
|
||||
用来描述Swift编程语言形式语法的记法遵循下面几个约定:
|
||||
用来描述Swift编程语言形式语法的标记遵循下面几个约定:
|
||||
|
||||
- 箭头(→)用来标记语法产式,可以被理解为“可以包含”。
|
||||
- 句法范畴由*斜体*文字表示,并出现在一个语法产式规则两侧。
|
||||
- 义词和标点符号由粗体固定宽度的文本显示和只出现在一个语法产式规则的右边。
|
||||
- 箭头(→)用来标记语法产式,可以理解为“可以包含”。
|
||||
- *斜体*文字用来表示句法分类,并出现在一个语法产式规则两侧。
|
||||
- 义词和标点符号由粗体固定宽度的文本标记,而且只出现在一个语法产式规则的右侧。
|
||||
- 选择性的语法产式由竖线(|)分隔。当可选用的语法产式太多时,为了阅读方便,它们将被拆分为多行语法产式规则。
|
||||
- 在少数情况下,常规字体文字用来描述语法产式规则的右边。
|
||||
- 可选的句法范畴和文字用尾标`opt`来标记。
|
||||
- 少数情况下,常规字体文字用来描述语法产式规则的右边。
|
||||
- 可选的句法分类和文字用尾标`opt`来标记。
|
||||
|
||||
举个例子,getter-setter的语法块的定义如下:
|
||||
|
||||
> GRAMMAR OF A GETTER-SETTER BLOCK
|
||||
> *getter-setter-block* → { [*getter-clause*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/getter-clause) [*setter-clause*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/setter-clause)*opt* } | { [*setter-clause*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/setter-clause) [*getter-clause*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/getter-clause)}
|
||||
|
||||
这个定义表明,一个getter-setter方法块可以由一个getter子句后跟一个可选的setter子句构成,用大括号括起来,或者由一个setter子句后跟一个getter子句构成,用大括号括起来。上述的文法产生等价于下面的两个产生,明确阐明如何二中择一:
|
||||
这个定义表明,一个getter-setter方法块可以由一个getter子句后跟一个可选的setter子句构成,然后用大括号括起来,或者由一个setter子句后跟一个getter子句构成,然后用大括号括起来。下面的两个语法产式等价于上述的语法产式,并明确指出了如何取舍:
|
||||
|
||||
> GRAMMAR OF A GETTER-SETTER BLOCK
|
||||
> getter-setter-block → { [*getter-clause*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/getter-clause) [*setter-clause*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/setter-clause)*opt* }
|
||||
> getter-setter-block → { [*setter-clause*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/setter-clause) [*getter-clause*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/getter-clause)}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,238 +1,284 @@
|
||||
> 翻译:[superkam](https://github.com/superkam)
|
||||
> 校对:[numbbbbb](https://github.com/numbbbbb)
|
||||
|
||||
# 词法结构
|
||||
-----------------
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [空白与注释(*Whitespace and Comments*)](#whitespace_and_comments)
|
||||
- [标识符(*Identifiers*)](#identifiers)
|
||||
- [关键字(*Keywords*)](#keywords)
|
||||
- [字面量(*Literals*)](#literals)
|
||||
- [运算符(*Operators*)](#operators)
|
||||
|
||||
Swift 的“词法结构(*lexical structure*)”描述了如何在该语言中用字符序列构建合法标记,组成该语言中最底层的代码块,并在之后的章节中用于描述语言的其他部分。
|
||||
|
||||
通常,标记在随后介绍的语法约束下,由 Swift 源文件的输入文本中提取可能的最长子串生成。这种方法称为“最长匹配项(*longest match*)”,或者“最大适合”(*maximal munch*)。
|
||||
|
||||
<a name="whitespace_and_comments"></a>
|
||||
## 空白与注释
|
||||
|
||||
空白(*whitespace*)有两个用途:分隔源文件中的标记和区分运算符属于前缀还是后缀,(参见 [运算符](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/doc/uid/TP40014097-CH30-XID_871))在其他情况下则会被忽略。以下的字符会被当作空白:空格(*space*)(U+0020)、换行符(*line feed*)(U+000A)、回车符(*carriage return*)(U+000D)、水平 tab(*horizontal tab*)(U+0009)、垂直 tab(*vertical tab*)(U+000B)、换页符(*form feed*)(U+000C)以及空(*null*)(U+0000)。
|
||||
|
||||
注释(*comments*)被编译器当作空白处理。单行注释由 `//` 开始直到该行结束。多行注释由 `/*` 开始,以 `*/` 结束。可以嵌套注释,但注意注释标记必须匹配。
|
||||
|
||||
<a name="identifiers"></a>
|
||||
## 标识符
|
||||
|
||||
标识符(*identifiers*)可以由以下的字符开始:大写或小写的字母 `A` 到 `Z`、下划线 `_`、基本多语言面(*Basic Multilingual Plane*)中的 Unicode 非组合字符以及基本多语言面以外的非专用区(*Private Use Area*)字符。首字符之后,标识符允许使用数字和 Unicode 字符组合。
|
||||
|
||||
使用保留字(*reserved word*)作为标识符,需要在其前后增加反引号 <code>\`</code>。例如,<code>class</code> 不是合法的标识符,但可以使用 <code>\`class\`</code>。反引号不属于标识符的一部分,<code>\`x\`</code> 和 `x` 表示同一标识符。
|
||||
|
||||
闭包(*closure*)中如果没有明确指定参数名称,参数将被隐式命名为 <code>$0</code>、<code>$1</code>、<code>$2</code>... 这些命名在闭包作用域内是合法的标识符。
|
||||
|
||||
> 标识符语法
|
||||
> *标识符* → [*标识符头(Head)*](LexicalStructure.html#identifier_head) [*标识符字符列表*](LexicalStructure.html#identifier_characters) _可选_
|
||||
> *标识符* → **`** [*标识符头(Head)*](LexicalStructure.html#identifier_head) [*标识符字符列表*](LexicalStructure.html#identifier_characters) _可选_ **`**
|
||||
> *标识符* → [*隐式参数名*](LexicalStructure.html#implicit_parameter_name)
|
||||
> *标识符列表* → [*标识符*](LexicalStructure.html#identifier) | [*标识符*](LexicalStructure.html#identifier) **,** [*标识符列表*](LexicalStructure.html#identifier_list)
|
||||
> *标识符头(Head)* → Upper- or lowercase letter A through Z
|
||||
> *标识符头(Head)* → U+00A8, U+00AA, U+00AD, U+00AF, U+00B2–U+00B5, or U+00B7–U+00BA
|
||||
> *标识符头(Head)* → U+00BC–U+00BE, U+00C0–U+00D6, U+00D8–U+00F6, or U+00F8–U+00FF
|
||||
> *标识符头(Head)* → U+0100–U+02FF, U+0370–U+167F, U+1681–U+180D, or U+180F–U+1DBF
|
||||
> *标识符头(Head)* → U+1E00–U+1FFF
|
||||
> *标识符头(Head)* → U+200B–U+200D, U+202A–U+202E, U+203F–U+2040, U+2054, or U+2060–U+206F
|
||||
> *标识符头(Head)* → U+2070–U+20CF, U+2100–U+218F, U+2460–U+24FF, or U+2776–U+2793
|
||||
> *标识符头(Head)* → U+2C00–U+2DFF or U+2E80–U+2FFF
|
||||
> *标识符头(Head)* → U+3004–U+3007, U+3021–U+302F, U+3031–U+303F, or U+3040–U+D7FF
|
||||
> *标识符头(Head)* → U+F900–U+FD3D, U+FD40–U+FDCF, U+FDF0–U+FE1F, or U+FE30–U+FE44
|
||||
> *标识符头(Head)* → U+FE47–U+FFFD
|
||||
> *标识符头(Head)* → U+10000–U+1FFFD, U+20000–U+2FFFD, U+30000–U+3FFFD, or U+40000–U+4FFFD
|
||||
> *标识符头(Head)* → U+50000–U+5FFFD, U+60000–U+6FFFD, U+70000–U+7FFFD, or U+80000–U+8FFFD
|
||||
> *标识符头(Head)* → U+90000–U+9FFFD, U+A0000–U+AFFFD, U+B0000–U+BFFFD, or U+C0000–U+CFFFD
|
||||
> *标识符头(Head)* → U+D0000–U+DFFFD or U+E0000–U+EFFFD
|
||||
> *标识符字符* → 数值 0 到 9
|
||||
> *标识符字符* → U+0300–U+036F, U+1DC0–U+1DFF, U+20D0–U+20FF, or U+FE20–U+FE2F
|
||||
> *标识符字符* → [*标识符头(Head)*](LexicalStructure.html#identifier_head)
|
||||
> *标识符字符列表* → [*标识符字符*](LexicalStructure.html#identifier_character) [*标识符字符列表*](LexicalStructure.html#identifier_characters) _可选_
|
||||
> *隐式参数名* → **$** [*十进制数字列表*](LexicalStructure.html#decimal_digits)
|
||||
|
||||
<a name="keywords"></a>
|
||||
## 关键字
|
||||
|
||||
被保留的关键字(*keywords*)不允许用作标识符,除非被反引号转义,参见 [标识符](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/doc/uid/TP40014097-CH30-XID_796)。
|
||||
|
||||
* **用作声明的关键字:** *class*、*deinit*、*enum*、*extension*、*func*、*import*、*init*、*let*、*protocol*、*static*、*struct*、*subscript*、*typealias*、*var*
|
||||
* **用作语句的关键字:** *break*、*case*、*continue*、*default*、*do*、*else*、*fallthrough*、*if*、*in*、*for*、*return*、*switch*、*where*、*while*
|
||||
* **用作表达和类型的关键字:** *as*、*dynamicType*、*is*、*new*、*super*、*self*、*Self*、*Type*、*\_\_COLUMN\_\_*、*\_\_FILE\_\_*、*\_\_FUNCTION\_\_*、*\_\_LINE\_\_*
|
||||
* **特定上下文中被保留的关键字:** *associativity*、*didSet*、*get*、*infix*、*inout*、*left*、*mutating*、*none*、*nonmutating*、*operator*、*override*、*postfix*、
|
||||
*precedence*、*prefix*、*right*、*set*、*unowned*、*unowned(safe)*、*unowned(unsafe)*、*weak*、*willSet*,这些关键字在特定上下文之外可以被用于标识符。
|
||||
|
||||
<a name="literals"></a>
|
||||
## 字面量
|
||||
|
||||
字面值表示整型、浮点型数字或文本类型的值,举例如下:
|
||||
|
||||
```swift
|
||||
42 // 整型字面量
|
||||
3.14159 // 浮点型字面量
|
||||
"Hello, world!" // 文本型字面量
|
||||
```
|
||||
|
||||
> 字面量语法
|
||||
> *字面量* → [*整型字面量*](LexicalStructure.html#integer_literal) | [*浮点数字面量*](LexicalStructure.html#floating_point_literal) | [*字符串字面量*](LexicalStructure.html#string_literal)
|
||||
|
||||
### 整型字面量
|
||||
|
||||
整型字面量(*integer literals*)表示未指定精度整型数的值。整型字面量默认用十进制表示,可以加前缀来指定其他的进制,二进制字面量加 `0b`,八进制字面量加 `0o`,十六进制字面量加 `0x`。
|
||||
|
||||
十进制字面量包含数字 `0` 至 `9`。二进制字面量只包含 `0` 或 `1`,八进制字面量包含数字 `0` 至 `7`,十六进制字面量包含数字 `0` 至 `9` 以及字母 `A` 至 `F` (大小写均可)。
|
||||
|
||||
负整数的字面量在数字前加减号 `-`,比如 `-42`。
|
||||
|
||||
允许使用下划线 `_` 来增加数字的可读性,下划线不会影响字面量的值。整型字面量也可以在数字前加 `0`,同样不会影响字面量的值。
|
||||
|
||||
```swift
|
||||
1000_000 // 等于 1000000
|
||||
005 // 等于 5
|
||||
```
|
||||
|
||||
除非特殊指定,整型字面量的默认类型为 Swift 标准库类型中的 `Int`。Swift 标准库还定义了其他不同长度以及是否带符号的整数类型,请参考 [整数类型](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_411)。
|
||||
|
||||
> 整型字面量语法
|
||||
> *整型字面量* → [*二进制字面量*](LexicalStructure.html#binary_literal)
|
||||
> *整型字面量* → [*八进制字面量*](LexicalStructure.html#octal_literal)
|
||||
> *整型字面量* → [*十进制字面量*](LexicalStructure.html#decimal_literal)
|
||||
> *整型字面量* → [*十六进制字面量*](LexicalStructure.html#hexadecimal_literal)
|
||||
> *二进制字面量* → **0b** [*二进制数字*](LexicalStructure.html#binary_digit) [*二进制字面量字符列表*](LexicalStructure.html#binary_literal_characters) _可选_
|
||||
> *二进制数字* → 数值 0 到 1
|
||||
> *二进制字面量字符* → [*二进制数字*](LexicalStructure.html#binary_digit) | **_**
|
||||
> *二进制字面量字符列表* → [*二进制字面量字符*](LexicalStructure.html#binary_literal_character) [*二进制字面量字符列表*](LexicalStructure.html#binary_literal_characters) _可选_
|
||||
> *八进制字面量* → **0o** [*八进字数字*](LexicalStructure.html#octal_digit) [*八进制字符列表*](LexicalStructure.html#octal_literal_characters) _可选_
|
||||
> *八进字数字* → 数值 0 到 7
|
||||
> *八进制字符* → [*八进字数字*](LexicalStructure.html#octal_digit) | **_**
|
||||
> *八进制字符列表* → [*八进制字符*](LexicalStructure.html#octal_literal_character) [*八进制字符列表*](LexicalStructure.html#octal_literal_characters) _可选_
|
||||
> *十进制字面量* → [*十进制数字*](LexicalStructure.html#decimal_digit) [*十进制字符列表*](LexicalStructure.html#decimal_literal_characters) _可选_
|
||||
> *十进制数字* → 数值 0 到 9
|
||||
> *十进制数字列表* → [*十进制数字*](LexicalStructure.html#decimal_digit) [*十进制数字列表*](LexicalStructure.html#decimal_digits) _可选_
|
||||
> *十进制字符* → [*十进制数字*](LexicalStructure.html#decimal_digit) | **_**
|
||||
> *十进制字符列表* → [*十进制字符*](LexicalStructure.html#decimal_literal_character) [*十进制字符列表*](LexicalStructure.html#decimal_literal_characters) _可选_
|
||||
> *十六进制字面量* → **0x** [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制字面量字符列表*](LexicalStructure.html#hexadecimal_literal_characters) _可选_
|
||||
> *十六进制数字* → 数值 0 到 9, a through f, or A through F
|
||||
> *十六进制字符* → [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) | **_**
|
||||
> *十六进制字面量字符列表* → [*十六进制字符*](LexicalStructure.html#hexadecimal_literal_character) [*十六进制字面量字符列表*](LexicalStructure.html#hexadecimal_literal_characters) _可选_
|
||||
|
||||
### 浮点型字面量
|
||||
|
||||
浮点型字面量(*floating-point literals*)表示未指定精度浮点数的值。
|
||||
|
||||
浮点型字面量默认用十进制表示(无前缀),也可以用十六进制表示(加前缀 `0x`)。
|
||||
|
||||
十进制浮点型字面量(*decimal floating-point literals*)由十进制数字串后跟小数部分或指数部分(或两者皆有)组成。十进制小数部分由小数点 `.` 后跟十进制数字串组成。指数部分由大写或小写字母 `e` 后跟十进制数字串组成,这串数字表示 `e` 之前的数量乘以 10 的几次方。例如:`1.25e2` 表示 `1.25 ⨉ 10^2`,也就是 `125.0`;同样,`1.25e-2` 表示 `1.25 ⨉ 10^-2`,也就是 `0.0125`。
|
||||
|
||||
十六进制浮点型字面量(*hexadecimal floating-point literals*)由前缀 `0x` 后跟可选的十六进制小数部分以及十六进制指数部分组成。十六进制小数部分由小数点后跟十六进制数字串组成。指数部分由大写或小写字母 `p` 后跟十进制数字串组成,这串数字表示 `p` 之前的数量乘以 2 的几次方。例如:`0xFp2` 表示 `15 ⨉ 2^2`,也就是 `60`;同样,`0xFp-2` 表示 `15 ⨉ 2^-2`,也就是 `3.75`。
|
||||
|
||||
与整型字面量不同,负的浮点型字面量由一元运算符减号 `-` 和浮点型字面量组成,例如 `-42.0`。这代表一个表达式,而不是一个浮点整型字面量。
|
||||
|
||||
允许使用下划线 `_` 来增强可读性,下划线不会影响字面量的值。浮点型字面量也可以在数字前加 `0`,同样不会影响字面量的值。
|
||||
|
||||
```swift
|
||||
10_000.56 // 等于 10000.56
|
||||
005000.76 // 等于 5000.76
|
||||
```
|
||||
|
||||
除非特殊指定,浮点型字面量的默认类型为 Swift 标准库类型中的 `Double`,表示64位浮点数。Swift 标准库也定义 `Float` 类型,表示32位浮点数。
|
||||
|
||||
> 浮点型字面量语法
|
||||
> *浮点数字面量* → [*十进制字面量*](LexicalStructure.html#decimal_literal) [*十进制分数*](LexicalStructure.html#decimal_fraction) _可选_ [*十进制指数*](LexicalStructure.html#decimal_exponent) _可选_
|
||||
> *浮点数字面量* → [*十六进制字面量*](LexicalStructure.html#hexadecimal_literal) [*十六进制分数*](LexicalStructure.html#hexadecimal_fraction) _可选_ [*十六进制指数*](LexicalStructure.html#hexadecimal_exponent)
|
||||
> *十进制分数* → **.** [*十进制字面量*](LexicalStructure.html#decimal_literal)
|
||||
> *十进制指数* → [*浮点数e*](LexicalStructure.html#floating_point_e) [*正负号*](LexicalStructure.html#sign) _可选_ [*十进制字面量*](LexicalStructure.html#decimal_literal)
|
||||
> *十六进制分数* → **.** [*十六进制字面量*](LexicalStructure.html#hexadecimal_literal) _可选_
|
||||
> *十六进制指数* → [*浮点数p*](LexicalStructure.html#floating_point_p) [*正负号*](LexicalStructure.html#sign) _可选_ [*十六进制字面量*](LexicalStructure.html#hexadecimal_literal)
|
||||
> *浮点数e* → **e** | **E**
|
||||
> *浮点数p* → **p** | **P**
|
||||
> *正负号* → **+** | **-**
|
||||
|
||||
### 文本型字面量
|
||||
|
||||
文本型字面量(*string literal*)由双引号中的字符串组成,形式如下:
|
||||
|
||||
```swift
|
||||
"characters"
|
||||
```
|
||||
|
||||
文本型字面量中不能包含未转义的双引号 `"`、未转义的反斜线`\`、回车符(*carriage return*)或换行符(*line feed*)。
|
||||
|
||||
可以在文本型字面量中使用的转义特殊符号如下:
|
||||
|
||||
* 空字符(Null Character)`\0`
|
||||
* 反斜线(Backslash)`\\`
|
||||
* 水平 Tab (Horizontal Tab)`\t`
|
||||
* 换行符(Line Feed)`\n`
|
||||
* 回车符(Carriage Return)`\r`
|
||||
* 双引号(Double Quote)`\"`
|
||||
* 单引号(Single Quote)`\'`
|
||||
|
||||
字符也可以用以下方式表示:
|
||||
|
||||
* `\x` 后跟两位十六进制数字
|
||||
* `\u` 后跟四位十六进制数字
|
||||
* `\U` 后跟八位十六进制数字
|
||||
|
||||
后跟的数字表示一个 Unicode 码点。
|
||||
|
||||
文本型字面量允许在反斜线小括号 `\()` 中插入表达式的值。插入表达式(*interpolated expression*)不能包含未转义的双引号 `"`、反斜线 `\`、回车符或者换行符。表达式值的类型必须在 *String* 类中有对应的初始化方法。
|
||||
|
||||
例如,以下所有文本型字面量的值相同:
|
||||
|
||||
```swift
|
||||
"1 2 3"
|
||||
"1 2 \(3)"
|
||||
"1 2 \(1 + 2)"
|
||||
var x = 3; "1 2 \(x)"
|
||||
```
|
||||
|
||||
文本型字面量的默认类型为 `String`。组成字符串的字符类型为 `Character`。更多有关 `String` 和 `Character` 的信息请参照 [字符串和字符](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html#//apple_ref/doc/uid/TP40014097-CH7-XID_368)。
|
||||
|
||||
> 字符型字面量语法
|
||||
> *字符串字面量* → **"** [*引用文本*](LexicalStructure.html#quoted_text) **"**
|
||||
> *引用文本* → [*引用文本条目*](LexicalStructure.html#quoted_text_item) [*引用文本*](LexicalStructure.html#quoted_text) _可选_
|
||||
> *引用文本条目* → [*转义字符*](LexicalStructure.html#escaped_character)
|
||||
> *引用文本条目* → **\(** [*表达式*](..\chapter3\04_Expressions.html#expression) **)**
|
||||
> *引用文本条目* → 除了", \, U+000A, or U+000D的所有Unicode的字符
|
||||
> *转义字符* → **\0** | **\\** | **\t** | **\n** | **\r** | **\"** | **\'**
|
||||
> *转义字符* → **\x** [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit)
|
||||
> *转义字符* → **\u** [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit)
|
||||
> *转义字符* → **\U** [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit) [*十六进制数字*](LexicalStructure.html#hexadecimal_digit)
|
||||
|
||||
<a name="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) 中进行了阐述。这里将描述哪些字符能用作运算符。
|
||||
|
||||
运算符由一个或多个以下字符组成:
|
||||
`/`、`=`、`-`、`+`、`!`、`*`、`%`、`<`、`>`、`&`、`|`、`^`、`~`、`.`。也就是说,标记 `=`, `->`、`//`、`/*`、`*/`、`.` 以及一元前缀运算符 `&` 属于保留字,这些标记不能被重写或用于自定义运算符。
|
||||
|
||||
运算符两侧的空白被用来区分该运算符是否为前缀运算符(*prefix operator*)、后缀运算符(*postfix operator*)或二元运算符(*binary operator*)。规则总结如下:
|
||||
|
||||
* 如果运算符两侧都有空白或两侧都无空白,将被看作二元运算符。例如:`a+b` 和 `a + b` 中的运算符 `+` 被看作二元运算符。
|
||||
* 如果运算符只有左侧空白,将被看作前缀一元运算符。例如 `a ++b` 中的 `++` 被看作前缀一元运算符。
|
||||
* 如果运算符只有右侧空白,将被看作后缀一元运算符。例如 `a++ b` 中的 `++` 被看作后缀一元运算符。
|
||||
* 如果运算符左侧没有空白并紧跟 `.`,将被看作后缀一元运算符。例如 `a++.b` 中的 `++` 被看作后缀一元运算符(同理, `a++ . b` 中的 `++` 是后缀一元运算符而 `a ++ .b` 中的 `++` 不是).
|
||||
|
||||
鉴于这些规则,运算符前的字符 `(`、`[` 和 `{` ;运算符后的字符 `)`、`]` 和 `}` 以及字符 `,`、`;` 和 `:` 都将用于空白检测。
|
||||
|
||||
以上规则需注意一点,如果运算符 `!` 或 `?` 左侧没有空白,则不管右侧是否有空白都将被看作后缀运算符。如果将 `?` 用作可选类型(*optional type*)修饰,左侧必须无空白。如果用于条件运算符 `? :`,必须两侧都有空白。
|
||||
|
||||
在特定构成中 ,以 `<` 或 `>` 开头的运算符会被分离成两个或多个标记,剩余部分以同样的方式会被再次分离。因此,在 `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)。
|
||||
|
||||
> 运算符语法语法
|
||||
> *运算符* → [*运算符字符*](LexicalStructure.html#operator_character) [*运算符*](LexicalStructure.html#operator) _可选_
|
||||
> *运算符字符* → **/** | **=** | **-** | **+** | **!** | ***** | **%** | **<** | **>** | **&** | **|** | **^** | **~** | **.**
|
||||
> *二元运算符* → [*运算符*](LexicalStructure.html#operator)
|
||||
> *前置运算符* → [*运算符*](LexicalStructure.html#operator)
|
||||
> *后置运算符* → [*运算符*](LexicalStructure.html#operator)
|
||||
# 词法结构(Lexical Structure)
|
||||
-----------------
|
||||
|
||||
> 1.0
|
||||
> 翻译:[superkam](https://github.com/superkam)
|
||||
> 校对:[numbbbbb](https://github.com/numbbbbb)
|
||||
|
||||
> 2.0
|
||||
> 翻译+校对:[buginux](https://github.com/buginux)
|
||||
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [空白与注释(*Whitespace and Comments*)](#whitespace_and_comments)
|
||||
- [标识符(*Identifiers*)](#identifiers)
|
||||
- [关键字(*Keywords*)](#keywords)
|
||||
- [字面量(*Literals*)](#literals)
|
||||
- [运算符(*Operators*)](#operators)
|
||||
|
||||
Swift 的“词法结构(*lexical structure*)”描述了能构成该语言中合法标记(*tokens*)的字符序列。这些合法标记组成了语言中最底层的构建基块,并在之后的章节中用于描述语言的其他部分。
|
||||
|
||||
通常情况下,标记是在随后将介绍的语法约束下,由 Swift 源文件的输入文本中提取可能的最长子串生成。这种方法称为“最长匹配项(*longest match*)”,或者“最大适合”(*maximal munch*)。
|
||||
|
||||
<a id="whitespace_and_comments"></a>
|
||||
## 空白与注释
|
||||
|
||||
空白(*whitespace*)有两个用途:分隔源文件中的标记和帮助区分运算符属于前缀还是后缀(参见 [运算符](#operators)),在其他情况下则会被忽略。以下的字符会被当作空白:空格(*space*)(U+0020)、换行符(*line feed*)(U+000A)、回车符(*carriage return*)(U+000D)、水平制表符(*horizontal tab*)(U+0009)、垂直制表符(*vertical tab*)(U+000B)、换页符(*form feed*)(U+000C)以及空(*null*)(U+0000)。
|
||||
|
||||
注释(*comments*)被编译器当作空白处理。单行注释由 `//` 开始直至遇到换行符(*line feed*)(U+000A)或者回车符(*carriage return*)(U+000D)。多行注释由 `/*` 开始,以 `*/` 结束。注释允许嵌套,但注释标记必须匹配。
|
||||
|
||||
<a id="identifiers"></a>
|
||||
## 标识符
|
||||
|
||||
标识符(*identifiers*)可以由以下的字符开始:大写或小写的字母 `A` 到 `Z`、下划线 `_`、基本多文种平面(*Basic Multilingual Plane*)中的 Unicode 非组合字符以及基本多文种平面以外的非专用区(*Private Use Area*)字符。首字符之后,允许使用数字和 Unicode 字符组合。
|
||||
|
||||
使用保留字(*reserved word*)作为标识符,需要在其前后增加反引号 `` `。例如,`class` 不是合法的标识符,但可以使用 <code>\`class\`</code>。反引号不属于标识符的一部分,<code>\`x\`</code> 和 `x` 表示同一标识符。
|
||||
|
||||
闭包(*closure*)中如果没有明确指定参数名称,参数将被隐式命名为 `$0`、`$1`、`$2`等等。 这些命名在闭包作用域范围内是合法的标识符。
|
||||
|
||||
> 标识符语法
|
||||
<a id="identifier"></a>
|
||||
> *标识符* → [*头部标识符*](#identifier_head) [*标识符字符组*](#identifier_characters)<sub>可选</sub>
|
||||
> *标识符* → \`[*头部标识符*](#identifier_head) [*标识符字符组*](#identifier_characters)<sub>可选</sub>\`
|
||||
> *标识符* → [*隐式参数名*](#implicit_parameter_name)
|
||||
> *标识符列表* → [*标识符*](#identifier) | [*标识符*](#identifier) **,** [*标识符列表*](#identifier_list)
|
||||
<a id="identifier_head"></a>
|
||||
> *头部标识符* → 大写或小写字母 A - Z
|
||||
> *头部标识符* → U+00A8, U+00AA, U+00AD, U+00AF, U+00B2–U+00B5, or U+00B7–U+00BA
|
||||
> *头部标识符* → U+00BC–U+00BE, U+00C0–U+00D6, U+00D8–U+00F6, or U+00F8–U+00FF
|
||||
> *头部标识符* → U+0100–U+02FF, U+0370–U+167F, U+1681–U+180D, or U+180F–U+1DBF
|
||||
> *头部标识符* → U+1E00–U+1FFF
|
||||
> *头部标识符* → U+200B–U+200D, U+202A–U+202E, U+203F–U+2040, U+2054, or U+2060–U+206F
|
||||
> *头部标识符* → U+2070–U+20CF, U+2100–U+218F, U+2460–U+24FF, or U+2776–U+2793
|
||||
> *头部标识符* → U+2C00–U+2DFF or U+2E80–U+2FFF
|
||||
> *头部标识符* → U+3004–U+3007, U+3021–U+302F, U+3031–U+303F, or U+3040–U+D7FF
|
||||
> *头部标识符* → U+F900–U+FD3D, U+FD40–U+FDCF, U+FDF0–U+FE1F, or U+FE30–U+FE44
|
||||
> *头部标识符* → U+FE47–U+FFFD
|
||||
> *头部标识符* → U+10000–U+1FFFD, U+20000–U+2FFFD, U+30000–U+3FFFD, or U+40000–U+4FFFD
|
||||
> *头部标识符* → U+50000–U+5FFFD, U+60000–U+6FFFD, U+70000–U+7FFFD, or U+80000–U+8FFFD
|
||||
> *头部标识符* → U+90000–U+9FFFD, U+A0000–U+AFFFD, U+B0000–U+BFFFD, or U+C0000–U+CFFFD
|
||||
> *头部标识符* → U+D0000–U+DFFFD or U+E0000–U+EFFFD
|
||||
> *标识符字符* → 数值 0 - 9
|
||||
> *标识符字符* → U+0300–U+036F, U+1DC0–U+1DFF, U+20D0–U+20FF, or U+FE20–U+FE2F
|
||||
> *标识符字符* → [*头部标识符*](#identifier_head)
|
||||
<a id="identifier_characters"></a>
|
||||
> *标识符字符组* → [*标识符字符*](#identifier_character) [*标识符字符列表*](#identifier_characters)<sub>可选</sub>
|
||||
<a id="implicit_parameter_name"></a>
|
||||
> *隐式参数名* → **$** [*十进制数字列表*](#decimal_digits)
|
||||
|
||||
<a id="keywords"></a>
|
||||
## 关键字和符号
|
||||
|
||||
下面这些被保留的关键字(*keywords*)不允许用作标识符,除非被反引号转义,具体描述请参考 [标识符](#identifiers)。
|
||||
|
||||
* **用在声明中的关键字:** *class*、*deinit*、*enum*、*extension*、*func*、*import*、*init*、*let*、*protocol*、*static*、*struct*、*subscript*、*typealias*、*var*
|
||||
* **用在语句中的关键字:** *break*、*case*、*continue*、*default*、*do*、*else*、*fallthrough*、*if*、*in*、*for*、*return*、*switch*、*where*、*while*
|
||||
* **用在表达式和类型中的关键字:** *as*、*dynamicType*、*is*、*new*、*super*、*self*、*Self*、*Type*、*\_\_COLUMN\_\_*、*\_\_FILE\_\_*、*\_\_FUNCTION\_\_*、*\_\_LINE\_\_*
|
||||
* **用在模式中的关键字:** *\_*
|
||||
* **特定上下文中被保留的关键字:** *associativity*、*didSet*、*get*、*infix*、*inout*、*left*、*mutating*、*none*、*nonmutating*、*operator*、*override*、*postfix*、*precedence*、*prefix*、*right*、*set*、*unowned*、*unowned(safe)*、*unowned(unsafe)*、*weak*、*willSet*,这些关键字在特定上下文之外可以被用于标识符。
|
||||
|
||||
以下标记被当作保留符号,不能用于自定义操作符:`(`、`)`、`{`、`}`、`[`、`]`、`.`、`,`、`:`、`;`、`=`、`@`、`#`、`&(作为前缀操作符)`、`->`、`` `、`?` 和 `!(作为后缀操作符)`。
|
||||
|
||||
<a id="literals"></a>
|
||||
## 字面量
|
||||
|
||||
字面量是用来表示源码中某种特定类型的值,比如一个数字或字符串。
|
||||
|
||||
下面是字面量的一些示例:
|
||||
|
||||
```swift
|
||||
42 // 整型字面量
|
||||
3.14159 // 浮点型字面量
|
||||
"Hello, world!" // 字符串型字面量
|
||||
true // 布尔型字面量
|
||||
```
|
||||
|
||||
字面量本身并不包含类型信息。事实上,一个字面量会被解析为拥有无限的精度,然后 Swift 的类型推导会尝试去推导出这个字面量的类型。比如,在 `let x: Int8 = 42` 这个声明中,Swift 使用了显式类型标注(`: Int8`)来推导出 `42` 这个整型字面量的类型是 `Int8`。如果没有可用的类型信息, Swift 则会从标准库中定义的字面量类型中推导出一个默认的类型。整型字面量的默认类型是 `Int`,浮点型字面量的默认类型是 `Double`,字符串型字面量的默认类型是 `String`,布尔型字面量的默认类型是 `Bool`。比如,在 `let str = "Hello, world"` 这个声明中,字符串 `"Hello, world"`的默认推导类型就是 `String`。
|
||||
|
||||
当为一个字面量值指定了类型标注的时候,这个注解的类型必须能通过这个字面量值实例化后得到。也就是说,这个类型必须遵守这些 Swift 标准库协议中的一个:整型字面量的`IntegerLiteralConvertible`协议、符点型字面量的`FloatingPointLiteralConvertible`协议、字符串字面量的`StringLiteralConvertible`协议以及布尔型字面量的`BooleanLiteralConvertible`协议。比如,`Int8` 遵守了 `IntegerLiteralConvertible`协议,因此它能在 `let x: Int8 = 42` 这个声明中作为整型字面量 `42` 的类型标注。
|
||||
|
||||
> 字面量语法
|
||||
> *字面量* → [*数字型字面量*](#numeric_literal) | [*字符串型字面量*](#string_literal) | [*布尔型字面量*](#boolean_literal) | [*nil型字面量*](#nil_literal)
|
||||
<a id="numeric_literal"></a>
|
||||
> *数字型字面量* → -<sub>可选</sub>[*整型字面量*](#integer_literal) | -<sub>可选</sub>[*符点型字面量*](#floating_point_literal)
|
||||
> *布尔型字面量* → **true** | **false**
|
||||
> *nil型字面量* → **nil**
|
||||
|
||||
### 整型字面量
|
||||
|
||||
整型字面量(*integer literals*)表示未指定精度整型数的值。整型字面量默认用十进制表示,可以加前缀来指定其他的进制,二进制字面量加 `0b`,八进制字面量加 `0o`,十六进制字面量加 `0x`。
|
||||
|
||||
十进制字面量包含数字 `0` 至 `9`。二进制字面量只包含 `0` 或 `1`,八进制字面量包含数字 `0` 至 `7`,十六进制字面量包含数字 `0` 至 `9` 以及字母 `A` 至 `F` (大小写均可)。
|
||||
|
||||
负整数的字面量在整型字面量前加减号 `-`,比如 `-42`。
|
||||
|
||||
整型字面面可以使用下划线 `_` 来增加数字的可读性,下划线会被系统忽略,因此不会影响字面量的值。同样地,也可以在数字前加 `0`,并不会影响字面量的值。
|
||||
|
||||
除非特别指定,整型字面量的默认推导类型为 Swift 标准库类型中的 `Int`。Swift 标准库还定义了其他不同长度以及是否带符号的整数类型,请参考 [整数类型](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-ID323)。
|
||||
|
||||
> 整型字面量语法
|
||||
> *整型字面量* → [*二进制字面量*](#binary_literal)
|
||||
> *整型字面量* → [*八进制字面量*](#octal_literal)
|
||||
> *整型字面量* → [*十进制字面量*](#decimal_literal)
|
||||
> *整型字面量* → [*十六进制字面量*](#hexadecimal_literal)
|
||||
<a id="binary_literal"></a>
|
||||
> *二进制字面量* → **0b** [*二进制数字*](#binary_digit) [*二进制字面量字符组*](#binary_literal_characters)<sub>可选</sub>
|
||||
> *二进制数字* → 数值 0 到 1
|
||||
> *二进制字面量字符* → [*二进制数字*](#binary_digit) | _
|
||||
> *二进制字面量字符组* → [*二进制字面量字符*](#binary_literal_character) [*二进制字面量字符组*](#binary_literal_characters)<sub>可选</sub>
|
||||
<a id="octal_literal"></a>
|
||||
> *八进制字面量* → **0o** [*八进字数字*](#octal_digit) [*八进制字符列表*](#octal_literal_characters)<sub>可选</sub>
|
||||
> *八进字数字* → 数值 0 到 7
|
||||
> *八进制字符* → [*八进字数字*](#octal_digit) | _
|
||||
> *八进制字符组* → [*八进制字符*](#octal_literal_character) [*八进制字符列表*](#octal_literal_characters)<sub>可选</sub>
|
||||
<a id="decimal_literal"></a>
|
||||
> *十进制字面量* → [*十进制数字*](#decimal_digit) [*十进制字符组*](#decimal_literal_characters)<sub>可选</sub>
|
||||
> *十进制数字* → 数值 0 到 9
|
||||
> *十进制数字列表* → [*十进制数字*](#decimal_digit) [*十进制数字列表*](#decimal_digits)<sub>可选</sub>
|
||||
> *十进制字符* → [*十进制数字*](#decimal_digit) | _
|
||||
> *十进制字符列表* → [*十进制字符*](#decimal_literal_character) [*十进制字符列表*](#decimal_literal_characters)<sub>可选</sub>
|
||||
<a id="hexadecimal_literal"></a>
|
||||
> *十六进制字面量* → **0x** [*十六进制数字*](#hexadecimal_digit) [*十六进制字面量字符列表*](#hexadecimal_literal_characters)<sub>可选</sub>
|
||||
> *十六进制数字* → 数值 0 到 9, 字母 a 到 f, 或 A 到 F
|
||||
> *十六进制字符* → [*十六进制数字*](#hexadecimal_digit) | _
|
||||
> *十六进制字面量字符列表* → [*十六进制字符*](#hexadecimal_literal_character) [*十六进制字面量字符列表*](#hexadecimal_literal_characters)<sub>可选</sub>
|
||||
|
||||
### 浮点型字面量
|
||||
|
||||
浮点型字面量(*floating-point literals*)表示未指定精度浮点数的值。
|
||||
|
||||
浮点型字面量默认用十进制表示(无前缀),也可以用十六进制表示(加前缀 `0x`)。
|
||||
|
||||
十进制浮点型字面量(*decimal floating-point literals*)由十进制数字串后跟小数部分或指数部分(或两者皆有)组成。十进制小数部分由小数点 `.` 后跟十进制数字串组成。指数部分由大写或小写字母 `e` 为前缀后跟十进制数字串组成,这串数字表示 `e` 之前的数量乘以 10 的几次方。例如:`1.25e2` 表示 `1.25 ⨉ 10^2`,也就是 `125.0`;同样,`1.25e-2` 表示 `1.25 ⨉ 10^-2`,也就是 `0.0125`。
|
||||
|
||||
十六进制浮点型字面量(*hexadecimal floating-point literals*)由前缀 `0x` 后跟可选的十六进制小数部分以及十六进制指数部分组成。十六进制小数部分由小数点后跟十六进制数字串组成。指数部分由大写或小写字母 `p` 为前缀后跟十进制数字串组成,这串数字表示 `p` 之前的数量乘以 2 的几次方。例如:`0xFp2` 表示 `15 ⨉ 2^2`,也就是 `60`;同样,`0xFp-2` 表示 `15 ⨉ 2^-2`,也就是 `3.75`。
|
||||
|
||||
负的浮点型字面量由一元运算符减号 `-` 和浮点型字面量组成,例如 `-42.5`。
|
||||
|
||||
浮点型字面量允许使用下划线 `_` 来增强数字的可读性,下划线会被系统忽略,因此不会影响字面量的值。同样地,也可以在数字前加 `0`,并不会影响字面量的值。
|
||||
|
||||
除非特别指定,浮点型字面量的默认推导类型为 Swift 标准库类型中的 `Double`,表示64位浮点数。Swift 标准库也定义了 `Float` 类型,表示32位浮点数。
|
||||
|
||||
> 浮点型字面量语法
|
||||
> *浮点数字面量* → [*十进制字面量*](#decimal_literal) [*十进制分数*](#decimal_fraction)<sub>可选</sub> [*十进制指数*](#decimal_exponent)<sub>可选</sub>
|
||||
> *浮点数字面量* → [*十六进制字面量*](#hexadecimal_literal) [*十六进制分数*](#hexadecimal_fraction)<sub>可选</sub> [*十六进制指数*](#hexadecimal_exponent)
|
||||
<a id="decimal_fraction"></a>
|
||||
> *十进制分数* → **.** [*十进制字面量*](#decimal_literal)
|
||||
> *十进制指数* → [*浮点数e*](#floating_point_e) [*正负号*](#sign)<sub>可选</sub> [*十进制字面量*](#decimal_literal)
|
||||
<a id="hexadecimal_literal"></a>
|
||||
> *十六进制分数* → **.** [*十六进制数字*](#hexadecimal_digit) [*十六进制字面量字符列表*](#hexadecimal_literal_characters)<sub>可选</sub>
|
||||
> *十六进制指数* → [*浮点数p*](#floating_point_p) [*正负号*](#sign)<sub>可选</sub> [*十进制字面量*](#decimal_literal)
|
||||
<a id="floating_point_e"></a>
|
||||
> *浮点数e* → **e** | **E**
|
||||
> *浮点数p* → **p** | **P**
|
||||
> *正负号* → **+** | **-**
|
||||
|
||||
|
||||
### 字符串型字面量
|
||||
|
||||
字符串型字面量(*string literal*)由被包在双引号中的一串字符组成,形式如下:
|
||||
|
||||
```
|
||||
"characters"
|
||||
```
|
||||
|
||||
字符串型字面量中不能包含未转义的双引号 (`"`)、未转义的反斜线(`\`)、回车符(*carriage return*)或换行符(*line feed*)。
|
||||
|
||||
可以在字符串字面量中使用的转义特殊符号如下:
|
||||
|
||||
* 空字符(Null Character)`\0`
|
||||
* 反斜线(Backslash)`\\`
|
||||
* 水平制表符(Horizontal Tab)`\t`
|
||||
* 换行符(Line Feed)`\n`
|
||||
* 回车符(Carriage Return)`\r`
|
||||
* 双引号(Double Quote)`\"`
|
||||
* 单引号(Single Quote)`\'`
|
||||
* Unicode标量 `\u{n}`,n为一到八位的十六进制数字
|
||||
|
||||
字符串字面量允许在反斜杠小括号 `\()` 中插入表达式的值。插入表达式(*interpolated expression*)不能包含未转义的双引号 `"`、未转义的反斜线 `\`、回车符或者换行符。表达式结果的类型必须在 *String* 类中有对应的初始化方法。
|
||||
|
||||
例如,以下所有字符串字面量的值都是相同的:
|
||||
|
||||
```
|
||||
"1 2 3"
|
||||
"1 2 \(3)"
|
||||
"1 2 \(1 + 2)"
|
||||
let x = 3; "1 2 \(x)"
|
||||
```
|
||||
|
||||
字符串字面量的默认推导类型为 `String`。组成字符串的字符默认推导类型为 `Character`。更多有关 `String` 和 `Character` 的信息请参照 [字符串和字符](../chapter2/03_Strings_and_Characters.html)。
|
||||
|
||||
> 字符型字面量语法
|
||||
> *字符串字面量* → **"** [*引用文本*](#quoted_text)<sub>可选</sub> **"**
|
||||
<a id="quoted_text"></a>
|
||||
> *引用文本* → [*引用文本条目*](#quoted_text_item) [*引用文本*](#quoted_text) <sub>可选</sub>
|
||||
> *引用文本条目* → [*转义字符*](#escaped_character)
|
||||
> *引用文本条目* → **\(** [*表达式*](./04_Expressions.html) **)**
|
||||
> *引用文本条目* → **除了", \, U+000A, 或者 U+000D的所有Unicode的字符**
|
||||
> *转义字符* → **\0** | **\\** | **\t** | **\n** | **\r** | **\"** | **\'**
|
||||
> *转义字符* → **\u {** [*unicode标量数字*](#unicode_scalar_digits) **}**
|
||||
> *unicode标量数字* → 一到八位的十六进制数字
|
||||
|
||||
<a id="operators"></a>
|
||||
## 运算符
|
||||
|
||||
Swift 标准库定义了许多可供使用的运算符,其中大部分在 [基础运算符](../chapter2/02_Basic_Operators.html) 和 [高级运算符](../chapter2/25_Advanced_Operators.html) 中进行了阐述。这一小节将描述哪些字符能用于自定义运算符。
|
||||
|
||||
自定义运算符可以由以下其中之一的 ASCII 字符 `/`、`=`、 `-`、`+`、`!`、`*`、`%`、`<`、`>`、`&`、`|`、`^`、`?` 以及 `~`, 或者后面语法中规定的任一个 Unicode 字符开始。在第一个字符之后,允许使用组合型 Unicode 字符。也可以使用两个或者多个的点号来自定义运算符(比如, `....`)。虽然可以自定义包含问号`?`的运算符,但是这个运算符不能只包含单独的一个问号。
|
||||
|
||||
注意:
|
||||
以下这些标记 =, ->, //, /*, */, ., <(前缀运算符), &, and ?, ?(中缀运算符), >(后缀运算符), ! 以及 ? 是被系统保留的。这些标记不能被重载,也不能用于自定义操作符。
|
||||
|
||||
运算符两侧的空白被用来区分该运算符是否为前缀运算符(*prefix operator*)、后缀运算符(*postfix operator*)或二元运算符(*binary operator*)。规则总结如下:
|
||||
|
||||
* 如果运算符两侧都有空白或两侧都无空白,将被看作二元运算符。例如:`a+b` 和 `a + b` 中的运算符 `+` 被看作二元运算符。
|
||||
* 如果运算符只有左侧空白,将被看作前缀一元运算符。例如 `a ++b` 中的 `++` 被看作前缀一元运算符。
|
||||
* 如果运算符只有右侧空白,将被看作后缀一元运算符。例如 `a++ b` 中的 `++` 被看作后缀一元运算符。
|
||||
* 如果运算符左侧没有空白并紧跟 `.`,将被看作后缀一元运算符。例如 `a++.b` 中的 `++` 被看作后缀一元运算符(即上式被视为 `a++ .b` 而非 `a ++ .b`)。
|
||||
|
||||
鉴于这些规则,运算符前的字符 `(`、`[` 和 `{` ;运算符后的字符 `)`、`]` 和 `}` 以及字符 `,`、`;` 和 `:` 都被视为空白。
|
||||
|
||||
以上规则需注意一点,如果预定义运算符 `!` 或 `?` 左侧没有空白,则不管右侧是否有空白都将被看作后缀运算符。如果将 `?` 用作可选链(*optional-chaining*)操作符,左侧必须无空白。如果用于条件运算符 `? :`,必须两侧都有空白。
|
||||
|
||||
在某些特定的构造中 ,以 `<` 或 `>` 开头的运算符会被分离成两个或多个标记,剩余部分以同样的方式会被再次分离。因此,在 `Dictionary<String, Array<Int>>` 中没有必要添加空白来消除闭合字符 `>` 的歧义。在这个例子中, 闭合字符 `>` 不会被视为单独的标记,因而不会被误解析为 `>>` 运算符的一部分。
|
||||
|
||||
要学习如何自定义运算符,请参考 [自定义操作符](../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>
|
||||
> *运算符* → [*头部点运算符*](#dot_operator_head) [*点运算符字符组*](#dot_operator_characters)<sub>可选</sub>
|
||||
<a id="operator_head"></a>
|
||||
> *头部运算符* → **/** | **=** | **+** | **!** |**\*** | **%** |**<** | **>** |**&** | **|** |**/** | **~** | **?** |
|
||||
> *头部运算符* → U+00A1–U+00A7
|
||||
> *头部运算符* → U+00A9 or U+00AB
|
||||
> *头部运算符* → U+00AC or U+00AE
|
||||
> *头部运算符* → U+00B0–U+00B1, U+00B6, U+00BB, U+00BF, U+00D7, or U+00F7
|
||||
> *头部运算符* → U+2016–U+2017 or U+2020–U+2027
|
||||
> *头部运算符* → U+2030–U+203E
|
||||
> *头部运算符* → U+2041–U+2053
|
||||
> *头部运算符* → U+2055–U+205E
|
||||
> *头部运算符* → U+2190–U+23FF
|
||||
> *头部运算符* → U+2500–U+2775
|
||||
> *头部运算符* → U+2794–U+2BFF
|
||||
> *头部运算符* → U+2E00–U+2E7F
|
||||
> *头部运算符* → U+3001–U+3003
|
||||
> *头部运算符* → U+3008–U+3030
|
||||
<a id="operator_character"></a>
|
||||
> *运算符字符* → [*头部运算符*](#operator_head)
|
||||
> *运算符字符* → U+0300–U+036F
|
||||
> *运算符字符* → U+1DC0–U+1DFF
|
||||
> *运算符字符* → U+20D0–U+20FF
|
||||
> *运算符字符* → U+FE00–U+FE0F
|
||||
> *运算符字符* → U+FE20–U+FE2F
|
||||
> *运算符字符* → U+E0100–U+E01EF
|
||||
<a id="operator_characters"></a>
|
||||
> *运算符字符组* → [*运算符字符*](#operator_character) [*运算符字符组*] (#operator_characters)<sub>可选</sub>
|
||||
<a id="dot_operator_head"></a>
|
||||
> *头部点运算符* → **..**
|
||||
> *头部点运算符字符* → . | [*运算符字符*](#operator_character)
|
||||
> *头部点运算符字符组* → [*点运算符字符*](#dot_operator_character) [*点运算符字符组*](#dot_operator_characters)<sub>可选</sub>
|
||||
|
||||
> *二元运算符* → [*运算符*](#operator)
|
||||
> *前置运算符* → [*运算符*](#operator)
|
||||
> *后置运算符* → [*运算符*](#operator)
|
||||
|
||||
163
source/chapter3/03_Types.md
Executable file → Normal 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)
|
||||
|
||||
# 类型(Types)
|
||||
-----------------
|
||||
> 2.0
|
||||
> 翻译+校对:[EudeMorgen](https://github.com/EudeMorgen)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
@ -18,16 +22,16 @@
|
||||
- [类型继承子句(Type Inheritance Clause)](#type_inheritance_clause)
|
||||
- [类型推断(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 中的类型推断行为。
|
||||
|
||||
> 类型语法
|
||||
> *类型* → [*数组类型*](..\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>
|
||||
##类型注解
|
||||
@ -43,16 +47,16 @@ func someFunction(a: Int){ /* ... */ }
|
||||
类型注解可以在类型之前包含一个类型特性(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>
|
||||
##类型标识符
|
||||
|
||||
类型标识符引用命名型类型或者是命名型/复合型类型的别名。
|
||||
|
||||
大多数情况下,类型标识符引用的是同名的命名型类型。例如类型标识符`Int`引用命名型类型`Int`,同样,类型标识符`Dictionary<String, Int>`引用命名型类型`Dictionary<String, Int>`。
|
||||
大多数情况下,类型标识符引用的是与之同名的命名型类型。例如类型标识符`Int`引用命名型类型`Int`,同样,类型标识符`Dictionary<String, Int>`引用命名型类型`Dictionary<String, Int>`。
|
||||
|
||||
在两种情况下类型标识符引用的不是同名的类型。情况一,类型标识符引用的是命名型/复合型类型的类型别名。比如,在下面的例子中,类型标识符使用`Point`来引用元组`(Int, Int)`:
|
||||
在两种情况下类型标识符不引用同名的类型。情况一,类型标识符引用的是命名型/复合型类型的类型别名。比如,在下面的例子中,类型标识符使用`Point`来引用元组`(Int, Int)`:
|
||||
|
||||
```swift
|
||||
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)
|
||||
|
||||
<a name="tuple_type"></a>
|
||||
@ -74,15 +78,15 @@ var someValue: ExampleModule.MyType
|
||||
|
||||
元组类型使用逗号隔开并使用括号括起来的0个或多个类型组成的列表。
|
||||
|
||||
你可以使用元组类型作为一个函数的返回类型,这样就可以使函数返回多个值。你也可以命名元组类型中的元素,然后用这些名字来引用每个元素的值。元素的名字由一个标识符和`:`组成。“函数和多返回值”章节里有一个展示上述特性的例子。
|
||||
你可以使用元组类型作为一个函数的返回类型,这样就可以使函数返回多个值。你也可以命名元组类型中的元素,然后用这些名字来引用每个元素的值。元素的名字由一个标识符紧跟一个冒号`(:)`组成。“函数和多返回值”章节里有一个展示上述特性的例子。
|
||||
|
||||
`void`是空元组类型`()`的别名。如果括号内只有一个元素,那么该类型就是括号内元素的类型。比如,`(Int)`的类型是`Int`而不是`(Int)`。所以,只有当元组类型包含两个元素以上时才可以标记元组元素。
|
||||
`void`是空元组类型`()`的别名。如果括号内只有一个元素,那么该类型就是括号内元素的类型。比如,`(Int)`的类型是`Int`而不是`(Int)`。所以,只有当元组类型包含的元素个数在两个及以上时才可以命名元组元素。
|
||||
|
||||
> 元组类型语法
|
||||
> *元组类型* → **(** [*元组类型主体*](..\chapter3\03_Types.html#tuple_type_body) _可选_ **)**
|
||||
> *元组类型主体* → [*元组类型的元素列表*](..\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)
|
||||
> *元组类型* → **(** [*元组类型主体*](../chapter3/03_Types.html#tuple_type_body) _可选_ **)**
|
||||
> *元组类型主体* → [*元组类型的元素列表*](../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)
|
||||
> *元素名* → [*标识符*](LexicalStructure.html#identifier)
|
||||
|
||||
<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
|
||||
func simpleAssert(condition: @auto_closure () -> Bool, message: String){
|
||||
if !condition(){
|
||||
println(message)
|
||||
func simpleAssert(@autoclosure condition: Void -> Bool, _ message: String) {
|
||||
if !condition() {
|
||||
print(message)
|
||||
}
|
||||
}
|
||||
let testNumber = 5
|
||||
simpleAssert(testNumber % 2 == 0, "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参数部分。
|
||||
|
||||
柯里化函数(curried function)的类型相当于一个嵌套函数类型。例如,下面的柯里化函数`addTwoNumber()()`的类型是`Int -> Int -> Int`:
|
||||
柯里化函数(Curried fuction)的函数类型从右向左递归地组成一组。例如,函数类型`Int -> Int -> Int`可以被理解为`Int -> (Int -> Int)`——也就是说,一个函数的参数为`Int`类型,其返回类型是一个参数类型为`Int`返回类型为`Int`的函数类型。关于柯里化函数的讨论见章节Curried Fuctions。
|
||||
|
||||
```swift
|
||||
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
|
||||
```
|
||||
函数类型若要抛出错误就必须使用`throws`关键字来标记,若要重抛错误则必须使用`rethrows`关键字来标记。`throws`关键字是函数类型的一部分,不抛出函数(nonthrowing function)是抛出函数(throwing function)函数的一个子类型。因此,在使用抛出函数的地方也可以使用不抛出函数。对于柯里化函数,`throws`关键字只应用于最里层的函数。抛出和重抛函数(rethrowing function)的相关描述见章节抛出函数与方法和重抛函数与方法。
|
||||
|
||||
> 函数类型语法
|
||||
> *函数类型* → [*类型*](..\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>
|
||||
##数组类型
|
||||
|
||||
Swift语言使用类型名紧接中括号`[]`来简化标准库中定义的命名型类型`Array<T>`。换句话说,下面两个声明是等价的:
|
||||
Swift语言中使用[`type`]来简化标准库中定义`Array<T>`类型的操作。
|
||||
换句话说,下面两个声明是等价的:
|
||||
|
||||
```swift
|
||||
let someArray: String[] = ["Alex", "Brian", "Dave"]
|
||||
let someArray: [String] = ["Alex", "Brian", "Dave"]
|
||||
let someArray: Array<String> = ["Alex", "Brian", "Dave"]
|
||||
```
|
||||
上面两种情况下,常量`someArray`都被声明为字符串数组。数组的元素也可以通过`[]`获取访问:`someArray[0]`是指第0个元素`“Alex”`。
|
||||
|
||||
上面的例子同时显示,你可以使用`[]`作为初始值构造数组,空的`[]`则用来来构造指定类型的空数组。
|
||||
你也可以嵌套多对方括号来创建多维数组,最里面的方括号中指明数组元素的基本类型。比如,下面例子中使用三对方括号创建三维整数数组。
|
||||
|
||||
```swift
|
||||
var emptyArray: Double[] = []
|
||||
```
|
||||
你也可以使用链接起来的多个`[]`集合来构造多维数组。例如,下例使用三个`[]`集合来构造三维整型数组:
|
||||
|
||||
```swift
|
||||
var array3D: Int[][][] = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
|
||||
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`。
|
||||
|
||||
关于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>
|
||||
##可选类型
|
||||
@ -177,8 +182,6 @@ var optionalInteger: Optional<Int>
|
||||
|
||||
如果你在声明或定义可选变量或特性的时候没有提供初始值,它的值则会自动赋成缺省值`nil`。
|
||||
|
||||
可选符合`LogicValue`协议,因此可以出现在布尔值环境下。此时,如果一个可选类型`T?`实例包含有类型为`T`的值(也就是说值为`Optional.Some(T)`),那么此可选类型就为`true`,否则为`false`。
|
||||
|
||||
如果一个可选类型的实例包含一个值,那么你就可以使用后缀操作符`!`来获取该值,正如下面描述的:
|
||||
|
||||
```swift
|
||||
@ -189,10 +192,10 @@ optionalInteger! // 42
|
||||
|
||||
你也可以使用可选链和可选绑定来选择性的执行可选表达式上的操作。如果值为`nil`,不会执行任何操作因此也就没有运行错误产生。
|
||||
|
||||
更多细节以及更多如何使用可选类型的例子,见章节“可选”。
|
||||
更多细节以及更多如何使用可选类型的例子,见章节Optionals。
|
||||
|
||||
> 可选类型语法
|
||||
> *可选类型* → [*类型*](..\chapter3\03_Types.html#type) **?**
|
||||
> *可选类型* → [*类型*](../chapter3/03_Types.html#type) **?**
|
||||
|
||||
<a name="implicitly_unwrapped_optional_type"></a>
|
||||
##隐式解析可选类型
|
||||
@ -205,7 +208,7 @@ var implicitlyUnwrappedString: ImplicitlyUnwrappedOptional<String>
|
||||
```
|
||||
上述两种情况下,变量`implicitlyUnwrappedString`被声明为一个隐式解析可选类型的字符串。注意类型与`!`之间没有空格。
|
||||
|
||||
你可以在使用可选的地方同样使用隐式解析可选。比如,你可以将隐式解析可选的值赋给变量、常量和可选特性,反之亦然。
|
||||
你可以在使用可选类型的地方同样使用隐式解析可选类型。比如,你可以将隐式解析可选类型的值赋给变量、常量和可选特性,反之亦然。
|
||||
|
||||
有了可选,你在声明隐式解析可选变量或特性的时候就不用指定初始值,因为它有缺省值`nil`。
|
||||
|
||||
@ -213,15 +216,15 @@ var implicitlyUnwrappedString: ImplicitlyUnwrappedOptional<String>
|
||||
|
||||
使用可选链会选择性的执行隐式解析可选表达式上的某一个操作。如果值为`nil`,就不会执行任何操作,因此也不会产生运行错误。
|
||||
|
||||
关于隐式解析可选的更多细节,见章节“隐式解析可选”。
|
||||
关于隐式解析可选的更多细节,见章节Implicitly Unwrapped Optionals。
|
||||
|
||||
> 隐式解析可选类型(Implicitly Unwrapped Optional Type)语法
|
||||
> *隐式解析可选类型* → [*类型*](..\chapter3\03_Types.html#type) **!**
|
||||
> *隐式解析可选类型* → [*类型*](../chapter3/03_Types.html#type) **!**
|
||||
|
||||
<a name="protocol_composition_type"></a>
|
||||
##协议合成类型
|
||||
|
||||
协议合成类型是一种符合每个协议的指定协议列表类型。协议合成类型可能会用在类型注解和泛型参数中。
|
||||
协议合成类型是一种遵循具体协议列表中每个协议的类型。协议合成类型可能会用在类型注解和泛型参数中。
|
||||
|
||||
协议合成类型的形式如下:
|
||||
|
||||
@ -229,23 +232,23 @@ var implicitlyUnwrappedString: ImplicitlyUnwrappedOptional<String>
|
||||
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) _可选_ **>**
|
||||
> *协议标识符列表* → [*协议标识符*](..\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)
|
||||
> *协议合成类型* → **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#type_identifier)
|
||||
|
||||
<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
|
||||
class SomeBaseClass {
|
||||
@ -264,29 +267,35 @@ let someInstance: SomeBaseClass = SomeSubClass()
|
||||
someInstance.dynamicType.printClassName()
|
||||
// prints "SomeSubClass
|
||||
```
|
||||
> 注意
|
||||
> **不能创建元类型类的实例,因为不能保证其子类会提供初始化的代码。**
|
||||
|
||||
|
||||
> 元(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>
|
||||
##类型继承子句
|
||||
|
||||
类型继承子句被用来指定一个命名型类型继承哪个类且适配哪些协议。类型继承子句开始于冒号`:`,紧跟由`,`隔开的类型标识符列表。
|
||||
类型继承子句被用来指定一个命名型类型继承的哪个类、遵循的哪些协议。类型继承子句也用来指定一个类需要遵循的协议。类型继承子句开始于冒号`:`,其后是类所需遵循的协议或者类型标识符列表或者两者均有。
|
||||
|
||||
类可以继承单个超类,适配任意数量的协议。当定义一个类时,超类的名字必须出现在类型标识符列表首位,然后跟上该类需要适配的任意数量的协议。如果一个类不是从其它类继承而来,那么列表可以以协议开头。关于类继承更多的讨论和例子,见章节“继承”。
|
||||
类可以继承单个超类,遵循任意数量的协议。当定义一个类时,超类的名字必须出现在类型标识符列表首位,然后跟上该类需要遵循的任意数量的协议。如果一个类不是从其它类继承而来,那么列表可以以协议开头。关于类继承更多的讨论和例子,见章节Inheritance。
|
||||
|
||||
其它命名型类型可能只继承或适配一个协议列表。协议类型可能继承于其它任意数量的协议。当一个协议类型继承于其它协议时,其它协议的条件集合会被集成在一起,然后其它从当前协议继承的任意类型必须适配所有这些条件。
|
||||
其它命名型类型可能只继承或遵循一个协议列表。协议类型可能继承于其它任意数量的协议。当一个协议类型继承于其它协议时,其它协议的条件集合会被整合在一起,然后其它从当前协议继承的任意类型必须遵循所有这些条件。正如在协议声明中所讨论的那样,可以把类的关键字放到类型继承子句中的首位,这样就可以用一个类的条件来标记一个协议声明。
|
||||
|
||||
枚举定义中的类型继承子句可以是一个协议列表,或是指定原始值的枚举,一个单独的指定原始值类型的命名型类型。使用类型继承子句来指定原始值类型的枚举定义的例子,见章节“原始值”。
|
||||
枚举定义中的类型继承子句可以是一个协议列表,或是指定原始值的枚举——一个单独的指定原始值类型的命名型类型。使用类型继承子句来指定原始值类型的枚举定义的例子,见章节Raw Values。
|
||||
|
||||
> 类型继承子句语法
|
||||
> *类型继承子句* → **:** [*类型继承列表*](..\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#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>
|
||||
##类型推断
|
||||
|
||||
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`)。
|
||||
|
||||
|
||||
434
source/chapter3/04_Expressions.md
Executable file → Normal 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)
|
||||
|
||||
# 表达式(Expressions)
|
||||
-----------------
|
||||
> 2.0
|
||||
> 翻译+校对:[EudeMorgen](https://github.com/EudeMorgen)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
@ -16,35 +20,36 @@
|
||||
|
||||
Swift 中存在四种表达式: 前缀(prefix)表达式,二元(binary)表达式,主要(primary)表达式和后缀(postfix)表达式。表达式可以返回一个值,以及运行某些逻辑(causes a side effect)。
|
||||
|
||||
前缀表达式和二元表达式就是对某些表达式使用各种运算符(operators)。 主要表达式是最短小的表达式,它提供了获取(变量的)值的一种途径。 后缀表达式则允许你建立复杂的表达式,例如配合函数调用和成员访问。 每种表达式都在下面有详细论述~
|
||||
前缀表达式和二元表达式就是对某些表达式使用各种运算符(operators)。 主要表达式是最短小的表达式,它提供了获取(变量的)值的一种途径。 后缀表达式则允许你建立复杂的表达式,例如配合函数调用和成员访问。 每种表达式都在下面有详细论述。
|
||||
|
||||
> 表达式语法
|
||||
> *表达式* → [*前置表达式*](..\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)
|
||||
> *表达式* → [*试算子(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)
|
||||
|
||||
<a name="prefix_expressions"></a>
|
||||
## 前缀表达式(Prefix Expressions)
|
||||
|
||||
前缀表达式由 前缀符号和表达式组成。(这个前缀符号只能接收一个参数)
|
||||
|
||||
Swift 标准库支持如下的前缀操作符:
|
||||
|
||||
- ++ 自增1 (increment)
|
||||
- -- 自减1 (decrement)
|
||||
- ! 逻辑否 (Logical NOT )
|
||||
- ~ 按位否 (Bitwise NOT )
|
||||
- \+ 加(Unary plus)
|
||||
- \- 减(Unary minus)
|
||||
|
||||
前缀表达式由可选的前缀符号和表达式组成。(这个前缀符号只能接收一个参数)
|
||||
|
||||
对于这些操作符的使用,请参见: Basic Operators and Advanced Operators
|
||||
|
||||
作为对上面标准库运算符的补充,你也可以对 某个函数的参数使用 '&'运算符。 更多信息,请参见: "In-Out parameters".
|
||||
|
||||
> 前置表达式语法
|
||||
> *前置表达式* → [*前置运算符*](LexicalStructure.html#prefix_operator) _可选_ [*后置表达式*](..\chapter3\04_Expressions.html#postfix_expression)
|
||||
> *前置表达式* → [*写入写出(in-out)表达式*](..\chapter3\04_Expressions.html#in_out_expression)
|
||||
> *前置表达式* → [*前置运算符*](LexicalStructure.html#prefix_operator) _可选_ [*后置表达式*](../chapter3/04_Expressions.html#postfix_expression)
|
||||
> *前置表达式* → [*写入写出(in-out)表达式*](../chapter3/04_Expressions.html#in_out_expression)
|
||||
> *写入写出(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>
|
||||
## 二元表达式(Binary Expressions)
|
||||
|
||||
@ -52,76 +57,18 @@ Swift 标准库支持如下的前缀操作符:
|
||||
|
||||
> `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)
|
||||
- < 小于
|
||||
- <= 小于等于
|
||||
- > 大于
|
||||
- >= 大于等于
|
||||
- == 等于
|
||||
- != 不等
|
||||
- === 恒等于
|
||||
- !== 不恒等
|
||||
- ~= 模式匹配( 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
|
||||
- >>= 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.
|
||||
|
||||
> 注意
|
||||
> 在解析时, 一个二元表达式表示为一个一级数组(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)
|
||||
> *二元表达式* → [*赋值运算符*](..\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#type_casting_operator)
|
||||
> *二元表达式列表* → [*二元表达式*](..\chapter3\04_Expressions.html#binary_expression) [*二元表达式列表*](..\chapter3\04_Expressions.html#binary_expressions) _可选_
|
||||
> *二元表达式* → [*二元运算符*](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#conditional_operator) [*前置表达式*](../chapter3/04_Expressions.html#prefix_expression)
|
||||
> *二元表达式* → [*类型转换运算符*](../chapter3/04_Expressions.html#type_casting_operator)
|
||||
> *二元表达式列表* → [*二元表达式*](../chapter3/04_Expressions.html#binary_expression) [*二元表达式列表*](../chapter3/04_Expressions.html#binary_expressions) _可选_
|
||||
> *赋值操作符*
|
||||
|
||||
|
||||
<a name="assignment_operator"></a>
|
||||
@ -155,54 +102,48 @@ Swift 标准库提供了如下的二元运算符:
|
||||
想看三元条件运算符的例子,请参见: Ternary Conditional Operator.
|
||||
|
||||
> 三元条件运算符语法
|
||||
> *三元条件运算符* → **?** [*表达式*](..\chapter3\04_Expressions.html#expression) **:**
|
||||
> *三元条件运算符* → **?** [*表达式*](../chapter3/04_Expressions.html#expression) **:**
|
||||
|
||||
<a name="type-casting_operators"></a>
|
||||
## 类型转换运算符(Type-Casting Operators)
|
||||
|
||||
有两种类型转换操作符: as 和 is. 它们有如下的形式:
|
||||
有4种类型转换运算符: `is`,`as`,`? `和`!`. 它们有如下的形式:
|
||||
|
||||
> `expression` as `type`
|
||||
> `expression` as? `type`
|
||||
> `expression` is `type`
|
||||
> `expression` as `type`
|
||||
> `expression` is? `type`
|
||||
> `expression` as! `type`
|
||||
|
||||
as 运算符会把`目标表达式`转换成指定的`类型`(specified type),过程如下:
|
||||
|
||||
- 如果类型转换成功, 那么目标表达式就会返回指定类型的实例(instance). 例如:把子类(subclass)变成父类(superclass)时.
|
||||
- 如果转换失败,则会抛出编译错误( compile-time error)。
|
||||
- 如果上述两个情况都不是(也就是说,编译器在编译时期无法确定转换能否成功,) 那么目标表达式就会变成指定的类型的optional. (is an optional of the specified type ) 然后在运行时,如果转换成功, 目标表达式就会作为 optional的一部分来返回, 否则,目标表达式返回nil. 对应的例子是: 把一个 superclass 转换成一个 subclass.
|
||||
`is`运算符在程序运行时检查表达式能否向下转化为指定的类型,如果可以在返回`ture`,如果不行,则返回`false`。
|
||||
|
||||
`as`运算符在程序编译时执行类型转化,且总是成功,比如进行向上转换(upcast)和桥接(bridging)。向上转换指把表达式转换成类型的超类的一个是实例而不使用中间的变量。以下表达式是等价的:
|
||||
```swift
|
||||
class SomeSuperType {}
|
||||
class SomeType: SomeSuperType {}
|
||||
class SomeChildType: SomeType {}
|
||||
let s = SomeType()
|
||||
|
||||
let x = s as SomeSuperType // known to succeed; type is SomeSuperType
|
||||
let y = s as Int // known to fail; compile-time error
|
||||
let z = s as SomeChildType // might fail at runtime; type is SomeChildType?
|
||||
func f(any: Any) { print("Function for Any") }
|
||||
func f(int: Int) { print("Function for Int") }
|
||||
let x = 10
|
||||
f(x)
|
||||
// prints "Function for Int"
|
||||
|
||||
let y: Any = x
|
||||
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
|
||||
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
|
||||
```
|
||||
`a!`操作符表示强制转换,其返回指定的类型,而不是可选的类型。如果转换失败,则会出现运行时错误。表达式`x as T` 效果等同于`(x as? T)!`。
|
||||
|
||||
关于类型转换的更多内容和例子,请参见: Type Casting.
|
||||
|
||||
> 类型转换运算符(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>
|
||||
## 主表达式(Primary Expressions)
|
||||
@ -211,13 +152,13 @@ let y2: SomeType = x // Type information from an annotation
|
||||
|
||||
> 主表达式语法
|
||||
> *主表达式* → [*标识符*](LexicalStructure.html#identifier) [*泛型参数子句*](GenericParametersAndArguments.html#generic_argument_clause) _可选_
|
||||
> *主表达式* → [*字面量表达式*](..\chapter3\04_Expressions.html#literal_expression)
|
||||
> *主表达式* → [*self表达式*](..\chapter3\04_Expressions.html#self_expression)
|
||||
> *主表达式* → [*超类表达式*](..\chapter3\04_Expressions.html#superclass_expression)
|
||||
> *主表达式* → [*闭包表达式*](..\chapter3\04_Expressions.html#closure_expression)
|
||||
> *主表达式* → [*圆括号表达式*](..\chapter3\04_Expressions.html#parenthesized_expression)
|
||||
> *主表达式* → [*隐式成员表达式*](..\chapter3\04_Expressions.html#implicit_member_expression)
|
||||
> *主表达式* → [*通配符表达式*](..\chapter3\04_Expressions.html#wildcard_expression)
|
||||
> *主表达式* → [*字符型表达式*](../chapter3/04_Expressions.html#literal_expression)
|
||||
> *主表达式* → [*self表达式*](../chapter3/04_Expressions.html#self_expression)
|
||||
> *主表达式* → [*超类表达式*](../chapter3/04_Expressions.html#superclass_expression)
|
||||
> *主表达式* → [*闭包表达式*](../chapter3/04_Expressions.html#closure_expression)
|
||||
> *主表达式* → [*圆括号表达式*](../chapter3/04_Expressions.html#parenthesized_expression)
|
||||
> *主表达式* → [*隐式成员表达式*](../chapter3/04_Expressions.html#implicit_member_expression)
|
||||
> *主表达式* → [*通配符表达式*](../chapter3/04_Expressions.html#wildcard_expression)
|
||||
|
||||
### 字符型表达式(Literal Expression)
|
||||
|
||||
@ -225,35 +166,53 @@ let y2: SomeType = x // Type information from an annotation
|
||||
|
||||
字符(Literal) | 类型(Type) | 值(Value)
|
||||
------------- | ---------- | ----------
|
||||
\__FILE__ | String | 所在的文件名
|
||||
\__LINE__ | Int | 所在的行数
|
||||
\__COLUMN__ | Int | 所在的列数
|
||||
\__FUNCTION__ | String | 所在的function 的名字
|
||||
/__FILE__ | String | 所在的文件名
|
||||
/__LINE__ | Int | 所在的行数
|
||||
/__COLUMN__ | Int | 所在的列数
|
||||
/__FUNCTION__ | String | 所在的function 的名字
|
||||
|
||||
在某个函数(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`, `...`]
|
||||
|
||||
数组中的最后一个表达式可以紧跟一个逗号(','). []表示空数组 。 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)的集合,它的形式是:
|
||||
|
||||
> [`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)
|
||||
> *字面量表达式* → [*数组字面量*](..\chapter3\04_Expressions.html#array_literal) | [*字典字面量*](..\chapter3\04_Expressions.html#dictionary_literal)
|
||||
> *字面量表达式* → [*数组字面量*](../chapter3/04_Expressions.html#array_literal) | [*字典字面量*](../chapter3/04_Expressions.html#dictionary_literal)
|
||||
> *字面量表达式* → **__FILE__** | **__LINE__** | **__COLUMN__** | **__FUNCTION__**
|
||||
> *数组字面量* → **[** [*数组字面量项列表*](..\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#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#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#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)
|
||||
|
||||
### self表达式(Self Expression)
|
||||
|
||||
@ -262,8 +221,8 @@ self表达式是对 当前type 或者当前instance的引用。它的形式如
|
||||
> self
|
||||
> self.`member name`
|
||||
> self[`subscript index`]
|
||||
> self(`initializer arguments`)
|
||||
> self.init(`initializer arguments`)
|
||||
> self(`initializer arguments`)
|
||||
> self.init(`initializer arguments`)
|
||||
|
||||
如果在 initializer, subscript, instance method中,self等同于当前type的instance. 在一个静态方法(static method), 类方法(class method)中, self等同于当前的type.
|
||||
|
||||
@ -273,7 +232,7 @@ self表达式是对 当前type 或者当前instance的引用。它的形式如
|
||||
```swift
|
||||
class SomeClass {
|
||||
var greeting: String
|
||||
init(greeting: String) {
|
||||
init(greeting: String) {
|
||||
self.greeting = greeting
|
||||
}
|
||||
}
|
||||
@ -284,8 +243,8 @@ class SomeClass {
|
||||
```swift
|
||||
struct Point {
|
||||
var x = 0.0, y = 0.0
|
||||
mutating func moveByX(deltaX: Double, y deltaY: Double) {
|
||||
self = Point(x: x + deltaX, y: y + deltaY)
|
||||
mutating func moveByX(deltaX: Double, y deltaY: Double) {
|
||||
self = Point(x: x + deltaX, y: y + deltaY)
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -293,7 +252,7 @@ struct Point {
|
||||
> Self 表达式语法
|
||||
> *self表达式* → **self**
|
||||
> *self表达式* → **self** **.** [*标识符*](LexicalStructure.html#identifier)
|
||||
> *self表达式* → **self** **[** [*表达式*](..\chapter3\04_Expressions.html#expression) **]**
|
||||
> *self表达式* → **self** **[** [*表达式*](../chapter3/04_Expressions.html#expression) **]**
|
||||
> *self表达式* → **self** **.** **init**
|
||||
|
||||
### 超类表达式(Superclass Expression)
|
||||
@ -302,16 +261,16 @@ struct Point {
|
||||
|
||||
> super.`member name`
|
||||
> super[`subscript index`]
|
||||
> super.init(`initializer arguments`)
|
||||
> super.init(`initializer arguments`)
|
||||
|
||||
形式1 用来访问超类的某个成员(member). 形式2 用来访问该超类的 subscript 实现。 形式3 用来访问该超类的 initializer.
|
||||
|
||||
子类(subclass)可以通过超类(superclass)表达式在它们的 member, subscripting 和 initializers 中来利用它们超类中的某些实现(既有的方法或者逻辑)。
|
||||
|
||||
> 超类(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** **[** [*表达式*](..\chapter3\04_Expressions.html#expression) **]**
|
||||
> *超类下标表达式* → **super** **[** [*表达式*](../chapter3/04_Expressions.html#expression) **]**
|
||||
> *超类构造器表达式* → **super** **.** **init**
|
||||
|
||||
### 闭包表达式(Closure Expression)
|
||||
@ -319,7 +278,7 @@ struct Point {
|
||||
闭包(closure) 表达式可以建立一个闭包(在其他语言中也叫 lambda, 或者 匿名函数(anonymous function)). 跟函数(function)的声明一样, 闭包(closure)包含了可执行的代码(跟方法主体(statement)类似) 以及接收(capture)的参数。 它的形式如下:
|
||||
|
||||
```swift
|
||||
{ (parameters) -> return type in
|
||||
{ (parameters) -> return type in
|
||||
statements
|
||||
}
|
||||
```
|
||||
@ -336,12 +295,12 @@ struct Point {
|
||||
|
||||
```swift
|
||||
myFunction {
|
||||
(x: Int, y: Int) -> Int in
|
||||
(x: Int, y: Int) -> Int in
|
||||
return x + y
|
||||
}
|
||||
|
||||
myFunction {
|
||||
(x, y) in
|
||||
(x, y) in
|
||||
return x + y
|
||||
}
|
||||
|
||||
@ -357,28 +316,28 @@ myFunction { $0 + $1 }
|
||||
在闭包的参数列表( capture list)中, 参数可以声明为 'weak' 或者 'unowned' .
|
||||
|
||||
```swift
|
||||
myFunction { print(self.title) } // strong capture
|
||||
myFunction { [weak self] in print(self!.title) } // weak capture
|
||||
myFunction { [unowned self] in print(self.title) } // unowned capture
|
||||
myFunction { print(self.title) } // strong capture
|
||||
myFunction { [weak self] in print(self!.title) } // weak capture
|
||||
myFunction { [unowned self] in print(self.title) } // unowned capture
|
||||
```
|
||||
|
||||
在参数列表中,也可以使用任意表达式来赋值. 该表达式会在 闭包被执行时赋值,然后按照不同的力度来获取(这句话请慎重理解)。(captured with the specified strength. ) 例如:
|
||||
|
||||
```swift
|
||||
// Weak capture of "self.parent" as "parent"
|
||||
myFunction { [weak parent = self.parent] in print(parent!.title) }
|
||||
myFunction { [weak parent = self.parent] in print(parent!.title) }
|
||||
```
|
||||
|
||||
关于闭包表达式的更多信息和例子,请参见: Closure Expressions.
|
||||
|
||||
> 闭包表达式语法
|
||||
> *闭包表达式* → **{** [*闭包签名(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)* → [*标识符列表*](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) [*标识符列表*](LexicalStructure.html#identifier_list) [*函数结果*](..\chapter3\05_Declarations.html#function_result) _可选_ **in**
|
||||
> *闭包签名(Signational)* → [*捕获(Capature)列表*](..\chapter3\04_Expressions.html#capture_list) **in**
|
||||
> *捕获(Capature)列表* → **[** [*捕获(Capature)说明符*](..\chapter3\04_Expressions.html#capture_specifier) [*表达式*](..\chapter3\04_Expressions.html#expression) **]**
|
||||
> *闭包表达式* → **{** [*闭包签名(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)* → [*标识符列表*](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) [*标识符列表*](LexicalStructure.html#identifier_list) [*函数结果*](../chapter3/05_Declarations.html#function_result) _可选_ **in**
|
||||
> *闭包签名(Signational)* → [*捕获(Capature)列表*](../chapter3/04_Expressions.html#capture_list) **in**
|
||||
> *捕获(Capature)列表* → **[** [*捕获(Capature)说明符*](../chapter3/04_Expressions.html#capture_specifier) [*表达式*](../chapter3/04_Expressions.html#expression) **]**
|
||||
> *捕获(Capature)说明符* → **weak** | **unowned** | **unowned(safe)** | **unowned(unsafe)**
|
||||
|
||||
### 隐式成员表达式(Implicit Member Expression)
|
||||
@ -395,7 +354,7 @@ x = .AnotherValue
|
||||
```
|
||||
|
||||
> 隐式成员表达式语法
|
||||
> *隐式成员表达式* → **.** [*标识符*](..\chapter3\02_Lexical_Structure.html#identifier)
|
||||
> *隐式成员表达式* → **.** [*标识符*](../chapter3/02_Lexical_Structure.html#identifier)
|
||||
|
||||
### 圆括号表达式(Parenthesized Expression)
|
||||
|
||||
@ -406,16 +365,16 @@ x = .AnotherValue
|
||||
圆括号表达式用来建立tuples , 然后把它做为参数传递给 function. 如果某个圆括号表达式中只有一个 子表达式,那么它的type就是 子表达式的type。例如: (1)的 type是Int, 而不是(Int)
|
||||
|
||||
> 圆括号表达式(Parenthesized Expression)语法
|
||||
> *圆括号表达式* → **(** [*表达式元素列表*](..\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_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)
|
||||
|
||||
### 通配符表达式(Wildcard Expression)
|
||||
|
||||
通配符表达式用来忽略传递进来的某个参数。例如:下面的代码中,10被传递给x, 20被忽略(译注:好奇葩的语法。。。)
|
||||
|
||||
```swift
|
||||
(x, _) = (10, 20)
|
||||
(x, _) = (10, 20)
|
||||
// x is 10, 20 is ignored
|
||||
```
|
||||
|
||||
@ -435,77 +394,91 @@ Swift 标准库提供了下列后缀表达式:
|
||||
对于这些操作符的使用,请参见: Basic Operators and Advanced Operators
|
||||
|
||||
> 后置表达式语法
|
||||
> *后置表达式* → [*主表达式*](..\chapter3\04_Expressions.html#primary_expression)
|
||||
> *后置表达式* → [*后置表达式*](..\chapter3\04_Expressions.html#postfix_expression) [*后置运算符*](..\chapter3\02_Lexical_Structure.html#postfix_operator)
|
||||
> *后置表达式* → [*函数调用表达式*](..\chapter3\04_Expressions.html#function_call_expression)
|
||||
> *后置表达式* → [*构造器表达式*](..\chapter3\04_Expressions.html#initializer_expression)
|
||||
> *后置表达式* → [*显示成员表达式*](..\chapter3\04_Expressions.html#explicit_member_expression)
|
||||
> *后置表达式* → [*后置self表达式*](..\chapter3\04_Expressions.html#postfix_self_expression)
|
||||
> *后置表达式* → [*动态类型表达式*](..\chapter3\04_Expressions.html#dynamic_type_expression)
|
||||
> *后置表达式* → [*下标表达式*](..\chapter3\04_Expressions.html#subscript_expression)
|
||||
> *后置表达式* → [*强制取值(Forced Value)表达式*](..\chapter3\04_Expressions.html#forced_value_expression)
|
||||
> *后置表达式* → [*可选链(Optional Chaining)表达式*](..\chapter3\04_Expressions.html#optional_chaining_expression)
|
||||
> *后置表达式* → [*主表达式*](../chapter3/04_Expressions.html#primary_expression)
|
||||
> *后置表达式* → [*后置表达式*](../chapter3/04_Expressions.html#postfix_expression) [*后置运算符*](../chapter3/02_Lexical_Structure.html#postfix_operator)
|
||||
> *后置表达式* → [*函数调用表达式*](../chapter3/04_Expressions.html#function_call_expression)
|
||||
> *后置表达式* → [*构造器表达式*](../chapter3/04_Expressions.html#initializer_expression)
|
||||
> *后置表达式* → [*显示成员表达式*](../chapter3/04_Expressions.html#explicit_member_expression)
|
||||
> *后置表达式* → [*后置self表达式*](../chapter3/04_Expressions.html#postfix_self_expression)
|
||||
> *后置表达式* → [*动态类型表达式*](../chapter3/04_Expressions.html#dynamic_type_expression)
|
||||
> *后置表达式* → [*下标表达式*](../chapter3/04_Expressions.html#subscript_expression)
|
||||
> *后置表达式* → [*强制取值(Forced Value)表达式*](../chapter3/04_Expressions.html#forced_value_expression)
|
||||
> *后置表达式* → [*可选链(Optional Chaining)表达式*](../chapter3/04_Expressions.html#optional_chaining_expression)
|
||||
|
||||
### 函数调用表达式(Function Call Expression)
|
||||
|
||||
函数调用表达式由函数名和参数列表组成。它的形式如下:
|
||||
|
||||
> `function name`(`argument value 1`, `argument value 2`)
|
||||
|
||||
The function name can be any expression whose value is of a function type.
|
||||
(不用翻译了, 太罗嗦)
|
||||
> `function name`(`argument value 1`, `argument value 2`)
|
||||
|
||||
如果该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) , 该闭包会被目标函数理解并执行。它具有如下两种写法:
|
||||
|
||||
```swift
|
||||
// someFunction takes an integer and a closure as its arguments
|
||||
someFunction(x, {$0 == 13})
|
||||
someFunction(x) {$0 == 13}
|
||||
someFunction(x, {$0 == 13}+
|
||||
someFunction(x) {$0 == 13}
|
||||
```
|
||||
|
||||
如果闭包是该函数的唯一参数,那么圆括号可以省略。
|
||||
|
||||
```swift
|
||||
// someFunction takes a closure as its only argument
|
||||
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) _可选_ [*后置闭包(Trailing Closure)*](..\chapter3\04_Expressions.html#trailing_closure)
|
||||
> *后置闭包(Trailing Closure)* → [*闭包表达式*](..\chapter3\04_Expressions.html#closure_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)
|
||||
> *后置闭包(Trailing Closure)* → [*闭包表达式*](../chapter3/04_Expressions.html#closure_expression)
|
||||
|
||||
### 初始化函数表达式(Initializer Expression)
|
||||
|
||||
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
|
||||
class SomeSubClass: SomeSuperClass {
|
||||
init() {
|
||||
init() {
|
||||
// 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)
|
||||
|
||||
@ -519,24 +492,23 @@ class SomeSubClass: SomeSuperClass {
|
||||
class SomeClass {
|
||||
var someProperty = 42
|
||||
}
|
||||
let c = SomeClass()
|
||||
let c = SomeClass()
|
||||
let y = c.someProperty // Member access
|
||||
```
|
||||
|
||||
对于tuple, 要根据它们出现的顺序(0, 1, 2...)来使用:
|
||||
|
||||
```swift
|
||||
var t = (10, 20, 30)
|
||||
var t = (10, 20, 30)
|
||||
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#identifier) [*泛型参数子句*](GenericParametersAndArguments.html#generic_argument_clause) _可选_
|
||||
> *显示成员表达式* → [*后置表达式*](../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) _可选_
|
||||
|
||||
### 后缀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。
|
||||
|
||||
> 后置Self 表达式语法
|
||||
> *后置self表达式* → [*后置表达式*](..\chapter3\04_Expressions.html#postfix_expression) **.** **self**
|
||||
> *后置self表达式* → [*后置表达式*](../chapter3/04_Expressions.html#postfix_expression) **.** **self**
|
||||
|
||||
### dynamic表达式(Dynamic Type Expression)
|
||||
|
||||
@ -564,25 +536,25 @@ dynamicType 表达式由 某个表达式 + '.dynamicType' 组成。
|
||||
|
||||
```swift
|
||||
class SomeBaseClass {
|
||||
class func printClassName() {
|
||||
println("SomeBaseClass")
|
||||
class func printClassName() {
|
||||
println("SomeBaseClass")
|
||||
}
|
||||
}
|
||||
class SomeSubClass: SomeBaseClass {
|
||||
override class func printClassName() {
|
||||
println("SomeSubClass")
|
||||
override class func printClassName() {
|
||||
println("SomeSubClass")
|
||||
}
|
||||
}
|
||||
let someInstance: SomeBaseClass = SomeSubClass()
|
||||
let someInstance: SomeBaseClass = SomeSubClass()
|
||||
|
||||
// someInstance is of type SomeBaseClass at compile time, but
|
||||
// someInstance is of type SomeSubClass at runtime
|
||||
someInstance.dynamicType.printClassName()
|
||||
someInstance.dynamicType.printClassName()
|
||||
// prints "SomeSubClass"
|
||||
```
|
||||
|
||||
> 动态类型表达式语法
|
||||
> *动态类型表达式* → [*后置表达式*](..\chapter3\04_Expressions.html#postfix_expression) **.** **dynamicType**
|
||||
> *动态类型表达式* → [*后置表达式*](../chapter3/04_Expressions.html#postfix_expression) **.** **dynamicType**
|
||||
|
||||
### 下标脚本表达式(Subscript Expression)
|
||||
|
||||
@ -595,7 +567,7 @@ someInstance.dynamicType.printClassName()
|
||||
关于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)
|
||||
|
||||
@ -604,9 +576,19 @@ someInstance.dynamicType.printClassName()
|
||||
> `expression`!
|
||||
|
||||
如果该表达式的值不是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)表达式* → [*后置表达式*](..\chapter3\04_Expressions.html#postfix_expression) **!**
|
||||
> *强制取值(Forced Value)表达式* → [*后置表达式*](../chapter3/04_Expressions.html#postfix_expression) **!**
|
||||
|
||||
### 可选链表达式(Optional-Chaining Expression)
|
||||
|
||||
@ -622,16 +604,32 @@ someInstance.dynamicType.printClassName()
|
||||
|
||||
```swift
|
||||
var c: SomeClass?
|
||||
var result: Bool? = c?.property.performAction()
|
||||
var result: Bool? = c?.property.performAction()
|
||||
```
|
||||
|
||||
如果不使用可选链表达式,那么 上面例子的代码跟下面例子等价:
|
||||
|
||||
```swift
|
||||
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) **?**
|
||||
|
||||
@ -1,15 +1,19 @@
|
||||
> 翻译:[Hawstein](https://github.com/Hawstein)
|
||||
# 特性(Attributes)
|
||||
-----------------
|
||||
|
||||
> 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)
|
||||
- [类型特性](#type_attributes)
|
||||
|
||||
特性提供了关于声明和类型的更多信息。在Swift中有两类特性,用于修饰声明的以及用于修饰类型的。例如,`required`特性,当应用于一个类的指定或便利初始化器声明时,表明它的每个子类都必须实现那个初始化器。再比如`noreturn`特性,当应用于函数或方法类型时,表明该函数或方法不会返回到它的调用者。
|
||||
特性提供了关于声明和类型的更多信息。在Swift中有两类特性,用于修饰声明的以及用于修饰类型的。
|
||||
|
||||
通过以下方式指定一个特性:符号`@`后面跟特性名,如果包含参数,则把参数带上:
|
||||
|
||||
@ -23,30 +27,44 @@
|
||||
|
||||
声明特性只能应用于声明。然而,你也可以将`noreturn`特性应用于函数或方法类型。
|
||||
|
||||
`availability`
|
||||
`autoclosure`
|
||||
|
||||
这个特性通过把表达式自动封装成无参数的闭包来延迟表达式的计算。它可以声明返回表达式自身类型的没有参数的方法类型,也可以用于函数参数的声明。含有`autoclosure`特性的声明同时也具有`noescape`的特性,除非传递可选参数`escaping`.关于怎样使用`autoclosure`特性的例子,参见[函数类型](./03_Types.html#function_type).
|
||||
|
||||
将`availability`特性用于声明时,将表示该声明的生命周期会依赖于特定的平台和操作系统版本。
|
||||
`availability`特性总会与参数列表一同出现,该参数列表至少有两个参数,参数之间由逗号分隔。第一个参数由以下这些平台名字中的一个起头:iOS, iOSApplicationExtension, OSX, or OSXApplicationExtension。当然,你也可以用一个星号(*)来表示,该声明在上面提到的所有平台上都是有效的。剩下的参数,可以以任何顺序出现,并且可以附加关于声明生命周期的附加信息,包括重要的里程碑。
|
||||
`available`
|
||||
|
||||
- `unavailable`参数表示该声明在特定的平台上是无效的
|
||||
将`available`特性用于声明时,意味着该声明的生命周期会依赖于特定的平台和操作系统版本。
|
||||
|
||||
`available`特性经常与参数列表一同出现,该参数列表至少有两个参数,参数之间由逗号分隔。这些参数由以下这些平台名字中的一个起头:
|
||||
|
||||
- `introduced`参数表示:特定的平台上,该声明被使用的第一个版本。格式如下:<p>`introduced=version number`<p>这里的`version number`由一个正的十进制整数或浮点数构成。
|
||||
- `iOS`
|
||||
- `iOSApplicationExtension`
|
||||
- `OSX`
|
||||
- `OSXApplicationExtension`
|
||||
- `watchOS`
|
||||
|
||||
- `deprecated`参数表示:特定的平台上,该声明被建议弃用的第一个版本。格式如下:
|
||||
当然,你也可以用一个星号(*)来表示,该声明在上面提到的所有平台上都是有效的。
|
||||
|
||||
剩下的参数,可以以任何顺序出现,并且可以添加关于声明生命周期的附加信息,包括重要的里程碑。
|
||||
|
||||
- `unavailable`参数表示:该声明在特定的平台上是无效的
|
||||
|
||||
- `introduced`参数表示:该声明第一次被引入时所在平台的版本。格式如下:
|
||||
<p>`introduced=version number`<p>这里的`version number`由一个正的十进制整数或浮点数构成。
|
||||
|
||||
- `deprecated`参数表示:该声明第一次被建议弃用时所在平台的版本。格式如下:
|
||||
<p>`deprecated=version number`<p>这里的`version number`由一个正的十进制整数或浮点数构成。
|
||||
|
||||
- `obsoleted`参数表示:特定的平台上,该声明被弃用的第一个版本。格式如下:
|
||||
<p>`deprecated=version number`<p>这里的`version number`由一个正的十进制整数或浮点数构成。
|
||||
- `obsoleted`参数表示:该声明第一次被弃用时所在平台的版本。当一个声明被弃用时,它就从此平台中被移除,不能再被使用。格式如下:
|
||||
<p>`obsoleted=version number`<p>这里的`version number`由一个正的十进制整数或浮点数构成。
|
||||
|
||||
- `message`参数用来提供文本信息,并在因使用建议弃用或者被弃用的声明而遇到警告或错误时,由编译器抛出。格式如下:
|
||||
- `message`参数用来提供文本信息。当使用建议弃用或者被弃用的声明时,编译器会抛出错误或警告信息。格式如下:
|
||||
<p>`message=message`<p>这里的`message`由一个字符串文字构成。
|
||||
|
||||
- `renamed`参数用来提供文本信息,用以表示被重命名的声明的新名字。当使用这个重命名的声明遇到错误时,该新名字会被编译器显示出来。格式如下:
|
||||
- `renamed`参数用来提供文本信息,用以表示被重命名的声明的新名字。当使用这个重命名的声明遇到错误时,编译器会显示出该新名字。格式如下:
|
||||
<p>`renamed=new name`<p>这里的`new name`由一个字符串文字构成。
|
||||
|
||||
你可以将`renamed`参数和`unavailable`参数以及类型别名声明组合使用,以向用户表示:在你的代码中,一个声明已经被重命名。当一个声明的名字在一个框架或者库的不同发布版本间发生变化时,这会相当管用。
|
||||
你可以将`renamed`参数和`unavailable`参数以及类型别名声明组合使用,以向用户表示:在你的代码中,一个声明已经被重命名。当一个声明的名字在一个框架或者库的不同发布版本间发生变化时,这会相当有用。
|
||||
|
||||
```swift
|
||||
// First release
|
||||
@ -58,50 +76,34 @@ protocol MyRenamedProtocol {
|
||||
// protocol definition
|
||||
}
|
||||
|
||||
@availability(*, unavailable, renamed="MyRenamedProtocol")
|
||||
@available(*, unavailable, renamed="MyRenamedProtocol")
|
||||
typealias MyProtocol = MyRenamedProtocol
|
||||
```
|
||||
|
||||
|
||||
你可以在一个单独的声明上使用多个`availability`特性,以详细说明该声明在不同平台上的有效性。编译器只有在当前的目标平台和`availability`特性中指定的平台匹配时,才会使用`availability`特性
|
||||
你可以在一个单独的声明上使用多个`available`特性,以详细说明该声明在不同平台上的有效性。编译器只有在当前的目标平台和`available`特性中指定的平台匹配时,才会使用`available`特性
|
||||
|
||||
`autoclosure`
|
||||
如果`available`特性除了平台名称参数外,只指定了一个`introduced `参数,那么可以使用以下简写语法代替:
|
||||
|
||||
这个属性通过把表达式自动封装成不带参数的闭包来延迟表达式的计算。这个属性使用在函数参数声明或者不带参数但是返回表达式类型的方法类型上。含有```autoclosure```属性的声明同时也具有```noescape```的特性,除非传递一个可选的参数属性```escaping```,请看[函数类型](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Types.html#//apple_ref/doc/uid/TP40014097-CH31-ID449)。
|
||||
@available(`platform name` `version number`, *)
|
||||
|
||||
`noescape`
|
||||
|
||||
在函数或者方法声明上使用该属性表示参数将不会被存储用作后续的计算,其用来确保不会超出函数调用的声明周期。使用```noescape```声明属性的函数类型不需要显式的使用```self```,对于其属性或者方法来说。
|
||||
|
||||
`noreturn`
|
||||
|
||||
该特性用于修饰函数或方法声明,表明该函数或方法的对应类型,`T`,是`@noreturn T`。你可以用这个特性修饰函数或方法的类型,这样一来,函数或方法就不会返回到它的调用者中去。
|
||||
|
||||
对于一个没有用`noreturn`特性标记的函数或方法,你可以将它重写(override)为用该特性标记的。相反,对于一个已经用`noreturn`特性标记的函数或方法,你则不可以将它重写为没使用该特性标记的。相同的规则试用于当你在一个comforming类型中实现一个协议方法时。
|
||||
|
||||
`NSApplicationMain`
|
||||
在类上使用该属性表示该类是应用程序委托类,使用该属性与调用```NSApplicationMain```函数并且把该类的名字作为委托类的名字传递给函数的效果相同。
|
||||
|
||||
如果你不想使用这个属性,可以提供一个```main.swift```文件,并且提供一个```main```函数去调用```NSApplicationMain```函数。比如,如果你的应用程序使用一个派生于```NSApplication```的自定义子类作为主要类,你可以调用```NSApplicationMain```函数而不是使用该属性。
|
||||
|
||||
|
||||
`NSCopying`
|
||||
|
||||
该特性用于修饰一个类的存储型变量属性。该特性将使属性的setter与属性值的一个副本合成,由`copyWithZone`方法返回,而不是属性本身的值。该属性的类型必需遵循`NSCopying`协议。
|
||||
|
||||
`NSCopying`特性的行为与Objective-C中的`copy`特性相似。
|
||||
|
||||
`NSManaged`
|
||||
|
||||
该特性用于修饰`NSManagedObject`子类中的存储型变量属性,表明属性的存储和实现由Core Data在运行时基于相关实体描述动态提供。
|
||||
`available`特性的简写语法可以简明地表达出多个平台的可用性。尽管这两种形式在功能上是相同的,但请尽可能地使用简明语法形式。
|
||||
```swift
|
||||
@available(iOS 8.0, OSX 10.10, *)
|
||||
class MyClass {
|
||||
// class definition
|
||||
}
|
||||
```
|
||||
|
||||
`objc`
|
||||
|
||||
该特性用于修饰任意可以在Objective-C中表示的声明,比如,非嵌套类,协议,类和协议中的属性和方法(包含getter和setter),初始化器,析构器,以及下标。`objc`特性告诉编译器该声明可以在Objective-C代码中使用。
|
||||
该特性用于修饰任何可以在Objective-C中表示的声明。比如,非嵌套类、协议、非泛型枚举(仅限整型值类型)、类和协议的属性和方法(包括`getter`和`setter`)、构造器、析构器以及下标。`objc`特性告诉编译器这个声明可以在Objective-C代码中使用。
|
||||
|
||||
如果你将`objc`特性应用于一个类或协议,它也会隐式地应用于那个类或协议的成员。对于标记了`objc`特性的类,编译器会隐式地为它的子类添加`objc`特性。标记了`objc`特性的协议不能继承自没有标记`objc`的协议。
|
||||
标有`objc`特性的类必须继承自Objective-C中定义的类。如果你将`objc`特性应用于一个类或协议,它也会隐式地应用于那个类的成员或协议。对于标记了`objc`特性的类,编译器会隐式地为它的子类添加`objc`特性。标记了`objc`特性的协议不能继承没有标记`objc`的协议。
|
||||
|
||||
`objc`特性有一个可选的参数,由标记符组成。当你想把`objc`所修饰的实体以一个不同的名字暴露给Objective-C,你就可以使用这个特性参数。你可以使用这个参数来命名类,协议,方法,getters,setters,以及初始化器。下面的例子把`ExampleClass`中`enabled`属性的getter暴露给Objective-C,名字是`isEnabled`,而不是它原来的属性名。
|
||||
如果你将`objc`特性应用于枚举,每一个枚举的`case`都会以枚举名称和`case`名称组合的方式暴露在Objective-C代码中。例如:一个名为`Venus`的`case`在`Planet`枚举中,这个`case`暴露在Objective-C代码中时叫做`PlanetVenus`。
|
||||
|
||||
`objc`特性有一个可选的参数,由标记符组成。当你想把`objc`所修饰的实体以一个不同的名字暴露给Objective-C时,你就可以使用这个特性参数。你可以使用这个参数来命名类,协议,方法,getters,setters,以及构造器。下面的例子把`ExampleClass`中`enabled`属性的getter暴露给Objective-C,名字是`isEnabled`,而不是它原来的属性名。
|
||||
|
||||
```swift
|
||||
@objc
|
||||
@ -114,17 +116,64 @@ class ExampleClass {
|
||||
}
|
||||
```
|
||||
|
||||
`optional`
|
||||
`noescape`
|
||||
|
||||
用该特性修饰协议的属性,方法或下标成员,表示实现这些成员并不需要一致性类型(conforming type)。
|
||||
在函数或者方法声明上使用该特性,它表示参数将不会被存储用作后续的计算,其用来确保不会超出函数调用的生命周期。对于其属性或方法来说,使用`noescape`声明属性的函数类型不需要显式的使用`self.`。
|
||||
|
||||
你只能用`optional`特性修饰那些标记了`objc`特性的协议。因此,只有类类型可以adopt和comform to那些包含可选成员需求的协议。更多关于如何使用`optional`特性以及如何访问可选协议成员的指导,例如,当你不确定一个conforming类型是否实现了它们,请见:[可选协议需求]()。
|
||||
`nonobjc`
|
||||
|
||||
`required`
|
||||
该特性用于方法、属性、下标、或构造器的声明,这些声明本是可以在Objective-C代码中表示的。使用`nonobjc`特性告诉编译器这个声明不能在Objective-C代码中使用。
|
||||
|
||||
用该特性修饰一个类的指定或便利初始化器,表示该类的所有子类都必需实现该初始化器。
|
||||
可以使用`nonobjc`特性解决标有`objc`的类中桥接方法的循环问题,该特性还允许标有`objc`的类的构造器和方法进行重载(overload)。
|
||||
|
||||
加了该特性的指定初始化器必需显式地实现,而便利初始化器既可显式地实现,也可以在子类实现了超类所有指定初始化器后继承而来(或者当子类使用便利初始化器重写了指定初始化器)。
|
||||
标有`nonobjc`特性的方法不能重写(override)一个标有`objc`特性的方法。然而,标有`objc`特性的方法可以重写标有`nonobjc`特性的方法。同样,标有`nonobjc`特性的方法不能满足一个需要标有`@objc`特性的方法的协议。
|
||||
|
||||
`noreturn`
|
||||
|
||||
该特性用于修饰函数或方法声明,表明该函数或方法的对应类型,`T`,是`@noreturn T`。你可以用这个特性修饰函数或方法的类型,这样一来,函数或方法就不会返回到它的调用者中去。
|
||||
|
||||
对于一个没有用`noreturn`特性标记的函数或方法,你可以将它重写为用该特性标记的。相反,对于一个已经用`noreturn`特性标记的函数或方法,你则不可以将它重写为没使用该特性标记的。当你在一个comforming类型中实现一个协议方法时,该规则同样适用。
|
||||
|
||||
`NSApplicationMain`
|
||||
|
||||
在类上使用该特性表示该类是应用程序委托类,使用该特性与调用`NSApplicationMain(_:_:)`函数并且把该类的名字作为委托类的名字传递给函数的效果相同。
|
||||
|
||||
如果你不想使用这个特性,可以提供一个`main.swift`文件,并且提供一个`main`函数去调用`NSApplicationMain(_:_:)`函数。比如,如果你的应用程序使用一个派生于`NSApplication`的自定义子类作为主要类,你可以调用`NSApplicationMain`函数而不是使用该特性。
|
||||
|
||||
`NSCopying`
|
||||
|
||||
该特性用于修饰一个类的存储型变量属性。该特性将使属性的setter与属性值的一个副本合成,这个值由`copyWithZone(_:)`方法返回,而不是属性本身的值。该属性的类型必需遵循`NSCopying`协议。
|
||||
|
||||
`NSCopying`特性的原理与Objective-C中的`copy`特性相似。
|
||||
|
||||
`NSManaged`
|
||||
|
||||
该特性用于修饰`NSManagedObject`子类中的存储型变量属性,表明属性的存储和实现由Core Data在运行时基于相关实体描述动态提供。
|
||||
|
||||
`testable`
|
||||
|
||||
该特性用于`import`声明可以测试的编译模块,它能访问任何标有`internal`权限标识符的实体,这和将它声明为`public`权限标识符有同样的效果。
|
||||
|
||||
`UIApplicationMain`
|
||||
|
||||
在类上使用该特性表示该类是应用程序委托类,使用该特性与调用`UIApplicationMain(_:_:)`函数并且把该类的名字作为委托类的名字传递给函数的效果相同。
|
||||
|
||||
如果你不想使用这个特性,可以提供一个`main.swift`文件,并且提供一个`main`函数去调用`UIApplicationMain(_:_:)`函数。比如,如果你的应用程序使用一个派生于`UIApplication`的自定义子类作为主要类,你可以调用`UIApplicationMain`函数而不是使用该特性。
|
||||
|
||||
`warn_unused_result`
|
||||
|
||||
该特性应用于方法或函数声明,当方法或函数被调用,但其结果未被使用时,该特性会让编译器会产生警告。
|
||||
|
||||
你可以使用这个特性提供一个警告信息,这个警告信息是关于不正确地使用未变异的方法的,这个方法也有一个对应的变异方法。
|
||||
|
||||
`warn_unused_result`有下面两个可选的参数。
|
||||
|
||||
- `message`参数用来提供警告信息,并在因当方法或函数被调用,但其结果未被使用时,显示警告信息。格式如下:
|
||||
<p>`message=message`<p>这里的`message`由一个字符串文字构成。
|
||||
|
||||
- `mutable_variant`参数用于提供变异方法的名称,如果未变异方法以一个可变的值被调用而且其结果并未被使用时,应该使用此变异方法。格式如下(方法名有字符串构成):<p>`mutable_variant=method name`<p>
|
||||
|
||||
比如,Swift标准库提供了变异方法`sortInPlace()`和未变异方法`sort()`集合,它们的元素生成器符合`Comparable`协议。如果你调用了`sort()`方法,而没有使用它的结果,很有可能,你打算使用变异方法`sortInPlace()`替代。
|
||||
|
||||
### Interface Builder使用的声明特性
|
||||
|
||||
@ -137,22 +186,36 @@ Interface Builder特性是Interface Builder用来与Xcode同步的声明特性
|
||||
|
||||
类型特性只能用于修饰类型。然而,你也可以用`noreturn`特性去修饰函数或方法声明。
|
||||
|
||||
`auto_closure`
|
||||
`convention`
|
||||
|
||||
这个特性通过自动地将表达式封闭到一个无参数闭包中来延迟表达式的求值。使用该特性修饰无参的函数或方法类型,返回表达式的类型。一个如何使用`auto_closure`特性的例子,见[函数类型]()
|
||||
该特性用于函数的类型,它指出函数调用的约定。
|
||||
|
||||
`convention`特性有下面几个可选的参数。
|
||||
|
||||
- `swift`参数用于表明一个Swift函数引用。这是Swift中标准的函数值调用约定。
|
||||
|
||||
- `block`参数用于表明一个Objective-C兼容的块引用。函数值表示为一个块对象的引用,这是一个`id-`兼容的Objective-C对象,对象中嵌入了调用函数。调用函数使用C的调用约定。
|
||||
|
||||
- `c`参数用于表明一个C函数引用。函数值没有上下文,这个函数也使用C的调用约定。
|
||||
|
||||
使用C函数调用约定的函数也可用作使用Objective-C块调用约定的函数,同时使用Objective-C块调用约定的函数也可用作使用Swift函数调用约定的函数。然而,只有非泛型的全局函数和本地函数或者不使用任何本地变量的闭包可以被用作使用C函数调用约定的函数。
|
||||
|
||||
`noreturn`
|
||||
|
||||
该特性用于修饰函数或方法的类型,表明该函数或方法不会返回到它的调用者中去。你也可以用它标记函数或方法的声明,表示函数或方法的相应类型,`T`,是`@noreturn T`。
|
||||
|
||||
> 特性语法
|
||||
> *特性* → **@** [*特性名*](..\chapter3\06_Attributes.html#attribute_name) [*特性参数子句*](..\chapter3\06_Attributes.html#attribute_argument_clause) _可选_
|
||||
> *特性名* → [*标识符*](LexicalStructure.html#identifier)
|
||||
> *特性参数子句* → **(** [*平衡令牌列表*](..\chapter3\06_Attributes.html#balanced_tokens) _可选_ **)**
|
||||
> *特性(Attributes)列表* → [*特色*](..\chapter3\06_Attributes.html#attribute) [*特性(Attributes)列表*](..\chapter3\06_Attributes.html#attributes) _可选_
|
||||
> *平衡令牌列表* → [*平衡令牌*](..\chapter3\06_Attributes.html#balanced_token) [*平衡令牌列表*](..\chapter3\06_Attributes.html#balanced_tokens) _可选_
|
||||
> *平衡令牌* → **(** [*平衡令牌列表*](..\chapter3\06_Attributes.html#balanced_tokens) _可选_ **)**
|
||||
> *平衡令牌* → **[** [*平衡令牌列表*](..\chapter3\06_Attributes.html#balanced_tokens) _可选_ **]**
|
||||
> *平衡令牌* → **{** [*平衡令牌列表*](..\chapter3\06_Attributes.html#balanced_tokens) _可选_ **}**
|
||||
> 特性语法
|
||||
> *特性* → **@** [*特性名*](#attribute_name) [*特性参数子句*](#attribute_argument_clause) <sub>_可选_</sub>
|
||||
> *特性名* → [*标识符*](02_Lexical_Structure.html#identifiers)
|
||||
> *特性参数子句* → **(** [*平衡令牌列表*](#balanced_tokens) <sub>_可选_</sub> **)**
|
||||
> *特性(Attributes)列表* → [*特色*](#attribute) [*特性(Attributes)列表*](#attributes) <sub>_可选_</sub>
|
||||
> *平衡令牌列表* → [*平衡令牌*](#balanced_token) [*平衡令牌列表*](#balanced_tokens) <sub>_可选_</sub>
|
||||
> *平衡令牌* → **(** [*平衡令牌列表*](#balanced_tokens) <sub>_可选_</sub> **)**
|
||||
> *平衡令牌* → **[** [*平衡令牌列表*](#balanced_tokens) <sub>_可选_</sub> **]**
|
||||
> *平衡令牌* → **{** [*平衡令牌列表*](#balanced_tokens) <sub>_可选_</sub> **}**
|
||||
> *平衡令牌* → **任意标识符, 关键字, 字面量或运算符**
|
||||
> *平衡令牌* → **任意标点除了(, ), [, ], {, 或 }**
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,8 +1,12 @@
|
||||
> 翻译:[honghaoz](https://github.com/honghaoz)
|
||||
# 模式(Patterns)
|
||||
-----------------
|
||||
|
||||
> 1.0
|
||||
> 翻译:[honghaoz](https://github.com/honghaoz)
|
||||
> 校对:[numbbbbb](https://github.com/numbbbbb), [stanzhai](https://github.com/stanzhai)
|
||||
|
||||
# 模式(Patterns)
|
||||
-----------------
|
||||
> 2.0
|
||||
> 翻译+校对:[ray16897188](https://github.com/ray16897188)
|
||||
|
||||
本页内容包括:
|
||||
|
||||
@ -11,28 +15,32 @@
|
||||
- [值绑定模式(Value-Binding Pattern)](#value-binding_pattern)
|
||||
- [元组模式(Tuple Pattern)](#tuple_pattern)
|
||||
- [枚举用例模式(Enumeration Case Pattern)](#enumeration_case_pattern)
|
||||
- [类型转换模式(Type-Casting Patterns)](#type-casting_patterns)
|
||||
- [可选模式(Optional Pattern)](#optional_pattern)
|
||||
- [类型转换模式(Type-Casting Pattern)](#type-casting_pattern)
|
||||
- [表达式模式(Expression Pattern)](#expression_pattern)
|
||||
|
||||
模式(pattern)代表了单个值或者复合值的结构。例如,元组`(1, 2)`的结构是逗号分隔的,包含两个元素的列表。因为模式代表一种值的结构,而不是特定的某个值,你可以把模式和各种同类型的值匹配起来。比如,`(x, y)`可以匹配元组`(1, 2)`,以及任何含两个元素的元组。除了将模式与一个值匹配外,你可以从合成值中提取出部分或全部,然后分别把各个部分和一个常量或变量绑定起来。
|
||||
模式(pattern)代表了单个值或者复合值的结构。例如,元组`(1, 2)`的结构是逗号分隔的,包含两个元素的列表。因为模式代表一种值的结构,而不是特定的某个值,你可以把模式和各种同类型的值匹配起来。比如,`(x, y)`可以匹配元组`(1, 2)`,以及任何含两个元素的元组。除了将模式与一个值匹配外,你可以从复合值中提取出部分或全部,然后分别把各个部分和一个常量或变量绑定起来。
|
||||
|
||||
在Swift中,模式出现在变量和常量的声明(在它们的左侧),`for-in`语句和`switch`语句(在它们的case标签)中。尽管任何模式都可以出现在`switch`语句的case标签中,但在其他情况下,只有通配符模式(wildcard pattern),标识符模式(identifier pattern)和包含这两种模式的模式才能出现。
|
||||
swift语言中模式有2个基本的分类:一类能成功和任何值的类型相匹配,另一类在运行时(runtime)和某特定值匹配时可能会失败。
|
||||
|
||||
你可以为通配符模式(wildcard pattern),标识符模式(identifier pattern)和元组模式(tuple pattern)指定类型注释,用来限制这种模式只匹配某种类型的值。
|
||||
第一类模式用于解构简单变量,常量和可选绑定中的值。此类模式包括通配符模式(wildcard patterns),标识符模式(identifier patterns),以及任何包含了它们的值绑定模式(value binding patterns)或者元祖模式(tuple patterns)。你可以为这类模式指定一个类型标注(type annotation)从而限制它们只能匹配某种特定类型的值。
|
||||
|
||||
第二类模式用于全模式匹配,这种情况下你用来相比较的值在运行时可能还不存在。此类模式包括枚举用例模式(enumeration case patterns),可选模式(optional patterns),表达式模式(expression patterns)和类型转换模式(type-casting patterns)。你在`switch`语句的case标签中,`do`语句的`catch`从句中,或者在`if, while, guard`和`for-in`语句的case条件句中使用这类模式。
|
||||
|
||||
> 模式(Patterns) 语法
|
||||
> *模式* → [*通配符模式*](..\chapter3\07_Patterns.html#wildcard_pattern) [*类型注解*](..\chapter3\03_Types.html#type_annotation) _可选_
|
||||
> *模式* → [*标识符模式*](..\chapter3\07_Patterns.html#identifier_pattern) [*类型注解*](..\chapter3\03_Types.html#type_annotati(Value Binding)on) _可选_
|
||||
> *模式* → [*值绑定模式*](..\chapter3\07_Patterns.html#value_binding_pattern)
|
||||
> *模式* → [*元组模式*](..\chapter3\07_Patterns.html#tuple_pattern) [*类型注解*](..\chapter3\03_Types.html#type_annotation) _可选_
|
||||
> *模式* → [*enum-case-pattern*](..\chapter3\07_Patterns.html#enum_case_pattern)
|
||||
> *模式* → [*type-casting-pattern*](..\chapter3\07_Patterns.html#type_casting_pattern)
|
||||
> *模式* → [*表达式模式*](..\chapter3\07_Patterns.html#expression_pattern)
|
||||
> *模式* → [*通配符模式*](../chapter3/07_Patterns.html#wildcard_pattern) [*类型标注*](../chapter3/03_Types.html#type_annotation) _可选_
|
||||
> *模式* → [*标识符模式*](../chapter3/07_Patterns.html#identifier_pattern) [*类型标注*](../chapter3/03_Types.html#type_annotati(Value Binding)on) _可选_
|
||||
> *模式* → [*值绑定模式*](../chapter3/07_Patterns.html#value_binding_pattern)
|
||||
> *模式* → [*元组模式*](../chapter3/07_Patterns.html#tuple_pattern) [*类型标注*](../chapter3/03_Types.html#type_annotation) _可选_
|
||||
> *模式* → [*枚举用例模式*](../chapter3/07_Patterns.html#enum_case_pattern)
|
||||
> *模式* → [*可选模式*](../chapter3/07_Patterns.html#optional_pattern)
|
||||
> *模式* → [*类型转换模式*](../chapter3/07_Patterns.html#type_casting_pattern)
|
||||
> *模式* → [*表达式模式*](../chapter3/07_Patterns.html#expression_pattern)
|
||||
|
||||
<a name="wildcard_pattern"></a>
|
||||
## 通配符模式(Wildcard Pattern)
|
||||
|
||||
通配符模式匹配并忽略任何值,包含一个下划线(_)。当你不关心被匹配的值时,可以使用此模式。例如,下面这段代码进行了`1...3`的循环,并忽略了每次循环的值:
|
||||
通配符模式由一个下划线(_)构成,且匹配并忽略任何值。当你不在乎被匹配的值时可以使用该模式。例如,下面这段代码在闭区间`1...3`中循环,每次循环时忽略该区间内的当前值:
|
||||
|
||||
```swift
|
||||
for _ in 1...3 {
|
||||
@ -46,7 +54,7 @@ for _ in 1...3 {
|
||||
<a name="identifier_pattern"></a>
|
||||
## 标识符模式(Identifier Pattern)
|
||||
|
||||
标识符模式匹配任何值,并将匹配的值和一个变量或常量绑定起来。例如,在下面的常量申明中,`someValue`是一个标识符模式,匹配了类型是`Int`的`42`。
|
||||
标识符模式匹配任何值,并将匹配的值和一个变量或常量绑定起来。例如,在下面的常量声明中,`someValue`是一个标识符模式,匹配了类型是`Int`的`42`。
|
||||
|
||||
```swift
|
||||
let someValue = 42
|
||||
@ -54,7 +62,7 @@ let someValue = 42
|
||||
|
||||
当匹配成功时,`42`被绑定(赋值)给常量`someValue`。
|
||||
|
||||
当一个变量或常量申明的左边是标识符模式时,此时,标识符模式是隐式的值绑定模式(value-binding pattern)。
|
||||
如果一个变量或常量声明的左边的模式是一个标识符模式,那么这个标识符模式是一个隐式的值绑定模式(value-binding pattern)。
|
||||
|
||||
> 标识符模式语法
|
||||
> *标识符模式* → [*标识符*](LexicalStructure.html#identifier)
|
||||
@ -62,33 +70,33 @@ let someValue = 42
|
||||
<a name="value-binding_pattern"></a>
|
||||
## 值绑定模式(Value-Binding Pattern)
|
||||
|
||||
值绑定模式绑定匹配的值到一个变量或常量。当绑定匹配值给常量时,用关键字`let`,绑定给变量时,用关键字`var`。
|
||||
值绑定模式把匹配到的值绑定给一个变量或常量名。把绑定匹配到的值绑定给常量时,用关键字`let`,绑定给变量时,用关键字`var`。
|
||||
|
||||
标识符模式包含在值绑定模式中,绑定新的变量或常量到匹配的值。例如,你可以分解一个元组的元素,并把每个元素绑定到相应的标识符模式中。
|
||||
在值绑定模式中的标识符模式会把新命名的变量或常量与匹配值做绑定。例如,你可以拆开一个元组的元素,然后把每个元素绑定到其相应一个的标识符模式中。
|
||||
|
||||
```swift
|
||||
let point = (3, 2)
|
||||
switch point {
|
||||
// Bind x and y to the elements of point.
|
||||
case let (x, y):
|
||||
println("The point is at (\(x), \(y)).")
|
||||
print("The point is at (\(x), \(y)).")
|
||||
}
|
||||
// prints "The point is at (3, 2).”
|
||||
```
|
||||
|
||||
在上面这个例子中,`let`将元组模式`(x, y)`分配到各个标识符模式。因为这种行为,`switch`语句中`case let (x, y):`和`case (let x, let y):`匹配的值是一样的。
|
||||
在上面这个例子中,`let`将元组模式`(x, y)`分配到各个标识符模式。正是由于这么做,`switch`语句中`case let (x, y):`和`case (let x, let y):`匹配到的值是一样的。
|
||||
|
||||
> 值绑定(Value Binding)模式语法
|
||||
> *值绑定模式* → **var** [*模式*](..\chapter3\07_Patterns.html#pattern) | **let** [*模式*](..\chapter3\07_Patterns.html#pattern)
|
||||
> *值绑定模式* → **var** [*模式*](../chapter3/07_Patterns.html#pattern) | **let** [*模式*](../chapter3/07_Patterns.html#pattern)
|
||||
|
||||
<a name="tuple_pattern"></a>
|
||||
## 元组模式(Tuple Pattern)
|
||||
|
||||
元组模式是逗号分隔的列表,包含一个或多个模式,并包含在一对圆括号中。元组模式匹配相应元组类型的值。
|
||||
元组模式是逗号分隔的,有零个或多个模式的列表,并被一对圆括号括起来。元组模式匹配相应元组类型的值。
|
||||
|
||||
你可以使用类型注释来限制一个元组模式来匹配某种元组类型。例如,在常量申明`let (x, y): (Int, Int) = (1, 2)`中的元组模式`(x, y): (Int, Int)`,只匹配两个元素都是`Int`这种类型的元组。如果仅需要限制一个元组模式中的某几个元素,只需要直接对这几个元素提供类型注释即可。例如,在`let (x: String, y)`中的元组模式,只要某个元组类型是包含两个元素,且第一个元素类型是`String`,则被匹配。
|
||||
你可以使用类型标注去限制一个元组模式能匹配哪些种元组类型。例如,在常量声明`let (x, y): (Int, Int) = (1, 2)`中的元组模式`(x, y): (Int, Int)`只匹配两个元素都是`Int`这种类型的元组。如果仅需要限制一个元组模式中的某几个元素,只需要直接对这几个元素提供类型标注即可。例如,在`let (x: String, y)`中的元组模式可以和任何有两个元素,且第一个元素类型是`String`的元组类型匹配。
|
||||
|
||||
当元组模式被用在`for-in`语句或者变量或常量申明时,它可以包含通配符模式,标识符模式或者其他包含这两种模式的模式。例如,下面这段代码是不正确的,因为`(x, 0)`中的元素`0`是一个表达式模式:
|
||||
当元组模式被用在`for-in`语句或者变量或常量声明时,它仅可以包含通配符模式,标识符模式,可选模式或者其他包含这些模式的元祖模式。比如下面这段代码就不正确,因为`(x, 0)`中的元素`0`是一个表达式模式:
|
||||
|
||||
```swift
|
||||
let points = [(0, 0), (1, 0), (1, 1), (2, 0), (2, 1)]
|
||||
@ -98,7 +106,7 @@ for (x, 0) in points {
|
||||
}
|
||||
```
|
||||
|
||||
对于只包含一个元素的元组,括号是不起作用的。模式匹配那个单个元素的类型。例如,下面是等效的:
|
||||
对于只包含一个元素的元组,括号是不起作用的。模式只匹配这个单个元素的类型。举例来说,下面3条语句是等效的:
|
||||
|
||||
```swift
|
||||
let a = 2 // a: Int = 2
|
||||
@ -107,60 +115,95 @@ let (a): Int = 2 // a: Int = 2
|
||||
```
|
||||
|
||||
> 元组模式语法
|
||||
> *元组模式* → **(** [*元组模式元素列表*](..\chapter3\07_Patterns.html#tuple_pattern_element_list) _可选_ **)**
|
||||
> *元组模式元素列表* → [*元组模式元素*](..\chapter3\07_Patterns.html#tuple_pattern_element) | [*元组模式元素*](..\chapter3\07_Patterns.html#tuple_pattern_element) **,** [*元组模式元素列表*](..\chapter3\07_Patterns.html#tuple_pattern_element_list)
|
||||
> *元组模式元素* → [*模式*](..\chapter3\07_Patterns.html#pattern)
|
||||
> *元组模式* → **(** [*元组模式元素列表*](../chapter3/07_Patterns.html#tuple_pattern_element_list) _可选_ **)**
|
||||
> *元组模式元素列表* → [*元组模式元素*](../chapter3/07_Patterns.html#tuple_pattern_element) | [*元组模式元素*](../chapter3/07_Patterns.html#tuple_pattern_element) **,** [*元组模式元素列表*](../chapter3/07_Patterns.html#tuple_pattern_element_list)
|
||||
> *元组模式元素* → [*模式*](../chapter3/07_Patterns.html#pattern)
|
||||
|
||||
<a name="enumeration_case_pattern"></a>
|
||||
## 枚举用例模式(Enumeration Case Pattern)
|
||||
|
||||
枚举用例模式匹配现有的枚举类型的某种用例。枚举用例模式仅在`switch`语句中的`case`标签中出现。
|
||||
一个枚举用例模式匹配现有的某个枚举类型的某个用例(case)。枚举用例模式出现在`switch`语句中的case标签中,以及`if`,`while`,`guard`和`for-in`语句的case条件中。
|
||||
|
||||
如果你准备匹配的枚举用例有任何关联的值,则相应的枚举用例模式必须指定一个包含每个关联值元素的元组模式。关于使用`switch`语句来匹配包含关联值枚举用例的例子,请参阅`Associated Values`.
|
||||
|
||||
> 枚举用例模式语法
|
||||
> *enum-case-pattern* → [*类型标识*](..\chapter3\03_Types.html#type_identifier) _可选_ **.** [*枚举的case名*](..\chapter3\05_Declarations.html#enum_case_name) [*元组模式*](..\chapter3\07_Patterns.html#tuple_pattern) _可选_
|
||||
> *enum-case-pattern* → [*类型标识*](../chapter3/03_Types.html#type_identifier) _可选_ **.** [*枚举的case名*](../chapter3/05_Declarations.html#enum_case_name) [*元组模式*](../chapter3/07_Patterns.html#tuple_pattern) _可选_
|
||||
|
||||
<a name="optional_pattern"></a>
|
||||
## 可选模式(Optional Pattern)
|
||||
|
||||
可选模式与封装在一个`Optional(T)`或者一个`ExplicitlyUnwrappedOptional(T)`枚举中的`Some(T)`用例相匹配。可选模式由一个标识符模式和紧随其后的一个问号组成,在某些情况下表现为枚举用例模式。
|
||||
|
||||
由于可选模式是`optional`和`ImplicitlyUnwrappedOptional`枚举用例模式的语法糖(syntactic sugar),下面的2种写法是一样的:
|
||||
|
||||
```swift
|
||||
let someOptional: Int? = 42
|
||||
// Match using an enumeration case pattern
|
||||
if case .Some(let x) = someOptional {
|
||||
print(x)
|
||||
}
|
||||
|
||||
// Match using an optional pattern
|
||||
if case let x? = someOptional {
|
||||
print(x)
|
||||
}
|
||||
```
|
||||
如果一个数组的元素是可选类型,可选模式为`for-in`语句提供了一种在该数组中迭代的简便方式,只为数组中的非空`non-nil`元素执行循环体。
|
||||
|
||||
```swift
|
||||
let arrayOfOptionalInts: [Int?] = [nil, 2, 3, nil, 5]
|
||||
// Match only non-nil values
|
||||
for case let number? in arrayOfOptinalInts {
|
||||
print("Found a \(number)")
|
||||
}
|
||||
//Found a 2
|
||||
//Found a 3
|
||||
//Found a 5
|
||||
|
||||
```
|
||||
> 可选模式语法
|
||||
> *可选模式* → [*标识符模式*](../chapter3/03_Types.html#type_identifier) ?
|
||||
|
||||
<a name="type-casting_patterns"></a>
|
||||
## 类型转换模式(Type-Casting Patterns)
|
||||
|
||||
有两种类型转换模式,`is`模式和`as`模式。这两种模式均只出现在`switch`语句中的`case`标签中。`is`模式和`as`模式有以下形式:
|
||||
有两种类型转换模式,`is`模式和`as`模式。这两种模式只出现在`switch`语句中的case标签中。`is`模式和`as`模式有以下形式:
|
||||
|
||||
> is `type`
|
||||
> `pattern` as `type`
|
||||
|
||||
`is`模式匹配一个值,如果这个值的类型在运行时(runtime)和`is`模式右边的指定类型(或者那个类型的子类)是一致的。`is`模式和`is`操作符一样,它们都进行类型转换,但是抛弃了返回的类型。
|
||||
`is`模式仅当一个值的类型在运行时(runtime)和`is`模式右边的指定类型一致 - 或者是该类型的子类 - 的情况下,才会匹配这个值。`is`模式和`is`操作符有相似表现,它们都进行类型转换,却舍弃返回的类型。
|
||||
|
||||
`as`模式匹配一个值,如果这个值的类型在运行时(runtime)和`as`模式右边的指定类型(或者那个类型的子类)是一致的。一旦匹配成功,匹配的值的类型被转换成`as`模式左边指定的模式。
|
||||
`as`模式仅当一个值的类型在运行时(runtime)和`as`模式右边的指定类型一致 - 或者是该类型的子类 - 的情况下,才会匹配这个值。如果匹配成功,被匹配的值的类型被转换成`as`模式左边指定的模式。
|
||||
|
||||
关于使用`switch`语句来匹配`is`模式和`as`模式值的例子,请参阅`Type Casting for Any and AnyObject`。
|
||||
|
||||
> 类型转换模式语法
|
||||
> *type-casting-pattern* → [*is模式*](..\chapter3\07_Patterns.html#is_pattern) | [*as模式*](..\chapter3\07_Patterns.html#as_pattern)
|
||||
> *is模式* → **is** [*类型*](..\chapter3\03_Types.html#type)
|
||||
> *as模式* → [*模式*](..\chapter3\07_Patterns.html#pattern) **as** [*类型*](..\chapter3\03_Types.html#type)
|
||||
> *type-casting-pattern* → [*is模式*](../chapter3/07_Patterns.html#is_pattern) | [*as模式*](../chapter3/07_Patterns.html#as_pattern)
|
||||
> *is模式* → **is** [*类型*](../chapter3/03_Types.html#type)
|
||||
> *as模式* → [*模式*](../chapter3/07_Patterns.html#pattern) **as** [*类型*](../chapter3/03_Types.html#type)
|
||||
|
||||
<a name="expression_pattern"></a>
|
||||
## 表达式模式(Expression Pattern)
|
||||
|
||||
表达式模式代表了一个表达式的值。这个模式只出现在`switch`语句中的`case`标签中。
|
||||
一个表达式模式代表了一个表达式的值。表达式模式只出现在`switch`语句中的`case`标签中。
|
||||
|
||||
由表达式模式所代表的表达式用Swift标准库中的`~=`操作符与输入表达式的值进行比较。如果`~=`操作符返回`true`,则匹配成功。默认情况下,`~=`操作符使用`==`操作符来比较两个相同类型的值。它也可以匹配一个整数值与一个`Range`对象中的整数范围,正如下面这个例子所示:
|
||||
由表达式模式所代表的表达式与使用了Swift标准库中`~=`操作符的输入表达式的值进行比较。如果`~=`操作符返回`true`,则匹配成功。默认情况下,`~=`操作符使用`==`操作符来比较两个相同类型的值。它也可以将一个整型数值与一个`Range`对象中的一段整数区间做匹配,正如下面这个例子所示:
|
||||
|
||||
```swift
|
||||
let point = (1, 2)
|
||||
switch point {
|
||||
case (0, 0):
|
||||
println("(0, 0) is at the origin.")
|
||||
print("(0, 0) is at the origin.")
|
||||
case (-2...2, -2...2):
|
||||
println("(\(point.0), \(point.1)) is near the origin.")
|
||||
print("(\(point.0), \(point.1)) is near the origin.")
|
||||
default:
|
||||
println("The point is at (\(point.0), \(point.1)).")
|
||||
print("The point is at (\(point.0), \(point.1)).")
|
||||
}
|
||||
// prints "(1, 2) is near the origin.”
|
||||
```
|
||||
|
||||
你可以重载`~=`操作符来提供自定义的表达式行为。例如,你可以重写上面的例子,以实现用字符串表达的点来比较`point`表达式。
|
||||
你可以重载`~=`操作符来提供自定义的表达式匹配行为。比如你可以重写上面的例子,拿`point`表达式去比较字符串形式的点。
|
||||
|
||||
```swift
|
||||
// Overload the ~= operator to match a string with an integer
|
||||
@ -169,14 +212,12 @@ func ~=(pattern: String, value: Int) -> Bool {
|
||||
}
|
||||
switch point {
|
||||
case ("0", "0"):
|
||||
println("(0, 0) is at the origin.")
|
||||
case ("-2...2", "-2...2"):
|
||||
println("(\(point.0), \(point.1)) is near the origin.")
|
||||
print("(0, 0) is at the origin.")
|
||||
default:
|
||||
println("The point is at (\(point.0), \(point.1)).")
|
||||
print("The point is at (\(point.0), \(point.1)).")
|
||||
}
|
||||
// prints "(1, 2) is near the origin.”
|
||||
```
|
||||
|
||||
> 表达式模式语法
|
||||
> *表达式模式* → [*表达式*](..\chapter3\04_Expressions.html#expression)
|
||||
> *表达式模式* → [*表达式*](../chapter3/04_Expressions.html#expression)
|
||||
|
||||
@ -1,36 +1,41 @@
|
||||
> 翻译:[fd5788](https://github.com/fd5788)
|
||||
# 泛型参数(Generic Parameters and Arguments)
|
||||
---------
|
||||
|
||||
> 1.0
|
||||
> 翻译:[fd5788](https://github.com/fd5788)
|
||||
> 校对:[yankuangshi](https://github.com/yankuangshi), [stanzhai](https://github.com/stanzhai)
|
||||
|
||||
# 泛型参数
|
||||
---------
|
||||
> 2.0
|
||||
> 翻译+校对:[wardenNScaiyi](https:github.com/wardenNScaiyi)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [泛型形参子句](#generic_parameter)
|
||||
- [泛型实参子句](#generic_argument)
|
||||
|
||||
本节涉及泛型类型、泛型函数以及泛型构造器的参数,包括形参和实参。声明泛型类型、函数或构造器时,须指定相应的类型参数。类型参数相当于一个占位符,当实例化泛型类型、调用泛型函数或泛型构造器时,就用具体的类型实参替代之。
|
||||
本节涉及泛型类型、泛型函数以及泛型初始化器(**initializer**)的参数,包括形参和实参。声明泛型类型、函数或初始化器时,须指定相应的类型参数。类型参数相当于一个占位符,当实例化泛型类型、调用泛型函数或泛型初始化器时,就用具体的类型实参替代之。
|
||||
|
||||
关于 Swift 语言的泛型概述,见[泛型](../charpter2/22_Generics.md)(第二部分第22章)。
|
||||
关于 Swift 语言的泛型概述,见[泛型](../chapter2/23_Generics.md)(第二部分第23章)。
|
||||
|
||||
<a name="generic_parameter"></a>
|
||||
## 泛型形参子句
|
||||
|
||||
泛型形参子句指定泛型类型或函数的类型形参,以及这些参数的关联约束和要求。泛型形参子句用尖括号(<>)包住,并且有以下两种形式:
|
||||
泛型形参子句指定泛型类型或函数的类型形参,以及这些参数的关联约束和关联类型要求(**requirement**)。泛型形参子句用尖括号(<>)包住,并且有以下两种形式:
|
||||
|
||||
> <`generic parameter list`>
|
||||
> <`generic parameter list` where `requirements`>
|
||||
> <`泛型形参列表`>
|
||||
> <`泛型形参列表` where `关联类型要求`>
|
||||
|
||||
泛型形参列表中泛型形参用逗号分开,每一个采用以下形式:
|
||||
泛型形参列表中泛型形参用逗号分开,其中每一个采用以下形式:
|
||||
|
||||
> `type parameter` : `constrain`
|
||||
> `类型形参` : `约束`
|
||||
|
||||
泛型形参由两部分组成:类型形参及其后的可选约束。类型形参只是占位符类型(如T,U,V,KeyType,ValueType等)的名字而已。你可以在泛型类型、函数的其余部分或者构造器声明,以及函数或构造器的签名中使用它。
|
||||
泛型形参由两部分组成:类型形参及其后的可选约束。类型形参只是占位符类型(如 T,U,V,Key,Value 等)的名字而已。你可以在泛型类型、函数的其余部分或者初始化器声明,包括函数或初始化器的签名中使用它(与其任何相关类型)。
|
||||
|
||||
约束用于指明该类型形参继承自某个类或者遵守某个协议或协议的一部分。例如,在下面的泛型函数中,泛型形参`T: Comparable`表示任何用于替代类型形参`T`的类型实参必须满足`Comparable`协议。
|
||||
|
||||
约束用于指明该类型形参继承自某个类或者遵守某个协议或协议的一部分。例如,在下面的泛型中,泛型形参`T: Comparable`表示任何用于替代类型形参`T`的类型实参必须满足`Comparable`协议。
|
||||
|
||||
```swift
|
||||
func simpleMin<T: Comparable>(x: T, y: T) -> T {
|
||||
func simpleMax<T: Comparable>(x: T, _ y: T) -> T {
|
||||
if x < y {
|
||||
return y
|
||||
}
|
||||
@ -38,41 +43,43 @@ func simpleMin<T: Comparable>(x: T, y: T) -> T {
|
||||
}
|
||||
```
|
||||
|
||||
如,`Int`和`Double`均满足`Comparable`协议,该函数接受任何一种类型。与泛型类型相反,调用泛型函数或构造器时不需要指定泛型实参子句。类型实参由传递给函数或构造器的实参推断而出。
|
||||
|
||||
```swift
|
||||
simpleMin(17, 42) // T is inferred to be Int
|
||||
simpleMin(3.14159, 2.71828) // T is inferred to be Double
|
||||
|
||||
如,`Int`和`Double`均满足`Comparable`协议,该函数接受任何一种类型。与泛型类型相反,调用泛型函数或初始化器时不需要指定泛型实参子句。类型实参由传递给函数或初始化器的实参推断而出。
|
||||
|
||||
|
||||
```
|
||||
simpleMax(17, 42) // T被推断出为Int类型
|
||||
simpleMax(3.14159, 2.71828) // T被推断出为Double类型
|
||||
```
|
||||
|
||||
## Where 子句
|
||||
|
||||
要想对类型形参及其关联类型指定额外要求,可以在泛型形参列表之后添加`where`子句。`where`子句由关键字`where`及其后的用逗号分割的多个要求组成。
|
||||
要想对类型形参及其关联类型指定额外关联类型要求,可以在泛型形参列表之后添加`where`子句。`where`子句由关键字`where`及其后的用逗号分割的多个关联类型要求组成。
|
||||
|
||||
`where`子句中的要求用于指明该类型形参继承自某个类或遵守某个协议或协议的一部分。尽管`where`子句有助于表达类型形参上的简单约束(如`T: Comparable`等同于`T where T: Comparable`,等等),但是依然可以用来对类型形参及其关联约束提供更复杂的约束。如,`<T where T: C, T: P>`表示泛型类型`T`继承自类`C`且遵守协议`P`。
|
||||
`where`子句中的关联关系用于指明该类型形参继承自某个类或遵守某个协议或协议的一部分。尽管`where`子句提供了语法糖使其有助于表达类型形参上的简单约束(如`T: Comparable`等同于`T where T: Comparable`,等等),但是依然可以用来对类型形参及其关联类型提供更复杂的约束。如,`<T where T: C, T: P>`表示泛型类型`T`继承自类`C`且遵守协议`P`。
|
||||
|
||||
如上所述,可以强制约束类型形参的关联类型遵守某个协议。`<T: Generator where T.Element: Equatable>`表示`T`遵守`Generator`协议,而且`T`的关联类型`T.Element`遵守`Eauatable`协议(`T`有关联类型是因为`Generator`声明了`Element`,而`T`遵守`Generator`协议)。
|
||||
如上所述,可以强制约束类型形参的关联类型遵守某个协议。例如`<T: Generator where T.Element: Equatable>`表示`T`遵守`Generator`协议,而且`T`的关联类型`T.Element`遵守`Eauatable`协议(`T`有关联类型`Element`是因为`Generator`声明了`Element`,而`T`遵守`Generator`协议)。
|
||||
|
||||
也可以用操作符`==`来指定两个类型等效的要求。例如,有这样一个约束:`T`和`U`遵守`Generator`协议,同时要求它们的关联类型等同,可以这样来表达:`<T: Generator, U: Generator where T.Element == U.Element>`。
|
||||
也可以用操作符`==`来指定两个类型等效的关联关系。例如,有这样一个约束:`T`和`U`遵守`Generator`协议,同时要求它们的关联类型等同,可以这样来表达:`<T: Generator, U: Generator where T.Element == U.Element>`。
|
||||
|
||||
当然,替代类型形参的类型实参必须满足所有类型形参所要求的约束和要求。
|
||||
当然,替代类型形参的类型实参必须满足所有类型形参的约束和关联类型要求。
|
||||
|
||||
泛型函数或构造器可以重载,但在泛型形参子句中的类型形参必须有不同的约束或要求,抑或二者皆不同。当调用重载的泛型函数或构造器时,编译器会用这些约束来决定调用哪个重载函数或构造器。
|
||||
泛型函数或初始化器可以重载,但在泛型形参子句中的类型形参必须有不同的约束或关联类型要求,抑或二者皆不同。当调用重载的泛型函数或始化器时,编译器会用这些约束来决定调用哪个重载函数或始化器。
|
||||
|
||||
泛型类可以生成一个子类,但是这个子类也必须是泛型类。
|
||||
|
||||
> 泛型形参子句语法
|
||||
> *泛型参数子句* → **<** [*泛型参数列表*](GenericParametersAndArguments.html#generic_parameter_list) [*约束子句*](GenericParametersAndArguments.html#requirement_clause) _可选_ **>**
|
||||
> *泛型参数列表* → [*泛形参数*](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_identifier)
|
||||
> *泛形参数* → [*类型名称*](..\chapter3\03_Types.html#type_name) **:** [*协议合成类型*](..\chapter3\03_Types.html#protocol_composition_type)
|
||||
> *泛形参数* → [*类型名称*](../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#protocol_composition_type)
|
||||
> *约束子句* → **where** [*约束列表*](GenericParametersAndArguments.html#requirement_list)
|
||||
> *约束列表* → [*约束*](GenericParametersAndArguments.html#requirement) | [*约束*](GenericParametersAndArguments.html#requirement) **,** [*约束列表*](GenericParametersAndArguments.html#requirement_list)
|
||||
> *约束* → [*一致性约束*](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#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)
|
||||
> *一致性约束* → [*类型标识*](../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)
|
||||
|
||||
|
||||
<a name="generic_argument"></a>
|
||||
@ -80,27 +87,30 @@ simpleMin(3.14159, 2.71828) // T is inferred to be Double
|
||||
|
||||
泛型实参子句指定_泛型类型_的类型实参。泛型实参子句用尖括号(<>)包住,形式如下:
|
||||
|
||||
> <`generic argument list`>
|
||||
> <`泛型实参列表`>
|
||||
|
||||
泛型实参列表中类型实参有逗号分开。类型实参是实际具体类型的名字,用来替代泛型类型的泛型形参子句中的相应的类型形参。从而得到泛型类型的一个特化版本。如,Swift标准库的泛型字典类型定义如下:
|
||||
|
||||
|
||||
```swift
|
||||
struct Dictionary<KeyTypel: Hashable, ValueType>: Collection, DictionaryLiteralConvertible {
|
||||
|
||||
/* .. */
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
泛型`Dictionary`类型的特化版本,`Dictionary<String, Int>`就是用具体的`String`和`Int`类型替代泛型类型`KeyType: Hashable`和`ValueType`产生的。每一个类型实参必须满足它所替代的泛型形参的所有约束,包括任何`where`子句所指定的额外的要求。上面的例子中,类型形参`KeyType`要求满足`Hashable`协议,因此`String`也必须满足`Hashable`协议。
|
||||
泛型`Dictionary`类型的特化版本,`Dictionary<String, Int>`就是用具体的`String`和`Int`类型替代泛型类型`KeyType: Hashable`和`ValueType`产生的。每一个类型实参必须满足它所替代的泛型形参的所有约束,包括任何`where`子句所指定的额外的关联类型要求。上面的例子中,类型形参`Key`类型要求满足`Hashable`协议,因此`String`也必须满足`Hashable`协议。
|
||||
|
||||
可以用本身就是泛型类型的特化版本的类型实参替代类型形参(假设已满足合适的约束和要求)。例如,为了生成一个元素类型是整型数组的数组,可以用数组的特化版本`Array<Int>`替代泛型类型`Array<T>`的类型形参`T`来实现。
|
||||
可以用本身就是泛型类型的特化版本的类型实参替代类型形参(假设已满足合适的约束和关联类型要求)。例如,为了生成一个元素类型是整型数组的数组,可以用数组的特化版本`Array<Int>`替代泛型类型`Array<T>`的类型形参 `T` 来实现。
|
||||
|
||||
```swift
|
||||
```
|
||||
let arrayOfArrays: Array<Array<Int>> = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
|
||||
```
|
||||
|
||||
如[泛型形参子句](#generic_parameter)所述,不能用泛型实参子句来指定泛型函数或构造器的类型实参。
|
||||
如[泛型形参子句](#generic_parameter)所述,不能用泛型实参子句来指定泛型函数或初始化器的类型实参。
|
||||
|
||||
> 泛型实参子句语法
|
||||
> *(泛型参数子句Generic Argument Clause)* → **<** [*泛型参数列表*](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)
|
||||
|
||||
@ -1,9 +1,14 @@
|
||||
> 翻译:[coverxit](https://github.com/coverxit)
|
||||
> 校对:[numbbbbb](https://github.com/numbbbbb), [coverxit](https://github.com/coverxit), [stanzhai](https://github.com/stanzhai)
|
||||
|
||||
# 语句
|
||||
<a name="statement_statements"></a>
|
||||
# 语句(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)
|
||||
@ -11,20 +16,21 @@
|
||||
- [带标签的语句](#labeled_statement)
|
||||
- [控制传递语句](#control_transfer_statements)
|
||||
|
||||
在 Swift 中,有两种类型的语句:简单语句和控制流语句。简单语句是最常见的,用于构造表达式和声明。控制流语句则用于控制程序执行的流程,Swift 中有三种类型的控制流语句:循环语句、分支语句和控制传递语句。
|
||||
在 Swift 中,有两种类型的语句:简单语句和控制流语句。简单语句是最常见的,用于构造表达式或者声明。控制流语句则用于控制程序执行的流程,Swift 中有三种类型的控制流语句:循环语句、分支语句和控制传递语句。
|
||||
|
||||
循环语句用于重复执行代码块;分支语句用于执行满足特定条件的代码块;控制传递语句则用于修改代码的执行顺序。在稍后的叙述中,将会详细地介绍每一种类型的控制流语句。
|
||||
|
||||
是否将分号(`;`)添加到语句的结尾处是可选的。但若要在同一行内写多条独立语句,请务必使用分号。
|
||||
|
||||
> 语句语法
|
||||
> *语句* → [*表达式*](..\chapter3\04_Expressions.html#expression) **;** _可选_
|
||||
> *语句* → [*声明*](..\chapter3\05_Declarations.html#declaration) **;** _可选_
|
||||
> *语句* → [*循环语句*](..\chapter3\10_Statements.html#loop_statement) **;** _可选_
|
||||
> *语句* → [*分支语句*](..\chapter3\10_Statements.html#branch_statement) **;** _可选_
|
||||
> *语句* → [*标记语句(Labeled Statement)*](..\chapter3\10_Statements.html#labeled_statement)
|
||||
> *语句* → [*控制转移语句*](..\chapter3\10_Statements.html#control_transfer_statement) **;** _可选_
|
||||
> *多条语句(Statements)* → [*语句*](..\chapter3\10_Statements.html#statement) [*多条语句(Statements)*](..\chapter3\10_Statements.html#statements) _可选_
|
||||
> *语句* → [*表达式*](../chapter3/04_Expressions.html#expression) **;** _可选_
|
||||
> *语句* → [*声明*](../chapter3/05_Declarations.html#declaration) **;** _可选_
|
||||
> *语句* → [*循环语句*](../chapter3/10_Statements.html#loop_statement) **;** _可选_
|
||||
> *语句* → [*分支语句*](../chapter3/10_Statements.html#branch_statement) **;** _可选_
|
||||
> *语句* → [*标记语句(Labeled Statement)*](../chapter3/10_Statements.html#labeled_statement)
|
||||
> *语句* → [*控制转移语句*](../chapter3/10_Statements.html#control_transfer_statement) **;** _可选_
|
||||
> *语句* → [*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>
|
||||
## 循环语句
|
||||
@ -34,14 +40,15 @@
|
||||
通过`break`语句和`continue`语句可以改变循环语句的控制流。有关这两条语句,详情参见 [Break 语句](#break_statement)和 [Continue 语句](#continue_statement)。
|
||||
|
||||
> 循环语句语法
|
||||
> *循环语句* → [*for语句*](..\chapter3\10_Statements.html#for_statement)
|
||||
> *循环语句* → [*for-in语句*](..\chapter3\10_Statements.html#for_in_statement)
|
||||
> *循环语句* → [*while语句*](..\chapter3\10_Statements.html#wheetatype类型ile_statement)
|
||||
> *循环语句* → [*do-while语句*](..\chapter3\10_Statements.html#do_while_statement)
|
||||
> *循环语句* → [*for语句*](../chapter3/10_Statements.html#for_statement)
|
||||
> *循环语句* → [*for-in语句*](../chapter3/10_Statements.html#for_in_statement)
|
||||
> *循环语句* → [*while语句*](../chapter3/10_Statements.html#wheetatype类型ile_statement)
|
||||
> *循环语句* → [*do-while语句*](../chapter3/10_Statements.html#do_while_statement)
|
||||
|
||||
<a name="for_statements"></a>
|
||||
### For 语句
|
||||
|
||||
`for`语句允许在重复执行代码块的同时,递增一个计数器。
|
||||
`for`语句只有在循环条件为真时重复执行代码块,此时计数器递增。
|
||||
|
||||
`for`语句的形式如下:
|
||||
|
||||
@ -53,18 +60,20 @@
|
||||
|
||||
`for`语句的执行流程如下:
|
||||
|
||||
1. *initialzation* 只会被执行一次,通常用于声明和初始化在接下来的循环中需要使用的变量。
|
||||
2. 计算 *condition* 表达式:
|
||||
如果为`true`,*statements* 将会被执行,然后转到第3步。如果为`false`,*statements* 和 *increment* 都不会被执行,`for`至此执行完毕。
|
||||
1. *initialzation* *循环变量* 只会被执行一次,通常用于声明和初始化在接下来的循环中需要使用的变量。
|
||||
2. 判断 *condition* 循环条件:
|
||||
如果为`true`,*statements* *循环体* 将会被执行,然后转到第3步。如果为`false`,*statements* 和 *increment* *循环增量* 都不会被执行,`for`至此执行完毕。
|
||||
3. 计算 *increment* 表达式,然后转到第2步。
|
||||
|
||||
定义在 *initialzation* 中的变量仅在`for`语句的作用域以内有效。*condition* 表达式的值的类型必须遵循`LogicValue`协议。
|
||||
在 *initialzation* 中定义的变量仅在`for`循环的作用域内有效。*condition* 表达式的值的类型必须遵循`BooleanType `协议。
|
||||
|
||||
> 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初始条件* → [*变量声明*](..\chapter3\05_Declarations.html#variable_declaration) | [*表达式列表*](..\chapter3\04_Expressions.html#expression_list)
|
||||
> *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)
|
||||
|
||||
|
||||
<a name="for-in_statements"></a>
|
||||
### For-In 语句
|
||||
|
||||
`for-in`语句允许在重复执行代码块的同时,迭代集合(或遵循`Sequence`协议的任意类型)中的每一项。
|
||||
@ -78,11 +87,13 @@
|
||||
`for-in`语句在循环开始前会调用 *collection* 表达式的`generate`方法来获取一个生成器类型(这是一个遵循`Generator`协议的类型)的值。接下来循环开始,调用 *collection* 表达式的`next`方法。如果其返回值不是`None`,它将会被赋给 *item*,然后执行 *statements*,执行完毕后回到循环开始处;否则,将不会赋值给 *item* 也不会执行 *statements*,`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`语句的形式如下:
|
||||
|
||||
@ -98,34 +109,47 @@
|
||||
|
||||
由于 *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条件*](..\chapter3\10_Statements.html#while_condition) [*代码块*](..\chapter3\05_Declarations.html#code_block)
|
||||
> *while条件* → [*表达式*](..\chapter3\04_Expressions.html#expression) | [*声明*](..\chapter3\05_Declarations.html#declaration)
|
||||
> *while语句* → **while** [*while条件*](../chapter3/10_Statements.html#while_condition) [*代码块*](../chapter3/05_Declarations.html#code_block)
|
||||
> *条件* → [*表达式*](../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`
|
||||
> } while `condition`
|
||||
|
||||
`do-while`语句的执行流程如下:
|
||||
`repeat-while`语句的执行流程如下:
|
||||
|
||||
1. 执行 *statements*,然后转到第2步。
|
||||
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 循环语法
|
||||
> *do-while语句* → **do** [*代码块*](..\chapter3\05_Declarations.html#code_block) **while** [*while条件*](..\chapter3\10_Statements.html#while_condition)
|
||||
> Repeat-While 循环语法
|
||||
> * repeat-while语句* → **repeat** [*代码块*](../chapter3/05_Declarations.html#code_block) **while** [*while条件*](../chapter3/10_Statements.html#while_condition)
|
||||
|
||||
<a name="branch_statements"></a>
|
||||
## 分支语句
|
||||
@ -135,9 +159,11 @@
|
||||
`switch`语句中的控制流可以用`break`语句修改,详情请见[Break 语句](#break_statement)。
|
||||
|
||||
> 分支语句语法
|
||||
> *分支语句* → [*if语句*](..\chapter3\10_Statements.html#if_statement)
|
||||
> *分支语句* → [*switch语句*](..\chapter3\10_Statements.html#switch_statement)
|
||||
> *分支语句* → [*if语句*](../chapter3/10_Statements.html#if_statement)
|
||||
> *分支语句* → [*switch语句*](../chapter3/10_Statements.html#switch_statement)
|
||||
|
||||
|
||||
<a name="if_statements"></a>
|
||||
### If 语句
|
||||
|
||||
取决于一个或多个条件的值,`if`语句将决定执行哪一块代码。
|
||||
@ -152,10 +178,10 @@
|
||||
|
||||
第二种形式是在第一种形式的基础上添加 *else 语句*,当只有一个 else 语句时,像下面这样:
|
||||
|
||||
> if `condition` {
|
||||
> `statements to execute if condition is true`
|
||||
> } else {
|
||||
> `statements to execute if condition is false`
|
||||
> if `condition` {
|
||||
> `statements to execute if condition is true`
|
||||
> } else {
|
||||
> `statements to execute if condition is false`
|
||||
> }
|
||||
|
||||
同时,else 语句也可包含`if`语句,从而形成一条链来测试更多的条件,像下面这样:
|
||||
@ -172,10 +198,35 @@
|
||||
`if`语句中条件的值的类型必须遵循`LogicValue`协议。同时,条件也可以使用可选绑定,详情参见[可选绑定](../chapter2/01_The_Basics.html#optional_binding)。
|
||||
|
||||
> 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条件* → [*表达式*](..\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)
|
||||
> *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)
|
||||
> *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`语句的*控制表达式(control expression)*,`switch`语句将决定执行哪一块代码。
|
||||
@ -219,18 +270,19 @@ case let (x, y) where x == y:
|
||||
当匹配的 case 分支中的代码执行完毕后,程序会终止`switch`语句,而不会继续执行下一个 case 分支。这就意味着,如果你想执行下一个 case 分支,需要显式地在你需要的 case 分支里使用`fallthrough`语句。关于`fallthrough`语句的更多信息,详情参见 [Fallthrough 语句](#fallthrough_statement)。
|
||||
|
||||
> Switch语句语法
|
||||
> *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* → [*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) **;**
|
||||
> *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)
|
||||
> *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* → [*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) **;**
|
||||
> *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)
|
||||
> *default标签* → **default** **:**
|
||||
> *guard-clause* → **where** [*guard-expression*](..\chapter3\10_Statements.html#guard_expression)
|
||||
> *guard-expression* → [*表达式*](..\chapter3\04_Expressions.html#expression)
|
||||
> *where-clause* → **where** [*guard-expression*](../chapter3/10_Statements.html#guard)
|
||||
> *where-expression* → [*表达式*](../chapter3/04_Expressions.html#expression)
|
||||
|
||||
<a name="labeled_statement"></a>
|
||||
<a name="control_transfer_statements"></a> 带标签的语句
|
||||
<a name="labeled_statements"></a>
|
||||
<a name="control_transfer_statements"></a>
|
||||
## 带标签的语句
|
||||
|
||||
你可以在循环语句或`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)。
|
||||
|
||||
> 标记语句语法
|
||||
> *标记语句(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\02_Lexical_Structure.html#identifier)
|
||||
> *标记语句(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/02_Lexical_Structure.html#identifier)
|
||||
|
||||
<a name="control_transfer_statements"></a>
|
||||
## 控制传递语句
|
||||
|
||||
通过无条件地把控制权从一片代码传递到另一片代码,控制传递语句能够改变代码执行的顺序。Swift 提供四种类型的控制传递语句:`break`语句、`continue`语句、`fallthrough`语句和`return`语句。
|
||||
|
||||
> 控制传递语句(Control Transfer Statement) 语法
|
||||
> *控制传递语句* → [*break语句*](..\chapter3\10_Statements.html#break_statement)
|
||||
> *控制传递语句* → [*continue语句*](..\chapter3\10_Statements.html#continue_statement)
|
||||
> *控制传递语句* → [*fallthrough语句*](..\chapter3\10_Statements.html#fallthrough_statement)
|
||||
> *控制传递语句* → [*return语句*](..\chapter3\10_Statements.html#return_statement)
|
||||
> *控制传递语句* → [*break语句*](../chapter3/10_Statements.html#break_statement)
|
||||
> *控制传递语句* → [*continue语句*](../chapter3/10_Statements.html#continue_statement)
|
||||
> *控制传递语句* → [*fallthrough语句*](../chapter3/10_Statements.html#fallthrough_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`语句用于终止循环或`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 语句语法
|
||||
> *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`后面跟上标签名(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 语句语法
|
||||
> *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`语句用于在`switch`语句中传递控制权。`fallthrough`语句会把控制权从`switch`语句中的一个 case 传递给下一个 case 。这种传递是无条件的,即使下一个 case 的模式与`switch`语句的控制表达式的值不匹配。
|
||||
@ -305,6 +359,7 @@ case let (x, y) where x == y:
|
||||
> Fallthrough 语句语法
|
||||
> *fallthrough语句* → **fallthrough**
|
||||
|
||||
<a name="return_statements"></a>
|
||||
### Return 语句
|
||||
|
||||
`return`语句用于在函数或方法的实现中将控制权传递给调用者,接着程序将会从调用者的位置继续向下执行。
|
||||
@ -319,4 +374,125 @@ case let (x, y) where x == y:
|
||||
而当只写`return`时,仅仅是将控制权从该函数或方法传递给调用者,而不返回一个值。(这就是说,该函数或方法的返回类型为`Void`或`()`)
|
||||
|
||||
> 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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
# Access Control 权限控制的黑与白
|
||||
|
||||
> 翻译:[老码团队翻译组-Arya](http://weibo.com/littlekok/)
|
||||
> 校对:[老码团队翻译组-Oberyn](http://weibo.com/u/5241713117)
|
||||
|
||||
# Access Control 权限控制的黑与白
|
||||
|
||||
如果您之前没有接触过权限控制,先来听一个小故事:
|
||||
|
||||
> 小明是五道口工业学院的一个大一新生,最近他有点烦恼,因为同屋经常用他的热水壶,好像那是自己家的一样,可是碍于同学情面,又不好意思说。直到有一天,他和学姐小K吐槽。
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
> 翻译:[老码团队翻译组-Tyrion](http://weibo.com/u/5241713117)
|
||||
> 校对:[老码团队翻译组-Oberyn](http://weibo.com/u/5241713117)
|
||||
|
||||
# 造个类型不是梦-白话Swift类型创建
|
||||
-----------------
|
||||
|
||||
> 翻译:[老码团队翻译组-Tyrion](http://weibo.com/u/5241713117)
|
||||
> 校对:[老码团队翻译组-Oberyn](http://weibo.com/u/5241713117)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [自定义原型](#prototype)
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
# WWDC里面的那个“大炮打气球”
|
||||
|
||||
> 翻译:[老码团队翻译组-Arya](http://weibo.com/littlekok/)
|
||||
> 校对:[老码团队翻译组-](Jame)
|
||||
|
||||
# WWDC里面的那个“大炮打气球”
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
> 翻译:[老码团队翻译组-Relly](http://weibo.com/penguinliong/)
|
||||
> 校对:[老码团队翻译组-Tyrion](http://weibo.com/u/5241713117)
|
||||
|
||||
# Swift与C语言指针友好合作
|
||||
-----------------
|
||||
|
||||
> 翻译:[老码团队翻译组-Relly](http://weibo.com/penguinliong/)
|
||||
> 校对:[老码团队翻译组-Tyrion](http://weibo.com/u/5241713117)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [用以输入/输出的参数指针](#inout-para-pointer)
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
> 翻译:[老码团队翻译组-Arya](http://weibo.com/littlekok/)
|
||||
> 校对:[老码团队翻译组-Jame](http://weibo.com/u/5241713117)
|
||||
|
||||
# Swift里的值类型与引用类型
|
||||
-----------------
|
||||
|
||||
> 翻译:[老码团队翻译组-Arya](http://weibo.com/littlekok/)
|
||||
> 校对:[老码团队翻译组-Jame](http://weibo.com/u/5241713117)
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [值类型与引用类型的区别](#difference-two)
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
> 翻译:[老码团队翻译组-Arya](http://weibo.com/littlekok/)
|
||||
> 校对:[老码团队翻译组-Jame](http://weibo.com/u/5241713117)
|
||||
|
||||
# 访问控制和protected
|
||||
-----------------
|
||||
|
||||
> 翻译:[老码团队翻译组-Arya](http://weibo.com/littlekok/)
|
||||
> 校对:[老码团队翻译组-Jame](http://weibo.com/u/5241713117)
|
||||
|
||||
原文再续,书折第一回。
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
89
source/v1.0.md
Normal 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个 Star,310个 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
|
After Width: | Height: | Size: 10 KiB |
BIN
swift 2.0引用图片/2.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
swift 2.0引用图片/3.png
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
swift 2.0引用图片/4.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
swift 2.0引用图片/5.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
swift 2.0引用图片/6.png
Normal file
|
After Width: | Height: | Size: 27 KiB |