fix conflict
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
.DS_Store
|
||||
131
README.md
131
README.md
@ -24,46 +24,44 @@ CocoaChina精校PDF→[点我下载](http://vdisk.weibo.com/s/EhsPPzRRQ5CZ/14026
|
||||
|
||||
# 译者记录
|
||||
|
||||
> 说明:翻译之前请先到PR列表中查看别人认领的内容,尽量不要重复,谢谢!
|
||||
|
||||
* 欢迎使用 Swift
|
||||
* 关于 Swift(完成 By numbbbbb)
|
||||
* Swift 初见(完成 By numbbbbb)
|
||||
* 关于 Swift ([numbbbbb])
|
||||
* Swift 初见 ([numbbbbb])
|
||||
* Swift 教程
|
||||
* 基础部分(完成 By numbbbbb, lyuka, JaySurplus)
|
||||
* 基本操作符(完成 By @xielingwang)
|
||||
* 字符串和字符(完成 By @wh1100717)
|
||||
* 集合类型(完成)
|
||||
* 控制流(完成 By @vclwei, @coverxit, @NicePiao)
|
||||
* 函数(完成 By @honghaoz)
|
||||
* 闭包(完成 By @wh1100717)
|
||||
* 枚举(完成 By @yankuangshi)
|
||||
* 类和结构体(完成 By @JaySurplus)
|
||||
* 属性(完成 By @shinyzhu)
|
||||
* 方法(完成 By @pp-prog)
|
||||
* 下标(完成 By @siemenliu)
|
||||
* 继承(完成 By @Hawstein)
|
||||
* 构造过程(完成 By @lifedim)
|
||||
* 析构过程(完成)
|
||||
* 自动引用计数(完成 By @TimothyYe)
|
||||
* 可选链(完成 By @Jasonbroker)
|
||||
* 类型检查(完成 By @xiehurricane)
|
||||
* 嵌套类型(完成 By @Lin-H)
|
||||
* 扩展(完成 By @lyuka)
|
||||
* 协议(完成 By @geek5nan)
|
||||
* 泛型(完成 By @takalard)
|
||||
* 高级操作符(完成 By @xielingwang)
|
||||
* 基础部分 ([numbbbbb], [lyuka], [JaySurplus])
|
||||
* 基本操作符 ([xielingwang])
|
||||
* 字符串和字符 ([wh1100717])
|
||||
* 集合类型 ([zqp])
|
||||
* 控制流 ([vclwei], [coverxit], [NicePiao])
|
||||
* 函数 ([honghaoz])
|
||||
* 闭包 ([wh1100717])
|
||||
* 枚举 ([yankuangshi])
|
||||
* 类和结构体 ([JaySurplus])
|
||||
* 属性 ([shinyzhu])
|
||||
* 方法 ([pp-prog])
|
||||
* 下标 ([siemenliu])
|
||||
* 继承 ([Hawstein])
|
||||
* 构造过程 ([lifedim])
|
||||
* 析构过程 ([bruce0505])
|
||||
* 自动引用计数 ([TimothyYe])
|
||||
* 可选链 ([Jasonbroker])
|
||||
* 类型检查 ([xiehurricane])
|
||||
* 嵌套类型 ([Lin-H])
|
||||
* 扩展 ([lyuka])
|
||||
* 协议 ([geek5nan])
|
||||
* 泛型 ([takalard])
|
||||
* 高级操作符 ([xielingwang])
|
||||
* 语言参考
|
||||
* 关于语言参考(完成 By @ChildhoodAndy)
|
||||
* 词法结构(完成 By @superkam)
|
||||
* 类型(完成 By @lyuka)
|
||||
* 表达式(完成 By @sg552 )
|
||||
* 语句(完成 By @coverxit)
|
||||
* 声明(完成 By @marsprince)
|
||||
* 特性(完成 By @Hawstein)
|
||||
* 模式(完成 By @honghaoz)
|
||||
* 泛型参数(完成 By @fd5788)
|
||||
* 语法总结(完成 By @StanZhai)
|
||||
* 关于语言参考 ([dabing1022])
|
||||
* 词法结构 ([superkam])
|
||||
* 类型 ([lyuka])
|
||||
* 表达式 ([sg552] )
|
||||
* 语句 ([coverxit])
|
||||
* 声明 ([marsprince])
|
||||
* 特性 ([Hawstein])
|
||||
* 模式 ([honghaoz])
|
||||
* 泛型参数 ([fd5788])
|
||||
* 语法总结 ([StanZhai])
|
||||
|
||||
# 贡献力量
|
||||
|
||||
@ -115,3 +113,62 @@ CocoaChina精校PDF→[点我下载](http://vdisk.weibo.com/s/EhsPPzRRQ5CZ/14026
|
||||
|
||||
# 开源协议
|
||||
基于[WTFPL](http://en.wikipedia.org/wiki/WTFPL)协议开源。
|
||||
|
||||
|
||||
|
||||
[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
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="1.1" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="1.1" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,10 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_30">
|
||||
<section class="normal" id="section-gitbook_4">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:numbbbbb<br>校对:yeahdongcn</p>
|
||||
<p>翻译:<a href="https://github.com/numbbbbb" target="_blank">numbbbbb</a><br>校对:<a href="https://github.com/yeahdongcn" target="_blank">yeahdongcn</a></p>
|
||||
</blockquote>
|
||||
<h1 id="-swift">关于 Swift</h1>
|
||||
<hr>
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="1.2" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="1.2" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,10 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_32">
|
||||
<section class="normal" id="section-gitbook_6">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:numbbbbb<br>校对:shinyzhu, stanzhai</p>
|
||||
<p>翻译:<a href="https://github.com/numbbbbb" target="_blank">numbbbbb</a><br>校对:<a href="https://github.com/shinyzhu" target="_blank">shinyzhu</a>, <a href="https://github.com/stanzhai" target="_blank">stanzhai</a></p>
|
||||
</blockquote>
|
||||
<h1 id="swift-">Swift 初见</h1>
|
||||
<hr>
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="1" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="1" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,7 +587,7 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_34">
|
||||
<section class="normal" id="section-gitbook_8">
|
||||
|
||||
<h1 id="-swift">欢迎使用 Swift</h1>
|
||||
<p>在本章中您将了解 Swift 的特性和开发历史,并对 Swift 有一个初步的了解。</p>
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="2.1" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="2.1" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,10 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_37">
|
||||
<section class="normal" id="section-gitbook_11">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:numbbbbb, lyuka, JaySurplus<br>校对:lslxdx </p>
|
||||
<p>翻译:<a href="https://github.com/numbbbbb" target="_blank">numbbbbb</a>, <a href="https://github.com/lyuka" target="_blank">lyuka</a>, <a href="https://github.com/JaySurplus" target="_blank">JaySurplus</a><br>校对:<a href="https://github.com/lslxdx" target="_blank">lslxdx</a></p>
|
||||
</blockquote>
|
||||
<h1 id="-">基础部分</h1>
|
||||
<hr>
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="2.2" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="2.2" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,10 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_55">
|
||||
<section class="normal" id="section-gitbook_32">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:xielingwang<br>校对:Evilcome </p>
|
||||
<p>翻译:<a href="https://github.com/xielingwang" target="_blank">xielingwang</a><br>校对:<a href="https://github.com/Evilcome" target="_blank">Evilcome</a></p>
|
||||
</blockquote>
|
||||
<h1 id="-">基本运算符</h1>
|
||||
<hr>
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="2.3" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="2.3" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,10 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_39">
|
||||
<section class="normal" id="section-gitbook_13">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:wh1100717<br>校对:Hawstein </p>
|
||||
<p>翻译:<a href="https://github.com/wh1100717" target="_blank">wh1100717</a><br>校对:<a href="https://github.com/Hawstein" target="_blank">Hawstein</a></p>
|
||||
</blockquote>
|
||||
<h1 id="-strings-and-characters-">字符串和字符(Strings and Characters)</h1>
|
||||
<hr>
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="2.4" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="2.4" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,11 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_41">
|
||||
<section class="normal" id="section-gitbook_15">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:zqp
|
||||
校对:shinyzhu</p>
|
||||
<p>翻译:<a href="https://github.com/zqp" target="_blank">zqp</a><br>校对:<a href="https://github.com/shinyzhu" target="_blank">shinyzhu</a>, <a href="https://github.com/stanzhai" target="_blank">stanzhai</a> </p>
|
||||
</blockquote>
|
||||
<h1 id="-collection-types-">集合类型 (Collection Types)</h1>
|
||||
<hr>
|
||||
@ -604,8 +603,7 @@
|
||||
<p>Swift 语言提供经典的数组和字典两种集合类型来存储集合数据。数组用来按顺序存储相同类型的数据。字典虽然无序存储相同类型数据值但是需要由独有的标识符引用和寻址(就是键值对)。</p>
|
||||
<p>Swift 语言里的数组和字典中存储的数据值类型必须明确。 这意味着我们不能把不正确的数据类型插入其中。 同时这也说明我们完全可以对获取出的值类型非常自信。 Swift 对显式类型集合的使用确保了我们的代码对工作所需要的类型非常清楚,也让我们在开发中可以早早地找到任何的类型不匹配错误。</p>
|
||||
<blockquote>
|
||||
<p>注意:
|
||||
Swift 的数组结构在被声明成常量和变量或者被传入函数与方法中时会相对于其他类型展现出不同的特性。 获取更多信息请参见<a href="#mutability_of_collections">集合的可变性</a>与<a href="09_Classes_and_Structures.html#assignment_and_copy_behavior_for_collection_types">集合在赋值和复制中的行为</a>章节。</p>
|
||||
<p>注意:<br>Swift 的数组结构在被声明成常量和变量或者被传入函数与方法中时会相对于其他类型展现出不同的特性。 获取更多信息请参见<a href="#mutability_of_collections">集合的可变性</a>与<a href="09_Classes_and_Structures.html#assignment_and_copy_behavior_for_collection_types">集合在赋值和复制中的行为</a>章节。</p>
|
||||
</blockquote>
|
||||
<p><a name="arrays"></a></p>
|
||||
<h2 id="-">数组</h2>
|
||||
@ -624,8 +622,7 @@ Swift 的数组结构在被声明成常量和变量或者被传入函数与方
|
||||
</code></pre>
|
||||
<p><code>shoppingList</code>变量被声明为“字符串值类型的数组“,记作<code>String[]</code>。 因为这个数组被规定只有<code>String</code>一种数据结构,所以只有<code>String</code>类型可以在其中被存取。 在这里,<code>shoppinglist</code>数组由两个<code>String</code>值(<code>"Eggs"</code> 和<code>"Milk"</code>)构造,并且由字面量定义。</p>
|
||||
<blockquote>
|
||||
<p>注意:
|
||||
<code>Shoppinglist</code>数组被声明为变量(<code>var</code>关键字创建)而不是常量(<code>let</code>创建)是因为以后可能会有更多的数据项被插入其中。</p>
|
||||
<p>注意:<br><code>Shoppinglist</code>数组被声明为变量(<code>var</code>关键字创建)而不是常量(<code>let</code>创建)是因为以后可能会有更多的数据项被插入其中。 </p>
|
||||
</blockquote>
|
||||
<p>在这个例子中,字面量仅仅包含两个<code>String</code>值。匹配了该数组的变量声明(只能包含<code>String</code>的数组),所以这个字面量的分配过程就是允许用两个初始项来构造<code>shoppinglist</code>。</p>
|
||||
<p>由于 Swift 的类型推断机制,当我们用字面量构造只拥有相同类型值数组的时候,我们不必把数组的类型定义清楚。 <code>shoppinglist</code>的构造也可以这样写:</p>
|
||||
@ -672,25 +669,8 @@ Swift 的数组结构在被声明成常量和变量或者被传入函数与方
|
||||
<pre><code class="lang-swift">shoppingList[4...6] = ["Bananas", "Apples"]
|
||||
// shoppingList 现在有六项
|
||||
</code></pre>
|
||||
<p><<<<<<< HEAD</p>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<h1 id="-count-count-0-count-1-">我们不能使用下标语法在数组尾部添加新项。如果我们试着用这种方法对索引越界的数据进行检索或者设置新值的操作,我们会引发一个运行期错误。我们可以使用索引值和数组的<code>count</code>属性进行比较来在使用某个索引之前先检验是否有效。除了当<code>count</code>等于 0 时(说明这是个空数组),最大索引值一直是<code>count - 1</code>,因为数组都是零起索引。</h1>
|
||||
<p>注意:</p>
|
||||
<p>我们不能使用下标语法在数组尾部添加新项。如果我们试着用这种方法对索引越界的数据进行检索或者设置新值的操作,我们会引发一个运行时错误。我们可以使用索引值和数组的<code>count</code>属性进行比较来在使用某个索引之前先检验是否有效。除了当<code>count</code>等于 0 时(说明这是个空数组),最大索引值一直是<code>count - 1</code>,因为数组都是零起索引。</p>
|
||||
<blockquote>
|
||||
<blockquote>
|
||||
<blockquote>
|
||||
<blockquote>
|
||||
<blockquote>
|
||||
<blockquote>
|
||||
<p>a516af6a531a104ec88da0d236ecf389a5ec72af</p>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
<p>注意:<br>我们不能使用下标语法在数组尾部添加新项。如果我们试着用这种方法对索引越界的数据进行检索或者设置新值的操作,我们会引发一个运行期错误。我们可以使用索引值和数组的<code>count</code>属性进行比较来在使用某个索引之前先检验是否有效。除了当<code>count</code>等于 0 时(说明这是个空数组),最大索引值一直是<code>count - 1</code>,因为数组都是零起索引。 </p>
|
||||
</blockquote>
|
||||
<p>调用数组的<code>insert(atIndex:)</code>方法来在某个具体索引值之前添加数据项:</p>
|
||||
<pre><code class="lang-swift">shoppingList.insert("Maple Syrup", atIndex: 0)
|
||||
@ -780,8 +760,7 @@ someInts = []
|
||||
</code></pre>
|
||||
<p><code>airports</code>字典被定义为一种<code>Dictionary<String, String></code>,它意味着这个字典的键和值都是<code>String</code>类型。</p>
|
||||
<blockquote>
|
||||
<p>注意:
|
||||
<code>airports</code>字典被声明为变量(用<code>var</code>关键字)而不是常量(<code>let</code>关键字)因为后来更多的机场信息会被添加到这个示例字典中。</p>
|
||||
<p>注意:<br><code>airports</code>字典被声明为变量(用<code>var</code>关键字)而不是常量(<code>let</code>关键字)因为后来更多的机场信息会被添加到这个示例字典中。 </p>
|
||||
</blockquote>
|
||||
<p><code>airports</code>字典使用字典字面量初始化,包含两个键值对。第一对的键是<code>TYO</code>,值是<code>Tokyo</code>。第二对的键是<code>DUB</code>,值是<code>Dublin</code>。</p>
|
||||
<p>这个字典语句包含了两个<code>String: String</code>类型的键值对。它们对应<code>airports</code>变量声明的类型(一个只有<code>String</code>键和<code>String</code>值的字典)所以这个字典字面量是构造两个初始数据项的<code>airport</code>字典。</p>
|
||||
@ -848,8 +827,8 @@ airports["APL"] = nil
|
||||
}
|
||||
// Airport code: TYO
|
||||
// Airport code: LHR
|
||||
</code></pre>
|
||||
<pre><code class="lang-swift">for airportName in airports.values {
|
||||
|
||||
for airportName in airports.values {
|
||||
println("Airport name: \(airportName)")
|
||||
}
|
||||
// Airport name: Tokyo
|
||||
@ -858,13 +837,12 @@ airports["APL"] = nil
|
||||
<p>如果我们只是需要使用某个字典的键集合或者值集合来作为某个接受<code>Array</code>实例 API 的参数,可以直接使用<code>keys</code>或者<code>values</code>属性直接构造一个新数组:</p>
|
||||
<pre><code class="lang-swift">let airportCodes = Array(airports.keys)
|
||||
// airportCodes is ["TYO", "LHR"]
|
||||
</code></pre>
|
||||
<pre><code class="lang-swift">let airportNames = Array(airports.values)
|
||||
|
||||
let airportNames = Array(airports.values)
|
||||
// airportNames is ["Tokyo", "London Heathrow"]
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>注意:
|
||||
Swift 的字典类型是无序集合类型。其中字典键,值,键值对在遍历的时候会重新排列,而且其中顺序是不固定的。</p>
|
||||
<p>注意:<br>Swift 的字典类型是无序集合类型。其中字典键,值,键值对在遍历的时候会重新排列,而且其中顺序是不固定的。 </p>
|
||||
</blockquote>
|
||||
<p><a name="creating_an_empty_dictionary"></a></p>
|
||||
<h3 id="-">创建一个空字典</h3>
|
||||
@ -880,8 +858,7 @@ namesOfIntegers = [:]
|
||||
// namesOfIntegers 又成为了一个 Int, String类型的空字典
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>注意:
|
||||
在后台,Swift 的数组和字典都是由泛型集合来实现的,想了解更多泛型和集合信息请参见<a href="22_Generics.html">泛型</a>。</p>
|
||||
<p>注意:<br>在后台,Swift 的数组和字典都是由泛型集合来实现的,想了解更多泛型和集合信息请参见<a href="22_Generics.html">泛型</a>。 </p>
|
||||
</blockquote>
|
||||
<p><a name="mutability_of_collections"></a></p>
|
||||
<h2 id="-">集合的可变性</h2>
|
||||
@ -890,8 +867,7 @@ namesOfIntegers = [:]
|
||||
不可变性对数组来说有一点不同,当然我们不能试着改变任何不可变数组的大小,但是我们可以重新设定相对现存索引所对应的值。这使得 Swift 数组在大小被固定的时候依然可以做的很棒。</p>
|
||||
<p>Swift 数组的可变性行为同时影响了数组实例如何被分配和修改,想获取更多信息,请参见<a href="09_Classes_and_Structures.html#assignment_and_copy_behavior_for_collection_types">集合在赋值和复制中的行为</a>。</p>
|
||||
<blockquote>
|
||||
<p>注意:
|
||||
在我们不需要改变数组大小的时候创建不可变数组是很好的习惯。如此 Swift 编译器可以优化我们创建的集合。</p>
|
||||
<p>注意:<br>在我们不需要改变数组大小的时候创建不可变数组是很好的习惯。如此 Swift 编译器可以优化我们创建的集合。 </p>
|
||||
</blockquote>
|
||||
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="2.5" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="2.5" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,10 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_43">
|
||||
<section class="normal" id="section-gitbook_17">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:vclwei, coverxit, NicePiao<br>校对:coverxit, stanzhai</p>
|
||||
<p>翻译:<a href="https://github.com/vclwei" target="_blank">vclwei</a>, <a href="https://github.com/coverxit" target="_blank">coverxit</a>, <a href="https://github.com/NicePiao" target="_blank">NicePiao</a><br>校对:<a href="https://github.com/coverxit" target="_blank">coverxit</a>, <a href="https://github.com/stanzhai" target="_blank">stanzhai</a></p>
|
||||
</blockquote>
|
||||
<h1 id="-">控制流</h1>
|
||||
<hr>
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="2.6" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="2.6" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,10 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_45">
|
||||
<section class="normal" id="section-gitbook_19">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:honghaoz<br>校对:LunaticM </p>
|
||||
<p>翻译:<a href="https://github.com/honghaoz" target="_blank">honghaoz</a><br>校对:<a href="https://github.com/LunaticM" target="_blank">LunaticM</a></p>
|
||||
</blockquote>
|
||||
<h1 id="-functions-">函数(Functions)</h1>
|
||||
<hr>
|
||||
@ -603,7 +603,7 @@
|
||||
<li><a href="#Nested_Functions">函数嵌套(Nested Functions)</a></li>
|
||||
</ul>
|
||||
<p>函数是用来完成特定任务的独立的代码块。你给一个函数起一个合适的名字,用来标示函数做什么,并且当函数需要执行的时候,这个名字会被“调用”。</p>
|
||||
<p>Swift 统一的函数语法足够灵活,可以用来表示任何函数,包括从最简单的没有参数名字的 C 风格函数,到复杂的带局部和外部参数名的 Objective-C 风格函数。参数可以提供默认值,以简化函数调用。参数也可以即当做传入参数,也当做传出参数,也就是说,一旦函数执行结束,传入的参数值可以被修改。</p>
|
||||
<p>Swift 统一的函数语法足够灵活,可以用来表示任何函数,包括从最简单的没有参数名字的 C 风格函数,到复杂的带局部和外部参数名的 Objective-C 风格函数。参数可以提供默认值,以简化函数调用。参数也可以既当做传入参数,也当做传出参数,也就是说,一旦函数执行结束,传入的参数值可以被修改。</p>
|
||||
<p>在 Swift 中,每个函数都有一种类型,包括函数的参数值类型和返回值类型。你可以把函数类型当做任何其他普通变量类型一样处理,这样就可以更简单地把函数当做别的函数的参数,也可以从其他函数中返回函数。函数的定义可以写在在其他函数定义中,这样可以在嵌套函数范围内实现功能封装。</p>
|
||||
<p><a name="Defining_and_Calling_Functions"></a></p>
|
||||
<h2 id="-defining-and-calling-functions-">函数的定义与调用(Defining and Calling Functions)</h2>
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="2.7" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="2.7" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,10 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_47">
|
||||
<section class="normal" id="section-gitbook_21">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:wh1100717<br>校对:lyuka </p>
|
||||
<p>翻译:<a href="https://github.com/wh1100717" target="_blank">wh1100717</a><br>校对:<a href="https://github.com/lyuka" target="_blank">lyuka</a></p>
|
||||
</blockquote>
|
||||
<h1 id="-closures-">闭包(Closures)</h1>
|
||||
<hr>
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="2.8" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="2.8" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,10 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_49">
|
||||
<section class="normal" id="section-gitbook_23">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:yankuangshi<br>校对:shinyzhu </p>
|
||||
<p>翻译:<a href="https://github.com/yankuangshi" target="_blank">yankuangshi</a><br>校对:<a href="https://github.com/shinyzhu" target="_blank">shinyzhu</a></p>
|
||||
</blockquote>
|
||||
<h1 id="-enumerations-">枚举(Enumerations)</h1>
|
||||
<hr>
|
||||
@ -609,7 +609,7 @@
|
||||
<p><a name="enumeration_syntax"></a></p>
|
||||
<h2 id="-">枚举语法</h2>
|
||||
<p>使用<code>enum</code>关键词并且把它们的整个定义放在一对大括号内:</p>
|
||||
<pre><code class="lang-swift">enum SomeEumeration {
|
||||
<pre><code class="lang-swift">enum SomeEnumeration {
|
||||
// enumeration definition goes here
|
||||
}
|
||||
</code></pre>
|
||||
@ -721,7 +721,7 @@ case let .QRCode(productCode):
|
||||
case CarriageReturn = "\r"
|
||||
}
|
||||
</code></pre>
|
||||
<p>在这里,称为<code>ASCIIControlCharacter</code>的枚举的原始值类型被定义为字符型<code>Character</code>,并被设置了一些比较常见的 ASCII 控制字符。字符值的描述请详见字符串和字符<code>Strings and Characters</code>部分。</p>
|
||||
<p>在这里,称为<code>ASCIIControlCharacter</code>的枚举的原始值类型被定义为字符型<code>Character</code>,并被设置了一些比较常见的 ASCII 控制字符。字符值的描述请详见字符串和字符<a href="03_Strings_and_Characters.html"><code>Strings and Characters</code></a>部分。</p>
|
||||
<p>注意,原始值和实例值是不相同的。当你开始在你的代码中定义枚举的时候原始值是被预先填充的值,像上述三个 ASCII 码。对于一个特定的枚举成员,它的原始值始终是相同的。实例值是当你在创建一个基于枚举成员的新常量或变量时才会被设置,并且每次当你这么做得时候,它的值可以是不同的。</p>
|
||||
<p>原始值可以是字符串,字符,或者任何整型值或浮点型值。每个原始值在它的枚举声明中必须是唯一的。当整型值被用于原始值,如果其他枚举成员没有值时,它们会自动递增。</p>
|
||||
<p>下面的枚举是对之前<code>Planet</code>这个枚举的一个细化,利用原始整型值来表示每个 planet 在太阳系中的顺序:</p>
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="2.9" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="2.9" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,10 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_59">
|
||||
<section class="normal" id="section-gitbook_25">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:JaySurplus<br>校对:sg552 </p>
|
||||
<p>翻译:<a href="https://github.com/JaySurplus" target="_blank">JaySurplus</a><br>校对:<a href="https://github.com/sg552" target="_blank">sg552</a></p>
|
||||
</blockquote>
|
||||
<h1 id="-">类和结构体</h1>
|
||||
<p>本页包含内容:</p>
|
||||
@ -609,36 +609,13 @@
|
||||
<p><a name="comparing_classes_and_structures"></a></p>
|
||||
<h3 id="-">类和结构体对比</h3>
|
||||
<p>Swift 中类和结构体有很多共同点。共同处在于:</p>
|
||||
<p><<<<<<< HEAD</p>
|
||||
<ul>
|
||||
<li>定义属性用于储存值</li>
|
||||
<li>定义方法用于提供功能</li>
|
||||
<li>定义附属脚本用于访问值</li>
|
||||
<li>定义构造器用于生成初始化值</li>
|
||||
<li>通过扩展以增加默认实现的功能</li>
|
||||
<li><h1 id="-">符合协议以对某类提供标准功能</h1>
|
||||
</li>
|
||||
<li>定义属性用于储存值</li>
|
||||
<li>定义方法用于提供功能</li>
|
||||
<li>定义下标脚本用于访问值</li>
|
||||
<li>定义构造器用于生成初始化值</li>
|
||||
<li>通过扩展以增加默认实现的功能</li>
|
||||
<li>符合协议以对某类提供标准功能<blockquote>
|
||||
<blockquote>
|
||||
<blockquote>
|
||||
<blockquote>
|
||||
<blockquote>
|
||||
<blockquote>
|
||||
<blockquote>
|
||||
<p>a516af6a531a104ec88da0d236ecf389a5ec72af</p>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
</li>
|
||||
<li>符合协议以对某类提供标准功能</li>
|
||||
</ul>
|
||||
<p>更多信息请参见 <a href="10_Properties.html">属性</a>,<a href="11_Methods.html">方法</a>,<a href="12_Subscripts.html">下标脚本</a>,<a href="14_Initialization.html">初始过程</a>,<a href="20_Extensions.html">扩展</a>,和<a href="21_Protocols.html">协议</a>。</p>
|
||||
<p>与结构体相比,类还有如下的附加功能:</p>
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="2.10" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="2.10" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,10 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_57">
|
||||
<section class="normal" id="section-gitbook_26">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:shinyzhu<br>校对:pp-prog </p>
|
||||
<p>翻译:<a href="https://github.com/shinyzhu" target="_blank">shinyzhu</a><br>校对:<a href="https://github.com/pp-prog" target="_blank">pp-prog</a></p>
|
||||
</blockquote>
|
||||
<h1 id="-properties-">属性 (Properties)</h1>
|
||||
<hr>
|
||||
@ -736,10 +736,6 @@ println("square.origin is now at (\(square.origin.x), \(square.origin.y))&q
|
||||
<p><a name="readonly_computed_properties"></a></p>
|
||||
<h3 id="-">只读计算属性</h3>
|
||||
<p>只有 getter 没有 setter 的计算属性就是<em>只读计算属性</em>。只读计算属性总是返回一个值,可以通过点运算符访问,但不能设置新的值。</p>
|
||||
<p><<<<<<< HEAD</p>
|
||||
<blockquote>
|
||||
<p>注意: </p>
|
||||
<h1 id="-var-let-">必须使用<code>var</code>关键字定义计算属性,包括只读计算属性,因为他们的值不是固定的。<code>let</code>关键字只用来声明常量属性,表示初始化后再也无法修改的值。 </h1>
|
||||
<p>注意:</p>
|
||||
<p>必须使用<code>var</code>关键字定义计算属性,包括只读计算属性,因为它们的值不是固定的。<code>let</code>关键字只用来声明常量属性,表示初始化后再也无法修改的值。</p>
|
||||
<blockquote>
|
||||
@ -782,10 +778,6 @@ println("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
|
||||
</ul>
|
||||
<p><code>willSet</code>监视器会将新的属性值作为固定参数传入,在<code>willSet</code>的实现代码中可以为这个参数指定一个名称,如果不指定则参数仍然可用,这时使用默认名称<code>newValue</code>表示。</p>
|
||||
<p>类似地,<code>didSet</code>监视器会将旧的属性值作为参数传入,可以为该参数命名或者使用默认参数名<code>oldValue</code>。</p>
|
||||
<p><<<<<<< HEAD</p>
|
||||
<blockquote>
|
||||
<p>注意: </p>
|
||||
<h1 id="-willset-didset-"><code>willSet</code>和<code>didSet</code>监视器在属性初始化过程中不会被调用,他们只会当属性的值在初始化之外的地方被设置时被调用。 </h1>
|
||||
<p>注意:</p>
|
||||
<p><code>willSet</code>和<code>didSet</code>监视器在属性初始化过程中不会被调用,它们只会当属性的值在初始化之外的地方被设置时被调用。</p>
|
||||
<blockquote>
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="2.11" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="2.11" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,10 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_51">
|
||||
<section class="normal" id="section-gitbook_28">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:pp-prog<br>校对:zqp </p>
|
||||
<p>翻译:<a href="https://github.com/pp-prog" target="_blank">pp-prog</a><br>校对:<a href="https://github.com/zqp" target="_blank">zqp</a></p>
|
||||
</blockquote>
|
||||
<h1 id="-methods-">方法(Methods)</h1>
|
||||
<hr>
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="2.12" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="2.12" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,10 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_53">
|
||||
<section class="normal" id="section-gitbook_30">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:siemenliu<br>校对:zq54zquan </p>
|
||||
<p>翻译:<a href="https://github.com/siemenliu" target="_blank">siemenliu</a><br>校对:<a href="https://github.com/zq54zquan" target="_blank">zq54zquan</a></p>
|
||||
</blockquote>
|
||||
<h1 id="-subscripts-">下标脚本(Subscripts)</h1>
|
||||
<hr>
|
||||
@ -602,23 +602,8 @@
|
||||
</ul>
|
||||
<p><em>下标脚本</em> 可以定义在类(Class)、结构体(structure)和枚举(enumeration)这些目标中,可以认为是访问对象、集合或序列的快捷方式,不需要再调用实例的特定的赋值和访问方法。举例来说,用下标脚本访问一个数组(Array)实例中的元素可以这样写 <code>someArray[index]</code> ,访问字典(Dictionary)实例中的元素可以这样写 <code>someDictionary[key]</code>。</p>
|
||||
<p>对于同一个目标可以定义多个下标脚本,通过索引值类型的不同来进行重载,而且索引值的个数可以是多个。</p>
|
||||
<p><<<<<<< HEAD</p>
|
||||
<h1 id="-">> 译者:这里附属脚本重载在本小节中原文并没有任何演示 </h1>
|
||||
<blockquote>
|
||||
<p>译者:这里下标脚本重载在本小节中原文并没有任何演示</p>
|
||||
<blockquote>
|
||||
<blockquote>
|
||||
<blockquote>
|
||||
<blockquote>
|
||||
<blockquote>
|
||||
<blockquote>
|
||||
<p>a516af6a531a104ec88da0d236ecf389a5ec72af</p>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
<p>译者:这里附属脚本重载在本小节中原文并没有任何演示 </p>
|
||||
</blockquote>
|
||||
<p><a name="subscript_syntax"></a></p>
|
||||
<h2 id="-">下标脚本语法</h2>
|
||||
@ -652,25 +637,8 @@ println("3的6倍是\(threeTimesTable[6])")
|
||||
</code></pre>
|
||||
<p>在上例中,通过<code>TimesTable</code>结构体创建了一个用来表示索引值三倍的实例。数值<code>3</code>作为结构体<code>构造函数</code>入参初始化实例成员<code>multiplier</code>。</p>
|
||||
<p>你可以通过下标脚本来得到结果,比如<code>threeTimesTable[6]</code>。这条语句访问了<code>threeTimesTable</code>的第六个元素,返回<code>6</code>的<code>3</code>倍即<code>18</code>。</p>
|
||||
<p><<<<<<< HEAD</p>
|
||||
<blockquote>
|
||||
<p>注意: </p>
|
||||
<h1 id="-timestable-threetimestable-someindex-"><code>TimesTable</code>例子是基于一个固定的数学公式。它并不适合开放写权限来对<code>threeTimesTable[someIndex]</code>进行赋值操作,这也是为什么附属脚本只定义为只读的原因。 </h1>
|
||||
<p>注意:</p>
|
||||
<p><code>TimesTable</code>例子是基于一个固定的数学公式。它并不适合开放写权限来对<code>threeTimesTable[someIndex]</code>进行赋值操作,这也是为什么下标脚本只定义为只读的原因。</p>
|
||||
<blockquote>
|
||||
<blockquote>
|
||||
<blockquote>
|
||||
<blockquote>
|
||||
<blockquote>
|
||||
<blockquote>
|
||||
<p>a516af6a531a104ec88da0d236ecf389a5ec72af</p>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
<p>注意:<br><code>TimesTable</code>例子是基于一个固定的数学公式。它并不适合开放写权限来对<code>threeTimesTable[someIndex]</code>进行赋值操作,这也是为什么附属脚本只定义为只读的原因。 </p>
|
||||
</blockquote>
|
||||
<p><a name="subscript_usage"></a></p>
|
||||
<h2 id="-">下标脚本用法</h2>
|
||||
@ -681,25 +649,8 @@ numberOfLegs["bird"] = 2
|
||||
</code></pre>
|
||||
<p>上例定义一个名为<code>numberOfLegs</code>的变量并用一个字典字面量初始化出了包含三对键值的字典实例。<code>numberOfLegs</code>的字典存放值类型推断为<code>Dictionary<String, Int></code>。字典实例创建完成之后通过下标脚本的方式将整型值<code>2</code>赋值到字典实例的索引为<code>bird</code>的位置中。</p>
|
||||
<p>更多关于字典(Dictionary)下标脚本的信息请参考<a href="../chapter2/04_Collection_Types.html">读取和修改字典</a></p>
|
||||
<p><<<<<<< HEAD</p>
|
||||
<blockquote>
|
||||
<p>注意: </p>
|
||||
<h1 id="swift-get-int-numberoflegs-int-int-nil-nil-">Swift 中字典的附属脚本实现中,在<code>get</code>部分返回值是<code>Int?</code>,上例中的<code>numberOfLegs</code>字典通过附属脚本返回的是一个<code>Int?</code>或者说“可选的int”,不是每个字典的索引都能得到一个整型值,对于没有设过值的索引的访问返回的结果就是<code>nil</code>;同样想要从字典实例中删除某个索引下的值也只需要给这个索引赋值为<code>nil</code>即可。 </h1>
|
||||
<p>注意:</p>
|
||||
<p>Swift 中字典的下标脚本实现中,在<code>get</code>部分返回值是<code>Int?</code>,上例中的<code>numberOfLegs</code>字典通过下标脚本返回的是一个<code>Int?</code>或者说“可选的int”,不是每个字典的索引都能得到一个整型值,对于没有设过值的索引的访问返回的结果就是<code>nil</code>;同样想要从字典实例中删除某个索引下的值也只需要给这个索引赋值为<code>nil</code>即可。</p>
|
||||
<blockquote>
|
||||
<blockquote>
|
||||
<blockquote>
|
||||
<blockquote>
|
||||
<blockquote>
|
||||
<blockquote>
|
||||
<p>a516af6a531a104ec88da0d236ecf389a5ec72af</p>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
<p>注意:<br>Swift 中字典的附属脚本实现中,在<code>get</code>部分返回值是<code>Int?</code>,上例中的<code>numberOfLegs</code>字典通过附属脚本返回的是一个<code>Int?</code>或者说“可选的int”,不是每个字典的索引都能得到一个整型值,对于没有设过值的索引的访问返回的结果就是<code>nil</code>;同样想要从字典实例中删除某个索引下的值也只需要给这个索引赋值为<code>nil</code>即可。 </p>
|
||||
</blockquote>
|
||||
<p><a name="subscript_options"></a></p>
|
||||
<h2 id="-">下标脚本选项</h2>
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="2.13" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="2.13" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,10 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_35">
|
||||
<section class="normal" id="section-gitbook_9">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:Hawstein<br>校对:menlongsheng </p>
|
||||
<p>翻译:<a href="https://github.com/Hawstein" target="_blank">Hawstein</a><br>校对:<a href="https://github.com/menlongsheng" target="_blank">menlongsheng</a></p>
|
||||
</blockquote>
|
||||
<h1 id="-inheritance-">继承(Inheritance)</h1>
|
||||
<hr>
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="2.14" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="2.14" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,10 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_60">
|
||||
<section class="normal" id="section-gitbook_34">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:lifedim<br>校对:lifedim </p>
|
||||
<p>翻译:<a href="https://github.com/lifedim" target="_blank">lifedim</a><br>校对:<a href="https://github.com/lifedim" target="_blank">lifedim</a></p>
|
||||
</blockquote>
|
||||
<h1 id="-initialization-">构造过程(Initialization)</h1>
|
||||
<hr>
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="2.15" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="2.15" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,10 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_62">
|
||||
<section class="normal" id="section-gitbook_36">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:bruce0505<br>校对:fd5788 </p>
|
||||
<p>翻译:<a href="https://github.com/bruce0505" target="_blank">bruce0505</a><br>校对:<a href="https://github.com/fd5788" target="_blank">fd5788</a></p>
|
||||
</blockquote>
|
||||
<h1 id="-deinitialization-">析构过程(Deinitialization)</h1>
|
||||
<hr>
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="2.16" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="2.16" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,11 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_64">
|
||||
<section class="normal" id="section-gitbook_38">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:TimothyYe</p>
|
||||
<p>校对:Hawstein</p>
|
||||
<p>翻译:<a href="https://github.com/TimothyYe" target="_blank">TimothyYe</a><br>校对:<a href="https://github.com/Hawstein" target="_blank">Hawstein</a></p>
|
||||
</blockquote>
|
||||
<h1 id="-">自动引用计数</h1>
|
||||
<hr>
|
||||
@ -607,8 +606,7 @@
|
||||
<p>Swift 使用自动引用计数(ARC)这一机制来跟踪和管理你的应用程序的内存。通常情况下,Swift 的内存管理机制会一直起着作用,你无须自己来考虑内存的管理。ARC 会在类的实例不再被使用时,自动释放其占用的内存。</p>
|
||||
<p>然而,在少数情况下,ARC 为了能帮助你管理内存,需要更多的关于你的代码之间关系的信息。本章描述了这些情况,并且为你示范怎样启用 ARC 来管理你的应用程序的内存。</p>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<p>引用计数仅仅应用于类的实例。结构体和枚举类型是值类型,不是引用类型,也不是通过引用的方式存储和传递。</p>
|
||||
<p>注意:<br>引用计数仅仅应用于类的实例。结构体和枚举类型是值类型,不是引用类型,也不是通过引用的方式存储和传递。</p>
|
||||
</blockquote>
|
||||
<p><a name="how_arc_works"></a></p>
|
||||
<h2 id="-">自动引用计数的工作机制</h2>
|
||||
@ -619,77 +617,86 @@
|
||||
<p><a name="arc_in_action"></a></p>
|
||||
<h2 id="-">自动引用计数实践</h2>
|
||||
<p>下面的例子展示了自动引用计数的工作机制。例子以一个简单的<code>Person</code>类开始,并定义了一个叫<code>name</code>的常量属性:</p>
|
||||
<pre><code> class Person {
|
||||
let name: String
|
||||
|
||||
init(name: String) {
|
||||
self.name = name
|
||||
println("\(name) is being initialized")
|
||||
}
|
||||
|
||||
deinit {
|
||||
println("\(name) is being deinitialized")
|
||||
}
|
||||
<pre><code class="lang-swift">class Person {
|
||||
let name: String
|
||||
init(name: String) {
|
||||
self.name = name
|
||||
println("\(name) is being initialized")
|
||||
}
|
||||
</code></pre><p><code>Person</code>类有一个构造函数,此构造函数为实例的<code>name</code>属性赋值并打印出信息,以表明初始化过程生效。<code>Person</code>类同时也拥有析构函数,同样会在实例被销毁的时候打印出信息。</p>
|
||||
deinit {
|
||||
println("\(name) is being deinitialized")
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p><code>Person</code>类有一个构造函数,此构造函数为实例的<code>name</code>属性赋值并打印出信息,以表明初始化过程生效。<code>Person</code>类同时也拥有析构函数,同样会在实例被销毁的时候打印出信息。</p>
|
||||
<p>接下来的代码片段定义了三个类型为<code>Person?</code>的变量,用来按照代码片段中的顺序,为新的<code>Person</code>实例建立多个引用。由于这些变量是被定义为可选类型(Person?,而不是Person),它们的值会被自动初始化为<code>nil</code>,目前还不会引用到<code>Person</code>类的实例。</p>
|
||||
<pre><code> var reference1: Person?
|
||||
var reference2: Person?
|
||||
var reference3: Person?
|
||||
</code></pre><p>现在你可以创建<code>Person</code>类的新实例,并且将它赋值给三个变量其中的一个:</p>
|
||||
<pre><code> reference1 = Person(name: "John Appleseed")
|
||||
// prints "John Appleseed is being initialized”
|
||||
</code></pre><p>应当注意到当你调用<code>Person</code>类的构造函数的时候,"John Appleseed is being initialized”会被打印出来。由此可以确定构造函数被执行。</p>
|
||||
<pre><code class="lang-swift">var reference1: Person?
|
||||
var reference2: Person?
|
||||
var reference3: Person?
|
||||
</code></pre>
|
||||
<p>现在你可以创建<code>Person</code>类的新实例,并且将它赋值给三个变量其中的一个:</p>
|
||||
<pre><code class="lang-swift">reference1 = Person(name: "John Appleseed")
|
||||
// prints "John Appleseed is being initialized”
|
||||
</code></pre>
|
||||
<p>应当注意到当你调用<code>Person</code>类的构造函数的时候,"John Appleseed is being initialized”会被打印出来。由此可以确定构造函数被执行。</p>
|
||||
<p>由于<code>Person</code>类的新实例被赋值给了<code>reference1</code>变量,所以<code>reference1</code>到<code>Person</code>类的新实例之间建立了一个强引用。正是因为这个强引用,ARC 会保证<code>Person</code>实例被保持在内存中不被销毁。</p>
|
||||
<p>如果你将同样的<code>Person</code>实例也赋值给其他两个变量,该实例又会多出两个强引用:</p>
|
||||
<pre><code> reference2 = reference1
|
||||
reference3 = reference1
|
||||
</code></pre><p>现在这个<code>Person</code>实例已经有三个强引用了。</p>
|
||||
<pre><code class="lang-swift">reference2 = reference1
|
||||
reference3 = reference1
|
||||
</code></pre>
|
||||
<p>现在这个<code>Person</code>实例已经有三个强引用了。</p>
|
||||
<p>如果你通过给两个变量赋值<code>nil</code>的方式断开两个强引用()包括最先的那个强引用),只留下一个强引用,<code>Person</code>实例不会被销毁:</p>
|
||||
<pre><code>reference2 = nil
|
||||
<pre><code class="lang-swift">reference2 = nil
|
||||
reference3 = nil
|
||||
</code></pre><p>ARC 会在第三个,也即最后一个强引用被断开的时候,销毁<code>Person</code>实例,这也意味着你不再使用这个<code>Person</code>实例:</p>
|
||||
<pre><code>reference3 = nil
|
||||
</code></pre>
|
||||
<p>ARC 会在第三个,也即最后一个强引用被断开的时候,销毁<code>Person</code>实例,这也意味着你不再使用这个<code>Person</code>实例:</p>
|
||||
<pre><code class="lang-swift">reference3 = nil
|
||||
// prints "John Appleseed is being deinitialized"
|
||||
</code></pre><p><a name="strong_reference_cycles_between_class_instances"></a></p>
|
||||
</code></pre>
|
||||
<p><a name="strong_reference_cycles_between_class_instances"></a></p>
|
||||
<h2 id="-">类实例之间的循环强引用</h2>
|
||||
<p>在上面的例子中,ARC 会跟踪你所新创建的<code>Person</code>实例的引用数量,并且会在<code>Person</code>实例不再被需要时销毁它。</p>
|
||||
<p>然而,我们可能会写出这样的代码,一个类永远不会有0个强引用。这种情况发生在两个类实例互相保持对方的强引用,并让对方不被销毁。这就是所谓的循环强引用。</p>
|
||||
<p>你可以通过定义类之间的关系为弱引用或者无主引用,以此替代强引用,从而解决循环强引用的问题。具体的过程在<a href="#resolving_strong_reference_cycles_between_class_instances">解决类实例之间的循环强引用</a>中有描述。不管怎样,在你学习怎样解决循环强引用之前,很有必要了解一下它是怎样产生的。</p>
|
||||
<p>下面展示了一个不经意产生循环强引用的例子。例子定义了两个类:<code>Person</code>和<code>Apartment</code>,用来建模公寓和它其中的居民:</p>
|
||||
<pre><code>class Person {
|
||||
<pre><code class="lang-swift">class Person {
|
||||
let name: String
|
||||
init(name: String) { self.name = name }
|
||||
var apartment: Apartment?
|
||||
deinit { println("\(name) is being deinitialized") }
|
||||
}
|
||||
|
||||
class Apartment {
|
||||
</code></pre>
|
||||
<pre><code class="lang-swift">class Apartment {
|
||||
let number: Int
|
||||
init(number: Int) { self.number = number }
|
||||
var tenant: Person?
|
||||
deinit { println("Apartment #\(number) is being deinitialized") }
|
||||
}
|
||||
</code></pre><p>每一个<code>Person</code>实例有一个类型为<code>String</code>,名字为<code>name</code>的属性,并有一个可选的初始化为<code>nil</code>的<code>apartment</code>属性。<code>apartment</code>属性是可选的,因为一个人并不总是拥有公寓。</p>
|
||||
</code></pre>
|
||||
<p>每一个<code>Person</code>实例有一个类型为<code>String</code>,名字为<code>name</code>的属性,并有一个可选的初始化为<code>nil</code>的<code>apartment</code>属性。<code>apartment</code>属性是可选的,因为一个人并不总是拥有公寓。</p>
|
||||
<p>类似的,每个<code>Apartment</code>实例有一个叫<code>number</code>,类型为<code>Int</code>的属性,并有一个可选的初始化为<code>nil</code>的<code>tenant</code>属性。<code>tenant</code>属性是可选的,因为一栋公寓并不总是有居民。</p>
|
||||
<p>这两个类都定义了析构函数,用以在类实例被析构的时候输出信息。这让你能够知晓<code>Person</code>和<code>Apartment</code>的实例是否像预期的那样被销毁。</p>
|
||||
<p>接下来的代码片段定义了两个可选类型的变量<code>john</code>和<code>number73</code>,并分别被设定为下面的<code>Apartment</code>和<code>Person</code>的实例。这两个变量都被初始化为<code>nil</code>,并为可选的:</p>
|
||||
<pre><code>var john: Person?
|
||||
<pre><code class="lang-swift">var john: Person?
|
||||
var number73: Apartment?
|
||||
</code></pre><p>现在你可以创建特定的<code>Person</code>和<code>Apartment</code>实例并将类实例赋值给<code>john</code>和<code>number73</code>变量:</p>
|
||||
<pre><code>john = Person(name: "John Appleseed")
|
||||
</code></pre>
|
||||
<p>现在你可以创建特定的<code>Person</code>和<code>Apartment</code>实例并将类实例赋值给<code>john</code>和<code>number73</code>变量:</p>
|
||||
<pre><code class="lang-swift">john = Person(name: "John Appleseed")
|
||||
number73 = Apartment(number: 73)
|
||||
</code></pre><p>在两个实例被创建和赋值后,下图表现了强引用的关系。变量<code>john</code>现在有一个指向<code>Person</code>实例的强引用,而变量<code>number73</code>有一个指向<code>Apartment</code>实例的强引用:</p>
|
||||
</code></pre>
|
||||
<p>在两个实例被创建和赋值后,下图表现了强引用的关系。变量<code>john</code>现在有一个指向<code>Person</code>实例的强引用,而变量<code>number73</code>有一个指向<code>Apartment</code>实例的强引用:</p>
|
||||
<p><img src="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/referenceCycle01_2x.png" alt=""></p>
|
||||
<p>现在你能够将这两个实例关联在一起,这样人就能有公寓住了,而公寓也有了房客。注意感叹号是用来展开和访问可选变量<code>john</code>和<code>number73</code>中的实例,这样实例的属性才能被赋值:</p>
|
||||
<pre><code>john!.apartment = number73
|
||||
<pre><code class="lang-swift">john!.apartment = number73
|
||||
number73!.tenant = john
|
||||
</code></pre><p>在将两个实例联系在一起之后,强引用的关系如图所示:</p>
|
||||
</code></pre>
|
||||
<p>在将两个实例联系在一起之后,强引用的关系如图所示:</p>
|
||||
<p><img src="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/referenceCycle02_2x.png" alt=""></p>
|
||||
<p>不幸的是,将这两个实例关联在一起之后,一个循环强引用被创建了。<code>Person</code>实例现在有了一个指向<code>Apartment</code>实例的强引用,而<code>Apartment</code>实例也有了一个指向<code>Person</code>实例的强引用。因此,当你断开<code>john</code>和<code>number73</code>变量所持有的强引用时,引用计数并不会降为 0,实例也不会被 ARC 销毁:</p>
|
||||
<pre><code>john = nil
|
||||
<pre><code class="lang-swift">john = nil
|
||||
number73 = nil
|
||||
</code></pre><p>注意,当你把这两个变量设为<code>nil</code>时,没有任何一个析构函数被调用。强引用循环阻止了<code>Person</code>和<code>Apartment</code>类实例的销毁,并在你的应用程序中造成了内存泄漏。</p>
|
||||
</code></pre>
|
||||
<p>注意,当你把这两个变量设为<code>nil</code>时,没有任何一个析构函数被调用。强引用循环阻止了<code>Person</code>和<code>Apartment</code>类实例的销毁,并在你的应用程序中造成了内存泄漏。</p>
|
||||
<p>在你将<code>john</code>和<code>number73</code>赋值为<code>nil</code>后,强引用关系如下图:</p>
|
||||
<p><img src="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/referenceCycle03_2x.png" alt=""></p>
|
||||
<p><code>Person</code>和<code>Apartment</code>实例之间的强引用关系保留了下来并且不会被断开。</p>
|
||||
@ -702,27 +709,27 @@ number73 = nil
|
||||
<p>弱引用不会牢牢保持住引用的实例,并且不会阻止 ARC 销毁被引用的实例。这种行为阻止了引用变为循环强引用。声明属性或者变量时,在前面加上<code>weak</code>关键字表明这是一个弱引用。</p>
|
||||
<p>在实例的生命周期中,如果某些时候引用没有值,那么弱引用可以阻止循环强引用。如果引用总是有值,则可以使用无主引用,在<a href="#2">无主引用</a>中有描述。在上面<code>Apartment</code>的例子中,一个公寓的生命周期中,有时是没有“居民”的,因此适合使用弱引用来解决循环强引用。</p>
|
||||
<blockquote>
|
||||
<p>注意:
|
||||
弱引用必须被声明为变量,表明其值能在运行时被修改。弱引用不能被声明为常量。</p>
|
||||
<p>注意:<br>弱引用必须被声明为变量,表明其值能在运行时被修改。弱引用不能被声明为常量。 </p>
|
||||
</blockquote>
|
||||
<p>因为弱引用可以没有值,你必须将每一个弱引用声明为可选类型。可选类型是在 Swift 语言中推荐的用来表示可能没有值的类型。</p>
|
||||
<p>因为弱引用不会保持所引用的实例,即使引用存在,实例也有可能被销毁。因此,ARC 会在引用的实例被销毁后自动将其赋值为<code>nil</code>。你可以像其他可选值一样,检查弱引用的值是否存在,你永远也不会遇到被销毁了而不存在的实例。</p>
|
||||
<p>下面的例子跟上面<code>Person</code>和<code>Apartment</code>的例子一致,但是有一个重要的区别。这一次,<code>Apartment</code>的<code>tenant</code>属性被声明为弱引用:</p>
|
||||
<pre><code>class Person {
|
||||
<pre><code class="lang-swift">class Person {
|
||||
let name: String
|
||||
init(name: String) { self.name = name }
|
||||
var apartment: Apartment?
|
||||
deinit { println("\(name) is being deinitialized") }
|
||||
}
|
||||
|
||||
class Apartment {
|
||||
</code></pre>
|
||||
<pre><code class="lang-swift">class Apartment {
|
||||
let number: Int
|
||||
init(number: Int) { self.number = number }
|
||||
weak var tenant: Person?
|
||||
deinit { println("Apartment #\(number) is being deinitialized") }
|
||||
}
|
||||
</code></pre><p>然后跟之前一样,建立两个变量(john和number73)之间的强引用,并关联两个实例:</p>
|
||||
<pre><code>var john: Person?
|
||||
</code></pre>
|
||||
<p>然后跟之前一样,建立两个变量(john和number73)之间的强引用,并关联两个实例:</p>
|
||||
<pre><code class="lang-swift">var john: Person?
|
||||
var number73: Apartment?
|
||||
|
||||
john = Person(name: "John Appleseed")
|
||||
@ -730,33 +737,34 @@ number73 = Apartment(number: 73)
|
||||
|
||||
john!.apartment = number73
|
||||
number73!.tenant = john
|
||||
</code></pre><p>现在,两个关联在一起的实例的引用关系如下图所示:</p>
|
||||
</code></pre>
|
||||
<p>现在,两个关联在一起的实例的引用关系如下图所示:</p>
|
||||
<p><img src="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/weakReference01_2x.png" alt=""></p>
|
||||
<p><code>Person</code>实例依然保持对<code>Apartment</code>实例的强引用,但是<code>Apartment</code>实例只是对<code>Person</code>实例的弱引用。这意味着当你断开<code>john</code>变量所保持的强引用时,再也没有指向<code>Person</code>实例的强引用了:</p>
|
||||
<p><img src="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/weakReference02_2x.png" alt=""></p>
|
||||
<p>由于再也没有指向<code>Person</code>实例的强引用,该实例会被销毁:</p>
|
||||
<pre><code>john = nil
|
||||
<pre><code class="lang-swift">john = nil
|
||||
// prints "John Appleseed is being deinitialized"
|
||||
</code></pre><p>唯一剩下的指向<code>Apartment</code>实例的强引用来自于变量<code>number73</code>。如果你断开这个强引用,再也没有指向<code>Apartment</code>实例的强引用了:</p>
|
||||
</code></pre>
|
||||
<p>唯一剩下的指向<code>Apartment</code>实例的强引用来自于变量<code>number73</code>。如果你断开这个强引用,再也没有指向<code>Apartment</code>实例的强引用了:</p>
|
||||
<p><img src="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/weakReference03_2x.png" alt=""></p>
|
||||
<p>由于再也没有指向<code>Apartment</code>实例的强引用,该实例也会被销毁:</p>
|
||||
<pre><code>number73 = nil
|
||||
<pre><code class="lang-swift">number73 = nil
|
||||
// prints "Apartment #73 is being deinitialized"
|
||||
</code></pre><p>上面的两段代码展示了变量<code>john</code>和<code>number73</code>在被赋值为<code>nil</code>后,<code>Person</code>实例和<code>Apartment</code>实例的析构函数都打印出“销毁”的信息。这证明了引用循环被打破了。</p>
|
||||
</code></pre>
|
||||
<p>上面的两段代码展示了变量<code>john</code>和<code>number73</code>在被赋值为<code>nil</code>后,<code>Person</code>实例和<code>Apartment</code>实例的析构函数都打印出“销毁”的信息。这证明了引用循环被打破了。</p>
|
||||
<p><a name="2"></a></p>
|
||||
<h3 id="-">无主引用</h3>
|
||||
<p>和弱引用类似,无主引用不会牢牢保持住引用的实例。和弱引用不同的是,无主引用是永远有值的。因此,无主引用总是被定义为非可选类型(non-optional type)。你可以在声明属性或者变量时,在前面加上关键字<code>unowned</code>表示这是一个无主引用。</p>
|
||||
<p>由于无主引用是非可选类型,你不需要在使用它的时候将它展开。无主引用总是可以被直接访问。不过 ARC 无法在实例被销毁后将无主引用设为<code>nil</code>,因为非可选类型的变量不允许被赋值为<code>nil</code>。</p>
|
||||
<blockquote>
|
||||
<p>注意:
|
||||
如果你试图在实例被销毁后,访问该实例的无主引用,会触发运行时错误。使用无主引用,你必须确保引用始终指向一个未销毁的实例。</p>
|
||||
<p>还需要注意的是如果你试图访问实例已经被销毁的无主引用,程序会直接崩溃,而不会发生无法预期的行为。所以你应当避免这样的事情发生。</p>
|
||||
<p>注意:<br>如果你试图在实例被销毁后,访问该实例的无主引用,会触发运行时错误。使用无主引用,你必须确保引用始终指向一个未销毁的实例。<br>还需要注意的是如果你试图访问实例已经被销毁的无主引用,程序会直接崩溃,而不会发生无法预期的行为。所以你应当避免这样的事情发生。 </p>
|
||||
</blockquote>
|
||||
<p>下面的例子定义了两个类,<code>Customer</code>和<code>CreditCard</code>,模拟了银行客户和客户的信用卡。这两个类中,每一个都将另外一个类的实例作为自身的属性。这种关系会潜在的创造循环强引用。</p>
|
||||
<p><code>Customer</code>和<code>CreditCard</code>之间的关系与前面弱引用例子中<code>Apartment</code>和<code>Person</code>的关系截然不同。在这个数据模型中,一个客户可能有或者没有信用卡,但是一张信用卡总是关联着一个客户。为了表示这种关系,<code>Customer</code>类有一个可选类型的<code>card</code>属性,但是<code>CreditCard</code>类有一个非可选类型的<code>customer</code>属性。</p>
|
||||
<p>此外,只能通过将一个<code>number</code>值和<code>customer</code>实例传递给<code>CreditCard</code>构造函数的方式来创建<code>CreditCard</code>实例。这样可以确保当创建<code>CreditCard</code>实例时总是有一个<code>customer</code>实例与之关联。</p>
|
||||
<p>由于信用卡总是关联着一个客户,因此将<code>customer</code>属性定义为无主引用,用以避免循环强引用:</p>
|
||||
<pre><code>class Customer {
|
||||
<pre><code class="lang-swift">class Customer {
|
||||
let name: String
|
||||
var card: CreditCard?
|
||||
init(name: String) {
|
||||
@ -764,8 +772,8 @@ number73!.tenant = john
|
||||
}
|
||||
deinit { println("\(name) is being deinitialized") }
|
||||
}
|
||||
|
||||
class CreditCard {
|
||||
</code></pre>
|
||||
<pre><code class="lang-swift">class CreditCard {
|
||||
let number: Int
|
||||
unowned let customer: Customer
|
||||
init(number: Int, customer: Customer) {
|
||||
@ -774,21 +782,25 @@ class CreditCard {
|
||||
}
|
||||
deinit { println("Card #\(number) is being deinitialized") }
|
||||
}
|
||||
</code></pre><p>下面的代码片段定义了一个叫<code>john</code>的可选类型<code>Customer</code>变量,用来保存某个特定客户的引用。由于是可选类型,所以变量被初始化为<code>nil</code>。</p>
|
||||
<pre><code>var john: Customer?
|
||||
</code></pre><p>现在你可以创建<code>Customer</code>类的实例,用它初始化<code>CreditCard</code>实例,并将新创建的<code>CreditCard</code>实例赋值为客户的<code>card</code>属性。</p>
|
||||
<pre><code>john = Customer(name: "John Appleseed")
|
||||
</code></pre>
|
||||
<p>下面的代码片段定义了一个叫<code>john</code>的可选类型<code>Customer</code>变量,用来保存某个特定客户的引用。由于是可选类型,所以变量被初始化为<code>nil</code>。</p>
|
||||
<pre><code class="lang-swift">var john: Customer?
|
||||
</code></pre>
|
||||
<p>现在你可以创建<code>Customer</code>类的实例,用它初始化<code>CreditCard</code>实例,并将新创建的<code>CreditCard</code>实例赋值为客户的<code>card</code>属性。</p>
|
||||
<pre><code class="lang-swift">john = Customer(name: "John Appleseed")
|
||||
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
|
||||
</code></pre><p>在你关联两个实例后,它们的引用关系如下图所示:</p>
|
||||
</code></pre>
|
||||
<p>在你关联两个实例后,它们的引用关系如下图所示:</p>
|
||||
<p><img src="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/unownedReference01_2x.png" alt=""></p>
|
||||
<p><code>Customer</code>实例持有对<code>CreditCard</code>实例的强引用,而<code>CreditCard</code>实例持有对<code>Customer</code>实例的无主引用。</p>
|
||||
<p>由于<code>customer</code>的无主引用,当你断开<code>john</code>变量持有的强引用时,再也没有指向<code>Customer</code>实例的强引用了:</p>
|
||||
<p><img src="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/unownedReference02_2x.png" alt=""></p>
|
||||
<p>由于再也没有指向<code>Customer</code>实例的强引用,该实例被销毁了。其后,再也没有指向<code>CreditCard</code>实例的强引用,该实例也随之被销毁了:</p>
|
||||
<pre><code>john = nil
|
||||
<pre><code class="lang-swift">john = nil
|
||||
// prints "John Appleseed is being deinitialized"
|
||||
// prints "Card #1234567890123456 is being deinitialized"
|
||||
</code></pre><p>最后的代码展示了在<code>john</code>变量被设为<code>nil</code>后<code>Customer</code>实例和<code>CreditCard</code>实例的构造函数都打印出了“销毁”的信息。</p>
|
||||
</code></pre>
|
||||
<p>最后的代码展示了在<code>john</code>变量被设为<code>nil</code>后<code>Customer</code>实例和<code>CreditCard</code>实例的构造函数都打印出了“销毁”的信息。</p>
|
||||
<h3 id="-">无主引用以及隐式解析可选属性</h3>
|
||||
<p>上面弱引用和无主引用的例子涵盖了两种常用的需要打破循环强引用的场景。</p>
|
||||
<p><code>Person</code>和<code>Apartment</code>的例子展示了两个属性的值都允许为<code>nil</code>,并会潜在的产生循环强引用。这种场景最适合用弱引用来解决。</p>
|
||||
@ -796,7 +808,7 @@ john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
|
||||
<p>然而,存在着第三种场景,在这种场景中,两个属性都必须有值,并且初始化完成后不能为<code>nil</code>。在这种场景中,需要一个类使用无主属性,而另外一个类使用隐式解析可选属性。</p>
|
||||
<p>这使两个属性在初始化完成后能被直接访问(不需要可选展开),同时避免了循环引用。这一节将为你展示如何建立这种关系。</p>
|
||||
<p>下面的例子定义了两个类,<code>Country</code>和<code>City</code>,每个类将另外一个类的实例保存为属性。在这个模型中,每个国家必须有首都,而每一个城市必须属于一个国家。为了实现这种关系,<code>Country</code>类拥有一个<code>capitalCity</code>属性,而<code>City</code>类有一个<code>country</code>属性:</p>
|
||||
<pre><code>class Country {
|
||||
<pre><code class="lang-swift">class Country {
|
||||
let name: String
|
||||
let capitalCity: City!
|
||||
init(name: String, capitalName: String) {
|
||||
@ -804,8 +816,8 @@ john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
|
||||
self.capitalCity = City(name: capitalName, country: self)
|
||||
}
|
||||
}
|
||||
|
||||
class City {
|
||||
</code></pre>
|
||||
<pre><code class="lang-swift">class City {
|
||||
let name: String
|
||||
unowned let country: Country
|
||||
init(name: String, country: Country) {
|
||||
@ -813,15 +825,17 @@ class City {
|
||||
self.country = country
|
||||
}
|
||||
}
|
||||
</code></pre><p>为了建立两个类的依赖关系,<code>City</code>的构造函数有一个<code>Country</code>实例的参数,并且将实例保存为<code>country</code>属性。</p>
|
||||
</code></pre>
|
||||
<p>为了建立两个类的依赖关系,<code>City</code>的构造函数有一个<code>Country</code>实例的参数,并且将实例保存为<code>country</code>属性。</p>
|
||||
<p><code>Country</code>的构造函数调用了<code>City</code>的构造函数。然而,只有<code>Country</code>的实例完全初始化完后,<code>Country</code>的构造函数才能把<code>self</code>传给<code>City</code>的构造函数。(<a href="14_Initialization.html">在两段式构造过程中有具体描述</a>)</p>
|
||||
<p>为了满足这种需求,通过在类型结尾处加上感叹号(City!)的方式,将<code>Country</code>的<code>capitalCity</code>属性声明为隐式解析可选类型的属性。这表示像其他可选类型一样,<code>capitalCity</code>属性的默认值为<code>nil</code>,但是不需要展开它的值就能访问它。(<a href="01_The_Basics.html">在隐式解析可选类型中有描述</a>)</p>
|
||||
<p>由于<code>capitalCity</code>默认值为<code>nil</code>,一旦<code>Country</code>的实例在构造函数中给<code>name</code>属性赋值后,整个初始化过程就完成了。这代表一旦<code>name</code>属性被赋值后,<code>Country</code>的构造函数就能引用并传递隐式的<code>self</code>。<code>Country</code>的构造函数在赋值<code>capitalCity</code>时,就能将<code>self</code>作为参数传递给<code>City</code>的构造函数。</p>
|
||||
<p>以上的意义在于你可以通过一条语句同时创建<code>Country</code>和<code>City</code>的实例,而不产生循环强引用,并且<code>capitalCity</code>的属性能被直接访问,而不需要通过感叹号来展开它的可选值:</p>
|
||||
<pre><code>var country = Country(name: "Canada", capitalName: "Ottawa")
|
||||
<pre><code class="lang-swift">var country = Country(name: "Canada", capitalName: "Ottawa")
|
||||
println("\(country.name)'s capital city is called \(country.capitalCity.name)")
|
||||
// prints "Canada's capital city is called Ottawa"
|
||||
</code></pre><p>在上面的例子中,使用隐式解析可选值的意义在于满足了两个类构造函数的需求。<code>capitalCity</code>属性在初始化完成后,能像非可选值一样使用和存取同时还避免了循环强引用。</p>
|
||||
</code></pre>
|
||||
<p>在上面的例子中,使用隐式解析可选值的意义在于满足了两个类构造函数的需求。<code>capitalCity</code>属性在初始化完成后,能像非可选值一样使用和存取同时还避免了循环强引用。</p>
|
||||
<p><a name="strong_reference_cycles_for_closures"></a></p>
|
||||
<h2 id="-">闭包引起的循环强引用</h2>
|
||||
<p>前面我们看到了循环强引用环是在两个类实例属性互相保持对方的强引用时产生的,还知道了如何用弱引用和无主引用来打破循环强引用。</p>
|
||||
@ -829,7 +843,7 @@ println("\(country.name)'s capital city is called \(country.capitalCity
|
||||
<p>循环强引用的产生,是因为闭包和类相似,都是引用类型。当你把一个闭包赋值给某个属性时,你也把一个引用赋值给了这个闭包。实质上,这跟之前的问题是一样的-两个强引用让彼此一直有效。但是,和两个类实例不同,这次一个是类实例,另一个是闭包。</p>
|
||||
<p>Swift 提供了一种优雅的方法来解决这个问题,称之为闭包占用列表(closuer capture list)。同样的,在学习如何用闭包占用列表破坏循环强引用之前,先来了解一下循环强引用是如何产生的,这对我们是很有帮助的。</p>
|
||||
<p>下面的例子为你展示了当一个闭包引用了<code>self</code>后是如何产生一个循环强引用的。例子中定义了一个叫<code>HTMLElement</code>的类,用一种简单的模型表示 HTML 中的一个单独的元素:</p>
|
||||
<pre><code>class HTMLElement {
|
||||
<pre><code class="lang-swift">class HTMLElement {
|
||||
|
||||
let name: String
|
||||
let text: String?
|
||||
@ -852,61 +866,61 @@ println("\(country.name)'s capital city is called \(country.capitalCity
|
||||
}
|
||||
|
||||
}
|
||||
</code></pre><p><code>HTMLElement</code>类定义了一个<code>name</code>属性来表示这个元素的名称,例如代表段落的"p",或者代表换行的"br"。<code>HTMLElement</code>还定义了一个可选属性<code>text</code>,用来设置和展现 HTML 元素的文本。</p>
|
||||
</code></pre>
|
||||
<p><code>HTMLElement</code>类定义了一个<code>name</code>属性来表示这个元素的名称,例如代表段落的"p",或者代表换行的"br"。<code>HTMLElement</code>还定义了一个可选属性<code>text</code>,用来设置和展现 HTML 元素的文本。</p>
|
||||
<p>除了上面的两个属性,<code>HTMLElement</code>还定义了一个<code>lazy</code>属性<code>asHTML</code>。这个属性引用了一个闭包,将<code>name</code>和<code>text</code>组合成 HTML 字符串片段。该属性是<code>() -> String</code>类型,或者可以理解为“一个没有参数,返回<code>String</code>的函数”。</p>
|
||||
<p>默认情况下,闭包赋值给了<code>asHTML</code>属性,这个闭包返回一个代表 HTML 标签的字符串。如果<code>text</code>值存在,该标签就包含可选值<code>text</code>;如果<code>text</code>不存在,该标签就不包含文本。对于段落元素,根据<code>text</code>是"some text"还是<code>nil</code>,闭包会返回"<code><p>some text</p></code>"或者"<code><p /></code>"。</p>
|
||||
<p>可以像实例方法那样去命名、使用<code>asHTML</code>属性。然而,由于<code>asHTML</code>是闭包而不是实例方法,如果你想改变特定元素的 HTML 处理的话,可以用自定义的闭包来取代默认值。</p>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<p><code>asHTML</code>声明为<code>lazy</code>属性,因为只有当元素确实需要处理为HTML输出的字符串时,才需要使用<code>asHTML</code>。也就是说,在默认的闭包中可以使用<code>self</code>,因为只有当初始化完成以及<code>self</code>确实存在后,才能访问<code>lazy</code>属性。</p>
|
||||
<p>注意:<br><code>asHTML</code>声明为<code>lazy</code>属性,因为只有当元素确实需要处理为HTML输出的字符串时,才需要使用<code>asHTML</code>。也就是说,在默认的闭包中可以使用<code>self</code>,因为只有当初始化完成以及<code>self</code>确实存在后,才能访问<code>lazy</code>属性。</p>
|
||||
</blockquote>
|
||||
<p><code>HTMLElement</code>类只提供一个构造函数,通过<code>name</code>和<code>text</code>(如果有的话)参数来初始化一个元素。该类也定义了一个析构函数,当<code>HTMLElement</code>实例被销毁时,打印一条消息。</p>
|
||||
<p>下面的代码展示了如何用<code>HTMLElement</code>类创建实例并打印消息。</p>
|
||||
<pre><code>var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
|
||||
<pre><code class="lang-swift">var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
|
||||
println(paragraph!.asHTML())
|
||||
// prints"hello, world"
|
||||
</code></pre><blockquote>
|
||||
<p>注意:</p>
|
||||
<p>上面的<code>paragraph</code>变量定义为<code>可选HTMLElement</code>,因此我们可以赋值<code>nil</code>给它来演示循环强引用。</p>
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>注意:<br>上面的<code>paragraph</code>变量定义为<code>可选HTMLElement</code>,因此我们可以赋值<code>nil</code>给它来演示循环强引用。</p>
|
||||
</blockquote>
|
||||
<p>不幸的是,上面写的<code>HTMLElement</code>类产生了类实例和<code>asHTML</code>默认值的闭包之间的循环强引用。循环强引用如下图所示:</p>
|
||||
<p><img src="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/closureReferenceCycle01_2x.png" alt=""></p>
|
||||
<p>实例的<code>asHTML</code>属性持有闭包的强引用。但是,闭包在其闭包体内使用了<code>self</code>(引用了<code>self.name</code>和<code>self.text</code>),因此闭包捕获了<code>self</code>,这意味着闭包又反过来持有了<code>HTMLElement</code>实例的强引用。这样两个对象就产生了循环强引用。(更多关于闭包捕获值的信息,请参考<a href="07_Closures.html">值捕获</a>)。</p>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<p>虽然闭包多次使用了<code>self</code>,它只捕获<code>HTMLElement</code>实例的一个强引用。</p>
|
||||
<p>注意:<br>虽然闭包多次使用了<code>self</code>,它只捕获<code>HTMLElement</code>实例的一个强引用。</p>
|
||||
</blockquote>
|
||||
<p>如果设置<code>paragraph</code>变量为<code>nil</code>,打破它持有的<code>HTMLElement</code>实例的强引用,<code>HTMLElement</code>实例和它的闭包都不会被销毁,也是因为循环强引用:</p>
|
||||
<pre><code>paragraph = nil
|
||||
</code></pre><p>注意<code>HTMLElementdeinitializer</code>中的消息并没有别打印,证明了<code>HTMLElement</code>实例并没有被销毁。</p>
|
||||
<pre><code class="lang-swift">paragraph = nil
|
||||
</code></pre>
|
||||
<p>注意<code>HTMLElementdeinitializer</code>中的消息并没有别打印,证明了<code>HTMLElement</code>实例并没有被销毁。</p>
|
||||
<p><a name="resolving_strong_reference_cycles_for_closures"></a></p>
|
||||
<h2 id="-">解决闭包引起的循环强引用</h2>
|
||||
<p>在定义闭包时同时定义捕获列表作为闭包的一部分,通过这种方式可以解决闭包和类实例之间的循环强引用。捕获列表定义了闭包体内捕获一个或者多个引用类型的规则。跟解决两个类实例间的循环强引用一样,声明每个捕获的引用为弱引用或无主引用,而不是强引用。应当根据代码关系来决定使用弱引用还是无主引用。</p>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<p>Swift 有如下要求:只要在闭包内使用<code>self</code>的成员,就要用<code>self.someProperty</code>或者<code>self.someMethod</code>(而不只是<code>someProperty</code>或<code>someMethod</code>)。这提醒你可能会不小心就捕获了<code>self</code>。</p>
|
||||
<p>注意:<br>Swift 有如下要求:只要在闭包内使用<code>self</code>的成员,就要用<code>self.someProperty</code>或者<code>self.someMethod</code>(而不只是<code>someProperty</code>或<code>someMethod</code>)。这提醒你可能会不小心就捕获了<code>self</code>。</p>
|
||||
</blockquote>
|
||||
<h3 id="-">定义捕获列表</h3>
|
||||
<p>捕获列表中的每个元素都是由<code>weak</code>或者<code>unowned</code>关键字和实例的引用(如<code>self</code>或<code>someInstance</code>)成对组成。每一对都在方括号中,通过逗号分开。</p>
|
||||
<p>捕获列表放置在闭包参数列表和返回类型之前:</p>
|
||||
<pre><code>@lazy var someClosure: (Int, String) -> String = {
|
||||
<pre><code class="lang-swift">@lazy var someClosure: (Int, String) -> String = {
|
||||
[unowned self] (index: Int, stringToProcess: String) -> String in
|
||||
// closure body goes here
|
||||
}
|
||||
</code></pre><p>如果闭包没有指定参数列表或者返回类型,则可以通过上下文推断,那么可以捕获列表放在闭包开始的地方,跟着是关键字<code>in</code>:</p>
|
||||
<pre><code>@lazy var someClosure: () -> String = {
|
||||
</code></pre>
|
||||
<p>如果闭包没有指定参数列表或者返回类型,则可以通过上下文推断,那么可以捕获列表放在闭包开始的地方,跟着是关键字<code>in</code>:</p>
|
||||
<pre><code class="lang-swift">@lazy var someClosure: () -> String = {
|
||||
[unowned self] in
|
||||
// closure body goes here
|
||||
}
|
||||
</code></pre><h3 id="-">弱引用和无主引用</h3>
|
||||
</code></pre>
|
||||
<h3 id="-">弱引用和无主引用</h3>
|
||||
<p>当闭包和捕获的实例总是互相引用时并且总是同时销毁时,将闭包内的捕获定义为无主引用。</p>
|
||||
<p>相反的,当捕获引用有时可能会是<code>nil</code>时,将闭包内的捕获定义为弱引用。弱引用总是可选类型,并且当引用的实例被销毁后,弱引用的值会自动置为<code>nil</code>。这使我们可以在闭包内检查它们是否存在。</p>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<p>如果捕获的引用绝对不会置为<code>nil</code>,应该用无主引用,而不是弱引用。</p>
|
||||
<p>注意:<br>如果捕获的引用绝对不会置为<code>nil</code>,应该用无主引用,而不是弱引用。</p>
|
||||
</blockquote>
|
||||
<p>前面的<code>HTMLElement</code>例子中,无主引用是正确的解决循环强引用的方法。这样编写<code>HTMLElement</code>类来避免循环强引用:</p>
|
||||
<pre><code>class HTMLElement {
|
||||
<pre><code class="lang-swift">class HTMLElement {
|
||||
|
||||
let name: String
|
||||
let text: String?
|
||||
@ -930,17 +944,20 @@ println(paragraph!.asHTML())
|
||||
}
|
||||
|
||||
}
|
||||
</code></pre><p>上面的<code>HTMLElement</code>实现和之前的实现一致,只是在<code>asHTML</code>闭包中多了一个捕获列表。这里,捕获列表是<code>[unowned self]</code>,表示“用无主引用而不是强引用来捕获<code>self</code>”。</p>
|
||||
</code></pre>
|
||||
<p>上面的<code>HTMLElement</code>实现和之前的实现一致,只是在<code>asHTML</code>闭包中多了一个捕获列表。这里,捕获列表是<code>[unowned self]</code>,表示“用无主引用而不是强引用来捕获<code>self</code>”。</p>
|
||||
<p>和之前一样,我们可以创建并打印<code>HTMLElement</code>实例:</p>
|
||||
<pre><code>var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
|
||||
<pre><code class="lang-swift">var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
|
||||
println(paragraph!.asHTML())
|
||||
// prints "<p>hello, world</p>"
|
||||
</code></pre><p>使用捕获列表后引用关系如下图所示:</p>
|
||||
</code></pre>
|
||||
<p>使用捕获列表后引用关系如下图所示:</p>
|
||||
<p><img src="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/closureReferenceCycle02_2x.png" alt=""></p>
|
||||
<p>这一次,闭包以无主引用的形式捕获<code>self</code>,并不会持有<code>HTMLElement</code>实例的强引用。如果将<code>paragraph</code>赋值为<code>nil</code>,<code>HTMLElement</code>实例将会被销毁,并能看到它的析构函数打印出的消息。</p>
|
||||
<pre><code>paragraph = nil
|
||||
<pre><code class="lang-swift">paragraph = nil
|
||||
// prints "p is being deinitialized"
|
||||
</code></pre>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="2.17" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="2.17" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,11 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_66">
|
||||
<section class="normal" id="section-gitbook_40">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:Jasonbroker</p>
|
||||
<p>校对:numbbbbb</p>
|
||||
<p>翻译:<a href="https://github.com/Jasonbroker" target="_blank">Jasonbroker</a><br>校对:<a href="https://github.com/numbbbbb" target="_blank">numbbbbb</a>, <a href="https://github.com/stanzhai" target="_blank">stanzhai</a></p>
|
||||
</blockquote>
|
||||
<h1 id="optional-chaining">Optional Chaining</h1>
|
||||
<hr>
|
||||
@ -607,8 +606,7 @@
|
||||
</ul>
|
||||
<p>可选链(Optional Chaining)是一种可以请求和调用属性、方法及子脚本的过程,它的可选性体现于请求或调用的目标当前可能为空(<code>nil</code>)。如果可选的目标有值,那么调用就会成功;相反,如果选择的目标为空(<code>nil</code>),则这种调用将返回空(<code>nil</code>)。多次请求或调用可以被链接在一起形成一个链,如果任何一个节点为空(<code>nil</code>)将导致整个链失效。</p>
|
||||
<blockquote>
|
||||
<p>注意:
|
||||
Swift 的可选链和 Objective-C 中的消息为空有些相像,但是 Swift 可以使用在任意类型中,并且失败与否可以被检测到。</p>
|
||||
<p>注意:<br>Swift 的可选链和 Objective-C 中的消息为空有些相像,但是 Swift 可以使用在任意类型中,并且失败与否可以被检测到。</p>
|
||||
</blockquote>
|
||||
<p><a name="optional_chaining_as_an_alternative_to_forced_unwrapping"></a></p>
|
||||
<h2 id="-">可选链可替代强制解析</h2>
|
||||
@ -617,49 +615,56 @@ Swift 的可选链和 Objective-C 中的消息为空有些相像,但是 Swift
|
||||
<p>调用可选链的返回结果与原本的返回结果具有相同的类型,但是原本的返回结果被包装成了一个可选值,当可选链调用成功时,一个应该返回<code>Int</code>的属性将会返回<code>Int?</code>。</p>
|
||||
<p>下面几段代码将解释可选链和强制解析的不同。</p>
|
||||
<p>首先定义两个类<code>Person</code>和<code>Residence</code>。</p>
|
||||
<pre><code>class Person {
|
||||
<pre><code class="lang-swift">class Person {
|
||||
var residence: Residence?
|
||||
}
|
||||
|
||||
class Residence {
|
||||
var numberOfRooms = 1
|
||||
}
|
||||
</code></pre><p><code>Residence</code>具有一个<code>Int</code>类型的<code>numberOfRooms</code>,其值为 1。<code>Person</code>具有一个可选<code>residence</code>属性,它的类型是<code>Residence?</code>。</p>
|
||||
</code></pre>
|
||||
<p><code>Residence</code>具有一个<code>Int</code>类型的<code>numberOfRooms</code>,其值为 1。<code>Person</code>具有一个可选<code>residence</code>属性,它的类型是<code>Residence?</code>。</p>
|
||||
<p>如果你创建一个新的<code>Person</code>实例,它的<code>residence</code>属性由于是被定义为可选型的,此属性将默认初始化为空:</p>
|
||||
<pre><code>let john = Person()
|
||||
</code></pre><p>如果你想使用感叹号(<code>!</code>)强制解析获得这个人<code>residence</code>属性<code>numberOfRooms</code>属性值,将会引发运行时错误,因为这时没有可以供解析的<code>residence</code>值。</p>
|
||||
<pre><code>let roomCount = john.residence!.numberOfRooms
|
||||
<pre><code class="lang-swift">let john = Person()
|
||||
</code></pre>
|
||||
<p>如果你想使用感叹号(<code>!</code>)强制解析获得这个人<code>residence</code>属性<code>numberOfRooms</code>属性值,将会引发运行时错误,因为这时没有可以供解析的<code>residence</code>值。</p>
|
||||
<pre><code class="lang-swift">let roomCount = john.residence!.numberOfRooms
|
||||
//将导致运行时错误
|
||||
</code></pre><p>当<code>john.residence</code>不是<code>nil</code>时,会运行通过,且会将<code>roomCount</code> 设置为一个<code>int</code>类型的合理值。然而,如上所述,当<code>residence</code>为空时,这个代码将会导致运行时错误。</p>
|
||||
</code></pre>
|
||||
<p>当<code>john.residence</code>不是<code>nil</code>时,会运行通过,且会将<code>roomCount</code> 设置为一个<code>int</code>类型的合理值。然而,如上所述,当<code>residence</code>为空时,这个代码将会导致运行时错误。</p>
|
||||
<p>可选链提供了一种另一种获得<code>numberOfRooms</code>的方法。利用可选链,使用问号来代替原来<code>!</code>的位置:</p>
|
||||
<pre><code>if let roomCount = john.residence?.numberOfRooms {
|
||||
<pre><code class="lang-swift">if let roomCount = john.residence?.numberOfRooms {
|
||||
println("John's residence has \(roomCount) room(s).")
|
||||
} else {
|
||||
println("Unable to retrieve the number of rooms.")
|
||||
}
|
||||
// 打印 "Unable to retrieve the number of rooms.
|
||||
</code></pre><p>这告诉 Swift 来链接可选<code>residence?</code>属性,如果<code>residence</code>存在则取回<code>numberOfRooms</code>的值。</p>
|
||||
</code></pre>
|
||||
<p>这告诉 Swift 来链接可选<code>residence?</code>属性,如果<code>residence</code>存在则取回<code>numberOfRooms</code>的值。</p>
|
||||
<p>因为这种尝试获得<code>numberOfRooms</code>的操作有可能失败,可选链会返回<code>Int?</code>类型值,或者称作“可选<code>Int</code>”。当<code>residence</code>是空的时候(上例),选择<code>Int</code>将会为空,因此会出先无法访问<code>numberOfRooms</code>的情况。</p>
|
||||
<p>要注意的是,即使numberOfRooms是非可选<code>Int</code>(<code>Int?</code>)时这一点也成立。只要是通过可选链的请求就意味着最后<code>numberOfRooms</code>总是返回一个<code>Int?</code>而不是<code>Int</code>。</p>
|
||||
<p>你可以自己定义一个<code>Residence</code>实例给<code>john.residence</code>,这样它就不再为空了:</p>
|
||||
<pre><code>john.residence = Residence()
|
||||
</code></pre><p><code>john.residence</code> 现在有了实际存在的实例而不是nil了。如果你想使用和前面一样的可选链来获得<code>numberOfRoooms</code>,它将返回一个包含默认值 1 的<code>Int?</code>:</p>
|
||||
<pre><code>if let roomCount = john.residence?.numberOfRooms {
|
||||
<pre><code class="lang-swift">john.residence = Residence()
|
||||
</code></pre>
|
||||
<p><code>john.residence</code> 现在有了实际存在的实例而不是nil了。如果你想使用和前面一样的可选链来获得<code>numberOfRoooms</code>,它将返回一个包含默认值 1 的<code>Int?</code>:</p>
|
||||
<pre><code class="lang-swift">if let roomCount = john.residence?.numberOfRooms {
|
||||
println("John's residence has \(roomCount) room(s).")
|
||||
} else {
|
||||
println("Unable to retrieve the number of rooms.")
|
||||
}
|
||||
// 打印 "John's residence has 1 room(s)"。
|
||||
</code></pre><p><a name="defining_model_classes_for_optional_chaining"></a></p>
|
||||
</code></pre>
|
||||
<p><a name="defining_model_classes_for_optional_chaining"></a></p>
|
||||
<h2 id="-">为可选链定义模型类</h2>
|
||||
<p>你可以使用可选链来多层调用属性,方法,和子脚本。这让你可以利用它们之间的复杂模型来获取更底层的属性,并检查是否可以成功获取此类底层属性。</p>
|
||||
<p>后面的代码定义了四个将在后面使用的模型类,其中包括多层可选链。这些类是由上面的<code>Person</code>和<code>Residence</code>模型通过添加一个<code>Room</code>和一个<code>Address</code>类拓展来。</p>
|
||||
<p><code>Person</code>类定义与之前相同。</p>
|
||||
<pre><code>class Person {
|
||||
<pre><code class="lang-swift">class Person {
|
||||
var residence: Residence?
|
||||
}
|
||||
</code></pre><p><code>Residence</code>类比之前复杂些。这次,它定义了一个变量 <code>rooms</code>,它被初始化为一个<code>Room[]</code>类型的空数组:</p>
|
||||
<pre><code>class Residence {
|
||||
</code></pre>
|
||||
<p><code>Residence</code>类比之前复杂些。这次,它定义了一个变量 <code>rooms</code>,它被初始化为一个<code>Room[]</code>类型的空数组:</p>
|
||||
<pre><code class="lang-swift">class Residence {
|
||||
var rooms = Room[]()
|
||||
var numberOfRooms: Int {
|
||||
return rooms.count
|
||||
@ -672,17 +677,19 @@ class Residence {
|
||||
}
|
||||
var address: Address?
|
||||
}
|
||||
</code></pre><p>因为<code>Residence</code>存储了一个<code>Room</code>实例的数组,它的<code>numberOfRooms</code>属性值不是一个固定的存储值,而是通过计算而来的。<code>numberOfRooms</code>属性值是由返回<code>rooms</code>数组的<code>count</code>属性值得到的。</p>
|
||||
</code></pre>
|
||||
<p>因为<code>Residence</code>存储了一个<code>Room</code>实例的数组,它的<code>numberOfRooms</code>属性值不是一个固定的存储值,而是通过计算而来的。<code>numberOfRooms</code>属性值是由返回<code>rooms</code>数组的<code>count</code>属性值得到的。</p>
|
||||
<p>为了能快速访问<code>rooms</code>数组,<code>Residence</code>定义了一个只读的子脚本,通过插入数组的元素角标就可以成功调用。如果该角标存在,子脚本则将该元素返回。</p>
|
||||
<p><code>Residence</code>中也提供了一个<code>printNumberOfRooms</code>的方法,即简单的打印房间个数。</p>
|
||||
<p>最后,<code>Residence</code>定义了一个可选属性叫<code>address</code>(<code>address?</code>)。<code>Address</code>类的属性将在后面定义。
|
||||
用于<code>rooms</code>数组的<code>Room</code>类是一个很简单的类,它只有一个<code>name</code>属性和一个设定<code>room</code>名的初始化器。</p>
|
||||
<pre><code>class Room {
|
||||
<pre><code class="lang-swift">class Room {
|
||||
let name: String
|
||||
init(name: String) { self.name = name }
|
||||
}
|
||||
</code></pre><p>这个模型中的最终类叫做<code>Address</code>。它有三个类型是<code>String?</code>的可选属性。前面两个可选属性<code>buildingName</code>和 <code>buildingNumber</code>作为地址的一部分,是定义某个建筑物的两种方式。第三个属性<code>street</code>,用于命名地址的街道名:</p>
|
||||
<pre><code>class Address {
|
||||
</code></pre>
|
||||
<p>这个模型中的最终类叫做<code>Address</code>。它有三个类型是<code>String?</code>的可选属性。前面两个可选属性<code>buildingName</code>和 <code>buildingNumber</code>作为地址的一部分,是定义某个建筑物的两种方式。第三个属性<code>street</code>,用于命名地址的街道名:</p>
|
||||
<pre><code class="lang-swift">class Address {
|
||||
var buildingName: String?
|
||||
var buildingNumber: String?
|
||||
var street: String?
|
||||
@ -696,51 +703,55 @@ class Residence {
|
||||
}
|
||||
}
|
||||
}
|
||||
</code></pre><p><code>Address</code>类还提供了一个<code>buildingIdentifier</code>的方法,它的返回值类型为<code>String?</code>。这个方法检查<code>buildingName</code>和<code>buildingNumber</code>的属性,如果<code>buildingName</code>有值则将其返回,或者如果<code>buildingNumber</code>有值则将其返回,再或如果没有一个属性有值,返回空。</p>
|
||||
</code></pre>
|
||||
<p><code>Address</code>类还提供了一个<code>buildingIdentifier</code>的方法,它的返回值类型为<code>String?</code>。这个方法检查<code>buildingName</code>和<code>buildingNumber</code>的属性,如果<code>buildingName</code>有值则将其返回,或者如果<code>buildingNumber</code>有值则将其返回,再或如果没有一个属性有值,返回空。</p>
|
||||
<p><a name="calling_properties_through_optional_chaining"></a></p>
|
||||
<h2 id="-">通过可选链调用属性</h2>
|
||||
<p>正如上面“ <a href="#optional_chaining_as_an_alternative_to_forced_unwrapping">可选链可替代强制解析</a>”中所述,你可以利用可选链的可选值获取属性,并且检查属性是否获取成功。然而,你不能使用可选链为属性赋值。</p>
|
||||
<p>使用上述定义的类来创建一个人实例,并再次尝试后去它的<code>numberOfRooms</code>属性:</p>
|
||||
<pre><code>let john = Person()
|
||||
<pre><code class="lang-swift">let john = Person()
|
||||
if let roomCount = john.residence?.numberOfRooms {
|
||||
println("John's residence has \(roomCount) room(s).")
|
||||
} else {
|
||||
println("Unable to retrieve the number of rooms.")
|
||||
}
|
||||
// 打印 "Unable to retrieve the number of rooms。
|
||||
</code></pre><p>由于<code>john.residence</code>是空,所以这个可选链和之前一样失败了,但是没有运行时错误。</p>
|
||||
</code></pre>
|
||||
<p>由于<code>john.residence</code>是空,所以这个可选链和之前一样失败了,但是没有运行时错误。</p>
|
||||
<p><a name="calling_methods_through_optional_chaining"></a></p>
|
||||
<h2 id="-">通过可选链调用方法</h2>
|
||||
<p>你可以使用可选链的来调用可选值的方法并检查方法调用是否成功。即使这个方法没有返回值,你依然可以使用可选链来达成这一目的。</p>
|
||||
<p><code>Residence</code>的<code>printNumberOfRooms</code>方法会打印<code>numberOfRooms</code>的当前值。方法如下:</p>
|
||||
<pre><code>func printNumberOfRooms(){
|
||||
<pre><code class="lang-swift">func printNumberOfRooms(){
|
||||
println(“The number of rooms is \(numberOfRooms)”)
|
||||
}
|
||||
</code></pre><p>这个方法没有返回值。但是,没有返回值类型的函数和方法有一个隐式的返回值类型<code>Void</code>(参见Function Without Return Values)。</p>
|
||||
</code></pre>
|
||||
<p>这个方法没有返回值。但是,没有返回值类型的函数和方法有一个隐式的返回值类型<code>Void</code>(参见Function Without Return Values)。</p>
|
||||
<p>如果你利用可选链调用此方法,这个方法的返回值类型将是<code>Void?</code>,而不是<code>Void</code>,因为当通过可选链调用方法时返回值总是可选类型(optional type)。即使这个方法本身没有定义返回值,你也可以使用<code>if</code>语句来检查是否能成功调用<code>printNumberOfRooms</code>方法:如果方法通过可选链调用成功,<code>printNumberOfRooms</code>的隐式返回值将会是<code>Void</code>,如果没有成功,将返回<code>nil</code>:</p>
|
||||
<pre><code>if john.residence?.printNumberOfRooms() {
|
||||
<pre><code class="lang-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.")
|
||||
}
|
||||
// 打印 "It was not possible to print the number of rooms."。
|
||||
</code></pre><p><a name="calling_subscripts_through_optional_chaining"></a></p>
|
||||
</code></pre>
|
||||
<p><a name="calling_subscripts_through_optional_chaining"></a></p>
|
||||
<h2 id="-">使用可选链调用子脚本</h2>
|
||||
<p>你可以使用可选链来尝试从子脚本获取值并检查子脚本的调用是否成功,然而,你不能通过可选链来设置子代码。</p>
|
||||
<blockquote>
|
||||
<p>注意:
|
||||
当你使用可选链来获取子脚本的时候,你应该将问号放在子脚本括号的前面而不是后面。可选链的问号一般直接跟在表达语句的后面。</p>
|
||||
<p>注意:<br>当你使用可选链来获取子脚本的时候,你应该将问号放在子脚本括号的前面而不是后面。可选链的问号一般直接跟在表达语句的后面。</p>
|
||||
</blockquote>
|
||||
<p>下面这个例子用在<code>Residence</code>类中定义的子脚本来获取<code>john.residence</code>数组中第一个房间的名字。因为<code>john.residence</code>现在是<code>nil</code>,子脚本的调用失败了。</p>
|
||||
<pre><code>if let firstRoomName = john.residence?[0].name {
|
||||
<pre><code class="lang-swift">if let firstRoomName = john.residence?[0].name {
|
||||
println("The first room name is \(firstRoomName).")
|
||||
} else {
|
||||
println("Unable to retrieve the first room name.")
|
||||
}
|
||||
// 打印 "Unable to retrieve the first room name."。
|
||||
</code></pre><p>在子代码调用中可选链的问号直接跟在<code>john.residence</code>的后面,在子脚本括号的前面,因为<code>john.residence</code>是可选链试图获得的可选值。</p>
|
||||
</code></pre>
|
||||
<p>在子代码调用中可选链的问号直接跟在<code>john.residence</code>的后面,在子脚本括号的前面,因为<code>john.residence</code>是可选链试图获得的可选值。</p>
|
||||
<p>如果你创建一个<code>Residence</code>实例给<code>john.residence</code>,且在他的<code>rooms</code>数组中有一个或多个<code>Room</code>实例,那么你可以使用可选链通过<code>Residence</code>子脚本来获取在<code>rooms</code>数组中的实例了:</p>
|
||||
<pre><code>let johnsHouse = Residence()
|
||||
<pre><code class="lang-swift">let johnsHouse = Residence()
|
||||
johnsHouse.rooms += Room(name: "Living Room")
|
||||
johnsHouse.rooms += Room(name: "Kitchen")
|
||||
john.residence = johnsHouse
|
||||
@ -751,7 +762,8 @@ if let firstRoomName = john.residence?[0].name {
|
||||
println("Unable to retrieve the first room name.")
|
||||
}
|
||||
// 打印 "The first room name is Living Room."。
|
||||
</code></pre><p><a name="linking_multiple_levels_of_chaining"></a></p>
|
||||
</code></pre>
|
||||
<p><a name="linking_multiple_levels_of_chaining"></a></p>
|
||||
<h2 id="-">连接多层链接</h2>
|
||||
<p>你可以将多层可选链连接在一起,可以掘取模型内更下层的属性方法和子脚本。然而多层可选链不能再添加比已经返回的可选值更多的层。
|
||||
也就是说:</p>
|
||||
@ -761,43 +773,46 @@ if let firstRoomName = john.residence?[0].name {
|
||||
<p>如果你试图通过可选链获得<code>Int</code>值,不论使用了多少层链接返回的总是<code>Int?</code>。
|
||||
相似的,如果你试图通过可选链获得<code>Int?</code>值,不论使用了多少层链接返回的总是<code>Int?</code>。</p>
|
||||
<p>下面的例子试图获取<code>john</code>的<code>residence</code>属性里的<code>address</code>的<code>street</code>属性。这里使用了两层可选链来联系<code>residence</code>和<code>address</code>属性,它们两者都是可选类型:</p>
|
||||
<pre><code>if let johnsStreet = john.residence?.address?.street {
|
||||
<pre><code class="lang-swift">if let johnsStreet = john.residence?.address?.street {
|
||||
println("John's street name is \(johnsStreet).")
|
||||
} else {
|
||||
println("Unable to retrieve the address.")
|
||||
}
|
||||
// 打印 "Unable to retrieve the address.”。
|
||||
</code></pre><p><code>john.residence</code>的值现在包含一个<code>Residence</code>实例,然而<code>john.residence.address</code>现在是<code>nil</code>,因此<code>john.residence?.address?.street</code>调用失败。</p>
|
||||
</code></pre>
|
||||
<p><code>john.residence</code>的值现在包含一个<code>Residence</code>实例,然而<code>john.residence.address</code>现在是<code>nil</code>,因此<code>john.residence?.address?.street</code>调用失败。</p>
|
||||
<p>从上面的例子发现,你试图获得<code>street</code>属性值。这个属性的类型是<code>String?</code>。因此尽管在可选类型属性前使用了两层可选链,<code>john.residence?.address?.street</code>的返回值类型也是<code>String?</code>。</p>
|
||||
<p>如果你为<code>Address</code>设定一个实例来作为<code>john.residence.address</code>的值,并为<code>address</code>的<code>street</code>属性设定一个实际值,你可以通过多层可选链来得到这个属性值。</p>
|
||||
<pre><code>let johnsAddress = Address()
|
||||
<pre><code class="lang-swift">let johnsAddress = Address()
|
||||
johnsAddress.buildingName = "The Larches"
|
||||
johnsAddress.street = "Laurel Street"
|
||||
john.residence!.address = johnsAddress
|
||||
|
||||
if let johnsStreet = john.residence?.address?.street {
|
||||
</code></pre>
|
||||
<pre><code class="lang-swift">if let johnsStreet = john.residence?.address?.street {
|
||||
println("John's street name is \(johnsStreet).")
|
||||
} else {
|
||||
println("Unable to retrieve the address.")
|
||||
}
|
||||
// 打印 "John's street name is Laurel Street."。
|
||||
</code></pre><p>值得注意的是,“<code>!</code>”符号在给<code>john.residence.address</code>分配<code>address</code>实例时的使用。<code>john.residence</code>属性是一个可选类型,因此你需要在它获取<code>address</code>属性之前使用<code>!</code>解析以获得它的实际值。</p>
|
||||
</code></pre>
|
||||
<p>值得注意的是,“<code>!</code>”符号在给<code>john.residence.address</code>分配<code>address</code>实例时的使用。<code>john.residence</code>属性是一个可选类型,因此你需要在它获取<code>address</code>属性之前使用<code>!</code>解析以获得它的实际值。</p>
|
||||
<p><a name="chaining_on_methods_with_optional_return_values"></a></p>
|
||||
<h2 id="-">链接可选返回值的方法</h2>
|
||||
<p>前面的例子解释了如何通过可选链来获得可选类型属性值。你也可以通过可选链调用一个返回可选类型值的方法并按需链接该方法的返回值。</p>
|
||||
<p>下面的例子通过可选链调用了<code>Address</code>类中的<code>buildingIdentifier</code> 方法。这个方法的返回值类型是<code>String?</code>。如上所述,这个方法在可选链调用后最终的返回值类型依然是<code>String?</code>:</p>
|
||||
<pre><code>if let buildingIdentifier = john.residence?.address?.buildingIdentifier() {
|
||||
<pre><code class="lang-swift">if let buildingIdentifier = john.residence?.address?.buildingIdentifier() {
|
||||
println("John's building identifier is \(buildingIdentifier).")
|
||||
}
|
||||
// 打印 "John's building identifier is The Larches."。
|
||||
</code></pre><p>如果你还想进一步对方法返回值执行可选链,将可选链问号符放在方法括号的后面:</p>
|
||||
<pre><code>if let upper = john.residence?.address?.buildingIdentifier()?.uppercaseString {
|
||||
</code></pre>
|
||||
<p>如果你还想进一步对方法返回值执行可选链,将可选链问号符放在方法括号的后面:</p>
|
||||
<pre><code class="lang-swift">if let upper = john.residence?.address?.buildingIdentifier()?.uppercaseString {
|
||||
println("John's uppercase building identifier is \(upper).")
|
||||
}
|
||||
// 打印 "John's uppercase building identifier is THE LARCHES."。
|
||||
</code></pre><blockquote>
|
||||
<p>注意:
|
||||
在上面的例子中,你将可选链问号符放在括号后面是因为你想要链接的可选值是<code>buildingIdentifier</code>方法的返回值,不是<code>buildingIdentifier</code>方法本身。</p>
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>注意:<br>在上面的例子中,你将可选链问号符放在括号后面是因为你想要链接的可选值是<code>buildingIdentifier</code>方法的返回值,不是<code>buildingIdentifier</code>方法本身。</p>
|
||||
</blockquote>
|
||||
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="2.18" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="2.18" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,36 +587,36 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_68">
|
||||
<section class="normal" id="section-gitbook_42">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:xiehurricane</p>
|
||||
<p>校对:happyming</p>
|
||||
<p>翻译:<a href="https://github.com/xiehurricane" target="_blank">xiehurricane</a><br>校对:<a href="https://github.com/happyming" target="_blank">happyming</a></p>
|
||||
</blockquote>
|
||||
<h1 id="-type-casting-">类型转换(Type Casting)</h1>
|
||||
<h1 id="-type-casting-">类型检查(Type Casting)</h1>
|
||||
<hr>
|
||||
<p>本页包含内容:</p>
|
||||
<ul>
|
||||
<li><a href="#defining_a_class_hierarchy_for_type_casting">定义一个类层次作为例子</a></li>
|
||||
<li><a href="#checking_type">检查类型</a></li>
|
||||
<li><a href="#downcasting">向下转型(Downcasting)</a></li>
|
||||
<li><a href="#type_casting_for_any_and_anyobject"><code>Any</code>和<code>AnyObject</code>的类型转换</a></li>
|
||||
<li><a href="#type_casting_for_any_and_anyobject"><code>Any</code>和<code>AnyObject</code>的类型检查</a></li>
|
||||
</ul>
|
||||
<p> <em>类型检查</em>是一种检查类实例的方式,并且或者也是让实例作为它的父类或者子类的一种方式。</p>
|
||||
<p> 类型检查在 Swift 中使用<code>is</code> 和 <code>as</code>操作符实现。这两个操作符提供了一种简单达意的方式去检查值的类型或者转换它的类型。</p>
|
||||
<p> 你也可以用来检查一个类是否实现了某个协议,就像在 <a href="Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-XID_363">Checking for Protocol Conformance</a>部分讲述的一样。</p>
|
||||
<p><em>类型检查</em>是一种检查类实例的方式,并且或者也是让实例作为它的父类或者子类的一种方式。</p>
|
||||
<p>类型检查在 Swift 中使用<code>is</code> 和 <code>as</code>操作符实现。这两个操作符提供了一种简单达意的方式去检查值的类型或者转换它的类型。</p>
|
||||
<p>你也可以用来检查一个类是否实现了某个协议,就像在 <a href="Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-XID_363">Checking for Protocol Conformance</a>部分讲述的一样。</p>
|
||||
<p><a name="defining_a_class_hierarchy_for_type_casting"></a></p>
|
||||
<h2 id="-">定义一个类层次作为例子</h2>
|
||||
<p> 你可以将它用在类和子类的层次结构上,检查特定类实例的类型并且转换这个类实例的类型成为这个层次结构中的其他类型。这下面的三个代码段定义了一个类层次和一个包含了几个这些类实例的数组,作为类型检查的例子。</p>
|
||||
<p> 第一个代码片段定义了一个新的基础类<code>MediaItem</code>。这个类为任何出现在数字媒体库的媒体项提供基础功能。特别的,它声明了一个 <code>String</code> 类型的 <code>name</code> 属性,和一个<code>init name</code>初始化器。(它假定所有的媒体项都有个名称。)</p>
|
||||
<pre><code>class MediaItem {
|
||||
<p>你可以将它用在类和子类的层次结构上,检查特定类实例的类型并且转换这个类实例的类型成为这个层次结构中的其他类型。这下面的三个代码段定义了一个类层次和一个包含了几个这些类实例的数组,作为类型检查的例子。</p>
|
||||
<p>第一个代码片段定义了一个新的基础类<code>MediaItem</code>。这个类为任何出现在数字媒体库的媒体项提供基础功能。特别的,它声明了一个 <code>String</code> 类型的 <code>name</code> 属性,和一个<code>init name</code>初始化器。(它假定所有的媒体项都有个名称。)</p>
|
||||
<pre><code class="lang-swift">class MediaItem {
|
||||
var name: String
|
||||
init(name: String) {
|
||||
self.name = name
|
||||
}
|
||||
}
|
||||
</code></pre><p> 下一个代码段定义了 <code>MediaItem</code> 的两个子类。第一个子类<code>Movie</code>,在父类(或者说基类)的基础上增加了一个 <code>director</code>(导演) 属性,和相应的初始化器。第二个类在父类的基础上增加了一个 <code>artist</code>(艺术家) 属性,和相应的初始化器:</p>
|
||||
<pre><code>class Movie: MediaItem {
|
||||
</code></pre>
|
||||
<p>下一个代码段定义了 <code>MediaItem</code> 的两个子类。第一个子类<code>Movie</code>,在父类(或者说基类)的基础上增加了一个 <code>director</code>(导演) 属性,和相应的初始化器。第二个类在父类的基础上增加了一个 <code>artist</code>(艺术家) 属性,和相应的初始化器:</p>
|
||||
<pre><code class="lang-swift">class Movie: MediaItem {
|
||||
var director: String
|
||||
init(name: String, director: String) {
|
||||
self.director = director
|
||||
@ -631,9 +631,9 @@ class Song: MediaItem {
|
||||
super.init(name: name)
|
||||
}
|
||||
}
|
||||
</code></pre><p> 最后一个代码段创建了一个数组常量 <code>library</code>
|
||||
,包含两个<code>Movie</code>实例和三个<code>Song</code>实例。<code>library</code>的类型是在它被初始化时根据它数组中所包含的内容推断来的。Swift 的类型检测器能够演绎出<code>Movie</code> 和 <code>Song</code> 有共同的父类 <code>MediaItem</code> ,所以它推断出 <code>MediaItem[]</code> 类作为 <code>library</code> 的类型。</p>
|
||||
<pre><code>let library = [
|
||||
</code></pre>
|
||||
<p>最后一个代码段创建了一个数组常量 <code>library</code>,包含两个<code>Movie</code>实例和三个<code>Song</code>实例。<code>library</code>的类型是在它被初始化时根据它数组中所包含的内容推断来的。Swift 的类型检测器能够演绎出<code>Movie</code> 和 <code>Song</code> 有共同的父类 <code>MediaItem</code> ,所以它推断出 <code>MediaItem[]</code> 类作为 <code>library</code> 的类型。</p>
|
||||
<pre><code class="lang-swift">let library = [
|
||||
Movie(name: "Casablanca", director: "Michael Curtiz"),
|
||||
Song(name: "Blue Suede Shoes", artist: "Elvis Presley"),
|
||||
Movie(name: "Citizen Kane", director: "Orson Welles"),
|
||||
@ -641,12 +641,13 @@ class Song: MediaItem {
|
||||
Song(name: "Never Gonna Give You Up", artist: "Rick Astley")
|
||||
]
|
||||
// the type of "library" is inferred to be MediaItem[]
|
||||
</code></pre><p> 在幕后<code>library</code> 里存储的媒体项依然是 <code>Movie</code> 和 <code>Song</code> 类型的,但是,若你迭代它,取出的实例会是 <code>MediaItem</code> 类型的,而不是 <code>Movie</code> 和 <code>Song</code> 类型的。为了让它们作为它们本来的类型工作,你需要检查它们的类型或者向下转换它们的类型到其它类型,就像下面描述的一样。</p>
|
||||
</code></pre>
|
||||
<p>在幕后<code>library</code> 里存储的媒体项依然是 <code>Movie</code> 和 <code>Song</code> 类型的,但是,若你迭代它,取出的实例会是 <code>MediaItem</code> 类型的,而不是 <code>Movie</code> 和 <code>Song</code> 类型的。为了让它们作为它们本来的类型工作,你需要检查它们的类型或者向下转换它们的类型到其它类型,就像下面描述的一样。</p>
|
||||
<p><a name="checking_type"></a></p>
|
||||
<h2 id="-">检查类型</h2>
|
||||
<p> 用类型检查操作符(<code>is</code>)来检查一个实例是否属于特定子类型。若实例属于那个子类型,类型检查操作符返回 <code>true</code> ,否则返回 <code>false</code> 。</p>
|
||||
<p> 下面的例子定义了两个变量,<code>movieCount</code> 和 <code>songCount</code>,用来计算数组<code>library</code> 中 <code>Movie</code> 和 <code>Song</code> 类型的实例数量。</p>
|
||||
<pre><code>var movieCount = 0
|
||||
<h2 id="-checking-type-">检查类型(Checking Type)</h2>
|
||||
<p>用类型检查操作符(<code>is</code>)来检查一个实例是否属于特定子类型。若实例属于那个子类型,类型检查操作符返回 <code>true</code> ,否则返回 <code>false</code> 。</p>
|
||||
<p>下面的例子定义了两个变量,<code>movieCount</code> 和 <code>songCount</code>,用来计算数组<code>library</code> 中 <code>Movie</code> 和 <code>Song</code> 类型的实例数量。</p>
|
||||
<pre><code class="lang-swift">var movieCount = 0
|
||||
var songCount = 0
|
||||
|
||||
for item in library {
|
||||
@ -659,20 +660,21 @@ for item in library {
|
||||
|
||||
println("Media library contains \(movieCount) movies and \(songCount) songs")
|
||||
// prints "Media library contains 2 movies and 3 songs"
|
||||
</code></pre><p> 示例迭代了数组 <code>library</code> 中的所有项。每一次, <code>for</code>-<code>in</code> 循环设置
|
||||
<code>item</code> 为数组中的下一个 <code>MediaItem</code>。</p>
|
||||
<p> 若当前 <code>MediaItem</code> 是一个 <code>Movie</code> 类型的实例, <code>item is Movie</code> 返回
|
||||
<code>true</code>,相反返回 <code>false</code>。同样的,<code>item is
|
||||
Song</code>检查item是否为<code>Song</code>类型的实例。在循环结束后,<code>movieCount</code> 和 <code>songCount</code>的值就是被找到属于各自的类型的实例数量。</p>
|
||||
</code></pre>
|
||||
<p>示例迭代了数组 <code>library</code> 中的所有项。每一次, <code>for</code>-<code>in</code> 循环设置
|
||||
<code>item</code> 为数组中的下一个 <code>MediaItem</code>。</p>
|
||||
<p>若当前 <code>MediaItem</code> 是一个 <code>Movie</code> 类型的实例, <code>item is Movie</code> 返回
|
||||
<code>true</code>,相反返回 <code>false</code>。同样的,<code>item is
|
||||
Song</code>检查item是否为<code>Song</code>类型的实例。在循环结束后,<code>movieCount</code> 和 <code>songCount</code>的值就是被找到属于各自的类型的实例数量。</p>
|
||||
<p><a name="downcasting"></a></p>
|
||||
<h2 id="-downcasting-">向下转型(Downcasting)</h2>
|
||||
<p> 某类型的一个常量或变量可能在幕后实际上属于一个子类。你可以相信,上面就是这种情况。你可以尝试向下转到它的子类型,用类型转换操作符(<code>as</code>)</p>
|
||||
<p> 因为向下转型可能会失败,类型转型操作符带有两种不同形式。可选形式( optional form) <code>as?</code> 返回一个你试图下转成的类型的可选值(optional value)。强制形式 <code>as</code> 把试图向下转型和强制解包(force-unwraps)结果作为一个混合动作。</p>
|
||||
<p> 当你不确定向下转型可以成功时,用类型转换的可选形式(<code>as?</code>)。可选形式的类型转换总是返回一个可选值(optional value),并且若下转是不可能的,可选值将是 <code>nil</code> 。这使你能够检查向下转型是否成功。</p>
|
||||
<p> 只有你可以确定向下转型一定会成功时,才使用强制形式。当你试图向下转型为一个不正确的类型时,强制形式的类型转换会触发一个运行时错误。</p>
|
||||
<p> 下面的例子,迭代了<code>library</code>里的每一个 <code>MediaItem</code> ,并打印出适当的描述。要这样做,<code>item</code>需要真正作为<code>Movie</code> 或 <code>Song</code>的类型来使用。不仅仅是作为 <code>MediaItem</code>。为了能够使用<code>Movie</code> 或 <code>Song</code>的 <code>director</code> 或 <code>artist</code>属性,这是必要的。</p>
|
||||
<p> 在这个示例中,数组中的每一个<code>item</code>可能是 <code>Movie</code> 或 <code>Song</code>。 事前你不知道每个<code>item</code>的真实类型,所以这里使用可选形式的类型转换 (<code>as?</code>)去检查循环里的每次下转。</p>
|
||||
<pre><code>for item in library {
|
||||
<p>某类型的一个常量或变量可能在幕后实际上属于一个子类。你可以相信,上面就是这种情况。你可以尝试向下转到它的子类型,用类型检查操作符(<code>as</code>)</p>
|
||||
<p>因为向下转型可能会失败,类型转型操作符带有两种不同形式。可选形式( optional form) <code>as?</code> 返回一个你试图下转成的类型的可选值(optional value)。强制形式 <code>as</code> 把试图向下转型和强制解包(force-unwraps)结果作为一个混合动作。</p>
|
||||
<p>当你不确定向下转型可以成功时,用类型检查的可选形式(<code>as?</code>)。可选形式的类型检查总是返回一个可选值(optional value),并且若下转是不可能的,可选值将是 <code>nil</code> 。这使你能够检查向下转型是否成功。</p>
|
||||
<p>只有你可以确定向下转型一定会成功时,才使用强制形式。当你试图向下转型为一个不正确的类型时,强制形式的类型检查会触发一个运行时错误。</p>
|
||||
<p>下面的例子,迭代了<code>library</code>里的每一个 <code>MediaItem</code> ,并打印出适当的描述。要这样做,<code>item</code>需要真正作为<code>Movie</code> 或 <code>Song</code>的类型来使用。不仅仅是作为 <code>MediaItem</code>。为了能够使用<code>Movie</code> 或 <code>Song</code>的 <code>director</code> 或 <code>artist</code>属性,这是必要的。</p>
|
||||
<p>在这个示例中,数组中的每一个<code>item</code>可能是 <code>Movie</code> 或 <code>Song</code>。 事前你不知道每个<code>item</code>的真实类型,所以这里使用可选形式的类型检查 (<code>as?</code>)去检查循环里的每次下转。</p>
|
||||
<pre><code class="lang-swift">for item in library {
|
||||
if let movie = item as? Movie {
|
||||
println("Movie: '\(movie.name)', dir. \(movie.director)")
|
||||
} else if let song = item as? Song {
|
||||
@ -685,58 +687,58 @@ println("Media library contains \(movieCount) movies and \(songCount) songs
|
||||
// Movie: 'Citizen Kane', dir. Orson Welles
|
||||
// Song: 'The One And Only', by Chesney Hawkes
|
||||
// Song: 'Never Gonna Give You Up', by Rick Astley
|
||||
</code></pre><p> 示例首先试图将 <code>item</code> 下转为 <code>Movie</code>。因为 <code>item</code> 是一个 <code>MediaItem</code>
|
||||
类型的实例,它可能是一个<code>Movie</code>;同样,它可能是一个 <code>Song</code>,或者仅仅是基类
|
||||
<code>MediaItem</code>。因为不确定,<code>as?</code>形式在试图下转时将返还一个可选值。 <code>item as Movie</code> 的返回值是<code>Movie?</code>类型或 “optional <code>Movie</code>”。</p>
|
||||
<p> 当向下转型为 <code>Movie</code> 应用在两个 <code>Song</code>
|
||||
实例时将会失败。为了处理这种情况,上面的例子使用了可选绑定(optional binding)来检查可选 <code>Movie</code>真的包含一个值(这个是为了判断下转是否成功。)可选绑定是这样写的“<code>if let movie = item as? Movie</code>”,可以这样解读:</p>
|
||||
<p> “尝试将 <code>item</code> 转为 <code>Movie</code>类型。若成功,设置一个新的临时常量 <code>movie</code> 来存储返回的可选<code>Movie</code>”</p>
|
||||
<p> 若向下转型成功,然后<code>movie</code>的属性将用于打印一个<code>Movie</code>实例的描述,包括它的导演的名字<code>director</code>。当<code>Song</code>被找到时,一个相近的原理被用来检测 <code>Song</code> 实例和打印它的描述。</p>
|
||||
</code></pre>
|
||||
<p>示例首先试图将 <code>item</code> 下转为 <code>Movie</code>。因为 <code>item</code> 是一个 <code>MediaItem</code>
|
||||
类型的实例,它可能是一个<code>Movie</code>;同样,它可能是一个 <code>Song</code>,或者仅仅是基类
|
||||
<code>MediaItem</code>。因为不确定,<code>as?</code>形式在试图下转时将返还一个可选值。 <code>item as Movie</code> 的返回值是<code>Movie?</code>类型或 “optional <code>Movie</code>”。</p>
|
||||
<p>当向下转型为 <code>Movie</code> 应用在两个 <code>Song</code>
|
||||
实例时将会失败。为了处理这种情况,上面的例子使用了可选绑定(optional binding)来检查可选 <code>Movie</code>真的包含一个值(这个是为了判断下转是否成功。)可选绑定是这样写的“<code>if let movie = item as? Movie</code>”,可以这样解读:</p>
|
||||
<p>“尝试将 <code>item</code> 转为 <code>Movie</code>类型。若成功,设置一个新的临时常量 <code>movie</code> 来存储返回的可选<code>Movie</code>”</p>
|
||||
<p>若向下转型成功,然后<code>movie</code>的属性将用于打印一个<code>Movie</code>实例的描述,包括它的导演的名字<code>director</code>。当<code>Song</code>被找到时,一个相近的原理被用来检测 <code>Song</code> 实例和打印它的描述。</p>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<p>转换没有真的改变实例或它的值。潜在的根本的实例保持不变;只是简单地把它作为它被转换成的类来使用。</p>
|
||||
<p>注意:<br>转换没有真的改变实例或它的值。潜在的根本的实例保持不变;只是简单地把它作为它被转换成的类来使用。</p>
|
||||
</blockquote>
|
||||
<p><a name="type_casting_for_any_and_anyobject"></a></p>
|
||||
<h2 id="-any-anyobject-"><code>Any</code>和<code>AnyObject</code>的类型转换</h2>
|
||||
<p> Swift为不确定类型提供了两种特殊类型别名:</p>
|
||||
<h2 id="-any-anyobject-"><code>Any</code>和<code>AnyObject</code>的类型检查</h2>
|
||||
<p>Swift为不确定类型提供了两种特殊类型别名:</p>
|
||||
<ul>
|
||||
<li><p><code>AnyObject</code>可以代表任何class类型的实例。</p>
|
||||
</li>
|
||||
<li><p><code>Any</code>可以表示任何类型,除了方法类型(function types)。</p>
|
||||
</li>
|
||||
<li><code>AnyObject</code>可以代表任何class类型的实例。</li>
|
||||
<li><code>Any</code>可以表示任何类型,除了方法类型(function types)。</li>
|
||||
</ul>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<p>只有当你明确的需要它的行为和功能时才使用<code>Any</code>和<code>AnyObject</code>。在你的代码里使用你期望的明确的类型总是更好的。</p>
|
||||
<p>注意:<br>只有当你明确的需要它的行为和功能时才使用<code>Any</code>和<code>AnyObject</code>。在你的代码里使用你期望的明确的类型总是更好的。</p>
|
||||
</blockquote>
|
||||
<h3 id="-anyobject-"><code>AnyObject</code>类型</h3>
|
||||
<p> 当需要在工作中使用 Cocoa
|
||||
APIs,它一般接收一个<code>AnyObject[]</code>类型的数组,或者说“一个任何对象类型的数组”。这是因为 Objective-C 没有明确的类型化数组。但是,你常常可以确定包含在仅从你知道的 API 信息提供的这样一个数组中的对象的类型。</p>
|
||||
<p> 在这些情况下,你可以使用强制形式的类型转换(<code>as</code>)来下转在数组中的每一项到比 <code>AnyObject</code> 更明确的类型,不需要可选解析(optional unwrapping)。</p>
|
||||
<p> 下面的示例定义了一个 <code>AnyObject[]</code> 类型的数组并填入三个<code>Movie</code>类型的实例:</p>
|
||||
<pre><code>let someObjects: AnyObject[] = [
|
||||
<p>当需要在工作中使用 Cocoa
|
||||
APIs,它一般接收一个<code>AnyObject[]</code>类型的数组,或者说“一个任何对象类型的数组”。这是因为 Objective-C 没有明确的类型化数组。但是,你常常可以确定包含在仅从你知道的 API 信息提供的这样一个数组中的对象的类型。</p>
|
||||
<p>在这些情况下,你可以使用强制形式的类型检查(<code>as</code>)来下转在数组中的每一项到比 <code>AnyObject</code> 更明确的类型,不需要可选解析(optional unwrapping)。</p>
|
||||
<p>下面的示例定义了一个 <code>AnyObject[]</code> 类型的数组并填入三个<code>Movie</code>类型的实例:</p>
|
||||
<pre><code class="lang-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")
|
||||
]
|
||||
</code></pre><p> 因为知道这个数组只包含 <code>Movie</code> 实例,你可以直接用(<code>as</code>)下转并解包到不可选的<code>Movie</code>类型(ps:其实就是我们常用的正常类型,这里是为了和可选类型相对比)。</p>
|
||||
<pre><code>for object in someObjects {
|
||||
</code></pre>
|
||||
<p>因为知道这个数组只包含 <code>Movie</code> 实例,你可以直接用(<code>as</code>)下转并解包到不可选的<code>Movie</code>类型(ps:其实就是我们常用的正常类型,这里是为了和可选类型相对比)。</p>
|
||||
<pre><code class="lang-swift">for object in someObjects {
|
||||
let movie = object as Movie
|
||||
println("Movie: '\(movie.name)', dir. \(movie.director)")
|
||||
}
|
||||
// Movie: '2001: A Space Odyssey', dir. Stanley Kubrick
|
||||
// Movie: 'Moon', dir. Duncan Jones
|
||||
// Movie: 'Alien', dir. Ridley Scott
|
||||
</code></pre><p> 为了变为一个更短的形式,下转<code>someObjects</code>数组为<code>Movie[]</code>类型来代替下转每一项方式。</p>
|
||||
<pre><code>for movie in someObjects as Movie[] {
|
||||
</code></pre>
|
||||
<p>为了变为一个更短的形式,下转<code>someObjects</code>数组为<code>Movie[]</code>类型来代替下转每一项方式。</p>
|
||||
<pre><code class="lang-swift">for movie in someObjects as Movie[] {
|
||||
println("Movie: '\(movie.name)', dir. \(movie.director)")
|
||||
}
|
||||
// Movie: '2001: A Space Odyssey', dir. Stanley Kubrick
|
||||
// Movie: 'Moon', dir. Duncan Jones
|
||||
// Movie: 'Alien', dir. Ridley Scott
|
||||
</code></pre><h3 id="-any-"><code>Any</code>类型</h3>
|
||||
<p> 这里有个示例,使用 <code>Any</code> 类型来和混合的不同类型一起工作,包括非<code>class</code>类型。它创建了一个可以存储<code>Any</code>类型的数组 <code>things</code>。</p>
|
||||
<pre><code>var things = Any[]()
|
||||
</code></pre>
|
||||
<h3 id="-any-"><code>Any</code>类型</h3>
|
||||
<p>这里有个示例,使用 <code>Any</code> 类型来和混合的不同类型一起工作,包括非<code>class</code>类型。它创建了一个可以存储<code>Any</code>类型的数组 <code>things</code>。</p>
|
||||
<pre><code class="lang-swift">var things = Any[]()
|
||||
|
||||
things.append(0)
|
||||
things.append(0.0)
|
||||
@ -745,9 +747,10 @@ things.append(3.14159)
|
||||
things.append("hello")
|
||||
things.append((3.0, 5.0))
|
||||
things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman"))
|
||||
</code></pre><p> <code>things</code> 数组包含两个 <code>Int</code> 值,2个 <code>Double</code> 值,1个 <code>String</code> 值,一个元组 <code>(Double, Double)</code> ,Ivan Reitman 导演的电影“Ghostbusters”。</p>
|
||||
<p> 你可以在 <code>switch</code> <code>cases</code>里用<code>is</code> 和 <code>as</code> 操作符来发觉只知道是 <code>Any</code> 或 <code>AnyObject</code>的常量或变量的类型。 下面的示例迭代 <code>things</code>数组中的每一项的并用<code>switch</code>语句查找每一项的类型。这几种<code>switch</code>语句的情形绑定它们匹配的值到一个规定类型的常量,让它们可以打印它们的值:</p>
|
||||
<pre><code>for thing in things {
|
||||
</code></pre>
|
||||
<p><code>things</code> 数组包含两个 <code>Int</code> 值,2个 <code>Double</code> 值,1个 <code>String</code> 值,一个元组 <code>(Double, Double)</code> ,Ivan Reitman 导演的电影“Ghostbusters”。</p>
|
||||
<p>你可以在 <code>switch</code> <code>cases</code>里用<code>is</code> 和 <code>as</code> 操作符来发觉只知道是 <code>Any</code> 或 <code>AnyObject</code>的常量或变量的类型。 下面的示例迭代 <code>things</code>数组中的每一项的并用<code>switch</code>语句查找每一项的类型。这几种<code>switch</code>语句的情形绑定它们匹配的值到一个规定类型的常量,让它们可以打印它们的值:</p>
|
||||
<pre><code class="lang-swift">for thing in things {
|
||||
switch thing {
|
||||
case 0 as Int:
|
||||
println("zero as an Int")
|
||||
@ -777,10 +780,9 @@ things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman
|
||||
// a string value of "hello"
|
||||
// an (x, y) point at 3.0, 5.0
|
||||
// a movie called 'Ghostbusters', dir. Ivan Reitman
|
||||
</code></pre><p>。</p>
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<p>在一个switch语句的case中使用强制形式的类型转换操作符(as, 而不是 as?)来检查和转换到一个明确的类型。在 switch case 语句的内容中这种检查总是安全的。</p>
|
||||
<p>注意:<br>在一个switch语句的case中使用强制形式的类型检查操作符(as, 而不是 as?)来检查和转换到一个明确的类型。在 switch case 语句的内容中这种检查总是安全的。</p>
|
||||
</blockquote>
|
||||
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="2.19" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="2.19" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,11 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_70">
|
||||
<section class="normal" id="section-gitbook_44">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:Lin-H</p>
|
||||
<p>校对:shinyzhu</p>
|
||||
<p>翻译:<a href="https://github.com/Lin-H" target="_blank">Lin-H</a><br>校对:<a href="https://github.com/shinyzhu" target="_blank">shinyzhu</a></p>
|
||||
</blockquote>
|
||||
<h1 id="-">类型嵌套</h1>
|
||||
<hr>
|
||||
@ -606,11 +605,12 @@
|
||||
<h2 id="-">类型嵌套实例</h2>
|
||||
<p>下面这个例子定义了一个结构体<code>BlackjackCard</code>(二十一点),用来模拟<code>BlackjackCard</code>中的扑克牌点数。<code>BlackjackCard</code>结构体包含2个嵌套定义的枚举类型<code>Suit</code> 和 <code>Rank</code>。</p>
|
||||
<p>在<code>BlackjackCard</code>规则中,<code>Ace</code>牌可以表示1或者11,<code>Ace</code>牌的这一特征用一个嵌套在枚举型<code>Rank</code>的结构体<code>Values</code>来表示。</p>
|
||||
<pre><code>struct BlackjackCard {
|
||||
<pre><code class="lang-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
|
||||
@ -629,6 +629,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BlackjackCard 的属性和方法
|
||||
let rank: Rank, suit: Suit
|
||||
var description: String {
|
||||
@ -640,7 +641,8 @@
|
||||
return output
|
||||
}
|
||||
}
|
||||
</code></pre><p>枚举型的<code>Suit</code>用来描述扑克牌的四种花色,并分别用一个<code>Character</code>类型的值代表花色符号。</p>
|
||||
</code></pre>
|
||||
<p>枚举型的<code>Suit</code>用来描述扑克牌的四种花色,并分别用一个<code>Character</code>类型的值代表花色符号。</p>
|
||||
<p>枚举型的<code>Rank</code>用来描述扑克牌从<code>Ace</code>~10,<code>J</code>,<code>Q</code>,<code>K</code>,13张牌,并分别用一个<code>Int</code>类型的值表示牌的面值。(这个<code>Int</code>类型的值不适用于<code>Ace</code>,<code>J</code>,<code>Q</code>,<code>K</code>的牌)。</p>
|
||||
<p>如上文所提到的,枚举型<code>Rank</code>在自己内部定义了一个嵌套结构体<code>Values</code>。这个结构体包含两个变量,只有<code>Ace</code>有两个数值,其余牌都只有一个数值。结构体<code>Values</code>中定义的两个属性:</p>
|
||||
<p><code>first</code>, 为<code>Int</code>
|
||||
@ -648,16 +650,19 @@
|
||||
<p><code>Rank</code>定义了一个计算属性<code>values</code>,这个计算属性会根据牌的面值,用适当的数值去初始化<code>Values</code>实例,并赋值给<code>values</code>。对于<code>J</code>,<code>Q</code>,<code>K</code>,<code>Ace</code>会使用特殊数值,对于数字面值的牌使用<code>Int</code>类型的值。</p>
|
||||
<p><code>BlackjackCard</code>结构体自身有两个属性—<code>rank</code>与<code>suit</code>,也同样定义了一个计算属性<code>description</code>,<code>description</code>属性用<code>rank</code>和<code>suit</code>的中内容来构建对这张扑克牌名字和数值的描述,并用可选类型<code>second</code>来检查是否存在第二个值,若存在,则在原有的描述中增加对第二数值的描述。</p>
|
||||
<p>因为<code>BlackjackCard</code>是一个没有自定义构造函数的结构体,在<a href="https://github.com/CocoaChina-editors/Welcome-to-Swift/blob/master/The%20Swift%20Programming%20Language/02Language%20Guide/14Initialization.md" target="_blank">Memberwise Initializers for Structure Types</a>中知道结构体有默认的成员构造函数,所以你可以用默认的<code>initializer</code>去初始化新的常量<code>theAceOfSpades</code>:</p>
|
||||
<pre><code>let theAceOfSpades = BlackjackCard(rank: .Ace, suit: .Spades)
|
||||
<pre><code class="lang-swift">let theAceOfSpades = BlackjackCard(rank: .Ace, suit: .Spades)
|
||||
println("theAceOfSpades: \(theAceOfSpades.description)")
|
||||
// 打印出 "theAceOfSpades: suit is ♠, value is 1 or 11"
|
||||
</code></pre><p>尽管<code>Rank</code>和<code>Suit</code>嵌套在<code>BlackjackCard</code>中,但仍可被引用,所以在初始化实例时能够通过枚举类型中的成员名称单独引用。在上面的例子中<code>description</code>属性能正确得输出对<code>Ace</code>牌有1和11两个值。</p>
|
||||
</code></pre>
|
||||
<p>尽管<code>Rank</code>和<code>Suit</code>嵌套在<code>BlackjackCard</code>中,但仍可被引用,所以在初始化实例时能够通过枚举类型中的成员名称单独引用。在上面的例子中<code>description</code>属性能正确得输出对<code>Ace</code>牌有1和11两个值。</p>
|
||||
<p><a name="referring_to_nested_types"></a></p>
|
||||
<h2 id="-">类型嵌套的引用</h2>
|
||||
<p>在外部对嵌套类型的引用,以被嵌套类型的名字为前缀,加上所要引用的属性名:</p>
|
||||
<pre><code>let heartsSymbol = BlackjackCard.Suit.Hearts.toRaw()
|
||||
<pre><code class="lang-swift">let heartsSymbol = BlackjackCard.Suit.Hearts.toRaw()
|
||||
// 红心的符号 为 "♡"
|
||||
</code></pre><p>对于上面这个例子,这样可以使<code>Suit</code>, <code>Rank</code>, 和 <code>Values</code>的名字尽可能的短,因为它们的名字会自然的由被定义的上下文来限定。</p>
|
||||
</code></pre>
|
||||
<p>对于上面这个例子,这样可以使<code>Suit</code>, <code>Rank</code>, 和 <code>Values</code>的名字尽可能的短,因为它们的名字会自然的由被定义的上下文来限定。</p>
|
||||
<p>preview</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="2.20" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="2.20" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,11 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_72">
|
||||
<section class="normal" id="section-gitbook_46">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:lyuka</p>
|
||||
<p>校对:Hawstein</p>
|
||||
<p>翻译:<a href="https://github.com/lyuka" target="_blank">lyuka</a><br>校对:<a href="https://github.com/Hawstein" target="_blank">Hawstein</a></p>
|
||||
</blockquote>
|
||||
<h1 id="-extensions-">扩展(Extensions)</h1>
|
||||
<hr>
|
||||
@ -615,24 +614,25 @@
|
||||
<li>使一个已有类型符合某个协议</li>
|
||||
</ul>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<p>如果你定义了一个扩展向一个已有类型添加新功能,那么这个新功能对该类型的所有已有实例中都是可用的,即使它们是在你的这个扩展的前面定义的。</p>
|
||||
<p>注意:<br>如果你定义了一个扩展向一个已有类型添加新功能,那么这个新功能对该类型的所有已有实例中都是可用的,即使它们是在你的这个扩展的前面定义的。</p>
|
||||
</blockquote>
|
||||
<p><a name="extension_syntax"></a></p>
|
||||
<h2 id="-extension-syntax-">扩展语法(Extension Syntax)</h2>
|
||||
<p>声明一个扩展使用关键字<code>extension</code>:</p>
|
||||
<pre><code>extension SomeType {
|
||||
<pre><code class="lang-swift">extension SomeType {
|
||||
// 加到SomeType的新功能写到这里
|
||||
}
|
||||
</code></pre><p>一个扩展可以扩展一个已有类型,使其能够适配一个或多个协议(protocol)。当这种情况发生时,协议的名字应该完全按照类或结构体的名字的方式进行书写:</p>
|
||||
<pre><code>extension SomeType: SomeProtocol, AnotherProctocol {
|
||||
</code></pre>
|
||||
<p>一个扩展可以扩展一个已有类型,使其能够适配一个或多个协议(protocol)。当这种情况发生时,协议的名字应该完全按照类或结构体的名字的方式进行书写:</p>
|
||||
<pre><code class="lang-swift">extension SomeType: SomeProtocol, AnotherProctocol {
|
||||
// 协议实现写到这里
|
||||
}
|
||||
</code></pre><p>按照这种方式添加的协议遵循者(protocol conformance)被称之为<a href="21_Protocols.html#adding_protocol_conformance_with_an_extension">在扩展中添加协议遵循者</a></p>
|
||||
</code></pre>
|
||||
<p>按照这种方式添加的协议遵循者(protocol conformance)被称之为<a href="21_Protocols.html#adding_protocol_conformance_with_an_extension">在扩展中添加协议遵循者</a></p>
|
||||
<p><a name="computed_properties"></a></p>
|
||||
<h2 id="-computed-properties-">计算型属性(Computed Properties)</h2>
|
||||
<p>扩展可以向已有类型添加计算型实例属性和计算型类型属性。下面的例子向 Swift 的内建<code>Double</code>类型添加了5个计算型实例属性,从而提供与距离单位协作的基本支持。</p>
|
||||
<pre><code>extension Double {
|
||||
<pre><code class="lang-swift">extension Double {
|
||||
var km: Double { return self * 1_000.0 }
|
||||
var m : Double { return self }
|
||||
var cm: Double { return self / 100.0 }
|
||||
@ -645,27 +645,27 @@ println("One inch is \(oneInch) meters")
|
||||
let threeFeet = 3.ft
|
||||
println("Three feet is \(threeFeet) meters")
|
||||
// 打印输出:"Three feet is 0.914399970739201 meters"
|
||||
</code></pre><p>这些计算属性表达的含义是把一个<code>Double</code>型的值看作是某单位下的长度值。即使它们被实现为计算型属性,但这些属性仍可以接一个带有dot语法的浮点型字面值,而这恰恰是使用这些浮点型字面量实现距离转换的方式。</p>
|
||||
</code></pre>
|
||||
<p>这些计算属性表达的含义是把一个<code>Double</code>型的值看作是某单位下的长度值。即使它们被实现为计算型属性,但这些属性仍可以接一个带有dot语法的浮点型字面值,而这恰恰是使用这些浮点型字面量实现距离转换的方式。</p>
|
||||
<p>在上述例子中,一个<code>Double</code>型的值<code>1.0</code>被用来表示“1米”。这就是为什么<code>m</code>计算型属性返回<code>self</code>——表达式<code>1.m</code>被认为是计算<code>1.0</code>的<code>Double</code>值。</p>
|
||||
<p>其它单位则需要一些转换来表示在米下测量的值。1千米等于1,000米,所以<code>km</code>计算型属性要把值乘以<code>1_000.00</code>来转化成单位米下的数值。类似地,1米有3.28024英尺,所以<code>ft</code>计算型属性要把对应的<code>Double</code>值除以<code>3.28024</code>来实现英尺到米的单位换算。</p>
|
||||
<p>这些属性是只读的计算型属性,所有从简考虑它们不用<code>get</code>关键字表示。它们的返回值是<code>Double</code>型,而且可以用于所有接受<code>Double</code>的数学计算中:</p>
|
||||
<pre><code>let aMarathon = 42.km + 195.m
|
||||
<pre><code class="lang-swift">let aMarathon = 42.km + 195.m
|
||||
println("A marathon is \(aMarathon) meters long")
|
||||
// 打印输出:"A marathon is 42495.0 meters long"
|
||||
</code></pre><blockquote>
|
||||
<p>注意:</p>
|
||||
<p>扩展可以添加新的计算属性,但是不可以添加存储属性,也不可以向已有属性添加属性观测器(property observers)。</p>
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>注意:<br>扩展可以添加新的计算属性,但是不可以添加存储属性,也不可以向已有属性添加属性观测器(property observers)。</p>
|
||||
</blockquote>
|
||||
<p><a name="initializers"></a></p>
|
||||
<h2 id="-initializers-">构造器(Initializers)</h2>
|
||||
<p>扩展可以向已有类型添加新的构造器。这可以让你扩展其它类型,将你自己的定制类型作为构造器参数,或者提供该类型的原始实现中没有包含的额外初始化选项。 </p>
|
||||
<p>扩展能向类中添加新的便利构造器,但是它们不能向类中添加新的指定构造器或析构函数。指定构造器和析构函数必须总是由原始的类实现来提供。</p>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<p>如果你使用扩展向一个值类型添加一个构造器,该构造器向所有的存储属性提供默认值,而且没有定义任何定制构造器(custom initializers),那么对于来自你的扩展构造器中的值类型,你可以调用默认构造器(default initializers)和逐一成员构造器(memberwise initializers)。<br>正如在值类型的构造器授权中描述的,如果你已经把构造器写成值类型原始实现的一部分,上述规则不再适用。</p>
|
||||
<p>注意:<br>如果你使用扩展向一个值类型添加一个构造器,该构造器向所有的存储属性提供默认值,而且没有定义任何定制构造器(custom initializers),那么对于来自你的扩展构造器中的值类型,你可以调用默认构造器(default initializers)和逐一成员构造器(memberwise initializers)。<br>正如在值类型的构造器授权中描述的,如果你已经把构造器写成值类型原始实现的一部分,上述规则不再适用。</p>
|
||||
</blockquote>
|
||||
<p>下面的例子定义了一个用于描述几何矩形的定制结构体<code>Rect</code>。这个例子同时定义了两个辅助结构体<code>Size</code>和<code>Point</code>,它们都把<code>0.0</code>作为所有属性的默认值:</p>
|
||||
<pre><code>struct Size {
|
||||
<pre><code class="lang-swift">struct Size {
|
||||
var width = 0.0, height = 0.0
|
||||
}
|
||||
struct Point {
|
||||
@ -675,56 +675,62 @@ struct Rect {
|
||||
var origin = Point()
|
||||
var size = Size()
|
||||
}
|
||||
</code></pre><p>因为结构体<code>Rect</code>提供了其所有属性的默认值,所以正如默认构造器中描述的,它可以自动接受一个默认的构造器和一个成员级构造器。这些构造器可以用于构造新的<code>Rect</code>实例:</p>
|
||||
<pre><code>let defaultRect = Rect()
|
||||
</code></pre>
|
||||
<p>因为结构体<code>Rect</code>提供了其所有属性的默认值,所以正如默认构造器中描述的,它可以自动接受一个默认的构造器和一个成员级构造器。这些构造器可以用于构造新的<code>Rect</code>实例:</p>
|
||||
<pre><code class="lang-swift">let defaultRect = Rect()
|
||||
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0),
|
||||
size: Size(width: 5.0, height: 5.0))
|
||||
</code></pre><p>你可以提供一个额外的使用特殊中心点和大小的构造器来扩展<code>Rect</code>结构体:</p>
|
||||
<pre><code>extension Rect {
|
||||
</code></pre>
|
||||
<p>你可以提供一个额外的使用特殊中心点和大小的构造器来扩展<code>Rect</code>结构体:</p>
|
||||
<pre><code class="lang-swift">extension Rect {
|
||||
init(center: Point, size: Size) {
|
||||
let originX = center.x - (size.width / 2)
|
||||
let originY = center.y - (size.height / 2)
|
||||
self.init(origin: Point(x: originX, y: originY), size: size)
|
||||
}
|
||||
}
|
||||
</code></pre><p>这个新的构造器首先根据提供的<code>center</code>和<code>size</code>值计算一个合适的原点。然后调用该结构体自动的成员构造器<code>init(origin:size:)</code>,该构造器将新的原点和大小存到了合适的属性中:</p>
|
||||
<pre><code>let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
|
||||
</code></pre>
|
||||
<p>这个新的构造器首先根据提供的<code>center</code>和<code>size</code>值计算一个合适的原点。然后调用该结构体自动的成员构造器<code>init(origin:size:)</code>,该构造器将新的原点和大小存到了合适的属性中:</p>
|
||||
<pre><code class="lang-swift">let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
|
||||
size: Size(width: 3.0, height: 3.0))
|
||||
// centerRect的原点是 (2.5, 2.5),大小是 (3.0, 3.0)
|
||||
</code></pre><blockquote>
|
||||
<p>注意:</p>
|
||||
<p>如果你使用扩展提供了一个新的构造器,你依旧有责任保证构造过程能够让所有实例完全初始化。</p>
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>注意:<br>如果你使用扩展提供了一个新的构造器,你依旧有责任保证构造过程能够让所有实例完全初始化。</p>
|
||||
</blockquote>
|
||||
<p><a name="methods"></a></p>
|
||||
<h2 id="-methods-">方法(Methods)</h2>
|
||||
<p>扩展可以向已有类型添加新的实例方法和类型方法。下面的例子向<code>Int</code>类型添加一个名为<code>repetitions</code>的新实例方法:</p>
|
||||
<pre><code>extension Int {
|
||||
<pre><code class="lang-swift">extension Int {
|
||||
func repetitions(task: () -> ()) {
|
||||
for i in 0..self {
|
||||
task()
|
||||
}
|
||||
}
|
||||
}
|
||||
</code></pre><p>这个<code>repetitions</code>方法使用了一个<code>() -> ()</code>类型的单参数(single argument),表明函数没有参数而且没有返回值。</p>
|
||||
</code></pre>
|
||||
<p>这个<code>repetitions</code>方法使用了一个<code>() -> ()</code>类型的单参数(single argument),表明函数没有参数而且没有返回值。</p>
|
||||
<p>定义该扩展之后,你就可以对任意整数调用<code>repetitions</code>方法,实现的功能则是多次执行某任务:</p>
|
||||
<pre><code>3.repetitions({
|
||||
<pre><code class="lang-swift">3.repetitions({
|
||||
println("Hello!")
|
||||
})
|
||||
// Hello!
|
||||
// Hello!
|
||||
// Hello!
|
||||
</code></pre><p>可以使用 trailing 闭包使调用更加简洁:</p>
|
||||
<pre><code>3.repetitions{
|
||||
</code></pre>
|
||||
<p>可以使用 trailing 闭包使调用更加简洁:</p>
|
||||
<pre><code class="lang-swift">3.repetitions{
|
||||
println("Goodbye!")
|
||||
}
|
||||
// Goodbye!
|
||||
// Goodbye!
|
||||
// Goodbye!
|
||||
</code></pre><p><a name="mutating_instance_methods"></a></p>
|
||||
</code></pre>
|
||||
<p><a name="mutating_instance_methods"></a></p>
|
||||
<h3 id="-mutating-instance-methods-">修改实例方法(Mutating Instance Methods)</h3>
|
||||
<p>通过扩展添加的实例方法也可以修改该实例本身。结构体和枚举类型中修改<code>self</code>或其属性的方法必须将该实例方法标注为<code>mutating</code>,正如来自原始实现的修改方法一样。</p>
|
||||
<p>下面的例子向Swift的<code>Int</code>类型添加了一个新的名为<code>square</code>的修改方法,来实现一个原始值的平方计算:</p>
|
||||
<pre><code>extension Int {
|
||||
<pre><code class="lang-swift">extension Int {
|
||||
mutating func square() {
|
||||
self = self * self
|
||||
}
|
||||
@ -732,7 +738,8 @@ let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0),
|
||||
var someInt = 3
|
||||
someInt.square()
|
||||
// someInt 现在值是 9
|
||||
</code></pre><p><a name="subscripts"></a></p>
|
||||
</code></pre>
|
||||
<p><a name="subscripts"></a></p>
|
||||
<h2 id="-subscripts-">下标(Subscripts)</h2>
|
||||
<p>扩展可以向一个已有类型添加新下标。这个例子向Swift内建类型<code>Int</code>添加了一个整型下标。该下标<code>[n]</code>返回十进制数字从右向左数的第n个数字</p>
|
||||
<ul>
|
||||
@ -740,7 +747,7 @@ someInt.square()
|
||||
<li>123456789[1]返回8</li>
|
||||
</ul>
|
||||
<p>...等等</p>
|
||||
<pre><code>extension Int {
|
||||
<pre><code class="lang-swift">extension Int {
|
||||
subscript(digitIndex: Int) -> Int {
|
||||
var decimalBase = 1
|
||||
for _ in 1...digitIndex {
|
||||
@ -757,14 +764,16 @@ someInt.square()
|
||||
// returns 2
|
||||
746381295[8]
|
||||
// returns 7
|
||||
</code></pre><p>如果该<code>Int</code>值没有足够的位数,即下标越界,那么上述实现的下标会返回0,因为它会在数字左边自动补0:</p>
|
||||
<pre><code>746381295[9]
|
||||
</code></pre>
|
||||
<p>如果该<code>Int</code>值没有足够的位数,即下标越界,那么上述实现的下标会返回0,因为它会在数字左边自动补0:</p>
|
||||
<pre><code class="lang-swift">746381295[9]
|
||||
//returns 0, 即等同于:
|
||||
0746381295[9]
|
||||
</code></pre><p><a name="nested_types"></a></p>
|
||||
</code></pre>
|
||||
<p><a name="nested_types"></a></p>
|
||||
<h2 id="-nested-types-">嵌套类型(Nested Types)</h2>
|
||||
<p>扩展可以向已有的类、结构体和枚举添加新的嵌套类型:</p>
|
||||
<pre><code>extension Character {
|
||||
<pre><code class="lang-swift">extension Character {
|
||||
enum Kind {
|
||||
case Vowel, Consonant, Other
|
||||
}
|
||||
@ -780,10 +789,11 @@ someInt.square()
|
||||
}
|
||||
}
|
||||
}
|
||||
</code></pre><p>该例子向<code>Character</code>添加了新的嵌套枚举。这个名为<code>Kind</code>的枚举表示特定字符的类型。具体来说,就是表示一个标准的拉丁脚本中的字符是元音还是辅音(不考虑口语和地方变种),或者是其它类型。</p>
|
||||
</code></pre>
|
||||
<p>该例子向<code>Character</code>添加了新的嵌套枚举。这个名为<code>Kind</code>的枚举表示特定字符的类型。具体来说,就是表示一个标准的拉丁脚本中的字符是元音还是辅音(不考虑口语和地方变种),或者是其它类型。</p>
|
||||
<p>这个类子还向<code>Character</code>添加了一个新的计算实例属性,即<code>kind</code>,用来返回合适的<code>Kind</code>枚举成员。</p>
|
||||
<p>现在,这个嵌套枚举可以和一个<code>Character</code>值联合使用了:</p>
|
||||
<pre><code>func printLetterKinds(word: String) {
|
||||
<pre><code class="lang-swift">func printLetterKinds(word: String) {
|
||||
println("'\\(word)' is made up of the following kinds of letters:")
|
||||
for character in word {
|
||||
switch character.kind {
|
||||
@ -800,10 +810,10 @@ someInt.square()
|
||||
printLetterKinds("Hello")
|
||||
// 'Hello' is made up of the following kinds of letters:
|
||||
// consonant vowel consonant consonant vowel
|
||||
</code></pre><p>函数<code>printLetterKinds</code>的输入是一个<code>String</code>值并对其字符进行迭代。在每次迭代过程中,考虑当前字符的<code>kind</code>计算属性,并打印出合适的类别描述。所以<code>printLetterKinds</code>就可以用来打印一个完整单词中所有字母的类型,正如上述单词<code>"hello"</code>所展示的。</p>
|
||||
</code></pre>
|
||||
<p>函数<code>printLetterKinds</code>的输入是一个<code>String</code>值并对其字符进行迭代。在每次迭代过程中,考虑当前字符的<code>kind</code>计算属性,并打印出合适的类别描述。所以<code>printLetterKinds</code>就可以用来打印一个完整单词中所有字母的类型,正如上述单词<code>"hello"</code>所展示的。</p>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<p>由于已知<code>character.kind</code>是<code>Character.Kind</code>型,所以<code>Character.Kind</code>中的所有成员值都可以使用<code>switch</code>语句里的形式简写,比如使用 <code>.Vowel</code>代替<code>Character.Kind.Vowel</code></p>
|
||||
<p>注意:<br>由于已知<code>character.kind</code>是<code>Character.Kind</code>型,所以<code>Character.Kind</code>中的所有成员值都可以使用<code>switch</code>语句里的形式简写,比如使用 <code>.Vowel</code>代替<code>Character.Kind.Vowel</code></p>
|
||||
</blockquote>
|
||||
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="2.21" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="2.21" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,11 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_74">
|
||||
<section class="normal" id="section-gitbook_48">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:geek5nan</p>
|
||||
<p>校对:dabing1022</p>
|
||||
<p>翻译:<a href="https://github.com/geek5nan" target="_blank">geek5nan</a><br>校对:<a href="https://github.com/dabing1022" target="_blank">dabing1022</a></p>
|
||||
</blockquote>
|
||||
<h1 id="-">协议</h1>
|
||||
<hr>
|
||||
@ -604,7 +603,7 @@
|
||||
<li><a href="#protocols_as_types">协议类型(Protocols as Types)</a></li>
|
||||
<li><a href="#delegation">委托(代理)模式(Delegation)</a></li>
|
||||
<li><a href="#adding_protocol_conformance_with_an_extension">在扩展中添加协议成员(Adding Protocol Conformance with an Extension)</a></li>
|
||||
<li><a href="#declaring_protocol_adoption_with_an_extension">通过延展补充协议声明(Declaring Protocol Adoption with an Extension)</a></li>
|
||||
<li><a href="#declaring_protocol_adoption_with_an_extension">通过扩展补充协议声明(Declaring Protocol Adoption with an Extension)</a></li>
|
||||
<li><a href="#collections_of_protocol_types">集合中的协议类型(Collections of Protocol Types)</a></li>
|
||||
<li><a href="#protocol_inheritance">协议的继承(Protocol Inheritance)</a></li>
|
||||
<li><a href="#protocol_composition">协议合成(Protocol Composition)</a></li>
|
||||
@ -616,42 +615,49 @@
|
||||
<p><a name="protocol_syntax"></a></p>
|
||||
<h2 id="-">协议的语法</h2>
|
||||
<p><code>协议</code>的定义与类,结构体,枚举的定义非常相似,如下所示:</p>
|
||||
<pre><code>protocol SomeProtocol {
|
||||
<pre><code class="lang-swift">protocol SomeProtocol {
|
||||
// 协议内容
|
||||
}
|
||||
</code></pre><p>在类,结构体,枚举的名称后加上<code>协议名称</code>,中间以冒号<code>:</code>分隔即可实现协议;实现多个协议时,各协议之间用逗号<code>,</code>分隔,如下所示:</p>
|
||||
<pre><code>struct SomeStructure: FirstProtocol, AnotherProtocol {
|
||||
</code></pre>
|
||||
<p>在类,结构体,枚举的名称后加上<code>协议名称</code>,中间以冒号<code>:</code>分隔即可实现协议;实现多个协议时,各协议之间用逗号<code>,</code>分隔,如下所示:</p>
|
||||
<pre><code class="lang-swift">struct SomeStructure: FirstProtocol, AnotherProtocol {
|
||||
// 结构体内容
|
||||
}
|
||||
</code></pre><p>当某个类含有父类的同时并实现了协议,应当把父类放在所有的协议之前,如下所示:</p>
|
||||
<pre><code>class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
|
||||
</code></pre>
|
||||
<p>当某个类含有父类的同时并实现了协议,应当把父类放在所有的协议之前,如下所示:</p>
|
||||
<pre><code class="lang-swift">class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
|
||||
// 类的内容
|
||||
}
|
||||
</code></pre><p><a name="property_requirements"></a></p>
|
||||
</code></pre>
|
||||
<p><a name="property_requirements"></a></p>
|
||||
<h2 id="-">属性要求</h2>
|
||||
<p><code>协议</code>能够要求其<code>遵循者</code>必须含有一些<strong>特定名称和类型</strong>的<code>实例属性(instance property)</code>或<code>类属性 (type property)</code>,也能够要求属性的<code>(设置权限)settable</code> 和<code>(访问权限)gettable</code>,但它不要求<code>属性</code>是<code>存储型属性(stored property)</code>还是<code>计算型属性(calculate property)</code>。</p>
|
||||
<p><code>协议</code>能够要求其<code>遵循者</code>必须含有一些<strong>特定名称和类型</strong>的<code>实例属性(instance property)</code>或<code>类属性 (type property)</code>,也能够要求属性具有<code>(设置权限)settable</code> 和<code>(访问权限)gettable</code>,但它不要求<code>属性</code>是<code>存储型属性(stored property)</code>还是<code>计算型属性(calculate property)</code>。</p>
|
||||
<p>如果协议要求属性具有设置权限和访问权限,那常量存储型属性或者只读计算型属性都无法满足此要求。如果协议只要求属性具有访问权限,那任何类型的属性都可以满足此要求,无论这些属性是否具有设置权限。</p>
|
||||
<p>通常前置<code>var</code>关键字将属性声明为变量。在属性声明后写上<code>{ get set }</code>表示属性为可读写的。<code>{ get }</code>用来表示属性为可读的。即使你为可读的属性实现了<code>setter</code>方法,它也不会出错。</p>
|
||||
<pre><code>protocol SomeProtocol {
|
||||
<pre><code class="lang-swift">protocol SomeProtocol {
|
||||
var musBeSettable : Int { get set }
|
||||
var doesNotNeedToBeSettable: Int { get }
|
||||
}
|
||||
</code></pre><p>用类来实现协议时,使用<code>class</code>关键字来表示该属性为类成员;用结构体或枚举实现协议时,则使用<code>static</code>关键字来表示:</p>
|
||||
<pre><code>protocol AnotherProtocol {
|
||||
</code></pre>
|
||||
<p>用类来实现协议时,使用<code>class</code>关键字来表示该属性为类成员;用结构体或枚举实现协议时,则使用<code>static</code>关键字来表示:</p>
|
||||
<pre><code class="lang-swift">protocol AnotherProtocol {
|
||||
class var someTypeProperty: Int { get set }
|
||||
}
|
||||
|
||||
protocol FullyNamed {
|
||||
var fullName: String { get }
|
||||
}
|
||||
</code></pre><p><code>FullyNamed</code>协议含有<code>fullName</code>属性。因此其<code>遵循者</code>必须含有一个名为<code>fullName</code>,类型为<code>String</code>的可读属性。</p>
|
||||
<pre><code>struct Person: FullyNamed{
|
||||
</code></pre>
|
||||
<p><code>FullyNamed</code>协议含有<code>fullName</code>属性。因此其<code>遵循者</code>必须含有一个名为<code>fullName</code>,类型为<code>String</code>的可读属性。</p>
|
||||
<pre><code class="lang-swift">struct Person: FullyNamed{
|
||||
var fullName: String
|
||||
}
|
||||
let john = Person(fullName: "John Appleseed")
|
||||
//john.fullName 为 "John Appleseed"
|
||||
</code></pre><p><code>Person</code>结构体含有一个名为<code>fullName</code>的<code>存储型属性</code>,完整的<code>遵循</code>了协议。(<em>若协议未被完整遵循,编译时则会报错</em>)。</p>
|
||||
</code></pre>
|
||||
<p><code>Person</code>结构体含有一个名为<code>fullName</code>的<code>存储型属性</code>,完整的<code>遵循</code>了协议。(<em>若协议未被完整遵循,编译时则会报错</em>)。</p>
|
||||
<p>如下所示,<code>Startship</code>类<code>遵循</code>了<code>FullyNamed</code>协议:</p>
|
||||
<pre><code>class Starship: FullyNamed {
|
||||
<pre><code class="lang-swift">class Starship: FullyNamed {
|
||||
var prefix: String?
|
||||
var name: String
|
||||
init(name: String, prefix: String? = nil ) {
|
||||
@ -664,25 +670,27 @@ let john = Person(fullName: "John Appleseed")
|
||||
}
|
||||
var ncc1701 = Starship(name: "Enterprise", prefix: "USS")
|
||||
// ncc1701.fullName == "USS Enterprise"
|
||||
</code></pre><p><code>Starship</code>类将<code>fullName</code>实现为可读的<code>计算型属性</code>。它的每一个实例都有一个名为<code>name</code>的必备属性和一个名为<code>prefix</code>的可选属性。 当<code>prefix</code>存在时,将<code>prefix</code>插入到<code>name</code>之前来为<code>Starship</code>构建<code>fullName</code>。</p>
|
||||
</code></pre>
|
||||
<p><code>Starship</code>类将<code>fullName</code>实现为可读的<code>计算型属性</code>。它的每一个实例都有一个名为<code>name</code>的必备属性和一个名为<code>prefix</code>的可选属性。 当<code>prefix</code>存在时,将<code>prefix</code>插入到<code>name</code>之前来为<code>Starship</code>构建<code>fullName</code>。</p>
|
||||
<p><a name="method_requirements"></a></p>
|
||||
<h2 id="-">方法要求</h2>
|
||||
<p><code>协议</code>能够要求其<code>遵循者</code>必备某些特定的<code>实例方法</code>和<code>类方法</code>。协议方法的声明与普通方法声明相似,但它不需要<code>方法</code>内容。</p>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<p>协议方法支持<code>变长参数(variadic parameter)</code>,不支持<code>默认参数(default parameter)</code>。</p>
|
||||
<p>注意:
|
||||
协议方法支持<code>变长参数(variadic parameter)</code>,不支持<code>默认参数(default parameter)</code>。</p>
|
||||
</blockquote>
|
||||
<p>前置<code>class</code>关键字表示协议中的成员为<code>类成员</code>;当协议用于被<code>枚举</code>或<code>结构体</code>遵循时,则使用<code>static</code>关键字。如下所示:</p>
|
||||
<pre><code>protocol SomeProtocol {
|
||||
<pre><code class="lang-swift">protocol SomeProtocol {
|
||||
class func someTypeMethod()
|
||||
}
|
||||
|
||||
protocol RandomNumberGenerator {
|
||||
func random() -> Double
|
||||
}
|
||||
</code></pre><p><code>RandomNumberGenerator</code>协议要求其<code>遵循者</code>必须拥有一个名为<code>random</code>, 返回值类型为<code>Double</code>的实例方法。(我们假设随机数在[0,1]区间内)。</p>
|
||||
</code></pre>
|
||||
<p><code>RandomNumberGenerator</code>协议要求其<code>遵循者</code>必须拥有一个名为<code>random</code>, 返回值类型为<code>Double</code>的实例方法。(我们假设随机数在[0,1]区间内)。</p>
|
||||
<p><code>LinearCongruentialGenerator</code>类<code>遵循</code>了<code>RandomNumberGenerator</code>协议,并提供了一个叫做<em>线性同余生成器(linear congruential generator)</em>的伪随机数算法。</p>
|
||||
<pre><code>class LinearCongruentialGenerator: RandomNumberGenerator {
|
||||
<pre><code class="lang-swift">class LinearCongruentialGenerator: RandomNumberGenerator {
|
||||
var lastRandom = 42.0
|
||||
let m = 139968.0
|
||||
let a = 3877.0
|
||||
@ -697,21 +705,23 @@ println("Here's a random number: \(generator.random())")
|
||||
// 输出 : "Here's a random number: 0.37464991998171"
|
||||
println("And another one: \(generator.random())")
|
||||
// 输出 : "And another one: 0.729023776863283"
|
||||
</code></pre><p><a name="mutating_method_requirements"></a></p>
|
||||
</code></pre>
|
||||
<p><a name="mutating_method_requirements"></a></p>
|
||||
<h2 id="-">突变方法要求</h2>
|
||||
<p>能在<code>方法</code>或<code>函数</code>内部改变实例类型的方法称为<code>突变方法</code>。在<code>值类型(Value Type)</code>(<em>译者注:特指结构体和枚举</em>)中的的<code>函数</code>前缀加上<code>mutating</code>关键字来表示该函数允许改变该实例和其属性的类型。 这一变换过程在<a href="11_Methods.html#instance_methods">实例方法(Instance Methods)</a>章节中有详细描述。</p>
|
||||
<p>(<em>译者注:类中的成员为<code>引用类型(Reference Type)</code>,可以方便的修改实例及其属性的值而无需改变类型;而<code>结构体</code>和<code>枚举</code>中的成员均为<code>值类型(Value Type)</code>,修改变量的值就相当于修改变量的类型,而<code>Swift</code>默认不允许修改类型,因此需要前置<code>mutating</code>关键字用来表示该<code>函数</code>中能够修改类型</em>)</p>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<p>用<code>class</code>实现协议中的<code>mutating</code>方法时,不用写<code>mutating</code>关键字;用<code>结构体</code>,<code>枚举</code>实现协议中的<code>mutating</code>方法时,必须写<code>mutating</code>关键字。</p>
|
||||
<p>注意:
|
||||
用<code>class</code>实现协议中的<code>mutating</code>方法时,不用写<code>mutating</code>关键字;用<code>结构体</code>,<code>枚举</code>实现协议中的<code>mutating</code>方法时,必须写<code>mutating</code>关键字。</p>
|
||||
</blockquote>
|
||||
<p>如下所示,<code>Togglable</code>协议含有<code>toggle</code>函数。根据函数名称推测,<code>toggle</code>可能用于<strong>切换或恢复</strong>某个属性的状态。<code>mutating</code>关键字表示它为<code>突变方法</code>:</p>
|
||||
<pre><code>protocol Togglable {
|
||||
<pre><code class="lang-swift">protocol Togglable {
|
||||
mutating func toggle()
|
||||
}
|
||||
</code></pre><p>当使用<code>枚举</code>或<code>结构体</code>来实现<code>Togglabl</code>协议时,必须在<code>toggle</code>方法前加上<code>mutating</code>关键字。</p>
|
||||
</code></pre>
|
||||
<p>当使用<code>枚举</code>或<code>结构体</code>来实现<code>Togglabl</code>协议时,必须在<code>toggle</code>方法前加上<code>mutating</code>关键字。</p>
|
||||
<p>如下所示,<code>OnOffSwitch</code>枚举<code>遵循</code>了<code>Togglable</code>协议,<code>On</code>,<code>Off</code>两个成员用于表示当前状态</p>
|
||||
<pre><code>enum OnOffSwitch: Togglable {
|
||||
<pre><code class="lang-swift">enum OnOffSwitch: Togglable {
|
||||
case Off, On
|
||||
mutating func toggle() {
|
||||
switch self {
|
||||
@ -725,7 +735,8 @@ println("And another one: \(generator.random())")
|
||||
var lightSwitch = OnOffSwitch.Off
|
||||
lightSwitch.toggle()
|
||||
//lightSwitch 现在的值为 .On
|
||||
</code></pre><p><a name="protocols_as_types"></a></p>
|
||||
</code></pre>
|
||||
<p><a name="protocols_as_types"></a></p>
|
||||
<h2 id="-">协议类型</h2>
|
||||
<p><code>协议</code>本身不实现任何功能,但你可以将它当做<code>类型</code>来使用。</p>
|
||||
<p>使用场景:</p>
|
||||
@ -735,10 +746,10 @@ lightSwitch.toggle()
|
||||
<li>作为数组,字典或其他容器中的元素类型</li>
|
||||
</ul>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<p>协议类型应与其他类型(Int,Double,String)的写法相同,使用驼峰式</p>
|
||||
<p>注意:
|
||||
协议类型应与其他类型(Int,Double,String)的写法相同,使用驼峰式</p>
|
||||
</blockquote>
|
||||
<pre><code>class Dice {
|
||||
<pre><code class="lang-swift">class Dice {
|
||||
let sides: Int
|
||||
let generator: RandomNumberGenerator
|
||||
init(sides: Int, generator: RandomNumberGenerator) {
|
||||
@ -749,12 +760,13 @@ lightSwitch.toggle()
|
||||
return Int(generator.random() * Double(sides)) +1
|
||||
}
|
||||
}
|
||||
</code></pre><p>这里定义了一个名为 <code>Dice</code>的类,用来代表桌游中的N个面的骰子。</p>
|
||||
<p> <code>Dice</code>含有<code>sides</code>和<code>generator</code>两个属性,前者用来表示骰子有几个面,后者为骰子提供一个随机数生成器。由于后者为<code>RandomNumberGenerator</code>的协议类型。所以它能够被赋值为任意<code>遵循</code>该协议的类型。</p>
|
||||
</code></pre>
|
||||
<p>这里定义了一个名为 <code>Dice</code>的类,用来代表桌游中的N个面的骰子。</p>
|
||||
<p><code>Dice</code>含有<code>sides</code>和<code>generator</code>两个属性,前者用来表示骰子有几个面,后者为骰子提供一个随机数生成器。由于后者为<code>RandomNumberGenerator</code>的协议类型。所以它能够被赋值为任意<code>遵循</code>该协议的类型。</p>
|
||||
<p>此外,使用<code>构造器(init)</code>来代替之前版本中的<code>setup</code>操作。构造器中含有一个名为<code>generator</code>,类型为<code>RandomNumberGenerator</code>的形参,使得它可以接收任意遵循<code>RandomNumberGenerator</code>协议的类型。</p>
|
||||
<p><code>roll</code>方法用来模拟骰子的面值。它先使用<code>generator</code>的<code>random</code>方法来创建一个[0-1]区间内的随机数种子,然后加工这个随机数种子生成骰子的面值。</p>
|
||||
<p>如下所示,<code>LinearCongruentialGenerator</code>的实例作为随机数生成器传入<code>Dice</code>的<code>构造器</code></p>
|
||||
<pre><code>var d6 = Dice(sides: 6,generator: LinearCongruentialGenerator())
|
||||
<pre><code class="lang-swift">var d6 = Dice(sides: 6,generator: LinearCongruentialGenerator())
|
||||
for _ in 1...5 {
|
||||
println("Random dice roll is \(d6.roll())")
|
||||
}
|
||||
@ -764,24 +776,27 @@ for _ in 1...5 {
|
||||
//Random dice roll is 4
|
||||
//Random dice roll is 5
|
||||
//Random dice roll is 4
|
||||
</code></pre><p><a name="delegation"></a></p>
|
||||
</code></pre>
|
||||
<p><a name="delegation"></a></p>
|
||||
<h2 id="-">委托(代理)模式</h2>
|
||||
<p>委托是一种设计模式,它允许类或结构体将一些需要它们负责的功能<code>交由(委托)</code>给其他的类型。</p>
|
||||
<p>委托模式的实现很简单: 定义<code>协议</code>来<code>封装</code>那些需要被委托的<code>函数和方法</code>, 使其<code>遵循者</code>拥有这些被委托的<code>函数和方法</code>。</p>
|
||||
<p>委托模式可以用来响应特定的动作或接收外部数据源提供的数据,而无需要知道外部数据源的类型。</p>
|
||||
<p>下文是两个基于骰子游戏的协议:</p>
|
||||
<pre><code>protocol DiceGame {
|
||||
<pre><code class="lang-swift">protocol DiceGame {
|
||||
var dice: Dice { get }
|
||||
func play()
|
||||
}
|
||||
|
||||
protocol DiceGameDelegate {
|
||||
func gameDidStart(game: DiceGame)
|
||||
func game(game: DiceGame, didStartNewTurnWithDiceRoll diceRoll:Int)
|
||||
func gameDidEnd(game: DiceGame)
|
||||
}
|
||||
</code></pre><p><code>DiceGame</code>协议可以在任意含有骰子的游戏中实现,<code>DiceGameDelegate</code>协议可以用来追踪<code>DiceGame</code>的游戏过程。</p>
|
||||
</code></pre>
|
||||
<p><code>DiceGame</code>协议可以在任意含有骰子的游戏中实现,<code>DiceGameDelegate</code>协议可以用来追踪<code>DiceGame</code>的游戏过程。</p>
|
||||
<p>如下所示,<code>SnakesAndLadders</code>是<code>Snakes and Ladders</code>(译者注:<a href="05_Control_Flow.html">控制流</a>章节有该游戏的详细介绍)游戏的新版本。新版本使用<code>Dice</code>作为骰子,并且实现了<code>DiceGame</code>和<code>DiceGameDelegate</code>协议</p>
|
||||
<pre><code>class SnakesAndLadders: DiceGame {
|
||||
<pre><code class="lang-swift">class SnakesAndLadders: DiceGame {
|
||||
let finalSquare = 25
|
||||
let dic = Dice(sides: 6, generator: LinearCongruentialGenerator())
|
||||
var square = 0
|
||||
@ -811,15 +826,16 @@ protocol DiceGameDelegate {
|
||||
delegate?.gameDIdEnd(self)
|
||||
}
|
||||
}
|
||||
</code></pre><p>游戏的<code>初始化设置(setup)</code>被<code>SnakesAndLadders</code>类的<code>构造器(initializer)</code>实现。所有的游戏逻辑被转移到了<code>play</code>方法中。</p>
|
||||
</code></pre>
|
||||
<p>游戏的<code>初始化设置(setup)</code>被<code>SnakesAndLadders</code>类的<code>构造器(initializer)</code>实现。所有的游戏逻辑被转移到了<code>play</code>方法中。</p>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<p>因为<code>delegate</code>并不是该游戏的必备条件,<code>delegate</code>被定义为遵循<code>DiceGameDelegate</code>协议的可选属性</p>
|
||||
<p>注意:
|
||||
因为<code>delegate</code>并不是该游戏的必备条件,<code>delegate</code>被定义为遵循<code>DiceGameDelegate</code>协议的可选属性</p>
|
||||
</blockquote>
|
||||
<p><code>DicegameDelegate</code>协议提供了三个方法用来追踪游戏过程。被放置于游戏的逻辑中,即<code>play()</code>方法内。分别在游戏开始时,新一轮开始时,游戏结束时被调用。</p>
|
||||
<p>因为<code>delegate</code>是一个遵循<code>DiceGameDelegate</code>的可选属性,因此在<code>play()</code>方法中使用了<code>可选链</code>来调用委托方法。 若<code>delegate</code>属性为<code>nil</code>, 则委托调用<em>优雅地</em>失效。若<code>delegate</code>不为<code>nil</code>,则委托方法被调用</p>
|
||||
<p>如下所示,<code>DiceGameTracker</code>遵循了<code>DiceGameDelegate</code>协议</p>
|
||||
<pre><code>class DiceGameTracker: DiceGameDelegate {
|
||||
<pre><code class="lang-swift">class DiceGameTracker: DiceGameDelegate {
|
||||
var numberOfTurns = 0
|
||||
func gameDidStart(game: DiceGame) {
|
||||
numberOfTurns = 0
|
||||
@ -836,10 +852,11 @@ protocol DiceGameDelegate {
|
||||
println("The game lasted for \(numberOfTurns) turns")
|
||||
}
|
||||
}
|
||||
</code></pre><p><code>DiceGameTracker</code>实现了<code>DiceGameDelegate</code>协议的方法要求,用来记录游戏已经进行的轮数。 当游戏开始时,<code>numberOfTurns</code>属性被赋值为0;在每新一轮中递加;游戏结束后,输出打印游戏的总轮数。</p>
|
||||
</code></pre>
|
||||
<p><code>DiceGameTracker</code>实现了<code>DiceGameDelegate</code>协议的方法要求,用来记录游戏已经进行的轮数。 当游戏开始时,<code>numberOfTurns</code>属性被赋值为0;在每新一轮中递加;游戏结束后,输出打印游戏的总轮数。</p>
|
||||
<p><code>gameDidStart</code>方法从<code>game</code>参数获取游戏信息并输出。<code>game</code>在方法中被当做<code>DiceGame</code>类型而不是<code>SnakeAndLadders</code>类型,所以方法中只能访问<code>DiceGame</code>协议中的成员。</p>
|
||||
<p><code>DiceGameTracker</code>的运行情况,如下所示:</p>
|
||||
<pre><code>“let tracker = DiceGameTracker()
|
||||
<pre><code class="lang-swift">let tracker = DiceGameTracker()
|
||||
let game = SnakesAndLadders()
|
||||
game.delegate = tracker
|
||||
game.play()
|
||||
@ -849,80 +866,91 @@ game.play()
|
||||
// Rolled a 5
|
||||
// Rolled a 4
|
||||
// Rolled a 5
|
||||
// The game lasted for 4 turns”
|
||||
</code></pre><p><a name="adding_protocol_conformance_with_an_extension"></a></p>
|
||||
// The game lasted for 4 turns
|
||||
</code></pre>
|
||||
<p><a name="adding_protocol_conformance_with_an_extension"></a></p>
|
||||
<h2 id="-">在扩展中添加协议成员</h2>
|
||||
<p>即便无法修改源代码,依然可以通过<code>扩展(Extension)</code>来扩充已存在类型(<em>译者注: 类,结构体,枚举等</em>)。<code>扩展</code>可以为已存在的类型添加<code>属性</code>,<code>方法</code>,<code>下标</code>,<code>协议</code>等成员。详情请在<a href="20_Extensions.html">扩展</a>章节中查看。</p>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<p>通过<code>扩展</code>为已存在的类型<code>遵循</code>协议时,该类型的所有实例也会随之添加协议中的方法</p>
|
||||
<p>注意:
|
||||
通过<code>扩展</code>为已存在的类型<code>遵循</code>协议时,该类型的所有实例也会随之添加协议中的方法</p>
|
||||
</blockquote>
|
||||
<p><code>TextRepresentable</code>协议含有一个<code>asText</code>,如下所示:</p>
|
||||
<pre><code>protocol TextRepresentable {
|
||||
<pre><code class="lang-swift">protocol TextRepresentable {
|
||||
func asText() -> String
|
||||
}
|
||||
</code></pre><p>通过<code>扩展</code>为上一节中提到的<code>Dice</code>类遵循<code>TextRepresentable</code>协议</p>
|
||||
<pre><code>extension Dice: TextRepresentable {
|
||||
func asText() -> String {
|
||||
</code></pre>
|
||||
<p>通过<code>扩展</code>为上一节中提到的<code>Dice</code>类遵循<code>TextRepresentable</code>协议</p>
|
||||
<pre><code class="lang-swift">extension Dice: TextRepresentable {
|
||||
cun asText() -> String {
|
||||
return "A \(sides)-sided dice"
|
||||
}
|
||||
}
|
||||
</code></pre><p>从现在起,<code>Dice</code>类型的实例可被当作<code>TextRepresentable</code>类型:</p>
|
||||
<pre><code>let d12 = Dice(sides: 12,generator: LinearCongruentialGenerator())
|
||||
</code></pre>
|
||||
<p>从现在起,<code>Dice</code>类型的实例可被当作<code>TextRepresentable</code>类型:</p>
|
||||
<pre><code class="lang-swift">let d12 = Dice(sides: 12,generator: LinearCongruentialGenerator())
|
||||
println(d12.asText())
|
||||
// 输出 "A 12-sided dice"
|
||||
</code></pre><p><code>SnakesAndLadders</code>类也可以通过<code>扩展</code>的方式来遵循协议:</p>
|
||||
<pre><code>extension SnakeAndLadders: TextRepresentable {
|
||||
</code></pre>
|
||||
<p><code>SnakesAndLadders</code>类也可以通过<code>扩展</code>的方式来遵循协议:</p>
|
||||
<pre><code class="lang-swift">extension SnakeAndLadders: TextRepresentable {
|
||||
func asText() -> String {
|
||||
return "A game of Snakes and Ladders with \(finalSquare) squares"
|
||||
}
|
||||
}
|
||||
println(game.asText())
|
||||
// 输出 "A game of Snakes and Ladders with 25 squares"
|
||||
</code></pre><p><a name="declaring_protocol_adoption_with_an_extension"></a></p>
|
||||
<h2 id="-">通过延展补充协议声明</h2>
|
||||
</code></pre>
|
||||
<p><a name="declaring_protocol_adoption_with_an_extension"></a></p>
|
||||
<h2 id="-">通过扩展补充协议声明</h2>
|
||||
<p>当一个类型已经实现了协议中的所有要求,却没有声明时,可以通过<code>扩展</code>来补充协议声明:</p>
|
||||
<pre><code>struct Hamster {
|
||||
<pre><code class="lang-swift">struct Hamster {
|
||||
var name: String
|
||||
func asText() -> String {
|
||||
return "A hamster named \(name)"
|
||||
}
|
||||
}
|
||||
extension Hamster: TextRepresentabl {}
|
||||
</code></pre><p>从现在起,<code>Hamster</code>的实例可以作为<code>TextRepresentable</code>类型使用</p>
|
||||
<pre><code>let simonTheHamster = Hamster(name: "Simon")
|
||||
</code></pre>
|
||||
<p>从现在起,<code>Hamster</code>的实例可以作为<code>TextRepresentable</code>类型使用</p>
|
||||
<pre><code class="lang-swift">let simonTheHamster = Hamster(name: "Simon")
|
||||
let somethingTextRepresentable: TextRepresentabl = simonTheHamester
|
||||
println(somethingTextRepresentable.asText())
|
||||
// 输出 "A hamster named Simon"
|
||||
</code></pre><blockquote>
|
||||
<p>注意:</p>
|
||||
<p>即时满足了协议的所有要求,类型也不会自动转变,因此你必须为它做出明显的协议声明</p>
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>注意:
|
||||
即时满足了协议的所有要求,类型也不会自动转变,因此你必须为它做出明显的协议声明</p>
|
||||
</blockquote>
|
||||
<p><a name="collections_of_protocol_types"></a></p>
|
||||
<h2 id="-">集合中的协议类型</h2>
|
||||
<p>协议类型可以被集合使用,表示集合中的元素均为协议类型:</p>
|
||||
<pre><code>let things: TextRepresentable[] = [game,d12,simoTheHamster]
|
||||
</code></pre><p>如下所示,<code>things</code>数组可以被直接遍历,并调用其中元素的<code>asText()</code>函数:</p>
|
||||
<pre><code>for thing in things {
|
||||
<pre><code class="lang-swift">let things: TextRepresentable[] = [game,d12,simoTheHamster]
|
||||
</code></pre>
|
||||
<p>如下所示,<code>things</code>数组可以被直接遍历,并调用其中元素的<code>asText()</code>函数:</p>
|
||||
<pre><code class="lang-swift">for thing in things {
|
||||
println(thing.asText())
|
||||
}
|
||||
// A game of Snakes and Ladders with 25 squares
|
||||
// A 12-sided dice
|
||||
// A hamster named Simon
|
||||
</code></pre><p><code>thing</code>被当做是<code>TextRepresentable</code>类型而不是<code>Dice</code>,<code>DiceGame</code>,<code>Hamster</code>等类型。因此能且仅能调用<code>asText</code>方法</p>
|
||||
</code></pre>
|
||||
<p><code>thing</code>被当做是<code>TextRepresentable</code>类型而不是<code>Dice</code>,<code>DiceGame</code>,<code>Hamster</code>等类型。因此能且仅能调用<code>asText</code>方法</p>
|
||||
<p><a name="protocol_inheritance"></a></p>
|
||||
<h2 id="-">协议的继承</h2>
|
||||
<p>协议能够<em>继承</em>一到多个其他协议。语法与类的继承相似,多个协议间用逗号<code>,</code>分隔</p>
|
||||
<pre><code>protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
|
||||
<pre><code class="lang-swift">protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
|
||||
// 协议定义
|
||||
}
|
||||
</code></pre><p>如下所示,<code>PrettyTextRepresentable</code>协议继承了<code>TextRepresentable</code>协议</p>
|
||||
<pre><code>protocol PrettyTextRepresentable: TextRepresentable {
|
||||
</code></pre>
|
||||
<p>如下所示,<code>PrettyTextRepresentable</code>协议继承了<code>TextRepresentable</code>协议</p>
|
||||
<pre><code class="lang-swift">protocol PrettyTextRepresentable: TextRepresentable {
|
||||
func asPrettyText() -> String
|
||||
}
|
||||
</code></pre><p><code>遵循``PrettyTextRepresentable</code>协议的同时,也需要<code>遵循</code>TextRepresentable`协议。</p>
|
||||
</code></pre>
|
||||
<p><code>遵循``PrettyTextRepresentable</code>协议的同时,也需要<code>遵循</code>TextRepresentable`协议。</p>
|
||||
<p>如下所示,用<code>扩展</code>为<code>SnakesAndLadders</code>遵循<code>PrettyTextRepresentable</code>协议:</p>
|
||||
<pre><code>extension SnakesAndLadders: PrettyTextRepresentable {
|
||||
<pre><code class="lang-swift">extension SnakesAndLadders: PrettyTextRepresentable {
|
||||
func asPrettyText() -> String {
|
||||
var output = asText() + ":\n"
|
||||
for index in 1...finalSquare {
|
||||
@ -938,21 +966,23 @@ println(somethingTextRepresentable.asText())
|
||||
return output
|
||||
}
|
||||
}
|
||||
</code></pre><p>在<code>for in</code>中迭代出了<code>board</code>数组中的每一个元素:</p>
|
||||
</code></pre>
|
||||
<p>在<code>for in</code>中迭代出了<code>board</code>数组中的每一个元素:</p>
|
||||
<ul>
|
||||
<li>当从数组中迭代出的元素的值大于0时,用<code>▲</code>表示</li>
|
||||
<li>当从数组中迭代出的元素的值小于0时,用<code>▼</code>表示</li>
|
||||
<li>当从数组中迭代出的元素的值等于0时,用<code>○</code>表示</li>
|
||||
</ul>
|
||||
<p>任意<code>SankesAndLadders</code>的实例都可以使用<code>asPrettyText()</code>方法。</p>
|
||||
<pre><code>println(game.asPrettyText())
|
||||
<pre><code class="lang-swift">println(game.asPrettyText())
|
||||
// A game of Snakes and Ladders with 25 squares:
|
||||
// ○ ○ ▲ ○ ○ ▲ ○ ○ ▲ ▲ ○ ○ ○ ▼ ○ ○ ○ ○ ▼ ○ ○ ▼ ○ ▼ ○
|
||||
</code></pre><p><a name="protocol_composition"></a></p>
|
||||
</code></pre>
|
||||
<p><a name="protocol_composition"></a></p>
|
||||
<h2 id="-">协议合成</h2>
|
||||
<p>一个协议可由多个协议采用<code>protocol<SomeProtocol, AnotherProtocol></code>这样的格式进行组合,称为<code>协议合成(protocol composition)</code>。</p>
|
||||
<p>举个例子:</p>
|
||||
<pre><code>protocol Named {
|
||||
<pre><code class="lang-swift">protocol Named {
|
||||
var name: String { get }
|
||||
}
|
||||
protocol Aged {
|
||||
@ -968,11 +998,12 @@ func wishHappyBirthday(celebrator: protocol<Named, Aged>) {
|
||||
let birthdayPerson = Person(name: "Malcolm", age: 21)
|
||||
wishHappyBirthday(birthdayPerson)
|
||||
// 输出 "Happy birthday Malcolm - you're 21!
|
||||
</code></pre><p><code>Named</code>协议包含<code>String</code>类型的<code>name</code>属性;<code>Aged</code>协议包含<code>Int</code>类型的<code>age</code>属性。<code>Person</code>结构体<code>遵循</code>了这两个协议。</p>
|
||||
</code></pre>
|
||||
<p><code>Named</code>协议包含<code>String</code>类型的<code>name</code>属性;<code>Aged</code>协议包含<code>Int</code>类型的<code>age</code>属性。<code>Person</code>结构体<code>遵循</code>了这两个协议。</p>
|
||||
<p><code>wishHappyBirthday</code>函数的形参<code>celebrator</code>的类型为<code>protocol<Named,Aged></code>。可以传入任意<code>遵循</code>这两个协议的类型的实例</p>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<p><code>协议合成</code>并不会生成一个新协议类型,而是将多个协议合成为一个临时的协议,超出范围后立即失效。</p>
|
||||
<p>注意:
|
||||
<code>协议合成</code>并不会生成一个新协议类型,而是将多个协议合成为一个临时的协议,超出范围后立即失效。</p>
|
||||
</blockquote>
|
||||
<p><a name="checking_for_protocol_conformance"></a></p>
|
||||
<h2 id="-">检验协议的一致性</h2>
|
||||
@ -982,14 +1013,15 @@ wishHappyBirthday(birthdayPerson)
|
||||
<li><code>as?</code>返回一个可选值,当实例<code>遵循</code>协议时,返回该协议类型;否则返回<code>nil</code></li>
|
||||
<li><code>as</code>用以强制向下转换型。</li>
|
||||
</ul>
|
||||
<pre><code>@objc protocol HasArea {
|
||||
<pre><code class="lang-swift">@objc protocol HasArea {
|
||||
var area: Double { get }
|
||||
}
|
||||
</code></pre><blockquote>
|
||||
<p>注意:</p>
|
||||
<p><code>@objc</code>用来表示协议是可选的,也可以用来表示暴露给<code>Objective-C</code>的代码,此外,<code>@objc</code>型协议只对<code>类</code>有效,因此只能在<code>类</code>中检查协议的一致性。详情查看<em><a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216" target="_blank">Using Siwft with Cocoa and Objectivei-c</a></em>。</p>
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>注意:
|
||||
<code>@objc</code>用来表示协议是可选的,也可以用来表示暴露给<code>Objective-C</code>的代码,此外,<code>@objc</code>型协议只对<code>类</code>有效,因此只能在<code>类</code>中检查协议的一致性。详情查看<em><a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216" target="_blank">Using Siwft with Cocoa and Objectivei-c</a></em>。</p>
|
||||
</blockquote>
|
||||
<pre><code>class Circle: HasArea {
|
||||
<pre><code class="lang-swift">class Circle: HasArea {
|
||||
let pi = 3.1415927
|
||||
var radius: Double
|
||||
var area:≈radius }
|
||||
@ -999,20 +1031,23 @@ class Country: HasArea {
|
||||
var area: Double
|
||||
init(area: Double) { self.area = area }
|
||||
}
|
||||
</code></pre><p><code>Circle</code>和<code>Country</code>都遵循了<code>HasArea</code>协议,前者把<code>area</code>写为计算型属性(computed property),后者则把<code>area</code>写为存储型属性(stored property)。</p>
|
||||
</code></pre>
|
||||
<p><code>Circle</code>和<code>Country</code>都遵循了<code>HasArea</code>协议,前者把<code>area</code>写为计算型属性(computed property),后者则把<code>area</code>写为存储型属性(stored property)。</p>
|
||||
<p>如下所示,<code>Animal</code>类没有实现任何协议</p>
|
||||
<pre><code>class Animal {
|
||||
<pre><code class="lang-swift">class Animal {
|
||||
var legs: Int
|
||||
init(legs: Int) { self.legs = legs }
|
||||
}
|
||||
</code></pre><p><code>Circle,Country,Animal</code>并没有一个相同的基类,所以采用<code>AnyObject</code>类型的数组来装载在它们的实例,如下所示:</p>
|
||||
<pre><code>let objects: AnyObject[] = [
|
||||
</code></pre>
|
||||
<p><code>Circle,Country,Animal</code>并没有一个相同的基类,所以采用<code>AnyObject</code>类型的数组来装载在它们的实例,如下所示:</p>
|
||||
<pre><code class="lang-swift">let objects: AnyObject[] = [
|
||||
Circle(radius: 2.0),
|
||||
Country(area: 243_610),
|
||||
Animal(legs: 4)
|
||||
]
|
||||
</code></pre><p>如下所示,在迭代时检查<code>object</code>数组的元素是否<code>遵循</code>了<code>HasArea</code>协议:</p>
|
||||
<pre><code>for object in objects {
|
||||
</code></pre>
|
||||
<p>如下所示,在迭代时检查<code>object</code>数组的元素是否<code>遵循</code>了<code>HasArea</code>协议:</p>
|
||||
<pre><code class="lang-swift">for object in objects {
|
||||
if let objectWithArea = object as? HasArea {
|
||||
println("Area is \(objectWithArea.area)")
|
||||
} else {
|
||||
@ -1022,7 +1057,8 @@ class Country: HasArea {
|
||||
// Area is 12.5663708
|
||||
// Area is 243610.0
|
||||
// Something that doesn't have an area
|
||||
</code></pre><p>当数组中的元素遵循<code>HasArea</code>协议时,通过<code>as?</code>操作符将其<code>可选绑定(optional binding)</code>到<code>objectWithArea</code>常量上。</p>
|
||||
</code></pre>
|
||||
<p>当数组中的元素遵循<code>HasArea</code>协议时,通过<code>as?</code>操作符将其<code>可选绑定(optional binding)</code>到<code>objectWithArea</code>常量上。</p>
|
||||
<p><code>objects</code>数组中元素的类型并不会因为<code>向下转型</code>而改变,当它们被赋值给<code>objectWithArea</code>时只被视为<code>HasArea</code>类型,因此只有<code>area</code>属性能够被访问。</p>
|
||||
<p><a name="optional_protocol_requirements"></a></p>
|
||||
<h2 id="-">可选协议要求</h2>
|
||||
@ -1030,21 +1066,22 @@ class Country: HasArea {
|
||||
<p>可选协议在调用时使用<code>可选链</code>,详细内容在<a href="17_Optional_Chaining.html">可选链</a>章节中查看。</p>
|
||||
<p>像<code>someOptionalMethod?(someArgument)</code>一样,你可以在可选方法名称后加上<code>?</code>来检查该方法是否被实现。<code>可选方法</code>和<code>可选属性</code>都会返回一个<code>可选值(optional value)</code>,当其不可访问时,<code>?</code>之后语句不会执行,并返回<code>nil</code>。</p>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<p>可选协议只能在含有<code>@objc</code>前缀的协议中生效。且<code>@objc</code>的协议只能被<code>类</code>遵循。</p>
|
||||
<p>注意:
|
||||
可选协议只能在含有<code>@objc</code>前缀的协议中生效。且<code>@objc</code>的协议只能被<code>类</code>遵循。</p>
|
||||
</blockquote>
|
||||
<p><code>Counter</code>类使用<code>CounterDataSource</code>类型的外部数据源来提供<code>增量值(increment amount)</code>,如下所示:</p>
|
||||
<pre><code>@objc protocol CounterDataSource {
|
||||
<pre><code class="lang-swift">@objc protocol CounterDataSource {
|
||||
@optional func incrementForCount(count: Int) -> Int
|
||||
@optional var fixedIncrement: Int { get }
|
||||
}
|
||||
</code></pre><p><code>CounterDataSource</code>含有<code>incrementForCount</code>的<code>可选方法</code>和<code>fiexdIncrement</code>的<code>可选属性</code>。</p>
|
||||
</code></pre>
|
||||
<p><code>CounterDataSource</code>含有<code>incrementForCount</code>的<code>可选方法</code>和<code>fiexdIncrement</code>的<code>可选属性</code>。</p>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<p><code>CounterDataSource</code>中的属性和方法都是可选的,因此可以在类中声明但不实现这些成员,尽管技术上允许这样做,不过最好不要这样写。</p>
|
||||
<p>注意:
|
||||
<code>CounterDataSource</code>中的属性和方法都是可选的,因此可以在类中声明但不实现这些成员,尽管技术上允许这样做,不过最好不要这样写。</p>
|
||||
</blockquote>
|
||||
<p><code>Counter</code>类含有<code>CounterDataSource?</code>类型的可选属性<code>dataSource</code>,如下所示:</p>
|
||||
<pre><code>@objc class Counter {
|
||||
<pre><code class="lang-swift">@objc class Counter {
|
||||
var count = 0
|
||||
var dataSource: CounterDataSource?
|
||||
func increment() {
|
||||
@ -1055,7 +1092,8 @@ class Country: HasArea {
|
||||
}
|
||||
}
|
||||
}
|
||||
</code></pre><p><code>count</code>属性用于存储当前的值,<code>increment</code>方法用来为<code>count</code>赋值。</p>
|
||||
</code></pre>
|
||||
<p><code>count</code>属性用于存储当前的值,<code>increment</code>方法用来为<code>count</code>赋值。</p>
|
||||
<p><code>increment</code>方法通过<code>可选链</code>,尝试从两种<code>可选成员</code>中获取<code>count</code>。</p>
|
||||
<ol>
|
||||
<li>由于<code>dataSource</code>可能为<code>nil</code>,因此在<code>dataSource</code>后边加上了<code>?</code>标记来表明只在<code>dataSource</code>非空时才去调用incrementForCount`方法。</li>
|
||||
@ -1064,11 +1102,12 @@ class Country: HasArea {
|
||||
<p>在调用<code>incrementForCount</code>方法后,<code>Int</code>型<code>可选值</code>通过<code>可选绑定(optional binding)</code>自动拆包并赋值给常量<code>amount</code>。</p>
|
||||
<p>当<code>incrementForCount</code>不能被调用时,尝试使用<code>可选属性``fixedIncrement</code>来代替。</p>
|
||||
<p><code>ThreeSource</code>实现了<code>CounterDataSource</code>协议,如下所示:</p>
|
||||
<pre><code>class ThreeSource: CounterDataSource {
|
||||
<pre><code class="lang-swift">class ThreeSource: CounterDataSource {
|
||||
let fixedIncrement = 3
|
||||
}
|
||||
</code></pre><p>使用<code>ThreeSource</code>作为数据源开实例化一个<code>Counter</code>:</p>
|
||||
<pre><code>var counter = Counter()
|
||||
</code></pre>
|
||||
<p>使用<code>ThreeSource</code>作为数据源开实例化一个<code>Counter</code>:</p>
|
||||
<pre><code class="lang-swift">var counter = Counter()
|
||||
counter.dataSource = ThreeSource()
|
||||
for _ in 1...4 {
|
||||
counter.increment()
|
||||
@ -1078,8 +1117,9 @@ for _ in 1...4 {
|
||||
// 6
|
||||
// 9
|
||||
// 12
|
||||
</code></pre><p><code>TowardsZeroSource</code>实现了<code>CounterDataSource</code>协议中的<code>incrementForCount</code>方法,如下所示:</p>
|
||||
<pre><code>class TowardsZeroSource: CounterDataSource {
|
||||
</code></pre>
|
||||
<p><code>TowardsZeroSource</code>实现了<code>CounterDataSource</code>协议中的<code>incrementForCount</code>方法,如下所示:</p>
|
||||
<pre><code class="lang-swift">class TowardsZeroSource: CounterDataSource {
|
||||
func incrementForCount(count: Int) -> Int {
|
||||
if count == 0 {
|
||||
return 0
|
||||
@ -1090,8 +1130,9 @@ func incrementForCount(count: Int) -> Int {
|
||||
}
|
||||
}
|
||||
}
|
||||
</code></pre><p>下边是执行的代码:</p>
|
||||
<pre><code>counter.count = -4
|
||||
</code></pre>
|
||||
<p>下边是执行的代码:</p>
|
||||
<pre><code class="lang-swift">counter.count = -4
|
||||
counter.dataSource = TowardsZeroSource()
|
||||
for _ in 1...5 {
|
||||
counter.increment()
|
||||
@ -1103,6 +1144,7 @@ for _ in 1...5 {
|
||||
// 0
|
||||
// 0
|
||||
</code></pre>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="2.22" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="2.22" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,11 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_76">
|
||||
<section class="normal" id="section-gitbook_50">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:takalard</p>
|
||||
<p>校对:lifedim</p>
|
||||
<p>翻译:<a href="https://github.com/takalard" target="_blank">takalard</a><br>校对:<a href="https://github.com/lifedim" target="_blank">lifedim</a></p>
|
||||
</blockquote>
|
||||
<h1 id="-">泛型</h1>
|
||||
<hr>
|
||||
@ -611,20 +610,22 @@
|
||||
<p><a name="the_problem_that_generics_solve"></a></p>
|
||||
<h2 id="-">泛型所解决的问题</h2>
|
||||
<p>这里是一个标准的,非泛型函数<code>swapTwoInts</code>,用来交换两个Int值:</p>
|
||||
<pre><code>func swapTwoInts(inout a: Int, inout b: Int)
|
||||
let temporaryA = a
|
||||
a = b
|
||||
b = temporaryA
|
||||
<pre><code class="lang-swift">func swapTwoInts(inout a: Int, inout b: Int)
|
||||
let temporaryA = a
|
||||
a = b
|
||||
b = temporaryA
|
||||
}
|
||||
</code></pre><p>这个函数使用写入读出(in-out)参数来交换<code>a</code>和<code>b</code>的值,请参考[写入读出参数][1]。</p>
|
||||
</code></pre>
|
||||
<p>这个函数使用写入读出(in-out)参数来交换<code>a</code>和<code>b</code>的值,请参考[写入读出参数][1]。</p>
|
||||
<p><code>swapTwoInts</code>函数可以交换<code>b</code>的原始值到<code>a</code>,也可以交换a的原始值到<code>b</code>,你可以调用这个函数交换两个<code>Int</code>变量值:</p>
|
||||
<pre><code>var someInt = 3
|
||||
<pre><code class="lang-swift">var someInt = 3
|
||||
var anotherInt = 107
|
||||
swapTwoInts(&someInt, &anotherInt)
|
||||
println("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
|
||||
// 输出 "someInt is now 107, and anotherInt is now 3"
|
||||
</code></pre><p><code>swapTwoInts</code>函数是非常有用的,但是它只能交换<code>Int</code>值,如果你想要交换两个<code>String</code>或者<code>Double</code>,就不得不写更多的函数,如 <code>swapTwoStrings</code>和<code>swapTwoDoublesfunctions</code>,如同如下所示:</p>
|
||||
<pre><code>func swapTwoStrings(inout a: String, inout b: String) {
|
||||
</code></pre>
|
||||
<p><code>swapTwoInts</code>函数是非常有用的,但是它只能交换<code>Int</code>值,如果你想要交换两个<code>String</code>或者<code>Double</code>,就不得不写更多的函数,如 <code>swapTwoStrings</code>和<code>swapTwoDoublesfunctions</code>,如同如下所示:</p>
|
||||
<pre><code class="lang-swift">func swapTwoStrings(inout a: String, inout b: String) {
|
||||
let temporaryA = a
|
||||
a = b
|
||||
b = temporaryA
|
||||
@ -635,39 +636,41 @@ func swapTwoDoubles(inout a: Double, inout b: Double) {
|
||||
a = b
|
||||
b = temporaryA
|
||||
}
|
||||
</code></pre><p>你可能注意到 <code>swapTwoInts</code>、 <code>swapTwoStrings</code>和<code>swapTwoDoubles</code>函数功能都是相同的,唯一不同之处就在于传入的变量类型不同,分别是<code>Int</code>、<code>String</code>和<code>Double</code>。</p>
|
||||
</code></pre>
|
||||
<p>你可能注意到 <code>swapTwoInts</code>、 <code>swapTwoStrings</code>和<code>swapTwoDoubles</code>函数功能都是相同的,唯一不同之处就在于传入的变量类型不同,分别是<code>Int</code>、<code>String</code>和<code>Double</code>。</p>
|
||||
<p>但实际应用中通常需要一个用处更强大并且尽可能的考虑到更多的灵活性单个函数,可以用来交换两个任何类型值,很幸运的是,泛型代码帮你解决了这种问题。(一个这种泛型函数后面已经定义好了。)</p>
|
||||
<blockquote>
|
||||
<p>注意:
|
||||
在所有三个函数中,<code>a</code>和<code>b</code>的类型是一样的。如果<code>a</code>和<code>b</code>不是相同的类型,那它们俩就不能互换值。Swift 是类型安全的语言,所以它不允许一个<code>String</code>类型的变量和一个<code>Double</code>类型的变量互相交换值。如果一定要做,Swift 将报编译错误。</p>
|
||||
<p>注意:<br>在所有三个函数中,<code>a</code>和<code>b</code>的类型是一样的。如果<code>a</code>和<code>b</code>不是相同的类型,那它们俩就不能互换值。Swift 是类型安全的语言,所以它不允许一个<code>String</code>类型的变量和一个<code>Double</code>类型的变量互相交换值。如果一定要做,Swift 将报编译错误。</p>
|
||||
</blockquote>
|
||||
<p><a name="generic_functions"></a></p>
|
||||
<h2 id="-">泛型函数</h2>
|
||||
<p><code>泛型函数</code>可以工作于任何类型,这里是一个上面<code>swapTwoInts</code>函数的泛型版本,用于交换两个值:</p>
|
||||
<pre><code>func swapTwoValues<T>(inout a: T, inout b: T) {
|
||||
<pre><code class="lang-swift">func swapTwoValues<T>(inout a: T, inout b: T) {
|
||||
let temporaryA = a
|
||||
a = b
|
||||
b = temporaryA
|
||||
}
|
||||
</code></pre><p><code>swapTwoValues</code>函数主体和<code>swapTwoInts</code>函数是一样的,它只在第一行稍微有那么一点点不同于<code>swapTwoInts</code>,如下所示:</p>
|
||||
<pre><code>func swapTwoInts(inout a: Int, inout b: Int)
|
||||
</code></pre>
|
||||
<p><code>swapTwoValues</code>函数主体和<code>swapTwoInts</code>函数是一样的,它只在第一行稍微有那么一点点不同于<code>swapTwoInts</code>,如下所示:</p>
|
||||
<pre><code class="lang-swift">func swapTwoInts(inout a: Int, inout b: Int)
|
||||
func swapTwoValues<T>(inout a: T, inout b: T)
|
||||
</code></pre><p>这个函数的泛型版本使用了占位类型名字(通常此情况下用字母<code>T</code>来表示)来代替实际类型名(如<code>In</code>、<code>String</code>或<code>Doubl</code>)。占位类型名没有提示<code>T</code>必须是什么类型,但是它提示了<code>a</code>和<code>b</code>必须是同一类型<code>T</code>,而不管<code>T</code>表示什么类型。只有<code>swapTwoValues</code>函数在每次调用时所传入的实际类型才能决定<code>T</code>所代表的类型。</p>
|
||||
</code></pre>
|
||||
<p>这个函数的泛型版本使用了占位类型名字(通常此情况下用字母<code>T</code>来表示)来代替实际类型名(如<code>In</code>、<code>String</code>或<code>Doubl</code>)。占位类型名没有提示<code>T</code>必须是什么类型,但是它提示了<code>a</code>和<code>b</code>必须是同一类型<code>T</code>,而不管<code>T</code>表示什么类型。只有<code>swapTwoValues</code>函数在每次调用时所传入的实际类型才能决定<code>T</code>所代表的类型。</p>
|
||||
<p>另外一个不同之处在于这个泛型函数名后面跟着的展位类型名字(T)是用尖括号括起来的(<T>)。这个尖括号告诉 Swift 那个<code>T</code>是<code>swapTwoValues</code>函数所定义的一个类型。因为<code>T</code>是一个占位命名类型,Swift 不会去查找命名为T的实际类型。</p>
|
||||
<p><code>swapTwoValues</code>函数除了要求传入的两个任何类型值是同一类型外,也可以作为<code>swapTwoInts</code>函数被调用。每次<code>swapTwoValues</code>被调用,T所代表的类型值都会传给函数。</p>
|
||||
<p>在下面的两个例子中,<code>T</code>分别代表<code>Int</code>和<code>String</code>:</p>
|
||||
<pre><code>var someInt = 3
|
||||
<pre><code class="lang-swift">var someInt = 3
|
||||
var anotherInt = 107
|
||||
swapTwoValues(&someInt, &anotherInt)
|
||||
// someInt is now 107, and anotherInt is now 3
|
||||
|
||||
var someString = "hello"
|
||||
</code></pre>
|
||||
<pre><code class="lang-swift">var someString = "hello"
|
||||
var anotherString = "world"
|
||||
swapTwoValues(&someString, &anotherString)
|
||||
// someString is now "world", and anotherString is now "hello"
|
||||
</code></pre><blockquote>
|
||||
<p>注意
|
||||
上面定义的函数<code>swapTwoValues</code>是受<code>swap</code>函数启发而实现的。<code>swap</code>函数存在于 Swift 标准库,并可以在其它类中任意使用。如果你在自己代码中需要类似<code>swapTwoValues</code>函数的功能,你可以使用已存在的交换函数<code>swap</code>函数。</p>
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>注意<br>上面定义的函数<code>swapTwoValues</code>是受<code>swap</code>函数启发而实现的。<code>swap</code>函数存在于 Swift 标准库,并可以在其它类中任意使用。如果你在自己代码中需要类似<code>swapTwoValues</code>函数的功能,你可以使用已存在的交换函数<code>swap</code>函数。</p>
|
||||
</blockquote>
|
||||
<p><a name="type_parameters"></a></p>
|
||||
<h2 id="-">类型参数</h2>
|
||||
@ -679,16 +682,14 @@ swapTwoValues(&someString, &anotherString)
|
||||
<p>在简单的情况下,泛型函数或泛型类型需要指定一个占位类型(如上面的<code>swapTwoValues</code>泛型函数,或一个存储单一类型的泛型集,如数组),通常用一单个字母<code>T</code>来命名类型参数。不过,你可以使用任何有效的标识符来作为类型参数名。</p>
|
||||
<p>如果你使用多个参数定义更复杂的泛型函数或泛型类型,那么使用更多的描述类型参数是非常有用的。例如,Swift 字典(Dictionary)类型有两个类型参数,一个是键,另外一个是值。如果你自己写字典,你或许会定义这两个类型参数为<code>KeyType</code>和<code>ValueType</code>,用来记住它们在你的泛型代码中的作用。</p>
|
||||
<blockquote>
|
||||
<p>注意
|
||||
请始终使用大写字母开头的驼峰式命名法(例如<code>T</code>和<code>KeyType</code>)来给类型参数命名,以表明它们是类型的占位符,而非类型值。</p>
|
||||
<p>注意<br>请始终使用大写字母开头的驼峰式命名法(例如<code>T</code>和<code>KeyType</code>)来给类型参数命名,以表明它们是类型的占位符,而非类型值。</p>
|
||||
</blockquote>
|
||||
<p><a name="generic_types"></a></p>
|
||||
<h2 id="-">泛型类型</h2>
|
||||
<p>通常在泛型函数中,Swift 允许你定义你自己的泛型类型。这些自定义类、结构体和枚举作用于任何类型,如同<code>Array</code>和<code>Dictionary</code>的用法。</p>
|
||||
<p>这部分向你展示如何写一个泛型集类型--<code>Stack</code>(栈)。一个栈是一系列值域的集合,和<code>Array</code>(数组)类似,但其是一个比 Swift 的<code>Array</code>类型更多限制的集合。一个数组可以允许其里面任何位置的插入/删除操作,而栈,只允许在集合的末端添加新的项(如同<em>push</em>一个新值进栈)。同样的一个栈也只能从末端移除项(如同<em>pop</em>一个值出栈)。</p>
|
||||
<blockquote>
|
||||
<p>注意
|
||||
栈的概念已被<code>UINavigationController</code>类使用来模拟试图控制器的导航结构。你通过调用<code>UINavigationController</code>的<code>pushViewController:animated:</code>方法来为导航栈添加(add)新的试图控制器;而通过<code>popViewControllerAnimated:</code>的方法来从导航栈中移除(pop)某个试图控制器。每当你需要一个严格的<code>后进先出</code>方式来管理集合,堆栈都是最实用的模型。</p>
|
||||
<p>注意<br>栈的概念已被<code>UINavigationController</code>类使用来模拟试图控制器的导航结构。你通过调用<code>UINavigationController</code>的<code>pushViewController:animated:</code>方法来为导航栈添加(add)新的试图控制器;而通过<code>popViewControllerAnimated:</code>的方法来从导航栈中移除(pop)某个试图控制器。每当你需要一个严格的<code>后进先出</code>方式来管理集合,堆栈都是最实用的模型。</p>
|
||||
</blockquote>
|
||||
<p>下图展示了一个栈的压栈(push)/出栈(pop)的行为:</p>
|
||||
<p>![此处输入图片的描述][2]</p>
|
||||
@ -700,7 +701,7 @@ swapTwoValues(&someString, &anotherString)
|
||||
<li>移除掉一个值后,现在栈又重新只有三个值。</li>
|
||||
</ol>
|
||||
<p>这里展示了如何写一个非泛型版本的栈,<code>Int</code>值型的栈:</p>
|
||||
<pre><code>struct IntStack {
|
||||
<pre><code class="lang-swift">struct IntStack {
|
||||
var items = Int[]()
|
||||
mutating func push(item: Int) {
|
||||
items.append(item)
|
||||
@ -709,10 +710,11 @@ swapTwoValues(&someString, &anotherString)
|
||||
return items.removeLast()
|
||||
}
|
||||
}
|
||||
</code></pre><p>这个结构体在栈中使用一个<code>Array</code>性质的<code>items</code>存储值。<code>Stack</code>提供两个方法:<code>push</code>和<code>pop</code>,从栈中压进一个值和移除一个值。这些方法标记为可变的,因为它们需要修改(或<em>转换</em>)结构体的<code>items</code>数组。</p>
|
||||
</code></pre>
|
||||
<p>这个结构体在栈中使用一个<code>Array</code>性质的<code>items</code>存储值。<code>Stack</code>提供两个方法:<code>push</code>和<code>pop</code>,从栈中压进一个值和移除一个值。这些方法标记为可变的,因为它们需要修改(或<em>转换</em>)结构体的<code>items</code>数组。</p>
|
||||
<p>上面所展现的<code>IntStack</code>类型只能用于<code>Int</code>值,不过,其对于定义一个泛型<code>Stack</code>类(可以处理<em>任何</em>类型值的栈)是非常有用的。</p>
|
||||
<p>这里是一个相同代码的泛型版本:</p>
|
||||
<pre><code>struct Stack<T> {
|
||||
<pre><code class="lang-swift">struct Stack<T> {
|
||||
var items = T[]()
|
||||
mutating func push(item: T) {
|
||||
items.append(item)
|
||||
@ -721,7 +723,8 @@ swapTwoValues(&someString, &anotherString)
|
||||
return items.removeLast()
|
||||
}
|
||||
}
|
||||
</code></pre><p>注意到<code>Stack</code>的泛型版本基本上和非泛型版本相同,但是泛型版本的占位类型参数为T代替了实际<code>Int</code>类型。这种类型参数包含在一对尖括号里(<code><T></code>),紧随在结构体名字后面。</p>
|
||||
</code></pre>
|
||||
<p>注意到<code>Stack</code>的泛型版本基本上和非泛型版本相同,但是泛型版本的占位类型参数为T代替了实际<code>Int</code>类型。这种类型参数包含在一对尖括号里(<code><T></code>),紧随在结构体名字后面。</p>
|
||||
<p><code>T</code>定义了一个名为“某种类型T”的节点提供给后来用。这种将来类型可以在结构体的定义里任何地方表示为“T”。在这种情况下,<code>T</code>在如下三个地方被用作节点:</p>
|
||||
<ul>
|
||||
<li>创建一个名为<code>items</code>的属性,使用空的T类型值数组对其进行初始化;</li>
|
||||
@ -729,18 +732,20 @@ swapTwoValues(&someString, &anotherString)
|
||||
<li>指定一个<code>pop</code>方法的返回值,该返回值将是一个T类型值。</li>
|
||||
</ul>
|
||||
<p>当创建一个新单例并初始化时, 通过用一对紧随在类型名后的尖括号里写出实际指定栈用到类型,创建一个<code>Stack</code>实例,同创建<code>Array</code>和<code>Dictionary</code>一样:</p>
|
||||
<pre><code>var stackOfStrings = Stack<String>()
|
||||
<pre><code class="lang-swift">var stackOfStrings = Stack<String>()
|
||||
stackOfStrings.push("uno")
|
||||
stackOfStrings.push("dos")
|
||||
stackOfStrings.push("tres")
|
||||
stackOfStrings.push("cuatro")
|
||||
// 现在栈已经有4个string了
|
||||
</code></pre><p>下图将展示<code>stackOfStrings</code>如何<code>push</code>这四个值进栈的过程:</p>
|
||||
</code></pre>
|
||||
<p>下图将展示<code>stackOfStrings</code>如何<code>push</code>这四个值进栈的过程:</p>
|
||||
<p>![此处输入图片的描述][3]</p>
|
||||
<p>从栈中<code>pop</code>并移除值"cuatro":</p>
|
||||
<pre><code>let fromTheTop = stackOfStrings.pop()
|
||||
<pre><code class="lang-swift">let fromTheTop = stackOfStrings.pop()
|
||||
// fromTheTop is equal to "cuatro", and the stack now contains 3 strings
|
||||
</code></pre><p>下图展示了如何从栈中pop一个值的过程:
|
||||
</code></pre>
|
||||
<p>下图展示了如何从栈中pop一个值的过程:
|
||||
![此处输入图片的描述][4]</p>
|
||||
<p>由于<code>Stack</code>是泛型类型,所以在 Swift 中其可以用来创建任何有效类型的栈,这种方式如同<code>Array</code>和<code>Dictionary</code>。</p>
|
||||
<p><a name="type_constraints"></a></p>
|
||||
@ -751,13 +756,14 @@ stackOfStrings.push("cuatro")
|
||||
<p>当你创建自定义泛型类型时,你可以定义你自己的类型约束,当然,这些约束要支持泛型编程的强力特征中的多数。抽象概念如<code>可哈希</code>具有的类型特征是根据它们概念特征来界定的,而不是它们的直接类型特征。</p>
|
||||
<h3 id="-">类型约束语法</h3>
|
||||
<p>你可以写一个在一个类型参数名后面的类型约束,通过冒号分割,来作为类型参数链的一部分。这种作用于泛型函数的类型约束的基础语法如下所示(和泛型类型的语法相同):</p>
|
||||
<pre><code>func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
|
||||
<pre><code class="lang-swift">func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
|
||||
// function body goes here
|
||||
}
|
||||
</code></pre><p>上面这个假定函数有两个类型参数。第一个类型参数<code>T</code>,有一个需要<code>T</code>必须是<code>SomeClass</code>子类的类型约束;第二个类型参数<code>U</code>,有一个需要<code>U</code>必须遵循<code>SomeProtocol</code>协议的类型约束。</p>
|
||||
</code></pre>
|
||||
<p>上面这个假定函数有两个类型参数。第一个类型参数<code>T</code>,有一个需要<code>T</code>必须是<code>SomeClass</code>子类的类型约束;第二个类型参数<code>U</code>,有一个需要<code>U</code>必须遵循<code>SomeProtocol</code>协议的类型约束。</p>
|
||||
<h3 id="-">类型约束行为</h3>
|
||||
<p>这里有个名为<code>findStringIndex</code>的非泛型函数,该函数功能是去查找包含一给定<code>String</code>值的数组。若查找到匹配的字符串,<code>findStringIndex</code>函数返回该字符串在数组中的索引值(<code>Int</code>),反之则返回<code>nil</code>:</p>
|
||||
<pre><code>func findStringIndex(array: String[], valueToFind: String) -> Int? {
|
||||
<pre><code class="lang-swift">func findStringIndex(array: String[], valueToFind: String) -> Int? {
|
||||
for (index, value) in enumerate(array) {
|
||||
if value == valueToFind {
|
||||
return index
|
||||
@ -765,15 +771,17 @@ stackOfStrings.push("cuatro")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
</code></pre><p><code>findStringIndex</code>函数可以作用于查找一字符串数组中的某个字符串:</p>
|
||||
<pre><code>let strings = ["cat", "dog", "llama", "parakeet", "terrapin"]
|
||||
</code></pre>
|
||||
<p><code>findStringIndex</code>函数可以作用于查找一字符串数组中的某个字符串:</p>
|
||||
<pre><code class="lang-swift">let strings = ["cat", "dog", "llama", "parakeet", "terrapin"]
|
||||
if let foundIndex = findStringIndex(strings, "llama") {
|
||||
println("The index of llama is \(foundIndex)")
|
||||
}
|
||||
// 输出 "The index of llama is 2"
|
||||
</code></pre><p>如果只是针对字符串而言查找在数组中的某个值的索引,用处不是很大,不过,你可以写出相同功能的泛型函数<code>findIndex</code>,用某个类型<code>T</code>值替换掉提到的字符串。</p>
|
||||
</code></pre>
|
||||
<p>如果只是针对字符串而言查找在数组中的某个值的索引,用处不是很大,不过,你可以写出相同功能的泛型函数<code>findIndex</code>,用某个类型<code>T</code>值替换掉提到的字符串。</p>
|
||||
<p>这里展示如何写一个你或许期望的<code>findStringIndex</code>的泛型版本<code>findIndex</code>。请注意这个函数仍然返回<code>Int</code>,是不是有点迷惑呢,而不是泛型类型?那是因为函数返回的是一个可选的索引数,而不是从数组中得到的一个可选值。需要提醒的是,这个函数不会编译,原因在例子后面会说明:</p>
|
||||
<pre><code>func findIndex<T>(array: T[], valueToFind: T) -> Int? {
|
||||
<pre><code class="lang-swift">func findIndex<T>(array: T[], valueToFind: T) -> Int? {
|
||||
for (index, value) in enumerate(array) {
|
||||
if value == valueToFind {
|
||||
return index
|
||||
@ -781,10 +789,11 @@ if let foundIndex = findStringIndex(strings, "llama") {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
</code></pre><p>上面所写的函数不会编译。这个问题的位置在等式的检查上,<code>“if value == valueToFind”</code>。不是所有的 Swift 中的类型都可以用等式符(==)进行比较。例如,如果你创建一个你自己的类或结构体来表示一个复杂的数据模型,那么 Swift 没法猜到对于这个类或结构体而言“等于”的意思。正因如此,这部分代码不能可能保证工作于每个可能的类型<code>T</code>,当你试图编译这部分代码时估计会出现相应的错误。</p>
|
||||
</code></pre>
|
||||
<p>上面所写的函数不会编译。这个问题的位置在等式的检查上,<code>“if value == valueToFind”</code>。不是所有的 Swift 中的类型都可以用等式符(==)进行比较。例如,如果你创建一个你自己的类或结构体来表示一个复杂的数据模型,那么 Swift 没法猜到对于这个类或结构体而言“等于”的意思。正因如此,这部分代码不能可能保证工作于每个可能的类型<code>T</code>,当你试图编译这部分代码时估计会出现相应的错误。</p>
|
||||
<p>不过,所有的这些并不会让我们无从下手。Swift 标准库中定义了一个<code>Equatable</code>协议,该协议要求任何遵循的类型实现等式符(==)和不等符(!=)对任何两个该类型进行比较。所有的 Swift 标准类型自动支持<code>Equatable</code>协议。</p>
|
||||
<p>任何<code>Equatable</code>类型都可以安全的使用在<code>findIndex</code>函数中,因为其保证支持等式操作。为了说明这个事实,当你定义一个函数时,你可以写一个<code>Equatable</code>类型约束作为类型参数定义的一部分:</p>
|
||||
<pre><code>func findIndex<T: Equatable>(array: T[], valueToFind: T) -> Int? {
|
||||
<pre><code class="lang-swift">func findIndex<T: Equatable>(array: T[], valueToFind: T) -> Int? {
|
||||
for (index, value) in enumerate(array) {
|
||||
if value == valueToFind {
|
||||
return index
|
||||
@ -792,24 +801,27 @@ if let foundIndex = findStringIndex(strings, "llama") {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
</code></pre><p><code>findIndex</code>中这个单个类型参数写做:<code>T: Equatable</code>,也就意味着“任何T类型都遵循<code>Equatable</code>协议”。</p>
|
||||
</code></pre>
|
||||
<p><code>findIndex</code>中这个单个类型参数写做:<code>T: Equatable</code>,也就意味着“任何T类型都遵循<code>Equatable</code>协议”。</p>
|
||||
<p><code>findIndex</code>函数现在则可以成功的编译过,并且作用于任何遵循<code>Equatable</code>的类型,如<code>Double</code>或<code>String</code>:</p>
|
||||
<pre><code>let doubleIndex = findIndex([3.14159, 0.1, 0.25], 9.3)
|
||||
<pre><code class="lang-swift">let doubleIndex = findIndex([3.14159, 0.1, 0.25], 9.3)
|
||||
// doubleIndex is an optional Int with no value, because 9.3 is not in the array
|
||||
let stringIndex = findIndex(["Mike", "Malcolm", "Andrea"], "Andrea")
|
||||
// stringIndex is an optional Int containing a value of 2
|
||||
</code></pre><p><a name="associated_types"></a></p>
|
||||
</code></pre>
|
||||
<p><a name="associated_types"></a></p>
|
||||
<h2 id="-">关联类型</h2>
|
||||
<p>当定义一个协议时,有的时候声明一个或多个关联类型作为协议定义的一部分是非常有用的。一个关联类型给定作用于协议部分的类型一个节点名(或<em>别名</em>)。作用于关联类型上实际类型是不需要指定的,直到该协议接受。关联类型被指定为<code>typealias</code>关键字。</p>
|
||||
<h3 id="-">关联类型行为</h3>
|
||||
<p>这里是一个<code>Container</code>协议的例子,定义了一个ItemType关联类型:</p>
|
||||
<pre><code>protocol Container {
|
||||
<pre><code class="lang-swift">protocol Container {
|
||||
typealias ItemType
|
||||
mutating func append(item: ItemType)
|
||||
var count: Int { get }
|
||||
subscript(i: Int) -> ItemType { get }
|
||||
}
|
||||
</code></pre><p><code>Container</code>协议定义了三个任何容器必须支持的兼容要求:</p>
|
||||
</code></pre>
|
||||
<p><code>Container</code>协议定义了三个任何容器必须支持的兼容要求:</p>
|
||||
<ul>
|
||||
<li>必须可能通过<code>append</code>方法添加一个新item到容器里;</li>
|
||||
<li>必须可能通过使用<code>count</code>属性获取容器里items的数量,并返回一个<code>Int</code>值;</li>
|
||||
@ -820,7 +832,7 @@ let stringIndex = findIndex(["Mike", "Malcolm", "Andrea
|
||||
<p>为了定义这三个条件,<code>Container</code>协议需要一个方法指定容器里的元素将会保留,而不需要知道特定容器的类型。<code>Container</code>协议需要指定任何通过<code>append</code>方法添加到容器里的值和容器里元素是相同类型,并且通过容器下标返回的容器元素类型的值的类型是相同类型。</p>
|
||||
<p>为了达到此目的,<code>Container</code>协议声明了一个ItemType的关联类型,写作<code>typealias ItemType</code>。The protocol does not define what ItemType is an alias for—that information is left for any conforming type to provide(这个协议不会定义<code>ItemType</code>是遵循类型所提供的何种信息的别名)。尽管如此,<code>ItemType</code>别名支持一种方法识别在一个容器里的items类型,以及定义一种使用在<code>append</code>方法和下标中的类型,以便保证任何期望的<code>Container</code>的行为是强制性的。</p>
|
||||
<p>这里是一个早前IntStack类型的非泛型版本,适用于遵循Container协议:</p>
|
||||
<pre><code>struct IntStack: Container {
|
||||
<pre><code class="lang-swift">struct IntStack: Container {
|
||||
// original IntStack implementation
|
||||
var items = Int[]()
|
||||
mutating func push(item: Int) {
|
||||
@ -841,11 +853,12 @@ let stringIndex = findIndex(["Mike", "Malcolm", "Andrea
|
||||
return items[i]
|
||||
}
|
||||
}
|
||||
</code></pre><p><code>IntStack</code>类型实现了<code>Container</code>协议的所有三个要求,在<code>IntStack</code>类型的每个包含部分的功能都满足这些要求。</p>
|
||||
</code></pre>
|
||||
<p><code>IntStack</code>类型实现了<code>Container</code>协议的所有三个要求,在<code>IntStack</code>类型的每个包含部分的功能都满足这些要求。</p>
|
||||
<p>此外,<code>IntStack</code>指定了<code>Container</code>的实现,适用的ItemType被用作<code>Int</code>类型。对于这个<code>Container</code>协议实现而言,定义 <code>typealias ItemType = Int</code>,将抽象的<code>ItemType</code>类型转换为具体的<code>Int</code>类型。</p>
|
||||
<p>感谢Swift类型参考,你不用在<code>IntStack</code>定义部分声明一个具体的<code>Int</code>的<code>ItemType</code>。由于<code>IntStack</code>遵循<code>Container</code>协议的所有要求,只要通过简单的查找<code>append</code>方法的item参数类型和下标返回的类型,Swift就可以推断出合适的<code>ItemType</code>来使用。确实,如果上面的代码中你删除了 <code>typealias ItemType = Int</code>这一行,一切仍旧可以工作,因为它清楚的知道ItemType使用的是何种类型。</p>
|
||||
<p>你也可以生成遵循<code>Container</code>协议的泛型<code>Stack</code>类型:</p>
|
||||
<pre><code>struct Stack<T>: Container {
|
||||
<pre><code class="lang-swift">struct Stack<T>: Container {
|
||||
// original Stack<T> implementation
|
||||
var items = T[]()
|
||||
mutating func push(item: T) {
|
||||
@ -865,19 +878,21 @@ let stringIndex = findIndex(["Mike", "Malcolm", "Andrea
|
||||
return items[i]
|
||||
}
|
||||
}
|
||||
</code></pre><p>这个时候,占位类型参数<code>T</code>被用作<code>append</code>方法的item参数和下标的返回类型。Swift 因此可以推断出被用作这个特定容器的<code>ItemType</code>的<code>T</code>的合适类型。</p>
|
||||
</code></pre>
|
||||
<p>这个时候,占位类型参数<code>T</code>被用作<code>append</code>方法的item参数和下标的返回类型。Swift 因此可以推断出被用作这个特定容器的<code>ItemType</code>的<code>T</code>的合适类型。</p>
|
||||
<h3 id="-">扩展一个存在的类型为一指定关联类型</h3>
|
||||
<p>在[使用扩展来添加协议兼容性][6]中有描述扩展一个存在的类型添加遵循一个协议。这个类型包含一个关联类型的协议。</p>
|
||||
<p>Swift的<code>Array</code>已经提供<code>append</code>方法,一个<code>count</code>属性和通过下标来查找一个自己的元素。这三个功能都达到<code>Container</code>协议的要求。也就意味着你可以扩展<code>Array</code>去遵循<code>Container</code>协议,只要通过简单声明<code>Array</code>适用于该协议而已。如何实践这样一个空扩展,在[使用扩展来声明协议的采纳][7]中有描述这样一个实现一个空扩展的行为:</p>
|
||||
<pre><code>extension Array: Container {}
|
||||
</code></pre><p>如同上面的泛型<code>Stack</code>类型一样,<code>Array的append</code>方法和下标保证<code>Swift</code>可以推断出<code>ItemType</code>所使用的适用的类型。定义了这个扩展后,你可以将任何<code>Array</code>当作<code>Container</code>来使用。</p>
|
||||
<pre><code class="lang-swift">extension Array: Container {}
|
||||
</code></pre>
|
||||
<p>如同上面的泛型<code>Stack</code>类型一样,<code>Array的append</code>方法和下标保证<code>Swift</code>可以推断出<code>ItemType</code>所使用的适用的类型。定义了这个扩展后,你可以将任何<code>Array</code>当作<code>Container</code>来使用。</p>
|
||||
<p><a name="where_clauses"></a></p>
|
||||
<h2 id="where-">Where 语句</h2>
|
||||
<p>[类型约束][8]中描述的类型约束确保你定义关于类型参数的需求和一泛型函数或类型有关联。</p>
|
||||
<p>对于关联类型的定义需求也是非常有用的。你可以通过这样去定义<em>where语句</em>作为一个类型参数队列的一部分。一个<code>where</code>语句使你能够要求一个关联类型遵循一个特定的协议,以及(或)那个特定的类型参数和关联类型可以是相同的。你可写一个<code>where</code>语句,通过紧随放置<code>where</code>关键字在类型参数队列后面,其后跟着一个或者多个针对关联类型的约束,以及(或)一个或多个类型和关联类型的等于关系。</p>
|
||||
<p>下面的列子定义了一个名为<code>allItemsMatch</code>的泛型函数,用来检查是否两个<code>Container</code>单例包含具有相同顺序的相同元素。如果匹配到所有的元素,那么返回一个为<code>true</code>的<code>Boolean</code>值,反之,则相反。</p>
|
||||
<p>这两个容器可以被检查出是否是相同类型的容器(虽然它们可以是),但它们确实拥有相同类型的元素。这个需求通过一个类型约束和<code>where</code>语句结合来表示:</p>
|
||||
<pre><code>func allItemsMatch<
|
||||
<pre><code class="lang-swift">func allItemsMatch<
|
||||
C1: Container, C2: Container
|
||||
where C1.ItemType == C2.ItemType, C1.ItemType: Equatable>
|
||||
(someContainer: C1, anotherContainer: C2) -> Bool {
|
||||
@ -898,7 +913,8 @@ let stringIndex = findIndex(["Mike", "Malcolm", "Andrea
|
||||
return true
|
||||
|
||||
}
|
||||
</code></pre><p>这个函数用了两个参数:<code>someContainer</code>和<code>anotherContainer</code>。<code>someContainer</code>参数是类型<code>C1</code>,<code>anotherContainer</code>参数是类型<code>C2</code>。<code>C1</code>和<code>C2</code>是容器的两个占位类型参数,决定了这个函数何时被调用。</p>
|
||||
</code></pre>
|
||||
<p>这个函数用了两个参数:<code>someContainer</code>和<code>anotherContainer</code>。<code>someContainer</code>参数是类型<code>C1</code>,<code>anotherContainer</code>参数是类型<code>C2</code>。<code>C1</code>和<code>C2</code>是容器的两个占位类型参数,决定了这个函数何时被调用。</p>
|
||||
<p>这个函数的类型参数列紧随在两个类型参数需求的后面:</p>
|
||||
<ul>
|
||||
<li><code>C1</code>必须遵循<code>Container</code>协议 (写作 <code>C1: Container</code>)。</li>
|
||||
@ -918,7 +934,7 @@ let stringIndex = findIndex(["Mike", "Malcolm", "Andrea
|
||||
<p>检查完之后,函数通过<code>for-in</code>循环和半闭区间操作(..)来迭代<code>someContainer</code>中的所有元素。对于每个元素,函数检查是否<code>someContainer</code>中的元素不等于对应的<code>anotherContainer</code>中的元素,如果这两个元素不等,则这两个容器不匹配,返回<code>false</code>。</p>
|
||||
<p>如果循环体结束后未发现没有任何的不匹配,那表明两个容器匹配,函数返回<code>true</code>。</p>
|
||||
<p>这里演示了allItemsMatch函数运算的过程:</p>
|
||||
<pre><code>var stackOfStrings = Stack<String>()
|
||||
<pre><code class="lang-swift">var stackOfStrings = Stack<String>()
|
||||
stackOfStrings.push("uno")
|
||||
stackOfStrings.push("dos")
|
||||
stackOfStrings.push("tres")
|
||||
@ -931,7 +947,8 @@ if allItemsMatch(stackOfStrings, arrayOfStrings) {
|
||||
println("Not all items match.")
|
||||
}
|
||||
// 输出 "All items match."
|
||||
</code></pre><p> 上面的例子创建一个<code>Stack</code>单例来存储<code>String</code>,然后压了三个字符串进栈。这个例子也创建了一个<code>Array</code>单例,并初始化包含三个同栈里一样的原始字符串。即便栈和数组否是不同的类型,但它们都遵循<code>Container</code>协议,而且它们都包含同样的类型值。你因此可以调用<code>allItemsMatch</code>函数,用这两个容器作为它的参数。在上面的例子中,<code>allItemsMatch</code>函数正确的显示了所有的这两个容器的<code>items</code>匹配。</p>
|
||||
</code></pre>
|
||||
<p> 上面的例子创建一个<code>Stack</code>单例来存储<code>String</code>,然后压了三个字符串进栈。这个例子也创建了一个<code>Array</code>单例,并初始化包含三个同栈里一样的原始字符串。即便栈和数组否是不同的类型,但它们都遵循<code>Container</code>协议,而且它们都包含同样的类型值。你因此可以调用<code>allItemsMatch</code>函数,用这两个容器作为它的参数。在上面的例子中,<code>allItemsMatch</code>函数正确的显示了所有的这两个容器的<code>items</code>匹配。</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="2.23" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="2.23" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,11 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_78">
|
||||
<section class="normal" id="section-gitbook_52">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:xielingwang</p>
|
||||
<p>校对:numbbbbb</p>
|
||||
<p>翻译:<a href="https://github.com/xielingwang" target="_blank">xielingwang</a><br>校对:<a href="https://github.com/numbbbbb" target="_blank">numbbbbb</a></p>
|
||||
</blockquote>
|
||||
<h1 id="-">高级运算符</h1>
|
||||
<hr>
|
||||
@ -776,8 +775,7 @@ let y = x &/ 0
|
||||
<p>计算结果为 4。</p>
|
||||
<p>查阅Swift运算符的优先级和结合性的完整列表,请看<a href="../chapter3/04_Expressions.html">表达式</a>。</p>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<p>Swift的运算符较C语言和Objective-C来得更简单和保守,这意味着跟基于C的语言可能不一样。所以,在移植已有代码到Swift时,注意去确保代码按你想的那样去执行。</p>
|
||||
<p>注意:<br>Swift的运算符较C语言和Objective-C来得更简单和保守,这意味着跟基于C的语言可能不一样。所以,在移植已有代码到Swift时,注意去确保代码按你想的那样去执行。</p>
|
||||
</blockquote>
|
||||
<p><a name="operator_functions"></a></p>
|
||||
<h2 id="-">运算符函数</h2>
|
||||
@ -841,8 +839,7 @@ let afterIncrement = ++toIncrement
|
||||
// afterIncrement 现在也是 (4.0, 5.0)
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<p>默认的赋值符是不可重载的。只有组合赋值符可以重载。三目条件运算符 <code>a?b:c</code> 也是不可重载。</p>
|
||||
<p>注意:<br>默认的赋值符是不可重载的。只有组合赋值符可以重载。三目条件运算符 <code>a?b:c</code> 也是不可重载。</p>
|
||||
</blockquote>
|
||||
<h3 id="-">比较运算符</h3>
|
||||
<p>Swift无所知道自定义类型是否相等或不等,因为等于或者不等于由你的代码说了算了。所以自定义的类和结构要使用比较符<code>==</code>或<code>!=</code>就需要重载。</p>
|
||||
@ -895,7 +892,7 @@ let secondVector = Vector2D(x: 3.0, y: 4.0)
|
||||
let plusMinusVector = firstVector +- secondVector
|
||||
// plusMinusVector 此时的值为 (4.0, -2.0)
|
||||
</code></pre>
|
||||
<p>这个运算符把两个向量的<code>x</code>相加,把向量的<code>y</code>相减。因为它实际是属于加减运算,所以让它保持了和加法一样的结合性和优先级(<code>left</code>和<code>140</code>)。查阅完整的Swift默认结合性和优先级的设置,请移步<a href="../chapter3/04_Expressions.html">表达式</a>;</p>
|
||||
<p>这个运算符把两个向量的<code>x</code>相加,把向量的<code>y</code>相减。因为他实际是属于加减运算,所以让它保持了和加法一样的结合性和优先级(<code>left</code>和<code>140</code>)。查阅完整的Swift默认结合性和优先级的设置,请移步<a href="../chapter3/04_Expressions.html">表达式</a>;</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="2" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="2" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,7 +587,7 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_80">
|
||||
<section class="normal" id="section-gitbook_54">
|
||||
|
||||
<h1 id="swift-">Swift 教程</h1>
|
||||
<p>本章介绍了 Swift 的各种特性及其使用方法,是全书的核心部分。</p>
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="3.1" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="3.1" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,11 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_83">
|
||||
<section class="normal" id="section-gitbook_57">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:ChildhoodAndy</p>
|
||||
<p>校对:numbbbbb</p>
|
||||
<p>翻译:<a href="https://github.com/dabing1022" target="_blank">dabing1022</a><br>校对:<a href="https://github.com/numbbbbb" target="_blank">numbbbbb</a></p>
|
||||
</blockquote>
|
||||
<h1 id="-">关于语言附注</h1>
|
||||
<hr>
|
||||
@ -604,8 +603,8 @@
|
||||
<p><a name="how_to_read_the_grammar"></a></p>
|
||||
<h2 id="-">如何阅读语法</h2>
|
||||
<p>用来描述Swift编程语言形式语法的记法遵循下面几个约定:</p>
|
||||
<p>-](<a href="https://github.com/numbbbbb)箭头(→)用来标记语法产式,可以被理](https://github.com/numbbbbb)解为“可以包含”。" target="_blank">https://github.com/numbbbbb)箭头(→)用来标记语法产式,可以被理](https://github.com/numbbbbb)解为“可以包含”。</a></p>
|
||||
<ul>
|
||||
<li>箭头(→)用来标记语法产式,可以被理解为“可以包含”。</li>
|
||||
<li>句法范畴由<em>斜体</em>文字表示,并出现在一个语法产式规则两侧。</li>
|
||||
<li>义词和标点符号由粗体固定宽度的文本显示和只出现在一个语法产式规则的右边。</li>
|
||||
<li>选择性的语法产式由竖线(|)分隔。当可选用的语法产式太多时,为了阅读方便,它们将被拆分为多行语法产式规则。</li>
|
||||
@ -614,14 +613,11 @@
|
||||
</ul>
|
||||
<p>举个例子,getter-setter的语法块的定义如下:</p>
|
||||
<blockquote>
|
||||
<p>GRAMMAR OF A GETTER-SETTER BLOCK</p>
|
||||
<p><em>getter-setter-block</em> → { <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/getter-clause" target="_blank"><em>getter-clause</em></a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/setter-clause" target="_blank"><em>setter-clause</em></a><em>opt</em> } | { <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/setter-clause" target="_blank"><em>setter-clause</em></a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/getter-clause" target="_blank"><em>getter-clause</em></a>}</p>
|
||||
<p>GRAMMAR OF A GETTER-SETTER BLOCK<br><em>getter-setter-block</em> → { <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/getter-clause" target="_blank"><em>getter-clause</em></a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/setter-clause" target="_blank"><em>setter-clause</em></a><em>opt</em> } | { <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/setter-clause" target="_blank"><em>setter-clause</em></a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/getter-clause" target="_blank"><em>getter-clause</em></a>}</p>
|
||||
</blockquote>
|
||||
<p>这个定义表明,一个getter-setter方法块可以由一个getter子句后跟一个可选的setter子句构成,用大括号括起来,或者由一个setter子句后跟一个getter子句构成,用大括号括起来。上述的文法产生等价于下面的两个产生,明确阐明如何二中择一:</p>
|
||||
<blockquote>
|
||||
<p>GRAMMAR OF A GETTER-SETTER BLOCK</p>
|
||||
<p>getter-setter-block → { <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/getter-clause" target="_blank"><em>getter-clause</em></a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/setter-clause" target="_blank"><em>setter-clause</em></a><em>opt</em> }</p>
|
||||
<p>getter-setter-block → { <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/setter-clause" target="_blank"><em>setter-clause</em></a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/getter-clause" target="_blank"><em>getter-clause</em></a>}</p>
|
||||
<p>GRAMMAR OF A GETTER-SETTER BLOCK<br>getter-setter-block → { <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/getter-clause" target="_blank"><em>getter-clause</em></a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/setter-clause" target="_blank"><em>setter-clause</em></a><em>opt</em> }<br>getter-setter-block → { <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/setter-clause" target="_blank"><em>setter-clause</em></a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/getter-clause" target="_blank"><em>getter-clause</em></a>}</p>
|
||||
</blockquote>
|
||||
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="3.2" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="3.2" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,11 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_89">
|
||||
<section class="normal" id="section-gitbook_65">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:superkam</p>
|
||||
<p>校对:numbbbbb</p>
|
||||
<p>翻译:<a href="https://github.com/superkam" target="_blank">superkam</a><br>校对:<a href="https://github.com/numbbbbb" target="_blank">numbbbbb</a></p>
|
||||
</blockquote>
|
||||
<h1 id="-">词法结构</h1>
|
||||
<hr>
|
||||
@ -615,44 +614,16 @@
|
||||
<p>使用保留字(<em>reserved word</em>)作为标识符,需要在其前后增加反引号 <code>`</code>。例如,<code>class</code> 不是合法的标识符,但可以使用 <code>`class`</code>。反引号不属于标识符的一部分,<code>`x`</code> 和 <code>x</code> 表示同一标识符。</p>
|
||||
<p>闭包(<em>closure</em>)中如果没有明确指定参数名称,参数将被隐式命名为 <code>$0</code>、<code>$1</code>、<code>$2</code>... 这些命名在闭包作用域内是合法的标识符。</p>
|
||||
<blockquote>
|
||||
<p>标识符语法</p>
|
||||
<p><em>identifier</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/identifier-head" target="_blank">identifier-head</a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/identifier-characters" target="_blank">identifier-characters</a> <em>opt</em></p>
|
||||
<p><em>identifier</em> → ` <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/identifier-head" target="_blank">identifier-head</a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/identifier-characters" target="_blank">identifier-characters</a> <em>opt</em> `</p>
|
||||
<p><em>identifier</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/implicit-parameter-name" target="_blank">implicit-parameter-name</a></p>
|
||||
<p><em>identifier-list</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/identifier" target="_blank">identifier</a> | <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/identifier" target="_blank">identifier</a> , <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/identifier-list" target="_blank">identifier-list</a></p>
|
||||
<p><em>identifier-head</em> → A 到 Z 大写或小写字母</p>
|
||||
<p><em>identifier-head</em> → U+00A8, U+00AA, U+00AD, U+00AF, U+00B2–U+00B5, 或 U+00B7–U+00BA</p>
|
||||
<p><em>identifier-head</em> → U+00BC–U+00BE, U+00C0–U+00D6, U+00D8–U+00F6, 或 U+00F8–U+00FF</p>
|
||||
<p><em>identifier-head</em> → U+0100–U+02FF, U+0370–U+167F, U+1681–U+180D, 或 U+180F–U+1DBF</p>
|
||||
<p><em>identifier-head</em> → U+1E00–U+1FFF</p>
|
||||
<p><em>identifier-head</em> → U+200B–U+200D, U+202A–U+202E, U+203F–U+2040, U+2054, 或 U+2060–U+206F</p>
|
||||
<p><em>identifier-head</em> → U+2070–U+20CF, U+2100–U+218F, U+2460–U+24FF, 或 U+2776–U+2793</p>
|
||||
<p><em>identifier-head</em> → U+2C00–U+2DFF 或 U+2E80–U+2FFF</p>
|
||||
<p><em>identifier-head</em> → U+3004–U+3007, U+3021–U+302F, U+3031–U+303F, 或 U+3040–U+D7FF</p>
|
||||
<p><em>identifier-head</em> → U+F900–U+FD3D, U+FD40–U+FDCF, U+FDF0–U+FE1F, 或 U+FE30–U+FE44</p>
|
||||
<p><em>identifier-head</em> → U+FE47–U+FFFD</p>
|
||||
<p><em>identifier-head</em> → U+10000–U+1FFFD, U+20000–U+2FFFD, U+30000–U+3FFFD, 或 U+40000–U+4FFFD</p>
|
||||
<p><em>identifier-head</em> → U+50000–U+5FFFD, U+60000–U+6FFFD, U+70000–U+7FFFD, 或 U+80000–U+8FFFD</p>
|
||||
<p><em>identifier-head</em> → U+90000–U+9FFFD, U+A0000–U+AFFFD, U+B0000–U+BFFFD, 或 U+C0000–U+CFFFD</p>
|
||||
<p><em>identifier-head</em> → U+D0000–U+DFFFD 或 U+E0000–U+EFFFD</p>
|
||||
<p><em>identifier-character</em> → 数字 0 到 9</p>
|
||||
<p><em>identifier-character</em> → U+0300–U+036F, U+1DC0–U+1DFF, U+20D0–U+20FF, or U+FE20–U+FE2F</p>
|
||||
<p><em>identifier-character</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/identifier-head" target="_blank">identifier-head</a></p>
|
||||
<p><em>identifier-characters</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/identifier-character" target="_blank">identifier-character</a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/identifier-characters" target="_blank">identifier-characters</a> <em>opt</em></p>
|
||||
<p><em>implicit-parameter-name</em> → <strong>$</strong> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/decimal-digits" target="_blank">decimal-digits</a></p>
|
||||
<p>标识符语法<br><em>标识符</em> → <a href="LexicalStructure.html#identifier_head"><em>标识符头(Head)</em></a> <a href="LexicalStructure.html#identifier_characters"><em>标识符字符列表</em></a> <em>可选</em><br><em>标识符</em> → <strong>`</strong> <a href="LexicalStructure.html#identifier_head"><em>标识符头(Head)</em></a> <a href="LexicalStructure.html#identifier_characters"><em>标识符字符列表</em></a> <em>可选</em> <strong>`</strong><br><em>标识符</em> → <a href="LexicalStructure.html#implicit_parameter_name"><em>隐式参数名</em></a><br><em>标识符列表</em> → <a href="LexicalStructure.html#identifier"><em>标识符</em></a> | <a href="LexicalStructure.html#identifier"><em>标识符</em></a> <strong>,</strong> <a href="LexicalStructure.html#identifier_list"><em>标识符列表</em></a><br><em>标识符头(Head)</em> → Upper- or lowercase letter A through Z<br><em>标识符头(Head)</em> → U+00A8, U+00AA, U+00AD, U+00AF, U+00B2–U+00B5, or U+00B7–U+00BA<br><em>标识符头(Head)</em> → U+00BC–U+00BE, U+00C0–U+00D6, U+00D8–U+00F6, or U+00F8–U+00FF<br><em>标识符头(Head)</em> → U+0100–U+02FF, U+0370–U+167F, U+1681–U+180D, or U+180F–U+1DBF<br><em>标识符头(Head)</em> → U+1E00–U+1FFF<br><em>标识符头(Head)</em> → U+200B–U+200D, U+202A–U+202E, U+203F–U+2040, U+2054, or U+2060–U+206F<br><em>标识符头(Head)</em> → U+2070–U+20CF, U+2100–U+218F, U+2460–U+24FF, or U+2776–U+2793<br><em>标识符头(Head)</em> → U+2C00–U+2DFF or U+2E80–U+2FFF<br><em>标识符头(Head)</em> → U+3004–U+3007, U+3021–U+302F, U+3031–U+303F, or U+3040–U+D7FF<br><em>标识符头(Head)</em> → U+F900–U+FD3D, U+FD40–U+FDCF, U+FDF0–U+FE1F, or U+FE30–U+FE44<br><em>标识符头(Head)</em> → U+FE47–U+FFFD<br><em>标识符头(Head)</em> → U+10000–U+1FFFD, U+20000–U+2FFFD, U+30000–U+3FFFD, or U+40000–U+4FFFD<br><em>标识符头(Head)</em> → U+50000–U+5FFFD, U+60000–U+6FFFD, U+70000–U+7FFFD, or U+80000–U+8FFFD<br><em>标识符头(Head)</em> → U+90000–U+9FFFD, U+A0000–U+AFFFD, U+B0000–U+BFFFD, or U+C0000–U+CFFFD<br><em>标识符头(Head)</em> → U+D0000–U+DFFFD or U+E0000–U+EFFFD<br><em>标识符字符</em> → 数值 0 到 9<br><em>标识符字符</em> → U+0300–U+036F, U+1DC0–U+1DFF, U+20D0–U+20FF, or U+FE20–U+FE2F<br><em>标识符字符</em> → <a href="LexicalStructure.html#identifier_head"><em>标识符头(Head)</em></a><br><em>标识符字符列表</em> → <a href="LexicalStructure.html#identifier_character"><em>标识符字符</em></a> <a href="LexicalStructure.html#identifier_characters"><em>标识符字符列表</em></a> <em>可选</em><br><em>隐式参数名</em> → <strong>$</strong> <a href="LexicalStructure.html#decimal_digits"><em>十进制数字列表</em></a> </p>
|
||||
</blockquote>
|
||||
<p><a name="keywords"></a></p>
|
||||
<h2 id="-">关键字</h2>
|
||||
<p>被保留的关键字(<em>keywords</em>)不允许用作标识符,除非被反引号转义,参见 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/doc/uid/TP40014097-CH30-XID_796" target="_blank">标识符</a>。</p>
|
||||
<ul>
|
||||
<li><p><strong>用作声明的关键字:</strong> <em>class</em>、<em>deinit</em>、<em>enum</em>、<em>extension</em>、<em>func</em>、<em>import</em>、<em>init</em>、<em>let</em>、<em>protocol</em>、<em>static</em>、<em>struct</em>、<em>subscript</em>、<em>typealias</em>、<em>var</em></p>
|
||||
</li>
|
||||
<li><p><strong>用作语句的关键字:</strong> <em>break</em>、<em>case</em>、<em>continue</em>、<em>default</em>、<em>do</em>、<em>else</em>、<em>fallthrough</em>、<em>if</em>、<em>in</em>、<em>for</em>、<em>return</em>、<em>switch</em>、<em>where</em>、<em>while</em></p>
|
||||
</li>
|
||||
<li><p><strong>用作表达和类型的关键字:</strong> <em>as</em>、<em>dynamicType</em>、<em>is</em>、<em>new</em>、<em>super</em>、<em>self</em>、<em>Self</em>、<em>Type</em>、<em>__COLUMN__</em>、<em>__FILE__</em>、<em>__FUNCTION__</em>、<em>__LINE__</em></p>
|
||||
</li>
|
||||
<li><p><strong>特定上下文中被保留的关键字:</strong> <em>associativity</em>、<em>didSet</em>、<em>get</em>、<em>infix</em>、<em>inout</em>、<em>left</em>、<em>mutating</em>、<em>none</em>、<em>nonmutating</em>、<em>operator</em>、<em>override</em>、<em>postfix</em>、<em>precedence</em>、<em>prefix</em>、<em>right</em>、<em>set</em>、<em>unowned</em>、<em>unowned(safe)</em>、<em>unowned(unsafe)</em>、<em>weak</em>、<em>willSet</em>,这些关键字在特定上下文之外可以被用于标识符。</p>
|
||||
</li>
|
||||
<li><strong>用作声明的关键字:</strong> <em>class</em>、<em>deinit</em>、<em>enum</em>、<em>extension</em>、<em>func</em>、<em>import</em>、<em>init</em>、<em>let</em>、<em>protocol</em>、<em>static</em>、<em>struct</em>、<em>subscript</em>、<em>typealias</em>、<em>var</em></li>
|
||||
<li><strong>用作语句的关键字:</strong> <em>break</em>、<em>case</em>、<em>continue</em>、<em>default</em>、<em>do</em>、<em>else</em>、<em>fallthrough</em>、<em>if</em>、<em>in</em>、<em>for</em>、<em>return</em>、<em>switch</em>、<em>where</em>、<em>while</em></li>
|
||||
<li><strong>用作表达和类型的关键字:</strong> <em>as</em>、<em>dynamicType</em>、<em>is</em>、<em>new</em>、<em>super</em>、<em>self</em>、<em>Self</em>、<em>Type</em>、<em>__COLUMN__</em>、<em>__FILE__</em>、<em>__FUNCTION__</em>、<em>__LINE__</em></li>
|
||||
<li><strong>特定上下文中被保留的关键字:</strong> <em>associativity</em>、<em>didSet</em>、<em>get</em>、<em>infix</em>、<em>inout</em>、<em>left</em>、<em>mutating</em>、<em>none</em>、<em>nonmutating</em>、<em>operator</em>、<em>override</em>、<em>postfix</em>、<em>precedence</em>、<em>prefix</em>、<em>right</em>、<em>set</em>、<em>unowned</em>、<em>unowned(safe)</em>、<em>unowned(unsafe)</em>、<em>weak</em>、<em>willSet</em>,这些关键字在特定上下文之外可以被用于标识符。</li>
|
||||
</ul>
|
||||
<p><a name="literals"></a></p>
|
||||
<h2 id="-">字面量</h2>
|
||||
@ -662,8 +633,7 @@
|
||||
"Hello, world!" // 文本型字面量
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>字面量语法</p>
|
||||
<p><em>literal</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/integer-literal" target="_blank">integer-literal</a> | <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/floating-point-literal" target="_blank">floating-point-literal</a> | <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/string-literal" target="_blank">string-literal</a></p>
|
||||
<p>字面量语法<br><em>字面量</em> → <a href="LexicalStructure.html#integer_literal"><em>整型字面量</em></a> | <a href="LexicalStructure.html#floating_point_literal"><em>浮点数字面量</em></a> | <a href="LexicalStructure.html#string_literal"><em>字符串字面量</em></a> </p>
|
||||
</blockquote>
|
||||
<h3 id="-">整型字面量</h3>
|
||||
<p>整型字面量(<em>integer literals</em>)表示未指定精度整型数的值。整型字面量默认用十进制表示,可以加前缀来指定其他的进制,二进制字面量加 <code>0b</code>,八进制字面量加 <code>0o</code>,十六进制字面量加 <code>0x</code>。</p>
|
||||
@ -675,28 +645,7 @@
|
||||
</code></pre>
|
||||
<p>除非特殊指定,整型字面量的默认类型为 Swift 标准库类型中的 <code>Int</code>。Swift 标准库还定义了其他不同长度以及是否带符号的整数类型,请参考 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_411" target="_blank">整数类型</a>。</p>
|
||||
<blockquote>
|
||||
<p>整型字面量语法</p>
|
||||
<p><em>integer-literal</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/binary-literal" target="_blank">binary-literal</a></p>
|
||||
<p><em>integer-literal</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/octal-literal" target="_blank">octal-literal</a></p>
|
||||
<p><em>integer-literal</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/decimal-literal" target="_blank">decimal-literal</a></p>
|
||||
<p><em>integer-literal</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-literal" target="_blank">hexadecimal-literal</a></p>
|
||||
<p><em>binary-literal</em> → <strong>0b</strong> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/binary-digit" target="_blank">binary-digit</a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/binary-literal-characters" target="_blank">binary-literal-characters</a> <em>opt</em></p>
|
||||
<p><em>binary-digit</em> → 数字 0 或 1</p>
|
||||
<p><em>binary-literal-character</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/binary-digit" target="_blank">binary-digit</a> | _</p>
|
||||
<p><em>binary-literal-characters</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/binary-literal-character" target="_blank">binary-literal-character</a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/binary-literal-characters" target="_blank">binary-literal-characters</a> <em>opt</em></p>
|
||||
<p><em>octal-literal</em> → <strong>0o</strong> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/octal-digit" target="_blank">octal-digit</a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/octal-literal-characters" target="_blank">octal-literal-characters</a> <em>opt</em></p>
|
||||
<p><em>octal-digit</em> → 数字 0 至 7</p>
|
||||
<p><em>octal-literal-character</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/octal-digit" target="_blank">octal-digit</a> | _</p>
|
||||
<p><em>octal-literal-characters</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/octal-literal-character" target="_blank">octal-literal-character</a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/octal-literal-characters" target="_blank">octal-literal-characters</a> <em>opt</em></p>
|
||||
<p><em>decimal-literal</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/decimal-digit" target="_blank">decimal-digit</a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/decimal-literal-characters" target="_blank">decimal-literal-characters</a> <em>opt</em></p>
|
||||
<p><em>decimal-digit</em> → 数字 0 至 9</p>
|
||||
<p><em>decimal-digits</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/decimal-digit" target="_blank">decimal-digit</a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/decimal-digits" target="_blank">decimal-digits</a> <em>opt</em></p>
|
||||
<p><em>decimal-literal-character</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/decimal-digit" target="_blank">decimal-digit</a> | _</p>
|
||||
<p><em>decimal-literal-characters</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/decimal-literal-character" target="_blank">decimal-literal-character</a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/decimal-literal-characters" target="_blank">decimal-literal-characters</a> <em>opt</em></p>
|
||||
<p><em>hexadecimal-literal</em> → <strong>0x</strong> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-digit" target="_blank">hexadecimal-digit</a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-literal-characters" target="_blank">hexadecimal-literal-characters</a> <em>opt</em></p>
|
||||
<p><em>hexadecimal-digit</em> → 数字 0 到 9, a 到 f, 或 A 到 F</p>
|
||||
<p><em>hexadecimal-literal-character</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-digit" target="_blank">hexadecimal-digit</a> | _</p>
|
||||
<p><em>hexadecimal-literal-characters</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-literal-character" target="_blank">hexadecimal-literal-character</a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-literal-characters" target="_blank">hexadecimal-literal-characters</a> <em>opt</em></p>
|
||||
<p>整型字面量语法<br><em>整型字面量</em> → <a href="LexicalStructure.html#binary_literal"><em>二进制字面量</em></a><br><em>整型字面量</em> → <a href="LexicalStructure.html#octal_literal"><em>八进制字面量</em></a><br><em>整型字面量</em> → <a href="LexicalStructure.html#decimal_literal"><em>十进制字面量</em></a><br><em>整型字面量</em> → <a href="LexicalStructure.html#hexadecimal_literal"><em>十六进制字面量</em></a><br><em>二进制字面量</em> → <strong>0b</strong> <a href="LexicalStructure.html#binary_digit"><em>二进制数字</em></a> <a href="LexicalStructure.html#binary_literal_characters"><em>二进制字面量字符列表</em></a> <em>可选</em><br><em>二进制数字</em> → 数值 0 到 1<br><em>二进制字面量字符</em> → <a href="LexicalStructure.html#binary_digit"><em>二进制数字</em></a> | <strong>_</strong><br><em>二进制字面量字符列表</em> → <a href="LexicalStructure.html#binary_literal_character"><em>二进制字面量字符</em></a> <a href="LexicalStructure.html#binary_literal_characters"><em>二进制字面量字符列表</em></a> <em>可选</em><br><em>八进制字面量</em> → <strong>0o</strong> <a href="LexicalStructure.html#octal_digit"><em>八进字数字</em></a> <a href="LexicalStructure.html#octal_literal_characters"><em>八进制字符列表</em></a> <em>可选</em><br><em>八进字数字</em> → 数值 0 到 7<br><em>八进制字符</em> → <a href="LexicalStructure.html#octal_digit"><em>八进字数字</em></a> | <strong>_</strong><br><em>八进制字符列表</em> → <a href="LexicalStructure.html#octal_literal_character"><em>八进制字符</em></a> <a href="LexicalStructure.html#octal_literal_characters"><em>八进制字符列表</em></a> <em>可选</em><br><em>十进制字面量</em> → <a href="LexicalStructure.html#decimal_digit"><em>十进制数字</em></a> <a href="LexicalStructure.html#decimal_literal_characters"><em>十进制字符列表</em></a> <em>可选</em><br><em>十进制数字</em> → 数值 0 到 9<br><em>十进制数字列表</em> → <a href="LexicalStructure.html#decimal_digit"><em>十进制数字</em></a> <a href="LexicalStructure.html#decimal_digits"><em>十进制数字列表</em></a> <em>可选</em><br><em>十进制字符</em> → <a href="LexicalStructure.html#decimal_digit"><em>十进制数字</em></a> | <strong>_</strong><br><em>十进制字符列表</em> → <a href="LexicalStructure.html#decimal_literal_character"><em>十进制字符</em></a> <a href="LexicalStructure.html#decimal_literal_characters"><em>十进制字符列表</em></a> <em>可选</em><br><em>十六进制字面量</em> → <strong>0x</strong> <a href="LexicalStructure.html#hexadecimal_digit"><em>十六进制数字</em></a> <a href="LexicalStructure.html#hexadecimal_literal_characters"><em>十六进制字面量字符列表</em></a> <em>可选</em><br><em>十六进制数字</em> → 数值 0 到 9, a through f, or A through F<br><em>十六进制字符</em> → <a href="LexicalStructure.html#hexadecimal_digit"><em>十六进制数字</em></a> | <strong>_</strong><br><em>十六进制字面量字符列表</em> → <a href="LexicalStructure.html#hexadecimal_literal_character"><em>十六进制字符</em></a> <a href="LexicalStructure.html#hexadecimal_literal_characters"><em>十六进制字面量字符列表</em></a> <em>可选</em> </p>
|
||||
</blockquote>
|
||||
<h3 id="-">浮点型字面量</h3>
|
||||
<p>浮点型字面量(<em>floating-point literals</em>)表示未指定精度浮点数的值。</p>
|
||||
@ -710,21 +659,13 @@
|
||||
</code></pre>
|
||||
<p>除非特殊指定,浮点型字面量的默认类型为 Swift 标准库类型中的 <code>Double</code>,表示64位浮点数。Swift 标准库也定义 <code>Float</code> 类型,表示32位浮点数。</p>
|
||||
<blockquote>
|
||||
<p>浮点型字面量语法</p>
|
||||
<p><em>floating-point-literal</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/decimal-literal" target="_blank">decimal-literal</a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/decimal-fraction" target="_blank">decimal-fraction</a> <em>opt</em> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/decimal-exponent" target="_blank">decimal-exponent</a> <em>opt</em></p>
|
||||
<p><em>floating-point-literal</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-literal" target="_blank">hexadecimal-literal</a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-fraction" target="_blank">hexadecimal-fraction</a> <em>opt</em> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-exponent" target="_blank">hexadecimal-exponent</a></p>
|
||||
<p><em>decimal-fraction</em> → . <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/decimal-literal" target="_blank">decimal-literal</a></p>
|
||||
<p><em>decimal-exponent</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/floating-point-e" target="_blank">floating-point-e</a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/sign" target="_blank">sign</a> <em>opt</em> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/decimal-literal" target="_blank">decimal-literal</a></p>
|
||||
<p><em>hexadecimal-fraction</em> → . <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-literal" target="_blank">hexadecimal-literal</a> <em>opt</em></p>
|
||||
<p><em>hexadecimal-exponent</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/floating-point-p" target="_blank">floating-point-p</a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/sign" target="_blank">sign</a> <em>opt</em> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-literal" target="_blank">hexadecimal-literal</a></p>
|
||||
<p><em>floating-point-e</em> → <strong>e</strong> | <strong>E</strong></p>
|
||||
<p><em>floating-point-p</em> → <strong>p</strong> | <strong>P</strong></p>
|
||||
<p><em>sign</em> → <strong>+</strong> | <strong>-</strong></p>
|
||||
<p>浮点型字面量语法<br><em>浮点数字面量</em> → <a href="LexicalStructure.html#decimal_literal"><em>十进制字面量</em></a> <a href="LexicalStructure.html#decimal_fraction"><em>十进制分数</em></a> <em>可选</em> <a href="LexicalStructure.html#decimal_exponent"><em>十进制指数</em></a> <em>可选</em><br><em>浮点数字面量</em> → <a href="LexicalStructure.html#hexadecimal_literal"><em>十六进制字面量</em></a> <a href="LexicalStructure.html#hexadecimal_fraction"><em>十六进制分数</em></a> <em>可选</em> <a href="LexicalStructure.html#hexadecimal_exponent"><em>十六进制指数</em></a><br><em>十进制分数</em> → <strong>.</strong> <a href="LexicalStructure.html#decimal_literal"><em>十进制字面量</em></a><br><em>十进制指数</em> → <a href="LexicalStructure.html#floating_point_e"><em>浮点数e</em></a> <a href="LexicalStructure.html#sign"><em>正负号</em></a> <em>可选</em> <a href="LexicalStructure.html#decimal_literal"><em>十进制字面量</em></a><br><em>十六进制分数</em> → <strong>.</strong> <a href="LexicalStructure.html#hexadecimal_literal"><em>十六进制字面量</em></a> <em>可选</em><br><em>十六进制指数</em> → <a href="LexicalStructure.html#floating_point_p"><em>浮点数p</em></a> <a href="LexicalStructure.html#sign"><em>正负号</em></a> <em>可选</em> <a href="LexicalStructure.html#hexadecimal_literal"><em>十六进制字面量</em></a><br><em>浮点数e</em> → <strong>e</strong> | <strong>E</strong><br><em>浮点数p</em> → <strong>p</strong> | <strong>P</strong><br><em>正负号</em> → <strong>+</strong> | <strong>-</strong> </p>
|
||||
</blockquote>
|
||||
<h3 id="-">文本型字面量</h3>
|
||||
<p>文本型字面量(<em>string literal</em>)由双引号中的字符串组成,形式如下:</p>
|
||||
<pre><code>"characters"
|
||||
</code></pre><p>文本型字面量中不能包含未转义的双引号 <code>"</code>、未转义的反斜线<code>\</code>、回车符(<em>carriage return</em>)或换行符(<em>line feed</em>)。</p>
|
||||
<pre><code class="lang-swift">"characters"
|
||||
</code></pre>
|
||||
<p>文本型字面量中不能包含未转义的双引号 <code>"</code>、未转义的反斜线<code>\</code>、回车符(<em>carriage return</em>)或换行符(<em>line feed</em>)。</p>
|
||||
<p>可以在文本型字面量中使用的转义特殊符号如下:</p>
|
||||
<ul>
|
||||
<li>空字符(Null Character)<code>\0</code></li>
|
||||
@ -751,16 +692,7 @@ var x = 3; "1 2 \(x)"
|
||||
</code></pre>
|
||||
<p>文本型字面量的默认类型为 <code>String</code>。组成字符串的字符类型为 <code>Character</code>。更多有关 <code>String</code> 和 <code>Character</code> 的信息请参照 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html#//apple_ref/doc/uid/TP40014097-CH7-XID_368" target="_blank">字符串和字符</a>。</p>
|
||||
<blockquote>
|
||||
<p>文本型字面量语法</p>
|
||||
<p><em>string-literal</em> → <strong>"</strong> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/quoted-text" target="_blank">quoted-text</a> <strong>"</strong></p>
|
||||
<p><em>quoted-text</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/quoted-text-item" target="_blank">quoted-text-item</a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/quoted-text" target="_blank">quoted-text</a> <em>opt</em></p>
|
||||
<p><em>quoted-text-item</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/escaped-character" target="_blank">escaped-character</a></p>
|
||||
<p><em>quoted-text-item</em> → <strong>(</strong> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression" target="_blank">expression</a> <strong>)</strong></p>
|
||||
<p><em>quoted-text-item</em> → 除 <code>"</code>、<code>\</code>、<code>U+000A</code> 或 <code>U+000D</code> 以外的任何 Unicode 扩展字符集</p>
|
||||
<p><em>escaped-character</em> → <strong>\0</strong> | <strong>\</strong> | <strong>\t</strong> | <strong>\n</strong> | <strong>\r</strong> | <strong>\"</strong> | <strong>\'</strong></p>
|
||||
<p><em>escaped-character</em> → <strong>\x</strong> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-digit" target="_blank">hexadecimal-digit</a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-digit" target="_blank">hexadecimal-digit</a></p>
|
||||
<p><em>escaped-character</em> → <strong>\u</strong> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-digit" target="_blank">hexadecimal-digit</a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-digit" target="_blank">hexadecimal-digit</a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-digit" target="_blank">hexadecimal-digit</a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-digit" target="_blank">hexadecimal-digit</a></p>
|
||||
<p><em>escaped-character</em> → <strong>\U</strong> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-digit" target="_blank">hexadecimal-digit</a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-digit" target="_blank">hexadecimal-digit</a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-digit" target="_blank">hexadecimal-digit</a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-digit" target="_blank">hexadecimal-digit</a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-digit" target="_blank">hexadecimal-digit</a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-digit" target="_blank">hexadecimal-digit</a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-digit" target="_blank">hexadecimal-digit</a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-digit" target="_blank">hexadecimal-digit</a></p>
|
||||
<p>字符型字面量语法<br><em>字符串字面量</em> → <strong>"</strong> <a href="LexicalStructure.html#quoted_text"><em>引用文本</em></a> <strong>"</strong><br><em>引用文本</em> → <a href="LexicalStructure.html#quoted_text_item"><em>引用文本条目</em></a> <a href="LexicalStructure.html#quoted_text"><em>引用文本</em></a> <em>可选</em><br><em>引用文本条目</em> → <a href="LexicalStructure.html#escaped_character"><em>转义字符</em></a><br><em>引用文本条目</em> → <strong>(</strong> <a href="..\chapter3\04_Expressions.html#expression"><em>表达式</em></a> <strong>)</strong><br><em>引用文本条目</em> → 除了", \, U+000A, or U+000D的所有Unicode的字符<br><em>转义字符</em> → <strong>\0</strong> | <strong>\</strong> | <strong>\t</strong> | <strong>\n</strong> | <strong>\r</strong> | <strong>\"</strong> | <strong>\'</strong><br><em>转义字符</em> → <strong>\x</strong> <a href="LexicalStructure.html#hexadecimal_digit"><em>十六进制数字</em></a> <a href="LexicalStructure.html#hexadecimal_digit"><em>十六进制数字</em></a><br><em>转义字符</em> → <strong>\u</strong> <a href="LexicalStructure.html#hexadecimal_digit"><em>十六进制数字</em></a> <a href="LexicalStructure.html#hexadecimal_digit"><em>十六进制数字</em></a> <a href="LexicalStructure.html#hexadecimal_digit"><em>十六进制数字</em></a> <a href="LexicalStructure.html#hexadecimal_digit"><em>十六进制数字</em></a><br><em>转义字符</em> → <strong>\U</strong> <a href="LexicalStructure.html#hexadecimal_digit"><em>十六进制数字</em></a> <a href="LexicalStructure.html#hexadecimal_digit"><em>十六进制数字</em></a> <a href="LexicalStructure.html#hexadecimal_digit"><em>十六进制数字</em></a> <a href="LexicalStructure.html#hexadecimal_digit"><em>十六进制数字</em></a> <a href="LexicalStructure.html#hexadecimal_digit"><em>十六进制数字</em></a> <a href="LexicalStructure.html#hexadecimal_digit"><em>十六进制数字</em></a> <a href="LexicalStructure.html#hexadecimal_digit"><em>十六进制数字</em></a> <a href="LexicalStructure.html#hexadecimal_digit"><em>十六进制数字</em></a> </p>
|
||||
</blockquote>
|
||||
<p><a name="operators"></a></p>
|
||||
<h2 id="-">运算符</h2>
|
||||
@ -769,26 +701,17 @@ var x = 3; "1 2 \(x)"
|
||||
<code>/</code>、<code>=</code>、<code>-</code>、<code>+</code>、<code>!</code>、<code>*</code>、<code>%</code>、<code><</code>、<code>></code>、<code>&</code>、<code>|</code>、<code>^</code>、<code>~</code>、<code>.</code>。也就是说,标记 <code>=</code>, <code>-></code>、<code>//</code>、<code>/*</code>、<code>*/</code>、<code>.</code> 以及一元前缀运算符 <code>&</code> 属于保留字,这些标记不能被重写或用于自定义运算符。</p>
|
||||
<p>运算符两侧的空白被用来区分该运算符是否为前缀运算符(<em>prefix operator</em>)、后缀运算符(<em>postfix operator</em>)或二元运算符(<em>binary operator</em>)。规则总结如下:</p>
|
||||
<ul>
|
||||
<li><p>如果运算符两侧都有空白或两侧都无空白,将被看作二元运算符。例如:<code>a+b</code> 和 <code>a + b</code> 中的运算符 <code>+</code> 被看作二元运算符。</p>
|
||||
</li>
|
||||
<li><p>如果运算符只有左侧空白,将被看作前缀一元运算符。例如 <code>a ++b</code> 中的 <code>++</code> 被看作前缀一元运算符。</p>
|
||||
</li>
|
||||
<li><p>如果运算符只有右侧空白,将被看作后缀一元运算符。例如 <code>a++ b</code> 中的 <code>++</code> 被看作后缀一元运算符。</p>
|
||||
</li>
|
||||
<li><p>如果运算符左侧没有空白并紧跟 <code>.</code>,将被看作后缀一元运算符。例如 <code>a++.b</code> 中的 <code>++</code> 被看作后缀一元运算符(同理, <code>a++ . b</code> 中的 <code>++</code> 是后缀一元运算符而 <code>a ++ .b</code> 中的 <code>++</code> 不是).</p>
|
||||
</li>
|
||||
<li>如果运算符两侧都有空白或两侧都无空白,将被看作二元运算符。例如:<code>a+b</code> 和 <code>a + b</code> 中的运算符 <code>+</code> 被看作二元运算符。</li>
|
||||
<li>如果运算符只有左侧空白,将被看作前缀一元运算符。例如 <code>a ++b</code> 中的 <code>++</code> 被看作前缀一元运算符。</li>
|
||||
<li>如果运算符只有右侧空白,将被看作后缀一元运算符。例如 <code>a++ b</code> 中的 <code>++</code> 被看作后缀一元运算符。</li>
|
||||
<li>如果运算符左侧没有空白并紧跟 <code>.</code>,将被看作后缀一元运算符。例如 <code>a++.b</code> 中的 <code>++</code> 被看作后缀一元运算符(同理, <code>a++ . b</code> 中的 <code>++</code> 是后缀一元运算符而 <code>a ++ .b</code> 中的 <code>++</code> 不是).</li>
|
||||
</ul>
|
||||
<p>鉴于这些规则,运算符前的字符 <code>(</code>、<code>[</code> 和 <code>{</code> ;运算符后的字符 <code>)</code>、<code>]</code> 和 <code>}</code> 以及字符 <code>,</code>、<code>;</code> 和 <code>:</code> 都将用于空白检测。</p>
|
||||
<p>以上规则需注意一点,如果运算符 <code>!</code> 或 <code>?</code> 左侧没有空白,则不管右侧是否有空白都将被看作后缀运算符。如果将 <code>?</code> 用作可选类型(<em>optional type</em>)修饰,左侧必须无空白。如果用于条件运算符 <code>? :</code>,必须两侧都有空白。</p>
|
||||
<p>在特定构成中 ,以 <code><</code> 或 <code>></code> 开头的运算符会被分离成两个或多个标记,剩余部分以同样的方式会被再次分离。因此,在 <code>Dictionary<String, Array<Int>></code> 中没有必要添加空白来消除闭合字符 <code>></code> 的歧义。在这个例子中, 闭合字符 <code>></code> 被看作单字符标记,而不会被误解为移位运算符 <code>>></code>。</p>
|
||||
<p>要学习如何自定义新的运算符,请参考 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-XID_48" target="_blank">自定义操作符</a> 和 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-XID_644" target="_blank">运算符声明</a>。学习如何重写现有运算符,请参考 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-XID_43" target="_blank">运算符方法</a>。</p>
|
||||
<blockquote>
|
||||
<p>运算符语法</p>
|
||||
<p><em>operator</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/operator-character" target="_blank">operator-character</a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/operator" target="_blank">operator</a> <em>opt</em></p>
|
||||
<p><em>operator-character</em> → <strong>/</strong> | <strong>=</strong> | <strong>-</strong> | <strong>+</strong> | <strong>!</strong> | <strong>*</strong> | <strong>%</strong> | <strong><</strong> | <strong>></strong> | <strong>&</strong> | <strong>|</strong> | <strong>^</strong> | <strong>~</strong> | <strong>.</strong></p>
|
||||
<p><em>binary-operator</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/operator" target="_blank">operator</a></p>
|
||||
<p><em>prefix-operator</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/operator" target="_blank">operator</a></p>
|
||||
<p><em>postfix-operator</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/operator" target="_blank">operator</a></p>
|
||||
<p>运算符语法语法<br><em>运算符</em> → <a href="LexicalStructure.html#operator_character"><em>运算符字符</em></a> <a href="LexicalStructure.html#operator"><em>运算符</em></a> <em>可选</em><br><em>运算符字符</em> → <strong>/</strong> | <strong>=</strong> | <strong>-</strong> | <strong>+</strong> | <strong>!</strong> | <strong>*</strong> | <strong>%</strong> | <strong><</strong> | <strong>></strong> | <strong>&</strong> | <strong>|</strong> | <strong>^</strong> | <strong>~</strong> | <strong>.</strong><br><em>二元运算符</em> → <a href="LexicalStructure.html#operator"><em>运算符</em></a><br><em>前置运算符</em> → <a href="LexicalStructure.html#operator"><em>运算符</em></a><br><em>后置运算符</em> → <a href="LexicalStructure.html#operator"><em>运算符</em></a> </p>
|
||||
</blockquote>
|
||||
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="3.3" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="3.3" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,11 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_85">
|
||||
<section class="normal" id="section-gitbook_59">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:lyuka</p>
|
||||
<p>校对:numbbbbb</p>
|
||||
<p>翻译:<a href="https://github.com/lyuka" target="_blank">lyuka</a><br>校对:<a href="https://github.com/numbbbbb" target="_blank">numbbbbb</a>, <a href="https://github.com/stanzhai" target="_blank">stanzhai</a></p>
|
||||
</blockquote>
|
||||
<h1 id="-types-">类型(Types)</h1>
|
||||
<hr>
|
||||
@ -614,36 +613,32 @@
|
||||
<p><em>复合型类型</em>是没有名字的类型,它由 Swift 本身定义。Swift 存在两种复合型类型:函数类型和元组类型。一个复合型类型可以包含命名型类型和其它复合型类型。例如,元组类型<code>(Int, (Int, Int))</code>包含两个元素:第一个是命名型类型<code>Int</code>,第二个是另一个复合型类型<code>(Int, Int)</code>.</p>
|
||||
<p>本节讨论 Swift 语言本身定义的类型,并描述 Swift 中的类型推断行为。</p>
|
||||
<blockquote>
|
||||
<p>类型的语法:
|
||||
<em>type</em> → <em>array-type</em> | <em>function-type</em> | <em>type-identifier</em> | <em>tuple-type</em> | <em>optional-type</em> | <em>implicitly-unwrapped-optional-type</em> | protocol-composition-type | metatype-type</p>
|
||||
<p>类型语法<br><em>类型</em> → <a href="..\chapter3\03_Types.html#array_type"><em>数组类型</em></a> | <a href="..\chapter3\03_Types.html#function_type"><em>函数类型</em></a> | <a href="..\chapter3\03_Types.html#type_identifier"><em>类型标识</em></a> | <a href="..\chapter3\03_Types.html#tuple_type"><em>元组类型</em></a> | <a href="..\chapter3\03_Types.html#optional_type"><em>可选类型</em></a> | <a href="..\chapter3\03_Types.html#implicitly_unwrapped_optional_type"><em>隐式解析可选类型</em></a> | <a href="..\chapter3\03_Types.html#protocol_composition_type"><em>协议合成类型</em></a> | <a href="..\chapter3\03_Types.html#metatype_type"><em>元型类型</em></a> </p>
|
||||
</blockquote>
|
||||
<p><a name="type_annotation"></a></p>
|
||||
<h2 id="-">类型注解</h2>
|
||||
<p>类型注解显式地指定一个变量或表达式的值。类型注解始于冒号<code>:</code>终于类型,比如下面两个例子:</p>
|
||||
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> someTuple:(Double, Double) = (<span class="hljs-number">3.14159</span>, <span class="hljs-number">2.71828</span>)
|
||||
func someFunction(a: Int){ <span class="hljs-comment">/* ... */</span> }
|
||||
<pre><code class="lang-swift">let someTuple: (Double, Double) = (3.14159, 2.71828)
|
||||
func someFunction(a: Int){ /* ... */ }
|
||||
</code></pre>
|
||||
<p>在第一个例子中,表达式<code>someTuple</code>的类型被指定为<code>(Double, Double)</code>。在第二个例子中,函数<code>someFunction</code>的参数<code>a</code>的类型被指定为<code>Int</code>。</p>
|
||||
<p>类型注解可以在类型之前包含一个类型特性(type attributes)的可选列表。</p>
|
||||
<blockquote>
|
||||
<p>类型注解的语法:
|
||||
<em>type-annotation</em> → :<em>attributes</em>[opt] <em>type</em></p>
|
||||
<p>类型注解语法<br><em>类型注解</em> → <strong>:</strong> <a href="..\chapter3\06_Attributes.html#attributes"><em>特性(Attributes)列表</em></a> <em>可选</em> <a href="..\chapter3\03_Types.html#type"><em>类型</em></a> </p>
|
||||
</blockquote>
|
||||
<p><a name="type_identifier"></a></p>
|
||||
<h2 id="-">类型标识符</h2>
|
||||
<p>类型标识符引用命名型类型或者是命名型/复合型类型的别名。</p>
|
||||
<p>大多数情况下,类型标识符引用的是同名的命名型类型。例如类型标识符<code>Int</code>引用命名型类型<code>Int</code>,同样,类型标识符<code>Dictionary<String, Int></code>引用命名型类型<code>Dictionary<String, Int></code>。</p>
|
||||
<p>在两种情况下类型标识符引用的不是同名的类型。情况一,类型标识符引用的是命名型/复合型类型的类型别名。比如,在下面的例子中,类型标识符使用<code>Point</code>来引用元组<code>(Int, Int)</code>:</p>
|
||||
<pre><code class="lang-javascript">typealias Point = (Int, Int)
|
||||
<span class="hljs-keyword">let</span> origin: Point = (<span class="hljs-number">0</span>, <span class="hljs-number">0</span>)
|
||||
<pre><code class="lang-swift">typealias Point = (Int, Int)
|
||||
let origin: Point = (0, 0)
|
||||
</code></pre>
|
||||
<p>情况二,类型标识符使用dot(<code>.</code>)语法来表示在其它模块(modules)或其它类型嵌套内声明的命名型类型。例如,下面例子中的类型标识符引用在<code>ExampleModule</code>模块中声明的命名型类型<code>MyType</code>:</p>
|
||||
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> someValue: ExampleModule.MyType
|
||||
<pre><code class="lang-swift">var someValue: ExampleModule.MyType
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>类型标识符的语法:
|
||||
<em>type-identifier</em> → <em>type-name generic-argument-clause</em>[opt] | <em>type-name generic-argument-clause</em>[opt].<em>type-identifier</em>
|
||||
<em>type-name</em> → <em>identifier</em></p>
|
||||
<p>类型标识语法<br><em>类型标识</em> → <a href="..\chapter3\03_Types.html#type_name"><em>类型名称</em></a> <a href="GenericParametersAndArguments.html#generic_argument_clause"><em>泛型参数子句</em></a> <em>可选</em> | <a href="..\chapter3\03_Types.html#type_name"><em>类型名称</em></a> <a href="GenericParametersAndArguments.html#generic_argument_clause"><em>泛型参数子句</em></a> <em>可选</em> <strong>.</strong> <a href="..\chapter3\03_Types.html#type_identifier"><em>类型标识</em></a><br><em>类名</em> → <a href="LexicalStructure.html#identifier"><em>标识符</em></a> </p>
|
||||
</blockquote>
|
||||
<p><a name="tuple_type"></a></p>
|
||||
<h2 id="-">元组类型</h2>
|
||||
@ -651,12 +646,7 @@ func someFunction(a: Int){ <span class="hljs-comment">/* ... */</span> }
|
||||
<p>你可以使用元组类型作为一个函数的返回类型,这样就可以使函数返回多个值。你也可以命名元组类型中的元素,然后用这些名字来引用每个元素的值。元素的名字由一个标识符和<code>:</code>组成。“函数和多返回值”章节里有一个展示上述特性的例子。</p>
|
||||
<p><code>void</code>是空元组类型<code>()</code>的别名。如果括号内只有一个元素,那么该类型就是括号内元素的类型。比如,<code>(Int)</code>的类型是<code>Int</code>而不是<code>(Int)</code>。所以,只有当元组类型包含两个元素以上时才可以标记元组元素。</p>
|
||||
<blockquote>
|
||||
<p>元组类型语法:
|
||||
<em>tuple</em> → (<em>tuple-type-body</em>[opt])
|
||||
<em>tuple-type-body</em> → <em>tuple-type-element-list</em> ...[opt]
|
||||
<em>tuple-type-element-list</em> → <em>tuple-type-element</em> | <em>tuple-type-element</em>, <em>tuple-type-element-list</em>
|
||||
<em>tuple-type-element</em> → <em>attributes</em>[opt] <strong>inout</strong> [opt] <em>type</em> | <strong>inout</strong> [opt] <em>element-name type-annotation</em>
|
||||
<em>element-name</em> → <em>identifier</em></p>
|
||||
<p>元组类型语法<br><em>元组类型</em> → <strong>(</strong> <a href="..\chapter3\03_Types.html#tuple_type_body"><em>元组类型主体</em></a> <em>可选</em> <strong>)</strong><br><em>元组类型主体</em> → <a href="..\chapter3\03_Types.html#tuple_type_element_list"><em>元组类型的元素列表</em></a> <strong>...</strong> <em>可选</em><br><em>元组类型的元素列表</em> → <a href="..\chapter3\03_Types.html#tuple_type_element"><em>元组类型的元素</em></a> | <a href="..\chapter3\03_Types.html#tuple_type_element"><em>元组类型的元素</em></a> <strong>,</strong> <a href="..\chapter3\03_Types.html#tuple_type_element_list"><em>元组类型的元素列表</em></a><br><em>元组类型的元素</em> → <a href="..\chapter3\06_Attributes.html#attributes"><em>特性(Attributes)列表</em></a> <em>可选</em> <strong>inout</strong> <em>可选</em> <a href="..\chapter3\03_Types.html#type"><em>类型</em></a> | <strong>inout</strong> <em>可选</em> <a href="..\chapter3\03_Types.html#element_name"><em>元素名</em></a> <a href="..\chapter3\03_Types.html#type_annotation"><em>类型注解</em></a><br><em>元素名</em> → <a href="LexicalStructure.html#identifier"><em>标识符</em></a> </p>
|
||||
</blockquote>
|
||||
<p><a name="function_type"></a></p>
|
||||
<h2 id="-">函数类型</h2>
|
||||
@ -666,81 +656,78 @@ func someFunction(a: Int){ <span class="hljs-comment">/* ... */</span> }
|
||||
</ul>
|
||||
<p>由于 <em>参数类型</em> 和 <em>返回值类型</em> 可以是元组类型,所以函数类型可以让函数与方法支持多参数与多返回值。</p>
|
||||
<p>你可以对函数类型应用带有参数类型<code>()</code>并返回表达式类型的<code>auto_closure</code>属性(见类型属性章节)。一个自动闭包函数捕获特定表达式上的隐式闭包而非表达式本身。下面的例子使用<code>auto_closure</code>属性来定义一个很简单的assert函数:</p>
|
||||
<pre><code class="lang-javascript">func simpleAssert(condition: @auto_closure () -> Bool, message: <span class="hljs-built_in">String</span>){
|
||||
<span class="hljs-keyword">if</span> !condition(){
|
||||
<pre><code class="lang-swift">func simpleAssert(condition: @auto_closure () -> Bool, message: String){
|
||||
if !condition(){
|
||||
println(message)
|
||||
}
|
||||
}
|
||||
<span class="hljs-keyword">let</span> testNumber = <span class="hljs-number">5</span>
|
||||
simpleAssert(testNumber % <span class="hljs-number">2</span> == <span class="hljs-number">0</span>, <span class="hljs-string">"testNumber isn't an even number."</span>)
|
||||
<span class="hljs-comment">// prints "testNumber isn't an even number."</span>
|
||||
let testNumber = 5
|
||||
simpleAssert(testNumber % 2 == 0, "testNumber isn't an even number.")
|
||||
// prints "testNumber isn't an even number."
|
||||
</code></pre>
|
||||
<p>函数类型可以拥有一个可变长参数作为参数类型中的最后一个参数。从语法角度上讲,可变长参数由一个基础类型名字和<code>...</code>组成,如<code>Int...</code>。可变长参数被认为是一个包含了基础类型元素的数组。即<code>Int...</code>就是<code>Int[]</code>。关于使用可变长参数的例子,见章节“可变长参数”。</p>
|
||||
<p>为了指定一个<code>in-out</code>参数,可以在参数类型前加<code>inout</code>前缀。但是你不可以对可变长参数或返回值类型使用<code>inout</code>。关于In-Out参数的讨论见章节In-Out参数部分。</p>
|
||||
<p>柯里化函数(curried function)的类型相当于一个嵌套函数类型。例如,下面的柯里化函数<code>addTwoNumber()()</code>的类型是<code>Int -> Int -> Int</code>:</p>
|
||||
<pre><code class="lang-javascript">func addTwoNumbers(a: Int)(b: Int) -> Int{
|
||||
<span class="hljs-keyword">return</span> a + b
|
||||
<pre><code class="lang-swift">func addTwoNumbers(a: Int)(b: Int) -> Int{
|
||||
return a + b
|
||||
}
|
||||
addTwoNumbers(<span class="hljs-number">4</span>)(<span class="hljs-number">5</span>) <span class="hljs-comment">// returns 9</span>
|
||||
addTwoNumbers(4)(5) // returns 9
|
||||
</code></pre>
|
||||
<p>柯里化函数的函数类型从右向左组成一组。例如,函数类型<code>Int -> Int -> Int</code>可以被理解为<code>Int -> (Int -> Int)</code>——也就是说,一个函数传入一个<code>Int</code>然后输出作为另一个函数的输入,然后又返回一个<code>Int</code>。例如,你可以使用如下嵌套函数来重写柯里化函数<code>addTwoNumbers()()</code>:</p>
|
||||
<pre><code class="lang-javascript">func addTwoNumbers(a: Int) -> (Int -> Int){
|
||||
<pre><code class="lang-swift">func addTwoNumbers(a: Int) -> (Int -> Int){
|
||||
func addTheSecondNumber(b: Int) -> Int{
|
||||
<span class="hljs-keyword">return</span> a + b
|
||||
return a + b
|
||||
}
|
||||
<span class="hljs-keyword">return</span> addTheSecondNumber
|
||||
return addTheSecondNumber
|
||||
}
|
||||
addTwoNumbers(<span class="hljs-number">4</span>)(<span class="hljs-number">5</span>) <span class="hljs-comment">// Returns 9</span>
|
||||
addTwoNumbers(4)(5) // Returns 9
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>函数类型的语法:
|
||||
<em>function-type</em> → <em>type</em> <strong>-></strong> <em>type</em></p>
|
||||
<p>函数类型语法<br><em>函数类型</em> → <a href="..\chapter3\03_Types.html#type"><em>类型</em></a> <strong>-></strong> <a href="..\chapter3\03_Types.html#type"><em>类型</em></a> </p>
|
||||
</blockquote>
|
||||
<p><a name="array_type"></a></p>
|
||||
<h2 id="-">数组类型</h2>
|
||||
<p>Swift语言使用类型名紧接中括号<code>[]</code>来简化标准库中定义的命名型类型<code>Array<T></code>。换句话说,下面两个声明是等价的:</p>
|
||||
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> someArray: <span class="hljs-built_in">String</span>[] = [<span class="hljs-string">"Alex"</span>, <span class="hljs-string">"Brian"</span>, <span class="hljs-string">"Dave"</span>]
|
||||
<span class="hljs-keyword">let</span> someArray: <span class="hljs-built_in">Array</span><<span class="hljs-built_in">String</span>> = [<span class="hljs-string">"Alex"</span>, <span class="hljs-string">"Brian"</span>, <span class="hljs-string">"Dave"</span>]
|
||||
<pre><code class="lang-swift">let someArray: String[] = ["Alex", "Brian", "Dave"]
|
||||
let someArray: Array<String> = ["Alex", "Brian", "Dave"]
|
||||
</code></pre>
|
||||
<p>上面两种情况下,常量<code>someArray</code>都被声明为字符串数组。数组的元素也可以通过<code>[]</code>获取访问:<code>someArray[0]</code>是指第0个元素<code>“Alex”</code>。</p>
|
||||
<p>上面的例子同时显示,你可以使用<code>[]</code>作为初始值构造数组,空的<code>[]</code>则用来来构造指定类型的空数组。</p>
|
||||
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> emptyArray: Double[] = []
|
||||
<pre><code class="lang-swift">var emptyArray: Double[] = []
|
||||
</code></pre>
|
||||
<p>你也可以使用链接起来的多个<code>[]</code>集合来构造多维数组。例如,下例使用三个<code>[]</code>集合来构造三维整型数组:</p>
|
||||
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> array3D: Int[][][] = [[[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>], [<span class="hljs-number">3</span>, <span class="hljs-number">4</span>]], [[<span class="hljs-number">5</span>, <span class="hljs-number">6</span>], [<span class="hljs-number">7</span>, <span class="hljs-number">8</span>]]]
|
||||
<pre><code class="lang-swift">var array3D: Int[][][] = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
|
||||
</code></pre>
|
||||
<p>访问一个多维数组的元素时,最左边的下标指向最外层数组的相应位置元素。接下来往右的下标指向第一层嵌入的相应位置元素,依次类推。这就意味着,在上面的例子中,<code>array3D[0]</code>是指<code>[[1, 2], [3, 4]]</code>,<code>array3D[0][1]</code>是指<code>[3, 4]</code>,<code>array3D[0][1][1]</code>则是指值<code>4</code>。</p>
|
||||
<p>关于Swift标准库中<code>Array</code>类型的细节讨论,见章节Arrays。</p>
|
||||
<blockquote>
|
||||
<p>数组类型的语法:
|
||||
<em>array-type</em> → <em>type</em><code>[ ]</code> | <em>array-type</em><code>[ ]</code></p>
|
||||
<p>数组类型语法<br><em>数组类型</em> → <a href="..\chapter3\03_Types.html#type"><em>类型</em></a> <strong>[</strong> <strong>]</strong> | <a href="..\chapter3\03_Types.html#array_type"><em>数组类型</em></a> <strong>[</strong> <strong>]</strong> </p>
|
||||
</blockquote>
|
||||
<p><a name="optional_type"></a></p>
|
||||
<h2 id="-">可选类型</h2>
|
||||
<p>Swift定义后缀<code>?</code>来作为标准库中的定义的命名型类型<code>Optional<T></code>的简写。换句话说,下面两个声明是等价的:</p>
|
||||
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> optionalInteger: Int?
|
||||
<span class="hljs-keyword">var</span> optionalInteger: Optional<Int>
|
||||
<pre><code class="lang-swift">var optionalInteger: Int?
|
||||
var optionalInteger: Optional<Int>
|
||||
</code></pre>
|
||||
<p>在上述两种情况下,变量<code>optionalInteger</code>都被声明为可选整型类型。注意在类型和<code>?</code>之间没有空格。</p>
|
||||
<p>类型<code>Optional<T></code>是一个枚举,有两种形式,<code>None</code>和<code>Some(T)</code>,又来代表可能出现或可能不出现的值。任意类型都可以被显式的声明(或隐式的转换)为可选类型。当声明一个可选类型时,确保使用括号给<code>?</code>提供合适的作用范围。比如说,声明一个整型的可选数组,应写作<code>(Int[])?</code>,写成<code>Int[]?</code>的话则会出错。</p>
|
||||
<p>如果你在声明或定义可选变量或特性的时候没有提供初始值,它的值则会自动赋成缺省值<code>nil</code>。</p>
|
||||
<p>可选符合<code>LogicValue</code>协议,因此可以出现在布尔值环境下。此时,如果一个可选类型<code>T?</code>实例包含有类型为<code>T</code>的值(也就是说值为<code>Optional.Some(T)</code>),那么此可选类型就为<code>true</code>,否则为<code>false</code>。</p>
|
||||
<p>如果一个可选类型的实例包含一个值,那么你就可以使用后缀操作符<code>!</code>来获取该值,正如下面描述的:</p>
|
||||
<pre><code class="lang-javascript">optionalInteger = <span class="hljs-number">42</span>
|
||||
optionalInteger! <span class="hljs-comment">// 42</span>
|
||||
<pre><code class="lang-swift">optionalInteger = 42
|
||||
optionalInteger! // 42
|
||||
</code></pre>
|
||||
<p>使用<code>!</code>操作符获取值为<code>nil</code>的可选项会导致运行错误(runtime error)。</p>
|
||||
<p>你也可以使用可选链和可选绑定来选择性的执行可选表达式上的操作。如果值为<code>nil</code>,不会执行任何操作因此也就没有运行错误产生。</p>
|
||||
<p>更多细节以及更多如何使用可选类型的例子,见章节“可选”。</p>
|
||||
<blockquote>
|
||||
<p>可选类型语法:
|
||||
<em>optional-type</em> → <em>type</em>?</p>
|
||||
<p>可选类型语法<br><em>可选类型</em> → <a href="..\chapter3\03_Types.html#type"><em>类型</em></a> <strong>?</strong> </p>
|
||||
</blockquote>
|
||||
<p><a name="implicitly_unwrapped_optional_type"></a></p>
|
||||
<h2 id="-">隐式解析可选类型</h2>
|
||||
<p>Swift语言定义后缀<code>!</code>作为标准库中命名类型<code>ImplicitlyUnwrappedOptional<T></code>的简写。换句话说,下面两个声明等价:</p>
|
||||
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> implicitlyUnwrappedString: <span class="hljs-built_in">String</span>!
|
||||
<span class="hljs-keyword">var</span> implicitlyUnwrappedString: ImplicitlyUnwrappedOptional<<span class="hljs-built_in">String</span>>
|
||||
<pre><code class="lang-swift">var implicitlyUnwrappedString: String!
|
||||
var implicitlyUnwrappedString: ImplicitlyUnwrappedOptional<String>
|
||||
</code></pre>
|
||||
<p>上述两种情况下,变量<code>implicitlyUnwrappedString</code>被声明为一个隐式解析可选类型的字符串。注意类型与<code>!</code>之间没有空格。</p>
|
||||
<p>你可以在使用可选的地方同样使用隐式解析可选。比如,你可以将隐式解析可选的值赋给变量、常量和可选特性,反之亦然。</p>
|
||||
@ -749,47 +736,42 @@ optionalInteger! <span class="hljs-comment">// 42</span>
|
||||
<p>使用可选链会选择性的执行隐式解析可选表达式上的某一个操作。如果值为<code>nil</code>,就不会执行任何操作,因此也不会产生运行错误。</p>
|
||||
<p>关于隐式解析可选的更多细节,见章节“隐式解析可选”。</p>
|
||||
<blockquote>
|
||||
<p>隐式解析可选的语法:
|
||||
implicitly-unwrapped-optional-type → type!</p>
|
||||
<p>隐式解析可选类型(Implicitly Unwrapped Optional Type)语法<br><em>隐式解析可选类型</em> → <a href="..\chapter3\03_Types.html#type"><em>类型</em></a> <strong>!</strong> </p>
|
||||
</blockquote>
|
||||
<p><a name="protocol_composition_type"></a></p>
|
||||
<h2 id="-">协议合成类型</h2>
|
||||
<p>协议合成类型是一种符合每个协议的指定协议列表类型。协议合成类型可能会用在类型注解和泛型参数中。</p>
|
||||
<p>协议合成类型的形式如下:</p>
|
||||
<pre><code class="lang-javascript">protocol<Protocol <span class="hljs-number">1</span>, Procotol <span class="hljs-number">2</span>>
|
||||
<pre><code class="lang-swift">protocol<Protocol 1, Procotol 2>
|
||||
</code></pre>
|
||||
<p>协议合成类型允许你指定一个值,其类型可以适配多个协议的条件,而且不需要定义一个新的命名型协议来继承其它想要适配的各个协议。比如,协议合成类型<code>protocol<Protocol A, Protocol B, Protocol C></code>等效于一个从<code>Protocol A</code>,<code>Protocol B</code>, <code>Protocol C</code>继承而来的新协议<code>Protocol D</code>,很显然这样做有效率的多,甚至不需引入一个新名字。</p>
|
||||
<p>协议合成列表中的每项必须是协议名或协议合成类型的类型别名。如果列表为空,它就会指定一个空协议合成列表,这样每个类型都能适配。</p>
|
||||
<blockquote>
|
||||
<p>协议合成类型的语法:
|
||||
<em>protocol-composition-type</em> → <strong>protocol</strong> <<em>protocol-identifier-list[opt]</em>>
|
||||
<em>protocol-identifier-list</em> → <em>protocol-identifier</em> | <em>protocol-identifier, protocol-identifier-list</em>
|
||||
<em>protocol-identifier</em> → <em>type-identifier</em></p>
|
||||
<p>协议合成类型语法<br><em>协议合成类型</em> → <strong>protocol</strong> <strong><</strong> <a href="..\chapter3\03_Types.html#protocol_identifier_list"><em>协议标识符列表</em></a> <em>可选</em> <strong>></strong><br><em>协议标识符列表</em> → <a href="..\chapter3\03_Types.html#protocol_identifier"><em>协议标识符</em></a> | <a href="..\chapter3\03_Types.html#protocol_identifier"><em>协议标识符</em></a> <strong>,</strong> <a href="..\chapter3\03_Types.html#protocol_identifier_list"><em>协议标识符列表</em></a><br><em>协议标识符</em> → <a href="..\chapter3\03_Types.html#type_identifier"><em>类型标识</em></a> </p>
|
||||
</blockquote>
|
||||
<p><a name="metatype_type"></a></p>
|
||||
<h2 id="-">元类型</h2>
|
||||
<p>元类型是指所有类型的类型,包括类、结构体、枚举和协议。</p>
|
||||
<p>类、结构体或枚举类型的元类型是相应的类型名紧跟<code>.Type</code>。协议类型的元类型——并不是运行时适配该协议的具体类型——是该协议名字紧跟<code>.Protocol</code>。比如,类<code>SomeClass</code>的元类型就是<code>SomeClass.Type</code>,协议<code>SomeProtocol</code>的元类型就是<code>SomeProtocal.Protocol</code>。</p>
|
||||
<p>你可以使用后缀<code>self</code>表达式来获取类型。比如,<code>SomeClass.self</code>返回<code>SomeClass</code>本身,而不是<code>SomeClass</code>的一个实例。同样,<code>SomeProtocol.self</code>返回<code>SomeProtocol</code>本身,而不是运行时适配<code>SomeProtocol</code>的某个类型的实例。还可以对类型的实例使用<code>dynamicType</code>表达式来获取该实例在运行阶段的类型,如下所示:</p>
|
||||
<pre><code class="lang-javascript"><span class="hljs-keyword">class</span> SomeBaseClass {
|
||||
<span class="hljs-keyword">class</span> func printClassName() {
|
||||
println(<span class="hljs-string">"SomeBaseClass"</span>)
|
||||
<pre><code class="lang-swift">class SomeBaseClass {
|
||||
class func printClassName() {
|
||||
println("SomeBaseClass")
|
||||
}
|
||||
}
|
||||
<span class="hljs-keyword">class</span> SomeSubClass: SomeBaseClass {
|
||||
override <span class="hljs-keyword">class</span> func printClassName() {
|
||||
println(<span class="hljs-string">"SomeSubClass"</span>)
|
||||
class SomeSubClass: SomeBaseClass {
|
||||
override class func printClassName() {
|
||||
println("SomeSubClass")
|
||||
}
|
||||
}
|
||||
<span class="hljs-keyword">let</span> someInstance: SomeBaseClass = SomeSubClass()
|
||||
<span class="hljs-comment">// someInstance is of type SomeBaseClass at compile time, but</span>
|
||||
<span class="hljs-comment">// someInstance is of type SomeSubClass at runtime</span>
|
||||
let someInstance: SomeBaseClass = SomeSubClass()
|
||||
// someInstance is of type SomeBaseClass at compile time, but
|
||||
// someInstance is of type SomeSubClass at runtime
|
||||
someInstance.dynamicType.printClassName()
|
||||
<span class="hljs-comment">// prints "SomeSubClass</span>
|
||||
// prints "SomeSubClass
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>元类型的语法:
|
||||
<em>metatype-type</em> → <em>type</em>.<strong>Type</strong> | <em>type</em>.<strong>Protocol</strong></p>
|
||||
<p>元(Metatype)类型语法<br><em>元类型</em> → <a href="..\chapter3\03_Types.html#type"><em>类型</em></a> <strong>.</strong> <strong>Type</strong> | <a href="..\chapter3\03_Types.html#type"><em>类型</em></a> <strong>.</strong> <strong>Protocol</strong> x</p>
|
||||
</blockquote>
|
||||
<p><a name="type_inheritance_clause"></a></p>
|
||||
<h2 id="-">类型继承子句</h2>
|
||||
@ -798,17 +780,15 @@ someInstance.dynamicType.printClassName()
|
||||
<p>其它命名型类型可能只继承或适配一个协议列表。协议类型可能继承于其它任意数量的协议。当一个协议类型继承于其它协议时,其它协议的条件集合会被集成在一起,然后其它从当前协议继承的任意类型必须适配所有这些条件。</p>
|
||||
<p>枚举定义中的类型继承子句可以是一个协议列表,或是指定原始值的枚举,一个单独的指定原始值类型的命名型类型。使用类型继承子句来指定原始值类型的枚举定义的例子,见章节“原始值”。</p>
|
||||
<blockquote>
|
||||
<p>类型继承子句的语法:
|
||||
<em>type-inheritance-clause</em> → :<em>type-inheritance-list</em>
|
||||
<em>type-inheritance-list</em> → <em>type-identifier</em> | <em>type-identifier</em>, <em>type-inheritance-list</em></p>
|
||||
<p>类型继承子句语法<br><em>类型继承子句</em> → <strong>:</strong> <a href="..\chapter3\03_Types.html#type_inheritance_list"><em>类型继承列表</em></a><br><em>类型继承列表</em> → <a href="..\chapter3\03_Types.html#type_identifier"><em>类型标识</em></a> | <a href="..\chapter3\03_Types.html#type_identifier"><em>类型标识</em></a> <strong>,</strong> <a href="..\chapter3\03_Types.html#type_inheritance_list"><em>类型继承列表</em></a></p>
|
||||
</blockquote>
|
||||
<p><a name="type_inference"></a></p>
|
||||
<h2 id="-">类型推断</h2>
|
||||
<p>Swift广泛的使用类型推断,从而允许你可以忽略很多变量和表达式的类型或部分类型。比如,对于<code>var x: Int = 0</code>,你可以完全忽略类型而简写成<code>var x = 0</code>——编译器会正确的推断出<code>x</code>的类型<code>Int</code>。类似的,当完整的类型可以从上下文推断出来时,你也可以忽略类型的一部分。比如,如果你写了<code>let dict: Dictionary = ["A": 1]</code>,编译提也能推断出<code>dict</code>的类型是<code>Dictionary<String, Int></code>。</p>
|
||||
<p>在上面的两个例子中,类型信息从表达式树(expression tree)的叶子节点传向根节点。也就是说,<code>var x: Int = 0</code>中<code>x</code>的类型首先根据<code>0</code>的类型进行推断,然后将该类型信息传递到根节点(变量<code>x</code>)。</p>
|
||||
<p>在Swift中,类型信息也可以反方向流动——从根节点传向叶子节点。在下面的例子中,常量<code>eFloat</code>上的显式类型注解(<code>:Float</code>)导致数字字面量<code>2.71828</code>的类型是<code>Float</code>而非<code>Double</code>。</p>
|
||||
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> e = <span class="hljs-number">2.71828</span> <span class="hljs-comment">// The type of e is inferred to be Double.</span>
|
||||
<span class="hljs-keyword">let</span> eFloat: Float = <span class="hljs-number">2.71828</span> <span class="hljs-comment">// The type of eFloat is Float.</span>
|
||||
<pre><code class="lang-swift">let e = 2.71828 // The type of e is inferred to be Double.
|
||||
let eFloat: Float = 2.71828 // The type of eFloat is Float.
|
||||
</code></pre>
|
||||
<p>Swift中的类型推断在单独的表达式或语句水平上进行。这意味着所有用于推断类型的信息必须可以从表达式或其某个子表达式的类型检查中获取。</p>
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="3.4" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="3.4" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,11 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_87">
|
||||
<section class="normal" id="section-gitbook_61">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:sg552</p>
|
||||
<p>校对:numbbbbb</p>
|
||||
<p>翻译:<a href="https://github.com/sg552" target="_blank">sg552</a><br>校对:<a href="https://github.com/numbbbbb" target="_blank">numbbbbb</a>, <a href="https://github.com/stanzhai" target="_blank">stanzhai</a></p>
|
||||
</blockquote>
|
||||
<h1 id="-expressions-">表达式(Expressions)</h1>
|
||||
<hr>
|
||||
@ -608,9 +607,7 @@
|
||||
<p>Swift 中存在四种表达式: 前缀(prefix)表达式,二元(binary)表达式,主要(primary)表达式和后缀(postfix)表达式。表达式可以返回一个值,以及运行某些逻辑(causes a side effect)。</p>
|
||||
<p>前缀表达式和二元表达式就是对某些表达式使用各种运算符(operators)。 主要表达式是最短小的表达式,它提供了获取(变量的)值的一种途径。 后缀表达式则允许你建立复杂的表达式,例如配合函数调用和成员访问。 每种表达式都在下面有详细论述~</p>
|
||||
<blockquote>
|
||||
<p>表达式的语法</p>
|
||||
<p><em>expression</em> → <em>prefix-expression</em><em>binary-expressions(</em>opt)
|
||||
<em>expression-list</em> → <em>expression</em>| <em>expression</em>,<em>expression-list</em></p>
|
||||
<p>表达式语法<br><em>表达式</em> → <a href="..\chapter3\04_Expressions.html#prefix_expression"><em>前置表达式</em></a> <a href="..\chapter3\04_Expressions.html#binary_expressions"><em>二元表达式列表</em></a> <em>可选</em><br><em>表达式列表</em> → <a href="..\chapter3\04_Expressions.html#expression"><em>表达式</em></a> | <a href="..\chapter3\04_Expressions.html#expression"><em>表达式</em></a> <strong>,</strong> <a href="..\chapter3\04_Expressions.html#expression_list"><em>表达式列表</em></a> </p>
|
||||
</blockquote>
|
||||
<p><a name="prefix_expressions"></a></p>
|
||||
<h2 id="-prefix-expressions-">前缀表达式(Prefix Expressions)</h2>
|
||||
@ -627,15 +624,14 @@
|
||||
<p>对于这些操作符的使用,请参见: Basic Operators and Advanced Operators</p>
|
||||
<p>作为对上面标准库运算符的补充,你也可以对 某个函数的参数使用 '&'运算符。 更多信息,请参见: "In-Out parameters".</p>
|
||||
<blockquote>
|
||||
<p>前缀表达式的语法</p>
|
||||
<p><em>prefix-expression</em> → <em>prefix-operator</em> (opt) <em>postfix-expression</em>
|
||||
<em>prefix-expression</em> → <em>in-out-expression</em>
|
||||
<em>in-out-expression</em> → &<em>identifier</em></p>
|
||||
<p>前置表达式语法<br><em>前置表达式</em> → <a href="LexicalStructure.html#prefix_operator"><em>前置运算符</em></a> <em>可选</em> <a href="..\chapter3\04_Expressions.html#postfix_expression"><em>后置表达式</em></a><br><em>前置表达式</em> → <a href="..\chapter3\04_Expressions.html#in_out_expression"><em>写入写出(in-out)表达式</em></a><br><em>写入写出(in-out)表达式</em> → <strong>&</strong> <a href="LexicalStructure.html#identifier"><em>标识符</em></a> </p>
|
||||
</blockquote>
|
||||
<p><a name="binary_expressions"></a></p>
|
||||
<h2 id="-binary-expressions-">二元表达式(Binary Expressions)</h2>
|
||||
<p>二元表达式由 "左边参数" + "二元运算符" + "右边参数" 组成, 它有如下的形式:</p>
|
||||
<p> <code>left-hand argument</code> <code>operator</code> <code>right-hand argument</code></p>
|
||||
<blockquote>
|
||||
<p><code>left-hand argument</code> <code>operator</code> <code>right-hand argument</code></p>
|
||||
</blockquote>
|
||||
<p>Swift 标准库提供了如下的二元运算符:</p>
|
||||
<ul>
|
||||
<li>求幂相关(无结合,优先级160)<ul>
|
||||
@ -731,55 +727,50 @@
|
||||
</ul>
|
||||
<p>关于这些运算符(operators)的更多信息,请参见:Basic Operators and Advanced Operators.</p>
|
||||
<blockquote>
|
||||
<blockquote>
|
||||
<p>注意</p>
|
||||
<p>在解析时, 一个二元表达式表示为一个一级数组(a flat list), 这个数组(List)根据运算符的先后顺序,被转换成了一个tree. 例如: 2 + 3 <em> 5 首先被认为是: 2, + , <code>3</code>, </em>, 5. 随后它被转换成 tree (2 + (3 * 5))</p>
|
||||
<p>注意<br>在解析时, 一个二元表达式表示为一个一级数组(a flat list), 这个数组(List)根据运算符的先后顺序,被转换成了一个tree. 例如: 2 + 3 <em> 5 首先被认为是: 2, + , <code>3</code>, </em>, 5. 随后它被转换成 tree (2 + (3 * 5))</p>
|
||||
</blockquote>
|
||||
<p>二元表达式的语法</p>
|
||||
<p><em>binary-expression</em> → <em>binary-operator</em><em>prefix-expression</em>
|
||||
<em>binary-expression</em> → <em>assignment-operator</em>prefix-expression<em>
|
||||
</em>binary-expression<em> → </em>conditional-operator<em>prefix-expression</em>
|
||||
<em>binary-expression</em> → <em>type-casting-operator</em>
|
||||
<em>binary-expression</em>s → <em>binary-expression</em><em>binary-expressions</em>(opt)</p>
|
||||
<p></p>
|
||||
|
||||
<blockquote>
|
||||
<p>二元表达式语法<br><em>二元表达式</em> → <a href="LexicalStructure.html#binary_operator"><em>二元运算符</em></a> <a href="..\chapter3\04_Expressions.html#prefix_expression"><em>前置表达式</em></a><br><em>二元表达式</em> → <a href="..\chapter3\04_Expressions.html#assignment_operator"><em>赋值运算符</em></a> <a href="..\chapter3\04_Expressions.html#prefix_expression"><em>前置表达式</em></a><br><em>二元表达式</em> → <a href="..\chapter3\04_Expressions.html#conditional_operator"><em>条件运算符</em></a> <a href="..\chapter3\04_Expressions.html#prefix_expression"><em>前置表达式</em></a><br><em>二元表达式</em> → <a href="..\chapter3\04_Expressions.html#type_casting_operator"><em>类型转换运算符</em></a><br><em>二元表达式列表</em> → <a href="..\chapter3\04_Expressions.html#binary_expression"><em>二元表达式</em></a> <a href="..\chapter3\04_Expressions.html#binary_expressions"><em>二元表达式列表</em></a> <em>可选</em> </p>
|
||||
</blockquote>
|
||||
<p><a name="assignment_operator"></a></p>
|
||||
<h2 id="-assignment-operator-">赋值表达式(Assignment Operator)</h2>
|
||||
<p>The assigment operator sets a new value for a given expression. It has the following form:
|
||||
赋值表达式会对某个给定的表达式赋值。 它有如下的形式;</p>
|
||||
<blockquote>
|
||||
<p><code>expression</code> = <code>value</code></p>
|
||||
</blockquote>
|
||||
<p>就是把右边的 <em>value</em> 赋值给左边的 <em>expression</em>. 如果左边的<em>expression</em> 需要接收多个参数(是一个tuple ),那么右边必须也是一个具有同样数量参数的tuple. (允许嵌套的tuple)</p>
|
||||
<pre><code class="lang-swift">(a, _, (b, c)) = ("test", 9.45, (12, 3))
|
||||
// a is "test", b is 12, c is 3, and 9.45 is ignored
|
||||
</code></pre>
|
||||
<p>赋值运算符不返回任何值。</p>
|
||||
<blockquote>
|
||||
<p>赋值表达式的语法</p>
|
||||
<p><em>assignment-operator</em> → =</p>
|
||||
<p>赋值运算符语法<br><em>赋值运算符</em> → <strong>=</strong> </p>
|
||||
</blockquote>
|
||||
<p><a name="ternary_conditional_operator"></a></p>
|
||||
<h2 id="-ternary-conditional-operator-">三元条件运算符(Ternary Conditional Operator)</h2>
|
||||
<p>三元条件运算符 是根据条件来获取值。 形式如下:</p>
|
||||
<pre><code>`condition` ? `expression used if true` : `expression used if false`
|
||||
</code></pre><p>如果 <code>condition</code> 是true, 那么返回 第一个表达式的值(此时不会调用第二个表达式), 否则返回第二个表达式的值(此时不会调用第一个表达式)。</p>
|
||||
<blockquote>
|
||||
<p><code>condition</code> ? <code>expression used if true</code> : <code>expression used if false</code></p>
|
||||
</blockquote>
|
||||
<p>如果 <code>condition</code> 是true, 那么返回 第一个表达式的值(此时不会调用第二个表达式), 否则返回第二个表达式的值(此时不会调用第一个表达式)。</p>
|
||||
<p>想看三元条件运算符的例子,请参见: Ternary Conditional Operator.</p>
|
||||
<blockquote>
|
||||
<p>三元条件表达式</p>
|
||||
<p><code>conditional-operator</code> → ?<code>expression</code>:</p>
|
||||
<p>三元条件运算符语法<br><em>三元条件运算符</em> → <strong>?</strong> <a href="..\chapter3\04_Expressions.html#expression"><em>表达式</em></a> <strong>:</strong> </p>
|
||||
</blockquote>
|
||||
<p><a name="type-casting_operators"></a></p>
|
||||
<h2 id="-type-casting-operators-">类型转换运算符(Type-Casting Operators)</h2>
|
||||
<p>有两种类型转换操作符: as 和 is. 它们有如下的形式:</p>
|
||||
<pre><code>`expression` as `type`
|
||||
`expression` as? `type`
|
||||
`expression` is `type`
|
||||
</code></pre><p>as 运算符会把<code>目标表达式</code>转换成指定的<code>类型</code>(specified type),过程如下:</p>
|
||||
<blockquote>
|
||||
<p><code>expression</code> as <code>type</code><br><code>expression</code> as? <code>type</code><br><code>expression</code> is <code>type</code> </p>
|
||||
</blockquote>
|
||||
<p>as 运算符会把<code>目标表达式</code>转换成指定的<code>类型</code>(specified type),过程如下:</p>
|
||||
<ul>
|
||||
<li><p>如果类型转换成功, 那么目标表达式就会返回指定类型的实例(instance). 例如:把子类(subclass)变成父类(superclass)时.</p>
|
||||
</li>
|
||||
<li><p>如果转换失败,则会抛出编译错误( compile-time error)。</p>
|
||||
</li>
|
||||
<li><p>如果上述两个情况都不是(也就是说,编译器在编译时期无法确定转换能否成功,) 那么目标表达式就会变成指定的类型的optional. (is an optional of the specified type ) 然后在运行时,如果转换成功, 目标表达式就会作为 optional的一部分来返回, 否则,目标表达式返回nil. 对应的例子是: 把一个 superclass 转换成一个 subclass.</p>
|
||||
</li>
|
||||
<li>如果类型转换成功, 那么目标表达式就会返回指定类型的实例(instance). 例如:把子类(subclass)变成父类(superclass)时.</li>
|
||||
<li>如果转换失败,则会抛出编译错误( compile-time error)。</li>
|
||||
<li>如果上述两个情况都不是(也就是说,编译器在编译时期无法确定转换能否成功,) 那么目标表达式就会变成指定的类型的optional. (is an optional of the specified type ) 然后在运行时,如果转换成功, 目标表达式就会作为 optional的一部分来返回, 否则,目标表达式返回nil. 对应的例子是: 把一个 superclass 转换成一个 subclass.</li>
|
||||
</ul>
|
||||
<pre><code class="lang-swift">class SomeSuperType {}
|
||||
class SomeType: SomeSuperType {}
|
||||
@ -802,22 +793,13 @@ let y2: SomeType = x // Type information from an annotation
|
||||
</code></pre>
|
||||
<p>关于类型转换的更多内容和例子,请参见: Type Casting.</p>
|
||||
<blockquote>
|
||||
<p>类型转换的语法</p>
|
||||
<p><em>type-casting-operator</em> → is<em>type</em>| as?(opt)<em>type</em></p>
|
||||
<p>类型转换运算符(type-casting-operator)语法<br><em>类型转换运算符</em> → <strong>is</strong> <a href="..\chapter3\03_Types.html#type"><em>类型</em></a> | <strong>as</strong> <strong>?</strong> <em>可选</em> <a href="..\chapter3\03_Types.html#type"><em>类型</em></a> </p>
|
||||
</blockquote>
|
||||
<p><a name="primary_expressions"></a></p>
|
||||
<h2 id="-primary-expressions-">主要表达式(Primary Expressions)</h2>
|
||||
<p><code>主要表达式</code>是最基本的表达式。 它们可以跟 前缀表达式,二元表达式,后缀表达式以及其他主要表达式组合使用。</p>
|
||||
<h2 id="-primary-expressions-">主表达式(Primary Expressions)</h2>
|
||||
<p><code>主表达式</code>是最基本的表达式。 它们可以跟 前缀表达式,二元表达式,后缀表达式以及其他主要表达式组合使用。</p>
|
||||
<blockquote>
|
||||
<p>主要表达式的语法</p>
|
||||
<p><em>primary-expression</em> → <em>identifier</em><em>generic-argument-clause</em>(opt)
|
||||
<em>primary-expression</em> → <em>literal-expression</em>
|
||||
<em>primary-expression</em> → <em>self-expression</em>
|
||||
<em>primary-expression</em> → <em>superclass-expression</em>
|
||||
<em>primary-expression</em> → <em>closure-expression</em>
|
||||
<em>primary-expression</em> → <em>parenthesized-expression</em>
|
||||
<em>primary-expression</em> → <em>implicit-member-expression</em>
|
||||
<em>primary-expression</em> → <em>wildcard-expression</em></p>
|
||||
<p>主表达式语法<br><em>主表达式</em> → <a href="LexicalStructure.html#identifier"><em>标识符</em></a> <a href="GenericParametersAndArguments.html#generic_argument_clause"><em>泛型参数子句</em></a> <em>可选</em><br><em>主表达式</em> → <a href="..\chapter3\04_Expressions.html#literal_expression"><em>字面量表达式</em></a><br><em>主表达式</em> → <a href="..\chapter3\04_Expressions.html#self_expression"><em>self表达式</em></a><br><em>主表达式</em> → <a href="..\chapter3\04_Expressions.html#superclass_expression"><em>超类表达式</em></a><br><em>主表达式</em> → <a href="..\chapter3\04_Expressions.html#closure_expression"><em>闭包表达式</em></a><br><em>主表达式</em> → <a href="..\chapter3\04_Expressions.html#parenthesized_expression"><em>圆括号表达式</em></a><br><em>主表达式</em> → <a href="..\chapter3\04_Expressions.html#implicit_member_expression"><em>隐式成员表达式</em></a><br><em>主表达式</em> → <a href="..\chapter3\04_Expressions.html#wildcard_expression"><em>通配符表达式</em></a> </p>
|
||||
</blockquote>
|
||||
<h3 id="-literal-expression-">字符型表达式(Literal Expression)</h3>
|
||||
<p>由这些内容组成:普通的字符(string, number) , 一个字符的字典或者数组,或者下面列表中的特殊字符。</p>
|
||||
@ -854,31 +836,22 @@ let y2: SomeType = x // Type information from an annotation
|
||||
</table>
|
||||
<p>在某个函数(function)中,<code>__FUNCTION__</code> 会返回当前函数的名字。 在某个方法(method)中,它会返回当前方法的名字。 在某个property 的getter/setter中会返回这个属性的名字。 在init/subscript中 只有的特殊成员(member)中会返回这个keyword的名字,在某个文件的顶端(the top level of a file),它返回的是当前module的名字。</p>
|
||||
<p>一个array literal,是一个有序的值的集合。 它的形式是:</p>
|
||||
<pre><code>[`value 1`, `value 2`, `...`]
|
||||
</code></pre><p>数组中的最后一个表达式可以紧跟一个逗号(','). []表示空数组 。 array literal的type是 T[], 这个T就是数组中元素的type. 如果该数组中有多种type, T则是跟这些type的公共supertype最接近的type.(closest common supertype)</p>
|
||||
<p>一个<code>dictionary literal</code> 是一个包含无序的键值对(key-value pairs)的集合,它的形式是:</p>
|
||||
<pre><code>[`key 1`: `value 1`, `key 2`: `value 2`, `...`]
|
||||
</code></pre><p>dictionary 的最后一个表达式可以是一个逗号(','). [:] 表示一个空的dictionary. 它的type是 Dictionary<KeyType, ValueType> (这里KeyType表示 key的type, ValueType表示 value的type) 如果这个dictionary 中包含多种 types, 那么KeyType, Value 则对应着它们的公共supertype最接近的type( closest common supertype).</p>
|
||||
<blockquote>
|
||||
<p>字符型表达式的语法</p>
|
||||
<p><em>literal-expression</em> → <em>literal</em>
|
||||
<em>literal-expression</em> → <em>array-literal</em>| <em>dictionary-literal</em>
|
||||
<em>literal-expression</em> → <em>_<em>FILE_</em></em>| <em>_<em>LINE_</em></em>| <em>_<em>COLUMN_</em></em>| <em>_<em>FUNCTION_</em></em>
|
||||
<em>array-literal</em> → [<em>array-literal-items</em>opt]
|
||||
<em>array-literal-items</em> → <em>array-literal-item</em>,(opt) | <em>array-literal-item</em>,<em>array-literal-items</em>
|
||||
<em>array-literal-item</em> → <em>expression</em>
|
||||
<em>dictionary-literal</em> → [<em>dictionary-literal-items</em>] [:]
|
||||
<em>dictionary-literal-items</em> → <em>dictionary-literal-item</em>,(opt)| <em>dictionary-literal-item</em>,<em>dictionary-literal-items</em>
|
||||
<em>dictionary-literal-item</em> → <em>expression</em>:<em>expression</em></p>
|
||||
<p>[<code>value 1</code>, <code>value 2</code>, <code>...</code>]</p>
|
||||
</blockquote>
|
||||
<p>数组中的最后一个表达式可以紧跟一个逗号(','). []表示空数组 。 array literal的type是 T[], 这个T就是数组中元素的type. 如果该数组中有多种type, T则是跟这些type的公共supertype最接近的type.(closest common supertype)</p>
|
||||
<p>一个<code>dictionary literal</code> 是一个包含无序的键值对(key-value pairs)的集合,它的形式是:</p>
|
||||
<blockquote>
|
||||
<p>[<code>key 1</code>: <code>value 1</code>, <code>key 2</code>: <code>value 2</code>, <code>...</code>]</p>
|
||||
</blockquote>
|
||||
<p>dictionary 的最后一个表达式可以是一个逗号(','). [:] 表示一个空的dictionary. 它的type是 Dictionary<KeyType, ValueType> (这里KeyType表示 key的type, ValueType表示 value的type) 如果这个dictionary 中包含多种 types, 那么KeyType, Value 则对应着它们的公共supertype最接近的type( closest common supertype).</p>
|
||||
<blockquote>
|
||||
<p>字面量表达式语法<br><em>字面量表达式</em> → <a href="LexicalStructure.html#literal"><em>字面量</em></a><br><em>字面量表达式</em> → <a href="..\chapter3\04_Expressions.html#array_literal"><em>数组字面量</em></a> | <a href="..\chapter3\04_Expressions.html#dictionary_literal"><em>字典字面量</em></a><br><em>字面量表达式</em> → <strong>__FILE__</strong> | <strong>__LINE__</strong> | <strong>__COLUMN__</strong> | <strong>__FUNCTION__</strong><br><em>数组字面量</em> → <strong>[</strong> <a href="..\chapter3\04_Expressions.html#array_literal_items"><em>数组字面量项列表</em></a> <em>可选</em> <strong>]</strong><br><em>数组字面量项列表</em> → <a href="..\chapter3\04_Expressions.html#array_literal_item"><em>数组字面量项</em></a> <strong>,</strong> <em>可选</em> | <a href="..\chapter3\04_Expressions.html#array_literal_item"><em>数组字面量项</em></a> <strong>,</strong> <a href="..\chapter3\04_Expressions.html#array_literal_items"><em>数组字面量项列表</em></a><br><em>数组字面量项</em> → <a href="..\chapter3\04_Expressions.html#expression"><em>表达式</em></a><br><em>字典字面量</em> → <strong>[</strong> <a href="..\chapter3\04_Expressions.html#dictionary_literal_items"><em>字典字面量项列表</em></a> <strong>]</strong> | <strong>[</strong> <strong>:</strong> <strong>]</strong><br><em>字典字面量项列表</em> → <a href="..\chapter3\04_Expressions.html#dictionary_literal_item"><em>字典字面量项</em></a> <strong>,</strong> <em>可选</em> | <a href="..\chapter3\04_Expressions.html#dictionary_literal_item"><em>字典字面量项</em></a> <strong>,</strong> <a href="..\chapter3\04_Expressions.html#dictionary_literal_items"><em>字典字面量项列表</em></a><br><em>字典字面量项</em> → <a href="..\chapter3\04_Expressions.html#expression"><em>表达式</em></a> <strong>:</strong> <a href="..\chapter3\04_Expressions.html#expression"><em>表达式</em></a> </p>
|
||||
</blockquote>
|
||||
<h3 id="self-self-expression-">self表达式(Self Expression)</h3>
|
||||
<p>self表达式是对 当前type 或者当前instance的引用。它的形式如下:</p>
|
||||
<blockquote>
|
||||
<p>self
|
||||
self.<code>member name</code>
|
||||
self[<code>subscript index</code>]
|
||||
self(<code>initializer arguments</code>)
|
||||
self.init(<code>initializer arguments</code>)</p>
|
||||
<p>self<br>self.<code>member name</code><br>self[<code>subscript index</code>]<br>self(<code>initializer arguments</code>)<br>self.init(<code>initializer arguments</code>) </p>
|
||||
</blockquote>
|
||||
<p>如果在 initializer, subscript, instance method中,self等同于当前type的instance. 在一个静态方法(static method), 类方法(class method)中, self等同于当前的type.</p>
|
||||
<p>当访问 member(成员变量时), self 用来区分重名变量(例如函数的参数). 例如,
|
||||
@ -899,31 +872,23 @@ self.init(<code>initializer arguments</code>)</p>
|
||||
}
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>self表达式的语法</p>
|
||||
<p><em>self-expression</em> → self
|
||||
<em>self-expression</em> → self.<em>identifier</em>
|
||||
<em>self-expression</em> → self[<em>expression</em>]
|
||||
<em>self-expression</em> → self.init</p>
|
||||
<p>Self 表达式语法<br><em>self表达式</em> → <strong>self</strong><br><em>self表达式</em> → <strong>self</strong> <strong>.</strong> <a href="LexicalStructure.html#identifier"><em>标识符</em></a><br><em>self表达式</em> → <strong>self</strong> <strong>[</strong> <a href="..\chapter3\04_Expressions.html#expression"><em>表达式</em></a> <strong>]</strong><br><em>self表达式</em> → <strong>self</strong> <strong>.</strong> <strong>init</strong> </p>
|
||||
</blockquote>
|
||||
<h3 id="-superclass-expression-">超类表达式(Superclass Expression)</h3>
|
||||
<p>超类表达式可以使我们在某个class中访问它的超类. 它有如下形式:</p>
|
||||
<pre><code>super.`member name`
|
||||
super[`subscript index`]
|
||||
super.init(`initializer arguments`)
|
||||
</code></pre><p>形式1 用来访问超类的某个成员(member). 形式2 用来访问该超类的 subscript 实现。 形式3 用来访问该超类的 initializer.</p>
|
||||
<blockquote>
|
||||
<p>super.<code>member name</code><br>super[<code>subscript index</code>]<br>super.init(<code>initializer arguments</code>) </p>
|
||||
</blockquote>
|
||||
<p>形式1 用来访问超类的某个成员(member). 形式2 用来访问该超类的 subscript 实现。 形式3 用来访问该超类的 initializer.</p>
|
||||
<p>子类(subclass)可以通过超类(superclass)表达式在它们的 member, subscripting 和 initializers 中来利用它们超类中的某些实现(既有的方法或者逻辑)。</p>
|
||||
<blockquote>
|
||||
<p>GRAMMAR OF A SUPERCLASS EXPRESSION</p>
|
||||
<p><em>superclass-expression</em> → <em>superclass-method-expression</em> | <em>superclass-subscript-expression</em>| <em>superclass-initializer-expression</em>
|
||||
<em>superclass-method-expression</em> → super.<em>identifier</em>
|
||||
<em>superclass-subscript-expression</em> → super[<em>expression</em>]
|
||||
<em>superclass-initializer-expression</em> → super.init</p>
|
||||
<p>超类(superclass)表达式语法<br><em>超类表达式</em> → <a href="..\chapter3\04_Expressions.html#superclass_method_expression"><em>超类方法表达式</em></a> | <a href="..\chapter3\04_Expressions.html#超类下标表达式"><em>超类下标表达式</em></a> | <a href="..\chapter3\04_Expressions.html#superclass_initializer_expression"><em>超类构造器表达式</em></a><br><em>超类方法表达式</em> → <strong>super</strong> <strong>.</strong> <a href="LexicalStructure.html#identifier"><em>标识符</em></a><br><em>超类下标表达式</em> → <strong>super</strong> <strong>[</strong> <a href="..\chapter3\04_Expressions.html#expression"><em>表达式</em></a> <strong>]</strong><br><em>超类构造器表达式</em> → <strong>super</strong> <strong>.</strong> <strong>init</strong> </p>
|
||||
</blockquote>
|
||||
<h3 id="-closure-expression-">闭包表达式(Closure Expression)</h3>
|
||||
<p>闭包(closure) 表达式可以建立一个闭包(在其他语言中也叫 lambda, 或者 匿名函数(anonymous function)). 跟函数(function)的声明一样, 闭包(closure)包含了可执行的代码(跟方法主体(statement)类似) 以及接收(capture)的参数。 它的形式如下:</p>
|
||||
<pre><code class="lang-swift"> { (parameters) -> return type in
|
||||
statements
|
||||
}
|
||||
<pre><code class="lang-swift">{ (parameters) -> return type in
|
||||
statements
|
||||
}
|
||||
</code></pre>
|
||||
<p>闭包的参数声明形式跟方法中的声明一样, 请参见:Function Declaration.</p>
|
||||
<p>闭包还有几种特殊的形式, 让使用更加简洁:</p>
|
||||
@ -960,36 +925,28 @@ myFunction { [weak parent = self.parent] in print(parent!.title) }
|
||||
</code></pre>
|
||||
<p>关于闭包表达式的更多信息和例子,请参见: Closure Expressions.</p>
|
||||
<blockquote>
|
||||
<p>闭包表达式的语法</p>
|
||||
<p><em>closure-expression</em> → {<em>closure-signature</em>opt<em>statements</em>}
|
||||
<em>closure-signature</em> → <em>parameter-clause</em><em>function-result</em>(opt)in
|
||||
<em>closure-signature</em> → <em>identifier-list</em><em>function-result</em>(opt)in
|
||||
<em>closure-signature</em> → <em>capture-list</em><em>parameter-clause</em><em>function-result</em>(opt)in
|
||||
<em>closure-signature</em> → <em>capture-list</em><em>identifier-list</em><em>function-result</em>(opt)in
|
||||
<em>closure-signature</em> → <em>capture-list</em>in
|
||||
<em>capture-list</em> → [<em>capture-specifier</em><em>expression</em>]
|
||||
<em>capture-specifier</em> → weak| unowned| unowned(safe)| unowned(unsafe)</p>
|
||||
<p>闭包表达式语法<br><em>闭包表达式</em> → <strong>{</strong> <a href="..\chapter3\04_Expressions.html#closure_signature"><em>闭包签名(Signational)</em></a> <em>可选</em> <a href="..\chapter3\10_Statements.html#statements"><em>多条语句(Statements)</em></a> <strong>}</strong><br><em>闭包签名(Signational)</em> → <a href="..\chapter3\05_Declarations.html#parameter_clause"><em>参数子句</em></a> <a href="..\chapter3\05_Declarations.html#function_result"><em>函数结果</em></a> <em>可选</em> <strong>in</strong><br><em>闭包签名(Signational)</em> → <a href="LexicalStructure.html#identifier_list"><em>标识符列表</em></a> <a href="..\chapter3\05_Declarations.html#function_result"><em>函数结果</em></a> <em>可选</em> <strong>in</strong><br><em>闭包签名(Signational)</em> → <a href="..\chapter3\04_Expressions.html#capture_list"><em>捕获(Capature)列表</em></a> <a href="..\chapter3\05_Declarations.html#parameter_clause"><em>参数子句</em></a> <a href="..\chapter3\05_Declarations.html#function_result"><em>函数结果</em></a> <em>可选</em> <strong>in</strong><br><em>闭包签名(Signational)</em> → <a href="..\chapter3\04_Expressions.html#capture_list"><em>捕获(Capature)列表</em></a> <a href="LexicalStructure.html#identifier_list"><em>标识符列表</em></a> <a href="..\chapter3\05_Declarations.html#function_result"><em>函数结果</em></a> <em>可选</em> <strong>in</strong><br><em>闭包签名(Signational)</em> → <a href="..\chapter3\04_Expressions.html#capture_list"><em>捕获(Capature)列表</em></a> <strong>in</strong><br><em>捕获(Capature)列表</em> → <strong>[</strong> <a href="..\chapter3\04_Expressions.html#capture_specifier"><em>捕获(Capature)说明符</em></a> <a href="..\chapter3\04_Expressions.html#expression"><em>表达式</em></a> <strong>]</strong><br><em>捕获(Capature)说明符</em> → <strong>weak</strong> | <strong>unowned</strong> | <strong>unowned(safe)</strong> | <strong>unowned(unsafe)</strong> </p>
|
||||
</blockquote>
|
||||
<h3 id="-implicit-member-expression-">隐式成员表达式(Implicit Member Expression)</h3>
|
||||
<p>在可以判断出类型(type)的上下文(context)中,隐式成员表达式是访问某个type的member( 例如 class method, enumeration case) 的简洁方法。 它的形式是:</p>
|
||||
<blockquote>
|
||||
<p>.<code>member name</code></p>
|
||||
</blockquote>
|
||||
<p>例子:</p>
|
||||
<pre><code class="lang-swift">var x = MyEnumeration.SomeValue
|
||||
x = .AnotherValue
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p> 隐式成员表达式的语法</p>
|
||||
<p> <em>implicit-member-expression</em> → .<em>identifier</em></p>
|
||||
<p>隐式成员表达式语法<br><em>隐式成员表达式</em> → <strong>.</strong> <a href="..\chapter3\02_Lexical_Structure.html#identifier"><em>标识符</em></a> </p>
|
||||
</blockquote>
|
||||
<h3 id="-parenthesized-expression-">圆括号表达式(Parenthesized Expression)</h3>
|
||||
<p>圆括号表达式由多个子表达式和逗号','组成。 每个子表达式前面可以有 identifier x: 这样的可选前缀。形式如下:</p>
|
||||
<blockquote>
|
||||
<p>(<code>identifier 1</code>: <code>expression 1</code>, <code>identifier 2</code>: <code>expression 2</code>, <code>...</code>)</p>
|
||||
</blockquote>
|
||||
<p>圆括号表达式用来建立tuples , 然后把它做为参数传递给 function. 如果某个圆括号表达式中只有一个 子表达式,那么它的type就是 子表达式的type。例如: (1)的 type是Int, 而不是(Int)</p>
|
||||
<blockquote>
|
||||
<p>圆括号表达式的语法</p>
|
||||
<p><em>parenthesized-expression</em> → (<em>expression-element-list</em> (opt))
|
||||
<em>expression-element-list</em> → <em>expression-element</em>| <em>expression-element</em>,<em>expression-element-list</em>
|
||||
<em>expression-element</em> → <em>expression</em>| <em>identifier</em>:<em>expression</em></p>
|
||||
<p>圆括号表达式(Parenthesized Expression)语法<br><em>圆括号表达式</em> → <strong>(</strong> <a href="..\chapter3\04_Expressions.html#expression_element_list"><em>表达式元素列表</em></a> <em>可选</em> <strong>)</strong><br><em>表达式元素列表</em> → <a href="..\chapter3\04_Expressions.html#expression_element"><em>表达式元素</em></a> | <a href="..\chapter3\04_Expressions.html#expression_element"><em>表达式元素</em></a> <strong>,</strong> <a href="..\chapter3\04_Expressions.html#expression_element_list"><em>表达式元素列表</em></a><br><em>表达式元素</em> → <a href="..\chapter3\04_Expressions.html#expression"><em>表达式</em></a> | <a href="..\chapter3\02_Lexical_Structure.html#identifier"><em>标识符</em></a> <strong>:</strong> <a href="..\chapter3\04_Expressions.html#expression"><em>表达式</em></a> </p>
|
||||
</blockquote>
|
||||
<h3 id="-wildcard-expression-">通配符表达式(Wildcard Expression)</h3>
|
||||
<p>通配符表达式用来忽略传递进来的某个参数。例如:下面的代码中,10被传递给x, 20被忽略(译注:好奇葩的语法。。。)</p>
|
||||
@ -997,8 +954,7 @@ x = .AnotherValue
|
||||
// x is 10, 20 is ignored
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>通配符表达式的语法</p>
|
||||
<p><em>wildcard-expression</em> → _</p>
|
||||
<p>通配符表达式语法<br><em>通配符表达式</em> → <strong>_</strong> </p>
|
||||
</blockquote>
|
||||
<p><a name="postfix_expressions"></a></p>
|
||||
<h2 id="-postfix-expressions-">后缀表达式(Postfix Expressions)</h2>
|
||||
@ -1010,25 +966,19 @@ x = .AnotherValue
|
||||
</ul>
|
||||
<p>对于这些操作符的使用,请参见: Basic Operators and Advanced Operators</p>
|
||||
<blockquote>
|
||||
<p>后缀表达式的语法</p>
|
||||
<p><em>postfix-expression</em> → <em>primary-expression</em>
|
||||
<em>postfix-expression</em> → <em>postfix-expression</em><em>postfix-operator</em>
|
||||
<em>postfix-expression</em> → <em>function-call-expression</em>
|
||||
<em>postfix-expression</em> → <em>initializer-expression</em>
|
||||
<em>postfix-expression</em> → <em>explicit-member-expression</em>
|
||||
<em>postfix-expression</em> → <em>postfix-self-expression</em>
|
||||
<em>postfix-expression</em> → <em>dynamic-type-expression</em>
|
||||
<em>postfix-expression</em> → <em>subscript-expression</em>
|
||||
<em>postfix-expression</em> → <em>forced-value-expression</em>
|
||||
<em>postfix-expression</em> → <em>optional-chaining-expression</em></p>
|
||||
<p>后置表达式语法<br><em>后置表达式</em> → <a href="..\chapter3\04_Expressions.html#primary_expression"><em>主表达式</em></a><br><em>后置表达式</em> → <a href="..\chapter3\04_Expressions.html#postfix_expression"><em>后置表达式</em></a> <a href="..\chapter3\02_Lexical_Structure.html#postfix_operator"><em>后置运算符</em></a><br><em>后置表达式</em> → <a href="..\chapter3\04_Expressions.html#function_call_expression"><em>函数调用表达式</em></a><br><em>后置表达式</em> → <a href="..\chapter3\04_Expressions.html#initializer_expression"><em>构造器表达式</em></a><br><em>后置表达式</em> → <a href="..\chapter3\04_Expressions.html#explicit_member_expression"><em>显示成员表达式</em></a><br><em>后置表达式</em> → <a href="..\chapter3\04_Expressions.html#postfix_self_expression"><em>后置self表达式</em></a><br><em>后置表达式</em> → <a href="..\chapter3\04_Expressions.html#dynamic_type_expression"><em>动态类型表达式</em></a><br><em>后置表达式</em> → <a href="..\chapter3\04_Expressions.html#subscript_expression"><em>下标表达式</em></a><br><em>后置表达式</em> → <a href="..\chapter3\04_Expressions.html#forced_value_expression"><em>强制取值(Forced Value)表达式</em></a><br><em>后置表达式</em> → <a href="..\chapter3\04_Expressions.html#optional_chaining_expression"><em>可选链(Optional Chaining)表达式</em></a> </p>
|
||||
</blockquote>
|
||||
<h3 id="-function-call-expression-">函数调用表达式(Function Call Expression)</h3>
|
||||
<p>函数调用表达式由函数名和参数列表组成。它的形式如下:</p>
|
||||
<blockquote>
|
||||
<p><code>function name</code>(<code>argument value 1</code>, <code>argument value 2</code>)</p>
|
||||
</blockquote>
|
||||
<p>The function name can be any expression whose value is of a function type.
|
||||
(不用翻译了, 太罗嗦)</p>
|
||||
<p>如果该function 的声明中指定了参数的名字,那么在调用的时候也必须得写出来. 例如:</p>
|
||||
<blockquote>
|
||||
<p><code>function name</code>(<code>argument name 1</code>: <code>argument value 1</code>, <code>argument name 2</code>: <code>argument value 2</code>)</p>
|
||||
</blockquote>
|
||||
<p>可以在 函数调用表达式的尾部(最后一个参数之后)加上 一个闭包(closure) , 该闭包会被目标函数理解并执行。它具有如下两种写法:</p>
|
||||
<pre><code class="lang-swift">// someFunction takes an integer and a closure as its arguments
|
||||
someFunction(x, {$0 == 13})
|
||||
@ -1040,23 +990,19 @@ myData.someMethod() {$0 == 13}
|
||||
myData.someMethod {$0 == 13}
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>GRAMMAR OF A FUNCTION CALL EXPRESSION</p>
|
||||
<p><em>function-call-expression</em> → <em>postfix-expression</em><em>parenthesized-expression</em>
|
||||
<em>function-call-expression</em> → <em>postfix-expression</em><em>parenthesized-expression</em>(opt)<em>trailing-closure</em>
|
||||
<em>trailing-closure</em> → <em>closure-expression</em></p>
|
||||
<p>函数调用表达式语法<br><em>函数调用表达式</em> → <a href="..\chapter3\04_Expressions.html#postfix_expression"><em>后置表达式</em></a> <a href="..\chapter3\04_Expressions.html#parenthesized_expression"><em>圆括号表达式</em></a><br><em>函数调用表达式</em> → <a href="..\chapter3\04_Expressions.html#postfix_expression"><em>后置表达式</em></a> <a href="..\chapter3\04_Expressions.html#parenthesized_expression"><em>圆括号表达式</em></a> <em>可选</em> <a href="..\chapter3\04_Expressions.html#trailing_closure"><em>后置闭包(Trailing Closure)</em></a><br><em>后置闭包(Trailing Closure)</em> → <a href="..\chapter3\04_Expressions.html#closure_expression"><em>闭包表达式</em></a> </p>
|
||||
</blockquote>
|
||||
<h3 id="-initializer-expression-">初始化函数表达式(Initializer Expression)</h3>
|
||||
<p>Initializer表达式用来给某个Type初始化。 它的形式如下:</p>
|
||||
<blockquote>
|
||||
<p><code>expression</code>.init(<code>initializer arguments</code>)</p>
|
||||
</blockquote>
|
||||
<p>(Initializer表达式用来给某个Type初始化。) 跟函数(function)不同, initializer 不能返回值。</p>
|
||||
<pre><code class="lang-swift">var x = SomeClass.someClassFunction // ok
|
||||
var y = SomeClass.init // error
|
||||
```swift
|
||||
|
||||
可以通过 initializer 表达式来委托调用(delegate to )到superclass的initializers.
|
||||
|
||||
```swift
|
||||
class SomeSubClass: SomeSuperClass {
|
||||
</code></pre>
|
||||
<p>可以通过 initializer 表达式来委托调用(delegate to )到superclass的initializers.</p>
|
||||
<pre><code class="lang-swift">class SomeSubClass: SomeSuperClass {
|
||||
init() {
|
||||
// subclass initialization goes here
|
||||
super.init()
|
||||
@ -1064,12 +1010,13 @@ class SomeSubClass: SomeSuperClass {
|
||||
}
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>initializer表达式的语法</p>
|
||||
<p><em>initializer-expression</em> → <em>postfix-expression</em>.init</p>
|
||||
<p>构造器表达式语法<br><em>构造器表达式</em> → <a href="..\chapter3\04_Expressions.html#postfix_expression"><em>后置表达式</em></a> <strong>.</strong> <strong>init</strong> </p>
|
||||
</blockquote>
|
||||
<h3 id="-explicit-member-expression-">显式成员表达式(Explicit Member Expression)</h3>
|
||||
<p>显示成员表达式允许我们访问type, tuple, module的成员变量。它的形式如下:</p>
|
||||
<blockquote>
|
||||
<p><code>expression</code>.<code>member name</code></p>
|
||||
</blockquote>
|
||||
<p>该member 就是某个type在声明时候所定义(declaration or extension) 的变量, 例如:</p>
|
||||
<pre><code class="lang-swift">class SomeClass {
|
||||
var someProperty = 42
|
||||
@ -1085,24 +1032,24 @@ t.0 = t.1
|
||||
<p>The members of a module access the top-level declarations of that module.
|
||||
(不确定:对于某个module的member的调用,只能调用在top-level声明中的member.)</p>
|
||||
<blockquote>
|
||||
<p>显示成员表达式的语法</p>
|
||||
<p><em>explicit-member-expression</em> → <em>postfix-expression</em>.<em>decimal-digit</em>
|
||||
<em>explicit-member-expression</em> → <em>postfix-expression</em>.<em>identifier</em><em>generic-argument-clause</em>(opt)</p>
|
||||
<p>显式成员表达式语法<br><em>显示成员表达式</em> → <a href="..\chapter3\04_Expressions.html#postfix_expression"><em>后置表达式</em></a> <strong>.</strong> <a href="..\chapter3\02_Lexical_Structure.html#decimal_digit"><em>十进制数字</em></a><br><em>显示成员表达式</em> → <a href="..\chapter3\04_Expressions.html#postfix_expression"><em>后置表达式</em></a> <strong>.</strong> <a href="..\chapter3\02_Lexical_Structure.html#identifier"><em>标识符</em></a> <a href="GenericParametersAndArguments.html#generic_argument_clause"><em>泛型参数子句</em></a> <em>可选</em> </p>
|
||||
</blockquote>
|
||||
<h3 id="-self-postfix-self-expression-">后缀self表达式(Postfix Self Expression)</h3>
|
||||
<p>后缀表达式由 某个表达式 + '.self' 组成. 形式如下:</p>
|
||||
<p><code>expression</code>.self
|
||||
<code>type</code>.self</p>
|
||||
<blockquote>
|
||||
<p><code>expression</code>.self<br><code>type</code>.self </p>
|
||||
</blockquote>
|
||||
<p>形式1 表示会返回 expression 的值。例如: x.self 返回 x</p>
|
||||
<p>形式2:返回对应的type。我们可以用它来动态的获取某个instance的type。</p>
|
||||
<blockquote>
|
||||
<p>后缀self表达式的语法</p>
|
||||
<p><em>postfix-self-expression</em> → <em>postfix-expression</em>.self</p>
|
||||
<p>后置Self 表达式语法<br><em>后置self表达式</em> → <a href="..\chapter3\04_Expressions.html#postfix_expression"><em>后置表达式</em></a> <strong>.</strong> <strong>self</strong> </p>
|
||||
</blockquote>
|
||||
<h3 id="dynamic-dynamic-type-expression-">dynamic表达式(Dynamic Type Expression)</h3>
|
||||
<p>(因为dynamicType是一个独有的方法,所以这里保留了英文单词,未作翻译, --- 类似与self expression)</p>
|
||||
<p>dynamicType 表达式由 某个表达式 + '.dynamicType' 组成。</p>
|
||||
<blockquote>
|
||||
<p><code>expression</code>.dynamicType</p>
|
||||
</blockquote>
|
||||
<p>上面的形式中, expression 不能是某type的名字(当然了,如果我都知道它的名字了还需要动态来获取它吗)。动态类型表达式会返回"运行时"某个instance的type, 具体请看下面的列子:</p>
|
||||
<pre><code class="lang-swift">class SomeBaseClass {
|
||||
class func printClassName() {
|
||||
@ -1122,29 +1069,32 @@ someInstance.dynamicType.printClassName()
|
||||
// prints "SomeSubClass"
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>dynamic type 表达式</p>
|
||||
<p><em>dynamic-type-expression</em> → <em>postfix-expression</em>.dynamicType</p>
|
||||
<p>动态类型表达式语法<br><em>动态类型表达式</em> → <a href="..\chapter3\04_Expressions.html#postfix_expression"><em>后置表达式</em></a> <strong>.</strong> <strong>dynamicType</strong> </p>
|
||||
</blockquote>
|
||||
<h3 id="-subscript-expression-">下标脚本表达式(Subscript Expression)</h3>
|
||||
<p>下标脚本表达式提供了通过下标脚本访问getter/setter 的方法。它的形式是:</p>
|
||||
<blockquote>
|
||||
<p><code>expression</code>[<code>index expressions</code>]</p>
|
||||
</blockquote>
|
||||
<p>可以通过下标脚本表达式通过getter获取某个值,或者通过setter赋予某个值.</p>
|
||||
<p>关于subscript的声明,请参见: Protocol Subscript Declaration.</p>
|
||||
<blockquote>
|
||||
<p>下标脚本表达式的语法</p>
|
||||
<p><em>subscript-expression</em> → <em>postfix-expression</em>[<em>expression-list</em>]</p>
|
||||
<p>附属脚本表达式语法<br><em>附属脚本表达式</em> → <a href="..\chapter3\04_Expressions.html#postfix_expression"><em>后置表达式</em></a> <strong>[</strong> <a href="..\chapter3\04_Expressions.html#expression_list"><em>表达式列表</em></a> <strong>]</strong> </p>
|
||||
</blockquote>
|
||||
<h3 id="-forced-value-expression-">强制取值表达式(Forced-Value Expression)</h3>
|
||||
<p>强制取值表达式用来获取某个目标表达式的值(该目标表达式的值必须不是nil )。它的形式如下:</p>
|
||||
<blockquote>
|
||||
<p><code>expression</code>!</p>
|
||||
</blockquote>
|
||||
<p>如果该表达式的值不是nil, 则返回对应的值。 否则,抛出运行时错误(runtime error)。</p>
|
||||
<blockquote>
|
||||
<p>强制取值表达式的语法</p>
|
||||
<p><em>forced-value-expression</em> → <em>postfix-expression</em>!</p>
|
||||
<p>强制取值(Forced Value)语法<br><em>强制取值(Forced Value)表达式</em> → <a href="..\chapter3\04_Expressions.html#postfix_expression"><em>后置表达式</em></a> <strong>!</strong> </p>
|
||||
</blockquote>
|
||||
<h3 id="-optional-chaining-expression-">可选链表达式(Optional-Chaining Expression)</h3>
|
||||
<p>可选链表达式由目标表达式 + '?' 组成,形式如下:</p>
|
||||
<blockquote>
|
||||
<p><code>expression</code>?</p>
|
||||
</blockquote>
|
||||
<p>后缀'?' 返回目标表达式的值,把它做为可选的参数传递给后续的表达式</p>
|
||||
<p>如果某个后缀表达式包含了可选链表达式,那么它的执行过程就比较特殊: 首先先判断该可选链表达式的值,如果是 nil, 整个后缀表达式都返回 nil, 如果该可选链的值不是nil, 则正常返回该后缀表达式的值(依次执行它的各个子表达式)。在这两种情况下,该后缀表达式仍然是一个optional type(In either case, the value of the postfix expression is still of an optional type)</p>
|
||||
<p>如果某个"后缀表达式"的"子表达式"中包含了"可选链表达式",那么只有最外层的表达式返回的才是一个optional type. 例如,在下面的例子中, 如果c 不是nil, 那么 c?.property.performAction() 这句代码在执行时,就会先获得c 的property方法,然后调用 performAction()方法。 然后对于 "c?.property.performAction()" 这个整体,它的返回值是一个optional type.</p>
|
||||
@ -1157,8 +1107,7 @@ var result: Bool? = c?.property.performAction()
|
||||
}
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>可选链表达式的语法</p>
|
||||
<p><em>optional-chaining-expression</em> → <em>postfix-expression</em>?</p>
|
||||
<p>可选链表达式语法<br><em>可选链表达式</em> → <a href="..\chapter3\04_Expressions.html#postfix_expression"><em>后置表达式</em></a> <strong>?</strong> </p>
|
||||
</blockquote>
|
||||
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="3.6" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="3.6" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,11 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_91">
|
||||
<section class="normal" id="section-gitbook_63">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:marsprince</p>
|
||||
<p>校对:numbbbbb, stanzhai</p>
|
||||
<p>翻译:<a href="https://github.com/marsprince" target="_blank">marsprince</a><br>校对:<a href="https://github.com/numbbbbb" target="_blank">numbbbbb</a>, <a href="https://github.com/stanzhai" target="_blank">stanzhai</a></p>
|
||||
</blockquote>
|
||||
<h1 id="-">声明</h1>
|
||||
<hr>
|
||||
@ -632,10 +631,10 @@
|
||||
<p><a name="code_blocks"></a></p>
|
||||
<h2 id="-">代码块</h2>
|
||||
<p>代码块用来将一些声明和控制结构的语句组织在一起。它有如下的形式:</p>
|
||||
<pre><code>{
|
||||
`statements`
|
||||
}
|
||||
</code></pre><p>代码块中的语句包括声明,表达式和各种其他类型的语句,它们按照在源码中的出现顺序被依次执行。</p>
|
||||
<blockquote>
|
||||
<p>{<br> <code>statements</code><br>} </p>
|
||||
</blockquote>
|
||||
<p>代码块中的语句包括声明,表达式和各种其他类型的语句,它们按照在源码中的出现顺序被依次执行。</p>
|
||||
<blockquote>
|
||||
<p>代码块语法<br><em>代码块</em> → <strong>{</strong> <a href="..\chapter3\10_Statements.html#statements"><em>多条语句(Statements)</em></a> <em>可选</em> <strong>}</strong> </p>
|
||||
</blockquote>
|
||||
@ -643,30 +642,39 @@
|
||||
<h2 id="-">引入声明</h2>
|
||||
<p>引入声明使你可以使用在其他文件中声明的内容。引入语句的基本形式是引入整个代码模块;它由import关键字开始,后面
|
||||
紧跟一个模块名:</p>
|
||||
<pre><code>import module
|
||||
</code></pre><p>你可以提供更多的细节来限制引入的符号,如声明一个特殊的子模块或者在一个模块或子模块中做特殊的声明。(待改进)
|
||||
<blockquote>
|
||||
<p>import <code>module</code></p>
|
||||
</blockquote>
|
||||
<p>你可以提供更多的细节来限制引入的符号,如声明一个特殊的子模块或者在一个模块或子模块中做特殊的声明。(待改进)
|
||||
当你使用了这些细节后,在当前的程序汇总只有引入的符号是可用的(并不是声明的整个模块)。</p>
|
||||
<pre><code>import import kind module.symbol name
|
||||
import module.submodule
|
||||
</code></pre><blockquote>
|
||||
<blockquote>
|
||||
<p>import <code>import kind</code> <code>module</code>.<code>symbol name</code><br>import <code>module</code>.<code>submodule</code> </p>
|
||||
</blockquote>
|
||||
<p></p>
|
||||
|
||||
<blockquote>
|
||||
<p>导入(Import)声明语法<br><em>导入声明</em> → <a href="..\chapter3\06_Attributes.html#attributes"><em>特性(Attributes)列表</em></a> <em>可选</em> <strong>import</strong> <a href="..\chapter3\05_Declarations.html#import_kind"><em>导入类型</em></a> <em>可选</em> <a href="..\chapter3\05_Declarations.html#import_path"><em>导入路径</em></a><br><em>导入类型</em> → <strong>typealias</strong> | <strong>struct</strong> | <strong>class</strong> | <strong>enum</strong> | <strong>protocol</strong> | <strong>var</strong> | <strong>func</strong><br><em>导入路径</em> → <a href="..\chapter3\05_Declarations.html#import_path_identifier"><em>导入路径标识符</em></a> | <a href="..\chapter3\05_Declarations.html#import_path_identifier"><em>导入路径标识符</em></a> <strong>.</strong> <a href="..\chapter3\05_Declarations.html#import_path"><em>导入路径</em></a><br><em>导入路径标识符</em> → <a href="LexicalStructure.html#identifier"><em>标识符</em></a> | <a href="LexicalStructure.html#operator"><em>运算符</em></a> </p>
|
||||
</blockquote>
|
||||
<p><a name="constant_declaration"></a></p>
|
||||
<h2 id="-">常量声明</h2>
|
||||
<p>常量声明可以在你的程序里命名一个常量。常量以关键词let来声明,遵循如下的格式:</p>
|
||||
<pre><code>let constant name: type = expression
|
||||
</code></pre><p>当常量的值被给定后,常量就将常量名称和表达式初始值不变的结合在了一起,而且不能更改。
|
||||
<blockquote>
|
||||
<p>let <code>constant name</code>: <code>type</code> = <code>expression</code></p>
|
||||
</blockquote>
|
||||
<p>当常量的值被给定后,常量就将常量名称和表达式初始值不变的结合在了一起,而且不能更改。
|
||||
这意味着如果常量以类的形式被初始化,类本身的内容是可以改变的,但是常量和类之间的结合关系是不能改变的。
|
||||
当一个常量被声明为全局变量,它必须被给定一个初始值。当一个常量在类或者结构体中被声明时,它被认为是一个常量
|
||||
属性。常量并不是可计算的属性,因此不包含getters和setters。(译者注:getters和setters不知道怎么翻译,待改进)</p>
|
||||
<p>如果常量名是一个元祖形式,元祖中的每一项初始化表达式中都要有对应的值</p>
|
||||
<pre><code>let (firstNumber, secondNumber) = (10, 42)
|
||||
</code></pre><p>在上例中,firstNumber是一个值为10的常量,secnodeName是一个值为42的常量。所有常量都可以独立的使用:</p>
|
||||
<pre><code>println("The first number is \(firstNumber).")
|
||||
<pre><code class="lang-swift">let (firstNumber, secondNumber) = (10, 42)
|
||||
</code></pre>
|
||||
<p>在上例中,firstNumber是一个值为10的常量,secnodeName是一个值为42的常量。所有常量都可以独立的使用:</p>
|
||||
<pre><code class="lang-swift">println("The first number is \(firstNumber).")
|
||||
// prints "The first number is 10."
|
||||
println("The second number is \(secondNumber).")
|
||||
// prints "The second number is 42."
|
||||
</code></pre><p>类型注释(:type)在常量声明中是一个可选项,它可以用来描述在类型推断(type inference)中找到的类型。</p>
|
||||
</code></pre>
|
||||
<p>类型注释(:type)在常量声明中是一个可选项,它可以用来描述在类型推断(type inference)中找到的类型。</p>
|
||||
<p>声明一个静态常量要使用关键字static。静态属性在类型属性(type propetries)中有介绍。</p>
|
||||
<p>如果还想获得更多关于常量的信息或者想在使用中获得帮助,请查看常量和变量(constants and variables),
|
||||
存储属性(stored properties)等节。</p>
|
||||
@ -679,28 +687,24 @@ println("The second number is \(secondNumber).")
|
||||
变量和属性,存储变量和属性监视,和静态变量属性,有着不同的声明形式。(待改进)
|
||||
所使用的声明形式取决于变量所声明的范围和你打算声明的变量类型。</p>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<p>你也可以在协议声明的上下文声明属性,详情参见类型属性声明。</p>
|
||||
<p>注意:<br>你也可以在协议声明的上下文声明属性,详情参见类型属性声明。</p>
|
||||
</blockquote>
|
||||
<h3 id="-">存储型变量和存储型属性</h3>
|
||||
<p>下面的形式声明了一个存储型变量或存储型变量属性</p>
|
||||
<pre><code>var variable name: type = expression
|
||||
</code></pre><p>你可以在全局,函数内,或者在类和结构体的声明(context)中使用这种形式来声明一个变量。当变量以这种形式
|
||||
<blockquote>
|
||||
<p>var <code>variable name</code>: <code>type</code> = <code>expression</code></p>
|
||||
</blockquote>
|
||||
<p>你可以在全局,函数内,或者在类和结构体的声明(context)中使用这种形式来声明一个变量。当变量以这种形式
|
||||
在全局或者一个函数内被声明时,它代表一个存储型变量。当它在类或者结构体中被声明时,它代表一个存储型变量属性。</p>
|
||||
<p>构造器表达式可以被</p>
|
||||
<p>和常量声明相比,如果变量名是一个元祖类型,元祖的每一项的名字都要和初始化表达式一致。</p>
|
||||
<p>正如名字一样,存储型变量的值或存储型变量属性存储在内存中。</p>
|
||||
<h3 id="-">计算型变量和计算型属性</h3>
|
||||
<p>如下形式声明一个一个存储型变量或存储型属性:</p>
|
||||
<pre><code>var variable name: type {
|
||||
get {
|
||||
statements
|
||||
}
|
||||
set(setter name) {
|
||||
statements
|
||||
}
|
||||
}
|
||||
</code></pre><p>你可以在全局,函数体内或者类,结构体,枚举,扩展声明的上下文中使用这种形式的声明。
|
||||
<blockquote>
|
||||
<p>var <code>variable name</code>: <code>type</code> {<br>get {<br> <code>statements</code><br>}<br>set(<code>setter name</code>) {<br> <code>statements</code><br>}<br>} </p>
|
||||
</blockquote>
|
||||
<p>你可以在全局,函数体内或者类,结构体,枚举,扩展声明的上下文中使用这种形式的声明。
|
||||
当变量以这种形式在全局或者一个函数内被声明时,它代表一个计算型变量。当它在类,结构体,枚举,扩展声明的上下文
|
||||
中中被声明时,它代表一个计算型变量属性。</p>
|
||||
<p>getter用来读取变量值,setter用来写入变量值。setter子句是可选择的,只有getter是必需的,你可以将这些语句
|
||||
@ -712,15 +716,10 @@ setter的初始名为newValue,正如在seter声明速记(shorthand setter decl
|
||||
<p>获得更多信息,查看更多关于计算型属性的例子,请查看计算型属性(computed properties)一节。</p>
|
||||
<h3 id="-">存储型变量监视器和属性监视器</h3>
|
||||
<p>你可以用willset和didset监视器来声明一个存储型变量或属性。一个包含监视器的存储型变量或属性按如下的形式声明:</p>
|
||||
<pre><code>var variable name: type = expression {
|
||||
willSet(setter name) {
|
||||
statements
|
||||
}
|
||||
didSet(setter name {
|
||||
statements
|
||||
}
|
||||
}
|
||||
</code></pre><p>你可以在全局,函数体内或者类,结构体,枚举,扩展声明的上下文中使用这种形式的声明。
|
||||
<blockquote>
|
||||
<p>var <code>variable name</code>: <code>type</code> = expression {<br>willSet(setter name) {<br> <code>statements</code><br>}<br>didSet(<code>setter name</code>) {<br> <code>statements</code><br>}<br>} </p>
|
||||
</blockquote>
|
||||
<p>你可以在全局,函数体内或者类,结构体,枚举,扩展声明的上下文中使用这种形式的声明。
|
||||
当变量以这种形式在全局或者一个函数内被声明时,监视器代表一个存储型变量监视器;
|
||||
当它在类,结构体,枚举,扩展声明的上下文中被声明时,监视器代表属性监视器。</p>
|
||||
<p>你可以为适合的监视器添加任何存储型属性。你也可以通过重写子类属性的方式为适合的监视器添加任何继承的属性
|
||||
@ -746,8 +745,10 @@ willset监视器初始名为newvalue,didset监视器初始名为oldvalue。</p
|
||||
<h2 id="-">类型的别名声明</h2>
|
||||
<p>类型别名的声明可以在你的程序里为一个已存在的类型声明一个别名。类型的别名声明以关键字typealias开始,遵循如下的
|
||||
形式:</p>
|
||||
<pre><code>typealias name = existing type
|
||||
</code></pre><p>当一个类型被别名被声明后,你可以在你程序的任何地方使用别名来代替已存在的类型。已存在的类型可以是已经被命名的
|
||||
<blockquote>
|
||||
<p><code>typealias name</code> = <code>existing type</code></p>
|
||||
</blockquote>
|
||||
<p>当一个类型被别名被声明后,你可以在你程序的任何地方使用别名来代替已存在的类型。已存在的类型可以是已经被命名的
|
||||
类型或者是混合类型。类型的别名不产生新的类型,它只是简单的和已存在的类型做名称替换。</p>
|
||||
<p>查看更多Protocol Associated Type Declaration.</p>
|
||||
<blockquote>
|
||||
@ -757,14 +758,14 @@ willset监视器初始名为newvalue,didset监视器初始名为oldvalue。</p
|
||||
<h2 id="-">函数声明</h2>
|
||||
<p>你可以使用函数声明在你的程序里引入新的函数。函数可以在类的上下文,结构体,枚举,或者作为方法的协议中被声明。
|
||||
函数声明使用关键字func,遵循如下的形式:</p>
|
||||
<pre><code>func function name(parameters) -> return type {
|
||||
statements
|
||||
}
|
||||
</code></pre><p>如果函数不返回任何值,返回类型可以被忽略,如下所示:</p>
|
||||
<pre><code>func function name(parameters) {
|
||||
statements
|
||||
}
|
||||
</code></pre><p>每个参数的类型都要标明,它们不能被推断出来。初始时函数的参数是常值。在这些参数前面添加var使它们成为变量,
|
||||
<blockquote>
|
||||
<p>func <code>function name</code>(<code>parameters</code>) -> <code>return type</code> {<br> <code>statements</code><br>} </p>
|
||||
</blockquote>
|
||||
<p>如果函数不返回任何值,返回类型可以被忽略,如下所示:</p>
|
||||
<blockquote>
|
||||
<p>func <code>function name</code>(<code>parameters</code>) {<br> <code>statements</code><br>} </p>
|
||||
</blockquote>
|
||||
<p>每个参数的类型都要标明,它们不能被推断出来。初始时函数的参数是常值。在这些参数前面添加var使它们成为变量,
|
||||
作用域内任何对变量的改变只在函数体内有效,或者用inout使的这些改变可以在调用域内生效。
|
||||
更多关于in-out参数的讨论,参见in-out参数(in-out parameters)</p>
|
||||
<p>函数可以使用元组类型作为返回值来返回多个变量。</p>
|
||||
@ -772,36 +773,39 @@ willset监视器初始名为newvalue,didset监视器初始名为oldvalue。</p
|
||||
<h3 id="-">参数名</h3>
|
||||
<p>函数的参数是一个以逗号分隔的列表 。函数调用是的变量顺序必须和函数声明时的参数顺序一致。
|
||||
最简单的参数列表有着如下的形式:</p>
|
||||
<pre><code>parameter name: parameter type
|
||||
</code></pre><p>对于函数参数来讲,参数名在函数体内被使用,而不是在函数调用时使用。对于方法参数,参数名在函数体内被使用,
|
||||
<blockquote>
|
||||
<p><code>parameter name</code>: <code>parameter type</code></p>
|
||||
</blockquote>
|
||||
<p>对于函数参数来讲,参数名在函数体内被使用,而不是在函数调用时使用。对于方法参数,参数名在函数体内被使用,
|
||||
同时也在方法被调用时作为标签被使用。该方法的第一个参数名仅仅在函数体内被使用,就像函数的参数一样,举例来讲:</p>
|
||||
<pre><code>func f(x: Int, y: String) -> String {
|
||||
<pre><code class="lang-swift">func f(x: Int, y: String) -> String {
|
||||
return y + String(x)
|
||||
}
|
||||
f(7, "hello") // x and y have no name
|
||||
|
||||
class C {
|
||||
</code></pre>
|
||||
<pre><code class="lang-swift">class C {
|
||||
func f(x: Int, y: String) -> String {
|
||||
return y + String(x)
|
||||
}
|
||||
}
|
||||
let c = C()
|
||||
c.f(7, y: "hello") // x没有名称,y有名称
|
||||
</code></pre><p>你可以按如下的形式,重写参数名被使用的过程:</p>
|
||||
<pre><code>external parameter name local parameter name: parameter type
|
||||
#parameter name: parameter type
|
||||
_ local parameter name: parameter type
|
||||
</code></pre><p>在本地参数前命名的第二名称(second name)使得参数有一个扩展名。且不同于本地的参数名。
|
||||
</code></pre>
|
||||
<p>你可以按如下的形式,重写参数名被使用的过程:</p>
|
||||
<blockquote>
|
||||
<p><code>external parameter name</code> <code>local parameter name</code>: <code>parameter type</code><br>#<code>parameter name</code>: <code>parameter type</code><br>_ <code>local parameter name</code>: <code>parameter type</code> </p>
|
||||
</blockquote>
|
||||
<p>在本地参数前命名的第二名称(second name)使得参数有一个扩展名。且不同于本地的参数名。
|
||||
扩展参数名在函数被调用时必须被使用。对应的参数在方法或函数被调用时必须有扩展名 。</p>
|
||||
<p>在参数名前所写的哈希符号(#)代表着这个参数名可以同时作为外部或本体参数名来使用。等同于书写两次本地参数名。
|
||||
在函数或方法调用时,与其对应的语句必须包含这个名字。</p>
|
||||
<p>本地参数名前的强调字符(_)使参数在函数被调用时没有名称。在函数或方法调用时,与其对应的语句必须没有名字。</p>
|
||||
<h3 id="-">特殊类型的参数</h3>
|
||||
<p>参数可以被忽略,值可以是变化的,并且提供一个初始值,这种方法有着如下的形式:</p>
|
||||
<pre><code>_ : <#parameter type#.
|
||||
parameter name: parameter type...
|
||||
parameter name: parameter type = default argument value
|
||||
</code></pre><p>以强调符(_)命名的参数明确的在函数体内不能被访问。</p>
|
||||
<blockquote>
|
||||
<p>_ : <#parameter type#.<br><code>parameter name</code>: <code>parameter type</code>...<br><code>parameter name</code>: <code>parameter type</code> = <code>default argument value</code> </p>
|
||||
</blockquote>
|
||||
<p>以强调符(_)命名的参数明确的在函数体内不能被访问。</p>
|
||||
<p>一个以基础类型名的参数,如果紧跟着三个点(...),被理解为是可变参数。一个函数至多可以拥有一个可变参数,
|
||||
且必须是最后一个参数。可变参数被作为该基本类型名的数组来看待。举例来讲,可变参数int...被看做是int[]。
|
||||
查看可变参数的使用例子,详见可变参数(variadic parameters)一节。</p>
|
||||
@ -815,11 +819,11 @@ f()和f(x:7)都是只有一个变量x的函数的有效调用,但是f(7)是非
|
||||
<p>和类型相关而不是和类型实例相关的方法必须在static声明的结构以或枚举内,亦或是以class关键字定义的类内。</p>
|
||||
<h3 id="-">柯里化函数和方法</h3>
|
||||
<p>柯里化函数或方法有着如下的形式:</p>
|
||||
<pre><code>func function name(parameters)(parameters) -> return type {
|
||||
statements
|
||||
}
|
||||
</code></pre><p>以这种形式定义的函数的返回值是另一个函数。举例来说,下面的两个声明时等价的:</p>
|
||||
<pre><code>func addTwoNumbers(a: Int)(b: Int) -> Int {
|
||||
<blockquote>
|
||||
<p>func <code>function name</code>(<code>parameters</code>)(<code>parameters</code>) -> <code>return type</code> {<br> <code>statements</code><br>} </p>
|
||||
</blockquote>
|
||||
<p>以这种形式定义的函数的返回值是另一个函数。举例来说,下面的两个声明时等价的:</p>
|
||||
<pre><code class="lang-swift">func addTwoNumbers(a: Int)(b: Int) -> Int {
|
||||
return a + b
|
||||
}
|
||||
func addTwoNumbers(a: Int) -> (Int -> Int) {
|
||||
@ -828,9 +832,10 @@ func addTwoNumbers(a: Int) -> (Int -> Int) {
|
||||
}
|
||||
return addTheSecondNumber
|
||||
}
|
||||
|
||||
addTwoNumbers(4)(5) // Returns 9
|
||||
</code></pre><p>多级柯里化应用如下</p>
|
||||
</code></pre>
|
||||
<pre><code class="lang-swift">addTwoNumbers(4)(5) // Returns 9
|
||||
</code></pre>
|
||||
<p>多级柯里化应用如下</p>
|
||||
<blockquote>
|
||||
<p>函数声明语法<br><em>函数声明</em> → <a href="..\chapter3\05_Declarations.html#function_head"><em>函数头</em></a> <a href="..\chapter3\05_Declarations.html#function_name"><em>函数名</em></a> <a href="GenericParametersAndArguments.html#generic_parameter_clause"><em>泛型参数子句</em></a> <em>可选</em> <a href="..\chapter3\05_Declarations.html#function_signature"><em>函数签名(Signature)</em></a> <a href="..\chapter3\05_Declarations.html#function_body"><em>函数体</em></a><br><em>函数头</em> → <a href="..\chapter3\06_Attributes.html#attributes"><em>特性(Attributes)列表</em></a> <em>可选</em> <a href="..\chapter3\05_Declarations.html#declaration_specifiers"><em>声明描述符(Specifiers)列表</em></a> <em>可选</em> <strong>func</strong><br><em>函数名</em> → <a href="LexicalStructure.html#identifier"><em>标识符</em></a> | <a href="LexicalStructure.html#operator"><em>运算符</em></a><br><em>函数签名(Signature)</em> → <a href="..\chapter3\05_Declarations.html#parameter_clauses"><em>parameter-clauses</em></a> <a href="..\chapter3\05_Declarations.html#function_result"><em>函数结果</em></a> <em>可选</em><br><em>函数结果</em> → <strong>-></strong> <a href="..\chapter3\06_Attributes.html#attributes"><em>特性(Attributes)列表</em></a> <em>可选</em> <a href="..\chapter3\03_Types.html#type"><em>类型</em></a><br><em>函数体</em> → <a href="..\chapter3\05_Declarations.html#code_block"><em>代码块</em></a><br><em>parameter-clauses</em> → <a href="..\chapter3\05_Declarations.html#parameter_clause"><em>参数子句</em></a> <a href="..\chapter3\05_Declarations.html#parameter_clauses"><em>parameter-clauses</em></a> <em>可选</em><br><em>参数子句</em> → <strong>(</strong> <strong>)</strong> | <strong>(</strong> <a href="..\chapter3\05_Declarations.html#parameter_list"><em>参数列表</em></a> <strong>...</strong> <em>可选</em> <strong>)</strong><br><em>参数列表</em> → <a href="..\chapter3\05_Declarations.html#parameter"><em>参数</em></a> | <a href="..\chapter3\05_Declarations.html#parameter"><em>参数</em></a> <strong>,</strong> <a href="..\chapter3\05_Declarations.html#parameter_list"><em>参数列表</em></a><br><em>参数</em> → <strong>inout</strong> <em>可选</em> <strong>let</strong> <em>可选</em> <strong>#</strong> <em>可选</em> <a href="..\chapter3\05_Declarations.html#parameter_name"><em>参数名</em></a> <a href="..\chapter3\05_Declarations.html#local_parameter_name"><em>本地参数名</em></a> <em>可选</em> <a href="..\chapter3\03_Types.html#type_annotation"><em>类型注解</em></a> <a href="..\chapter3\05_Declarations.html#default_argument_clause"><em>默认参数子句</em></a> <em>可选</em><br><em>参数</em> → <strong>inout</strong> <em>可选</em> <strong>var</strong> <strong>#</strong> <em>可选</em> <a href="..\chapter3\05_Declarations.html#parameter_name"><em>参数名</em></a> <a href="..\chapter3\05_Declarations.html#local_parameter_name"><em>本地参数名</em></a> <em>可选</em> <a href="..\chapter3\03_Types.html#type_annotation"><em>类型注解</em></a> <a href="..\chapter3\05_Declarations.html#default_argument_clause"><em>默认参数子句</em></a> <em>可选</em><br><em>参数</em> → <a href="..\chapter3\06_Attributes.html#attributes"><em>特性(Attributes)列表</em></a> <em>可选</em> <a href="..\chapter3\03_Types.html#type"><em>类型</em></a><br><em>参数名</em> → <a href="LexicalStructure.html#identifier"><em>标识符</em></a> | <strong>_</strong><br><em>本地参数名</em> → <a href="LexicalStructure.html#identifier"><em>标识符</em></a> | <strong>_</strong><br><em>默认参数子句</em> → <strong>=</strong> <a href="..\chapter3\04_Expressions.html#expression"><em>表达式</em></a> </p>
|
||||
</blockquote>
|
||||
@ -847,29 +852,28 @@ addTwoNumbers(4)(5) // Returns 9
|
||||
<p>你可以扩展枚举类型,正如在扩展名声明(Extension Declaration)中讨论的一样。</p>
|
||||
<h3 id="-">任意事件类型的枚举</h3>
|
||||
<p>如下的形式声明了一个包含任意类型枚举时间的枚举变量</p>
|
||||
<pre><code>enum enumeration name {
|
||||
case enumeration case 1
|
||||
case enumeration case 2(associated value types)
|
||||
}
|
||||
</code></pre><p>这种形式的枚举声明在其他语言中有时被叫做可识别联合(discrinminated)。</p>
|
||||
<blockquote>
|
||||
<p>enum <code>enumeration name</code> {<br> case <code>enumeration case 1</code><br> case <code>enumeration case 2</code>(<code>associated value types</code>)<br>} </p>
|
||||
</blockquote>
|
||||
<p>这种形式的枚举声明在其他语言中有时被叫做可识别联合(discrinminated)。</p>
|
||||
<p>这种形式中,每一个事件块由关键字case开始,后面紧接着一个或多个以逗号分隔的枚举事件。每一个事件名必须是
|
||||
独一无二的。每一个事件也可以指定它所存储的指定类型的值,这些类型在关联值类型的元祖里被指定,立即书写在事件
|
||||
名后。获得更多关于关联值类型的信息和例子,请查看关联值(associated values)一节。</p>
|
||||
<h3 id="-">使用原始事件值的枚举</h3>
|
||||
<p>以下的形式声明了一个包含相同基础类型的枚举事件的枚举:</p>
|
||||
<pre><code>enum enumeration name: raw value type {
|
||||
case enumeration case 1 = raw value 1
|
||||
case enumeration case 2 = raw value 2
|
||||
}
|
||||
</code></pre><p>在这种形式中,每一个事件块由case关键字开始,后面紧接着一个或多个以逗号分隔的枚举事件。和第一种形式的枚举
|
||||
<blockquote>
|
||||
<p>enum <code>enumeration name</code>: <code>raw value type</code> {<br> case <code>enumeration case 1</code> = <code>raw value 1</code><br> case <code>enumeration case 2</code> = <code>raw value 2</code><br>} </p>
|
||||
</blockquote>
|
||||
<p>在这种形式中,每一个事件块由case关键字开始,后面紧接着一个或多个以逗号分隔的枚举事件。和第一种形式的枚举
|
||||
事件不同,这种形式的枚举事件包含一个同类型的基础值,叫做原始值(raw value)。这些值的类型在原始值类型(raw value type)
|
||||
中被指定,必须是字面上的整数,浮点数,字符或者字符串。</p>
|
||||
<p>每一个事件必须有唯一的名字,必须有一个唯一的初始值。如果初始值类型被指定为int,则不必为事件显式的指定值,
|
||||
它们会隐式的被标为值0,1,2等。每一个没有被赋值的Int类型时间会隐式的赋予一个初始值,它们是自动递增的。</p>
|
||||
<pre><code>num ExampleEnum: Int {
|
||||
<pre><code class="lang-swift">num ExampleEnum: Int {
|
||||
case A, B, C = 5, D
|
||||
}
|
||||
</code></pre><p>在上面的例子中,ExampleEnum.A的值是0,ExampleEnum.B的值是。因为ExampleEnum.C的值被显式的设定为5,因此
|
||||
</code></pre>
|
||||
<p>在上面的例子中,ExampleEnum.A的值是0,ExampleEnum.B的值是。因为ExampleEnum.C的值被显式的设定为5,因此
|
||||
ExampleEnum.D的值会自动增长为6.</p>
|
||||
<p>枚举事件的初始值可以调用方法roRaw获得,如ExampleEnum.B.toRaw()。你也可以通过调用fromRaw方法来使用初始值找到
|
||||
其对应的事件,并返回一个可选的事件。查看更多信息和获取初始值类型事件的信息,参阅初始值(raw values)。</p>
|
||||
@ -884,10 +888,10 @@ ExampleEnum.D的值会自动增长为6.</p>
|
||||
<p><a name="structure_declaration"></a></p>
|
||||
<h2 id="-">结构体声明</h2>
|
||||
<p>使用结构体声明可以在你的程序里引入一个结构体类型。结构体声明使用struct关键字,遵循如下的形式:</p>
|
||||
<pre><code>struct structure name: adopted protocols {
|
||||
declarations
|
||||
}
|
||||
</code></pre><p>结构体内包含零或多个声明。这些声明可以包括存储型和计算型属性,静态属性,实例方法,静态方法,构造器,
|
||||
<blockquote>
|
||||
<p>struct <code>structure name</code>: <code>adopted protocols</code> {<br> <code>declarations</code><br>} </p>
|
||||
</blockquote>
|
||||
<p>结构体内包含零或多个声明。这些声明可以包括存储型和计算型属性,静态属性,实例方法,静态方法,构造器,
|
||||
类型别名,甚至其他结构体,类,和枚举声明。结构体声明不能包含析构器或者协议声明。详细讨论和包含多种结构体
|
||||
声明的实例,参见类和结构体一节。</p>
|
||||
<p>结构体可以包含任意数量的协议,但是不能继承自类,枚举或者其他结构体。</p>
|
||||
@ -906,10 +910,10 @@ ExampleEnum.D的值会自动增长为6.</p>
|
||||
<p><a name="class_declaration"></a></p>
|
||||
<h2 id="-">类声明</h2>
|
||||
<p>你可以在你的程序中使用类声明来引入一个类。类声明使用关键字class,遵循如下的形式:</p>
|
||||
<pre><code>class class name: superclass, adopted protocols {
|
||||
declarations
|
||||
}
|
||||
</code></pre><p>一个类内包含零或多个声明。这些声明可以包括存储型和计算型属性,实例方法,类方法,构造器,单独的析构器方法,
|
||||
<blockquote>
|
||||
<p>class <code>class name</code>: <code>superclass</code>, <code>adopted protocols</code> {<br> <code>declarations</code><br>} </p>
|
||||
</blockquote>
|
||||
<p>一个类内包含零或多个声明。这些声明可以包括存储型和计算型属性,实例方法,类方法,构造器,单独的析构器方法,
|
||||
类型别名,甚至其他结构体,类,和枚举声明。类声明不能包含协议声明。详细讨论和包含多种类声明的实例,参见类和
|
||||
结构体一节。</p>
|
||||
<p>一个类只能继承一个父类,超类,但是可以包含任意数量的协议。这些超类第一次在type-inheritance-clause出现,遵循任意协议。</p>
|
||||
@ -920,8 +924,10 @@ ExampleEnum.D的值会自动增长为6.</p>
|
||||
<p>虽然超类的属性和方法声明可以被当前类继承,但是超类声明的指定构造器却不能。这意味着,如果当前类重写了超类
|
||||
的所有指定构造器,它就继承了超类的方便构造器。Swift的类并不是继承自一个全局基础类。</p>
|
||||
<p>有两种方法来创建已声明的类的实例:</p>
|
||||
<p>-调用类的一个构造器,参见构造器(initializers)。</p>
|
||||
<p>-如果没有声明构造器,而且类的所有属性都被赋予了初始值,调用类的默认构造器,参见默认构造器(default initializers).</p>
|
||||
<ul>
|
||||
<li>调用类的一个构造器,参见构造器(initializers)。</li>
|
||||
<li>如果没有声明构造器,而且类的所有属性都被赋予了初始值,调用类的默认构造器,参见默认构造器(default initializers).</li>
|
||||
</ul>
|
||||
<p>类实例属性可以用点(.)来获得,详情参见获得属性(Accessing Properties)一节。</p>
|
||||
<p>类是引用类型;当被赋予常量或变量,函数调用时,类的实例是被引用,而不是复制。获得更多关于引用类型的信息,
|
||||
结构体和枚举都是值类型(Structures and Enumerations Are Value Types)一节。</p>
|
||||
@ -932,22 +938,19 @@ ExampleEnum.D的值会自动增长为6.</p>
|
||||
<p><a name="protocol_declaration"></a></p>
|
||||
<h2 id="-translated-by-">协议声明(translated by 小一)</h2>
|
||||
<p>一个协议声明为你的程序引入一个命名了的协议类型。协议声明使用 <code>protocol</code> 关键词来进行声明并有下面这样的形式:</p>
|
||||
<pre><code class="lang-javascript">protocol protocol name: inherited protocols {
|
||||
protocol member declarations
|
||||
}
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>protocol <code>protocol name</code>: <code>inherited protocols</code> {<br> <code>protocol member declarations</code><br>} </p>
|
||||
</blockquote>
|
||||
<p>协议的主体包含零或多个协议成员声明,这些成员描述了任何采用该协议必须满足的一致性要求。特别的,一个协议可以声明必须实现某些属性、方法、初始化程序及下标脚本的一致性类型。协议也可以声明专用种类的类型别名,叫做关联类型,它可以指定协议的不同声明之间的关系。协议成员声明会在下面的详情里进行讨论。</p>
|
||||
<p>协议类型可以从很多其它协议那继承。当一个协议类型从其它协议那继承的时候,来自其它协议的所有要求就集合了,而且从当前协议继承的任何类型必须符合所有的这些要求。对于如何使用协议继承的例子,查看<a href="../chapter2/21_Protocols.html#protocol_inheritance">协议继承</a></p>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<p>你也可以使用协议合成类型集合多个协议的一致性要求,详情参见<a href="../chapter3/03_Types.html#protocol_composition_type">协议合成类型</a>和<a href="../chapter2/21_Protocols.html#protocol_composition">协议合成</a></p>
|
||||
<p>注意:<br>你也可以使用协议合成类型集合多个协议的一致性要求,详情参见<a href="../chapter3/03_Types.html#protocol_composition_type">协议合成类型</a>和<a href="../chapter2/21_Protocols.html#protocol_composition">协议合成</a></p>
|
||||
</blockquote>
|
||||
<p>你可以通过采用在类型的扩展声明中的协议来为之前声明的类型添加协议一致性。在扩展中你必须实现所有采用协议的要求。如果该类型已经实现了所有的要求,你可以让这个扩展声明的主题留空。</p>
|
||||
<p>默认地,符合某一个协议的类型必须实现所有声明在协议中的属性、方法和下标脚本。也就是说,你可以用<code>optional</code>属性标注这些协议成员声明以指定它们的一致性类型实现是可选的。<code>optional</code>属性仅仅可以用于使用<code>objc</code>属性标记过的协议。这样的结果就是仅仅类类型可以采用并符合包含可选成员要求的协议。更多关于如何使用<code>optional</code>属性的信息及如何访问可选协议成员的指导——比如当你不能肯定是否一致性的类型实现了它们——参见<a href="../chapter2/21_Protocols.html#optional_protocol_requirements">可选协议要求</a></p>
|
||||
<p>为了限制协议的采用仅仅针对类类型,需要使用<code>class_protocol</code>属性标记整个协议声明。任意继承自标记有<code>class_protocol</code>属性协议的协议都可以智能地仅能被类类型采用。</p>
|
||||
<blockquote>
|
||||
<p>注意:</p>
|
||||
<p>如果协议已经用<code>object</code>属性标记了,<code>class_protocol</code>属性就隐性地应用于该协议;没有必要再明确地使用<code>class_protocol</code>属性来标记该协议了。</p>
|
||||
<p>注意:<br>如果协议已经用<code>object</code>属性标记了,<code>class_protocol</code>属性就隐性地应用于该协议;没有必要再明确地使用<code>class_protocol</code>属性来标记该协议了。</p>
|
||||
</blockquote>
|
||||
<p>协议是命名的类型,因此它们可以以另一个命名类型出现在你代码的所有地方,就像<a href="../chapter2/21_Protocols.html#protocols_as_types">协议类型</a>里讨论的那样。然而你不能构造一个协议的实例,因为协议实际上不提供它们指定的要求的实现。</p>
|
||||
<p>你可以使用协议来声明一个类的代理的方法或者应该实现的结构,就像<a href="../chapter2/21_Protocols.html#delegation">委托(代理)模式</a>描述的那样。</p>
|
||||
@ -957,8 +960,10 @@ ExampleEnum.D的值会自动增长为6.</p>
|
||||
<p><a name="protocol_property_declaration"></a></p>
|
||||
<h3 id="-">协议属性声明</h3>
|
||||
<p>协议声明了一致性类型必须在协议声明的主体里通过引入一个协议属性声明来实现一个属性。协议属性声明有一种特殊的类型声明形式:</p>
|
||||
<pre><code>var property name: type { get set }
|
||||
</code></pre><p>同其它协议成员声明一样,这些属性声明仅仅针对符合该协议的类型声明了<code>getter</code>和<code>setter</code>要求。结果就是你不需要在协议里它被声明的地方实现<code>getter</code>和<code>setter</code>。</p>
|
||||
<blockquote>
|
||||
<p>var <code>property name</code>: <code>type</code> { get set }</p>
|
||||
</blockquote>
|
||||
<p>同其它协议成员声明一样,这些属性声明仅仅针对符合该协议的类型声明了<code>getter</code>和<code>setter</code>要求。结果就是你不需要在协议里它被声明的地方实现<code>getter</code>和<code>setter</code>。</p>
|
||||
<p><code>getter</code>和<code>setter</code>要求可以通过一致性类型以各种方式满足。如果属性声明包含<code>get</code>和<code>set</code>关键词,一致性类型就可以用可读写(实现了<code>getter</code>和<code>setter</code>)的存储型变量属性或计算型属性,但是属性不能以常量属性或只读计算型属性实现。如果属性声明仅仅包含<code>get</code>关键词的话,它可以作为任意类型的属性被实现。比如说实现了协议的属性要求的一致性类型,参见<a href="../chapter2/21_Protocols.html#property_requirements">属性要求</a></p>
|
||||
<p>更多参见<a href="../chapter3/05_Declarations.html#variable_declaration">变量声明</a></p>
|
||||
<blockquote>
|
||||
@ -985,8 +990,10 @@ ExampleEnum.D的值会自动增长为6.</p>
|
||||
<h3 id="-">协议下标脚本声明</h3>
|
||||
<p>协议声明了一致性类型必须在协议声明的主体里通过引入一个协议下标脚本声明来实现一个下标脚本。协议属性声明
|
||||
对下标脚本声明有一个特殊的形式:</p>
|
||||
<pre><code>subscript (parameters) -> return type { get set }
|
||||
</code></pre><p>下标脚本声明只为和协议一致的类型声明了必需的最小数量的的getter和setter。如果下标脚本申明包含get和set关键字,
|
||||
<blockquote>
|
||||
<p>subscript (<code>parameters</code>) -> <code>return type</code> { get set }</p>
|
||||
</blockquote>
|
||||
<p>下标脚本声明只为和协议一致的类型声明了必需的最小数量的的getter和setter。如果下标脚本申明包含get和set关键字,
|
||||
一致的类型也必须有一个getter和setter语句。如果下标脚本声明值包含get关键字,一致的类型必须至少包含一个
|
||||
getter语句,可以选择是否包含setter语句。</p>
|
||||
<p>更多参阅下标脚本声明。</p>
|
||||
@ -1006,19 +1013,19 @@ getter语句,可以选择是否包含setter语句。</p>
|
||||
<p>结构体,枚举,类可以有任意数量的构造器,但是类的构造器的规则和行为是不一样的。不像结构体和枚举那样,类
|
||||
有两种结构体,designed initializers 和convenience initializers,参见构造器一节。</p>
|
||||
<p>如下的形式声明了结构体,枚举和类的指定构造器:</p>
|
||||
<pre><code>init(parameters) {
|
||||
statements
|
||||
}
|
||||
</code></pre><p>类的指定构造器将类的所有属性直接初始化。如果类有超类,它不能调用该类的其他构造器,它只能调用超类的一个
|
||||
<blockquote>
|
||||
<p>init(<code>parameters</code>) {<br> <code>statements</code><br>} </p>
|
||||
</blockquote>
|
||||
<p>类的指定构造器将类的所有属性直接初始化。如果类有超类,它不能调用该类的其他构造器,它只能调用超类的一个
|
||||
指定构造器。如果该类从它的超类处继承了任何属性,这些属性在当前类内被赋值或修饰时,必须带哦用一个超类的
|
||||
指定构造器。</p>
|
||||
<p>指定构造器可以在类声明的上下文中声明,因此它不能用扩展声明的方法加入一个类中。</p>
|
||||
<p>结构体和枚举的构造器可以带哦用其他的已声明的构造器,来委托其中一个火全部进行初始化过程。</p>
|
||||
<p>以关键字convenience来声明一个类的便利构造器:</p>
|
||||
<pre><code>convenience init(parameters) {
|
||||
statements
|
||||
}
|
||||
</code></pre><p>便利构造器可以将初始化过程委托给另一个便利构造器或类的一个指定构造器。这意味着,类的初始化过程必须
|
||||
<blockquote>
|
||||
<p>convenience init(<code>parameters</code>) {<br> <code>statements</code><br>} </p>
|
||||
</blockquote>
|
||||
<p>便利构造器可以将初始化过程委托给另一个便利构造器或类的一个指定构造器。这意味着,类的初始化过程必须
|
||||
以一个将所有类属性完全初始化的指定构造器的调用作为结束。便利构造器不能调用超类的构造器。</p>
|
||||
<p>你可以使用requierd关键字,将便利构造器和指定构造器标记为每个子类的构造器都必须拥有的。因为指定构造器
|
||||
不被子类继承,它们必须被立即执行。当子类直接执行所有超类的指定构造器(或使用便利构造器重写指定构造器)时,
|
||||
@ -1031,10 +1038,10 @@ overrride关键字。</p>
|
||||
<p><a name="deinitializer_declaration"></a></p>
|
||||
<h2 id="-">析构声明</h2>
|
||||
<p>析构声明为类声明了一个析构器。析构器没有参数,遵循如下的格式:</p>
|
||||
<pre><code>deinit {
|
||||
statements
|
||||
}
|
||||
</code></pre><p>当类没有任何语句时将要被释放时,析构器会自动的被调用。析构器在类的声明体内只能被声明一次——但是不能在
|
||||
<blockquote>
|
||||
<p>deinit {<br> <code>statements</code><br>} </p>
|
||||
</blockquote>
|
||||
<p>当类没有任何语句时将要被释放时,析构器会自动的被调用。析构器在类的声明体内只能被声明一次——但是不能在
|
||||
类的扩展声明内,每个类最多只能有一个。</p>
|
||||
<p>子类继承了它的超类的析构器,在子类将要被释放时隐式的调用。子类在所有析构器被执行完毕前不会被释放。</p>
|
||||
<p>析构器不会被直接调用。</p>
|
||||
@ -1045,10 +1052,10 @@ overrride关键字。</p>
|
||||
<p><a name="extension_declaration"></a></p>
|
||||
<h2 id="-">扩展声明</h2>
|
||||
<p>扩展声明用于扩展一个现存的类,结构体,枚举的行为。扩展声明以关键字extension开始,遵循如下的规则:</p>
|
||||
<pre><code>extension type: adopted protocols {
|
||||
declarations
|
||||
}
|
||||
</code></pre><p>一个扩展声明体包括零个或多个声明。这些声明可以包括计算型属性,计算型静态属性,实例方法,静态和类方法,构造器,
|
||||
<blockquote>
|
||||
<p>extension <code>type</code>: <code>adopted protocols</code> {<br> <code>declarations</code><br>} </p>
|
||||
</blockquote>
|
||||
<p>一个扩展声明体包括零个或多个声明。这些声明可以包括计算型属性,计算型静态属性,实例方法,静态和类方法,构造器,
|
||||
下标脚本声明,甚至其他结构体,类,和枚举声明。扩展声明不能包含析构器,协议声明,存储型属性,属性监测器或其他
|
||||
的扩展属性。详细讨论和查看包含多种扩展声明的实例,参见扩展一节。</p>
|
||||
<p>扩展声明可以向现存的类,结构体,枚举内添加一致的协议。扩展声明不能向一个类中添加继承的类,因此
|
||||
@ -1061,37 +1068,11 @@ type-inheritance-clause是一个只包含协议列表的扩展声明。</p>
|
||||
</blockquote>
|
||||
<p><a name="subscript_declaration"></a></p>
|
||||
<h2 id="-translated-by-">下标脚本声明(translated by 林)</h2>
|
||||
<p><<<<<<< HEAD
|
||||
附属脚本用于向特定类型添加附属脚本支持,通常为访问集合,列表和序列的元素时提供语法便利。附属脚本声明使用关键字<code>subscript</code>,声明形式如下:</p>
|
||||
<p>附属脚本用于向特定类型添加附属脚本支持,通常为访问集合,列表和序列的元素时提供语法便利。附属脚本声明使用关键字<code>subscript</code>,声明形式如下:</p>
|
||||
<blockquote>
|
||||
<p>subscript (<code>parameter</code>) -> (return type){<br> get{<br> <code>statements</code><br> }<br> set(<code>setter name</code>){<br> <code>statements</code><br> }<br>} </p>
|
||||
</blockquote>
|
||||
<h1 id="-">附属脚本声明只能在类,结构体,枚举,扩展和协议声明的上下文进行声明。</h1>
|
||||
<p>下标脚本用于向特定类型添加下标脚本支持,通常为访问集合,列表和序列的元素时提供语法便利。下标脚本声明使用关键字<code>subscript</code>,声明形式如下:</p>
|
||||
<blockquote>
|
||||
<p>subscript (<code>parameter</code>) -> (return type){
|
||||
get{
|
||||
<code>statements</code>
|
||||
}
|
||||
set(<code>setter name</code>){
|
||||
<code>statements</code>
|
||||
}
|
||||
}
|
||||
下标脚本声明只能在类,结构体,枚举,扩展和协议声明的上下文进行声明。</p>
|
||||
<blockquote>
|
||||
<blockquote>
|
||||
<blockquote>
|
||||
<blockquote>
|
||||
<blockquote>
|
||||
<blockquote>
|
||||
<p>a516af6a531a104ec88da0d236ecf389a5ec72af</p>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
<p>subscript (<code>parameter</code>) -> (return type){<br> get{<br> <code>statements</code><br> }<br> set(<code>setter name</code>){<br> <code>statements</code><br> }<br>} </p>
|
||||
</blockquote>
|
||||
<p>附属脚本声明只能在类,结构体,枚举,扩展和协议声明的上下文进行声明。</p>
|
||||
<p><em>变量(parameters)</em>指定一个或多个用于在相关类型的下标脚本中访问元素的索引(例如,表达式<code>object[i]</code>中的<code>i</code>)。尽管用于元素访问的索引可以是任意类型的,但是每个变量必须包含一个用于指定每种索引类型的类型标注。<em>返回类型(return type)</em>指定被访问的元素的类型。</p>
|
||||
<p>和计算性属性一样,下标脚本声明支持对访问元素的读写操作。getter用于读取值,setter用于写入值。setter子句是可选的,当仅需要一个getter子句时,可以将二者都忽略且直接返回请求的值即可。也就是说,如果使用了setter子句,就必须使用getter子句。</p>
|
||||
<p>setter的名字和封闭的括号是可选的。如果使用了setter名称,它会被当做传给setter的变量的名称。如果不使用setter名称,那么传给setter的变量的名称默认是<code>value</code>。setter名称的类型必须与<em>返回类型(return type)</em>的类型相同。</p>
|
||||
@ -1099,7 +1080,7 @@ type-inheritance-clause是一个只包含协议列表的扩展声明。</p>
|
||||
<p>同样可以在协议声明的上下文中声明下标脚本,<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-XID_619" target="_blank">Protocol Subscript Declaration</a>中有所描述。</p>
|
||||
<p>更多关于下标脚本和下标脚本声明的例子,请参考<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Subscripts.html#//apple_ref/doc/uid/TP40014097-CH16-XID_393" target="_blank">Subscripts</a>。</p>
|
||||
<blockquote>
|
||||
<p>附属脚本声明语法<br><em>附属脚本声明</em> → <a href="..\chapter3\05_Declarations.html#subscript_head"><em>附属脚本头(Head)</em></a> <a href="..\chapter3\05_Declarations.html#subscript_result"><em>附属脚本结果(Result)</em></a> <a href="..\chapter3\05_Declarations.html#code_block"><em>代码块</em></a><br><em>附属脚本声明</em> → <a href="..\chapter3\05_Declarations.html#subscript_head"><em>附属脚本头(Head)</em></a> <a href="..\chapter3\05_Declarations.html#subscript_result"><em>附属脚本结果(Result)</em></a> <a href="..\chapter3\05_Declarations.html#getter_setter_block"><em>getter-setter块</em></a><br><em>附属脚本声明</em> → <a href="..\chapter3\05_Declarations.html#subscript_head"><em>附属脚本头(Head)</em></a> <a href="..\chapter3\05_Declarations.html#subscript_result"><em>附属脚本结果(Result)</em></a> <a href="..\chapter3\05_Declarations.html#getter_setter_keyword_block"><em>getter-setter关键字(Keyword)块</em></a><br><em>附属脚本头(Head)</em> → <a href="..\chapter3\06_Attributes.html#attributes"><em>特性(Attributes)列表</em></a> <em>可选</em> <strong>subscript</strong> <a href="..\chapter3\05_Declarations.html#parameter_clause"><em>参数子句</em></a><br><em>附属脚本结果(Result)</em> → <strong>-></strong> <a href="..\chapter3\06_Attributes.html#attributes"><em>特性(Attributes)列表</em></a> <em>可选</em> <a href="..\chapter3\03_Types.html#type"><em>类型</em></a> </p>
|
||||
<p>附属脚本声明语法<br><em>附属脚本声明</em> → <a href="..\chapter3\05_Declarations.html#subscript_head"><em>附属脚本头(Head)</em></a> <a href="..\chapter3\05_Declarations.html#subscript_result"><em>附属脚本结果(Result)</em></a> <a href="..\chapter3\05_Declarations.html#code_block"><em>代码块</em></a><br><em>附属脚本声明</em> → <a href="..\chapter3\05_Declarations.html#subscript_head"><em>附属脚本头(Head)</em></a> <a href="..\chapter3\05_Declarations.html#subscript_result"><em>附属脚本结果(Result)</em></a> <a href="..\chapter3\05_Declarations.html#getter_setter_block"><em>getter-setter块</em></a><br><em>附属脚本声明</em> → <a href="..\chapter3\05_Declarations.html#subscript_head"><em>附属脚本头(Head)</em></a> <a href="..\chapter3\05_Declarations.html#subscript_result"><em>附属脚本结果(Result)</em></a> <a href="..\chapter3\05_Declarations.html#getter_setter_keyword_block"><em>getter-setter关键字(Keyword)块</em></a><br><em>附属脚本头(Head)</em> → <a href="..\chapter3\06_Attributes.html#attributes"><em>特性(Attributes)列表</em></a> <em>可选</em> <strong>subscript</strong> <a href="..\chapter3\05_Declarations.html#parameter_clause"><em>参数子句</em></a><br><em>附属脚本结果(Result)</em> → <strong>-></strong> <a href="..\chapter3\06_Attributes.html#attributes"><em>特性(Attributes)列表</em></a> <em>可选</em> <a href="..\chapter3\03_Types.html#type"><em>类型</em></a> </p>
|
||||
</blockquote>
|
||||
<p><a name="operator_declaration"></a></p>
|
||||
<h2 id="-translated-by-">运算符声明(translated by 林)</h2>
|
||||
@ -1108,10 +1089,7 @@ type-inheritance-clause是一个只包含协议列表的扩展声明。</p>
|
||||
运算符声明有三种基本形式,每种缀性各一种。运算符的缀性通过在<code>operator</code>和运算符之间添加上下文关键字<code>infix</code>,<code>prefix</code>或<code>postfix</code>来指定。每种形式中,运算符的名字只能包含<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/doc/uid/TP40014097-CH30-XID_871" target="_blank">Operators</a>中定义的运算符字符。</p>
|
||||
<p>下面的这种形式声明了一个新的中缀运算符:</p>
|
||||
<blockquote>
|
||||
<p>operator infix <code>operator name</code>{
|
||||
precedence <code>precedence level</code>
|
||||
associativity <code>associativity</code>
|
||||
}</p>
|
||||
<p>operator infix <code>operator name</code>{<br> previewprecedence <code>precedence level</code><br> associativity <code>associativity</code><br>} </p>
|
||||
</blockquote>
|
||||
<p><em>中缀</em>运算符是二元运算符,它可以被置于两个操作数之间,比如表达式<code>1 + 2</code> 中的加法运算符(<code>+</code>)。</p>
|
||||
<p>中缀运算符可以可选地指定优先级,结合性,或两者同时指定。</p>
|
||||
@ -1121,13 +1099,13 @@ type-inheritance-clause是一个只包含协议列表的扩展声明。</p>
|
||||
<p>声明时不指定任何优先级或结合性的中缀运算符,它们的优先级会被初始化为100,结合性被初始化为<code>none</code>。</p>
|
||||
<p>下面的这种形式声明了一个新的前缀运算符:</p>
|
||||
<blockquote>
|
||||
<p>operator prefix <code>operator name</code>{}</p>
|
||||
<p>operator prefix <code>operator name</code>{} </p>
|
||||
</blockquote>
|
||||
<p>紧跟在操作数前边的<em>前缀运算符(prefix operator)</em>是一元运算符,例如表达式<code>++i</code>中的前缀递增运算符(<code>++</code>)。</p>
|
||||
<p>前缀运算符的声明中不指定优先级。前缀运算符是非结合的。</p>
|
||||
<p>下面的这种形式声明了一个新的后缀运算符:</p>
|
||||
<blockquote>
|
||||
<p>operator postfix <code>operator name</code>{}</p>
|
||||
<p>operator postfix <code>operator name</code>{} </p>
|
||||
</blockquote>
|
||||
<p>紧跟在操作数后边的<em>后缀运算符(postfix operator)</em>是一元运算符,例如表达式<code>i++</code>中的前缀递增运算符(<code>++</code>)。</p>
|
||||
<p>和前缀运算符一样,后缀运算符的声明中不指定优先级。后缀运算符是非结合的。</p>
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="3.7" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="3.7" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,11 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_81">
|
||||
<section class="normal" id="section-gitbook_55">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:Hawstein</p>
|
||||
<p>校对:numbbbbb</p>
|
||||
<p>翻译:<a href="https://github.com/Hawstein" target="_blank">Hawstein</a><br>校对:<a href="https://github.com/numbbbbb" target="_blank">numbbbbb</a>, <a href="https://github.com/stanzhai" target="_blank">stanzhai</a></p>
|
||||
</blockquote>
|
||||
<h1 id="-">特性</h1>
|
||||
<hr>
|
||||
@ -602,9 +601,10 @@
|
||||
</ul>
|
||||
<p>特性提供了关于声明和类型的更多信息。在Swift中有两类特性,用于修饰声明的以及用于修饰类型的。例如,<code>required</code>特性,当应用于一个类的指定或便利初始化器声明时,表明它的每个子类都必须实现那个初始化器。再比如<code>noreturn</code>特性,当应用于函数或方法类型时,表明该函数或方法不会返回到它的调用者。</p>
|
||||
<p>通过以下方式指定一个特性:符号<code>@</code>后面跟特性名,如果包含参数,则把参数带上:</p>
|
||||
<pre><code>@attribute name
|
||||
@attribute name(attribute arguments)
|
||||
</code></pre><p>有些声明特性通过接收参数来指定特性的更多信息以及它是如何修饰一个特定的声明的。这些特性的参数写在小括号内,它们的格式由它们所属的特性来定义。</p>
|
||||
<blockquote>
|
||||
<p>@<code>attribute name</code><br>@<code>attribute name</code>(<code>attribute arguments</code>) </p>
|
||||
</blockquote>
|
||||
<p>有些声明特性通过接收参数来指定特性的更多信息以及它是如何修饰一个特定的声明的。这些特性的参数写在小括号内,它们的格式由它们所属的特性来定义。</p>
|
||||
<p><a name="declaration_attributes"></a></p>
|
||||
<h2 id="-">声明特性</h2>
|
||||
<p>声明特性只能应用于声明。然而,你也可以将<code>noreturn</code>特性应用于函数或方法类型。</p>
|
||||
@ -631,7 +631,7 @@
|
||||
<p>该特性用于修饰任意可以在Objective-C中表示的声明,比如,非嵌套类,协议,类和协议中的属性和方法(包含getter和setter),初始化器,析构器,以下下标。<code>objc</code>特性告诉编译器该声明可以在Objective-C代码中使用。</p>
|
||||
<p>如果你将<code>objc</code>特性应用于一个类或协议,它也会隐式地应用于那个类或协议的成员。对于标记了<code>objc</code>特性的类,编译器会隐式地为它的子类添加<code>objc</code>特性。标记了<code>objc</code>特性的协议不能继承自没有标记<code>objc</code>的协议。</p>
|
||||
<p><code>objc</code>特性有一个可选的参数,由标记符组成。当你想把<code>objc</code>所修饰的实体以一个不同的名字暴露给Objective-C,你就可以使用这个特性参数。你可以使用这个参数来命名类,协议,方法,getters,setters,以及初始化器。下面的例子把<code>ExampleClass</code>中<code>enabled</code>属性的getter暴露给Objective-C,名字是<code>isEnabled</code>,而不是它原来的属性名。</p>
|
||||
<pre><code>@objc
|
||||
<pre><code class="lang-swift">@objc
|
||||
class ExampleClass {
|
||||
var enabled: Bool {
|
||||
@objc(isEnabled) get {
|
||||
@ -639,7 +639,8 @@ class ExampleClass {
|
||||
}
|
||||
}
|
||||
}
|
||||
</code></pre><p><code>optional</code></p>
|
||||
</code></pre>
|
||||
<p><code>optional</code></p>
|
||||
<p>用该特性修饰协议的属性,方法或下标成员,表示实现这些成员并不需要一致性类型(conforming type)。</p>
|
||||
<p>你只能用<code>optional</code>特性修饰那些标记了<code>objc</code>特性的协议。因此,只有类类型可以adopt和comform to那些包含可选成员需求的协议。更多关于如何使用<code>optional</code>特性以及如何访问可选协议成员的指导,例如,当你不确定一个conforming类型是否实现了它们,请见:<a href="">可选协议需求</a>。</p>
|
||||
<p><code>required</code></p>
|
||||
@ -656,7 +657,7 @@ class ExampleClass {
|
||||
<p><code>noreturn</code></p>
|
||||
<p>该特性用于修饰函数或方法的类型,表明该函数或方法不会返回到它的调用者中去。你也可以用它标记函数或方法的声明,表示函数或方法的相应类型,<code>T</code>,是<code>@noreturn T</code>。</p>
|
||||
<blockquote>
|
||||
<p>特性语法<br><em>特色</em> → <strong>@</strong> <a href="..\chapter3\06_Attributes.html#attribute_name"><em>特性名</em></a> <a href="..\chapter3\06_Attributes.html#attribute_argument_clause"><em>特性参数子句</em></a> <em>可选</em><br><em>特性名</em> → <a href="LexicalStructure.html#identifier"><em>标识符</em></a><br><em>特性参数子句</em> → <strong>(</strong> <a href="..\chapter3\06_Attributes.html#balanced_tokens"><em>平衡令牌列表</em></a> <em>可选</em> <strong>)</strong><br><em>特性(Attributes)列表</em> → <a href="..\chapter3\06_Attributes.html#attribute"><em>特色</em></a> <a href="..\chapter3\06_Attributes.html#attributes"><em>特性(Attributes)列表</em></a> <em>可选</em><br><em>平衡令牌列表</em> → <a href="..\chapter3\06_Attributes.html#balanced_token"><em>平衡令牌</em></a> <a href="..\chapter3\06_Attributes.html#balanced_tokens"><em>平衡令牌列表</em></a> <em>可选</em><br><em>平衡令牌</em> → <strong>(</strong> <a href="..\chapter3\06_Attributes.html#balanced_tokens"><em>平衡令牌列表</em></a> <em>可选</em> <strong>)</strong><br><em>平衡令牌</em> → <strong>[</strong> <a href="..\chapter3\06_Attributes.html#balanced_tokens"><em>平衡令牌列表</em></a> <em>可选</em> <strong>]</strong><br><em>平衡令牌</em> → <strong>{</strong> <a href="..\chapter3\06_Attributes.html#balanced_tokens"><em>平衡令牌列表</em></a> <em>可选</em> <strong>}</strong><br><em>平衡令牌</em> → <strong>任意标识符, 关键字, 字面量或运算符</strong><br><em>平衡令牌</em> → <strong>任意标点除了(, ), [, ], {, 或 }</strong></p>
|
||||
<p>特性语法<br><em>特性</em> → <strong>@</strong> <a href="..\chapter3\06_Attributes.html#attribute_name"><em>特性名</em></a> <a href="..\chapter3\06_Attributes.html#attribute_argument_clause"><em>特性参数子句</em></a> <em>可选</em><br><em>特性名</em> → <a href="LexicalStructure.html#identifier"><em>标识符</em></a><br><em>特性参数子句</em> → <strong>(</strong> <a href="..\chapter3\06_Attributes.html#balanced_tokens"><em>平衡令牌列表</em></a> <em>可选</em> <strong>)</strong><br><em>特性(Attributes)列表</em> → <a href="..\chapter3\06_Attributes.html#attribute"><em>特色</em></a> <a href="..\chapter3\06_Attributes.html#attributes"><em>特性(Attributes)列表</em></a> <em>可选</em><br><em>平衡令牌列表</em> → <a href="..\chapter3\06_Attributes.html#balanced_token"><em>平衡令牌</em></a> <a href="..\chapter3\06_Attributes.html#balanced_tokens"><em>平衡令牌列表</em></a> <em>可选</em><br><em>平衡令牌</em> → <strong>(</strong> <a href="..\chapter3\06_Attributes.html#balanced_tokens"><em>平衡令牌列表</em></a> <em>可选</em> <strong>)</strong><br><em>平衡令牌</em> → <strong>[</strong> <a href="..\chapter3\06_Attributes.html#balanced_tokens"><em>平衡令牌列表</em></a> <em>可选</em> <strong>]</strong><br><em>平衡令牌</em> → <strong>{</strong> <a href="..\chapter3\06_Attributes.html#balanced_tokens"><em>平衡令牌列表</em></a> <em>可选</em> <strong>}</strong><br><em>平衡令牌</em> → <strong>任意标识符, 关键字, 字面量或运算符</strong><br><em>平衡令牌</em> → <strong>任意标点除了(, ), [, ], {, 或 }</strong></p>
|
||||
</blockquote>
|
||||
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="3.8" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="3.8" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,11 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_93">
|
||||
<section class="normal" id="section-gitbook_67">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:honghaoz</p>
|
||||
<p>校对:numbbbbb, stanzhai</p>
|
||||
<p>翻译:<a href="https://github.com/honghaoz" target="_blank">honghaoz</a><br>校对:<a href="https://github.com/numbbbbb" target="_blank">numbbbbb</a>, <a href="https://github.com/stanzhai" target="_blank">stanzhai</a></p>
|
||||
</blockquote>
|
||||
<h1 id="-patterns-">模式(Patterns)</h1>
|
||||
<hr>
|
||||
@ -614,17 +613,19 @@
|
||||
<p><a name="wildcard_pattern"></a></p>
|
||||
<h2 id="-wildcard-pattern-">通配符模式(Wildcard Pattern)</h2>
|
||||
<p>通配符模式匹配并忽略任何值,包含一个下划线(_)。当你不关心被匹配的值时,可以使用此模式。例如,下面这段代码进行了<code>1...3</code>的循环,并忽略了每次循环的值:</p>
|
||||
<pre><code>for _ in 1...3 {
|
||||
<pre><code class="lang-swift">for _ in 1...3 {
|
||||
// Do something three times.
|
||||
}
|
||||
</code></pre><blockquote>
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>通配符模式语法<br><em>通配符模式</em> → <strong>_</strong> </p>
|
||||
</blockquote>
|
||||
<p><a name="identifier_pattern"></a></p>
|
||||
<h2 id="-identifier-pattern-">标识符模式(Identifier Pattern)</h2>
|
||||
<p>标识符模式匹配任何值,并将匹配的值和一个变量或常量绑定起来。例如,在下面的常量申明中,<code>someValue</code>是一个标识符模式,匹配了类型是<code>Int</code>的<code>42</code>。</p>
|
||||
<pre><code> let someValue = 42
|
||||
</code></pre><p>当匹配成功时,<code>42</code>被绑定(赋值)给常量<code>someValue</code>。</p>
|
||||
<pre><code class="lang-swift">let someValue = 42
|
||||
</code></pre>
|
||||
<p>当匹配成功时,<code>42</code>被绑定(赋值)给常量<code>someValue</code>。</p>
|
||||
<p>当一个变量或常量申明的左边是标识符模式时,此时,标识符模式是隐式的值绑定模式(value-binding pattern)。</p>
|
||||
<blockquote>
|
||||
<p>标识符模式语法<br><em>标识符模式</em> → <a href="LexicalStructure.html#identifier"><em>标识符</em></a> </p>
|
||||
@ -633,14 +634,15 @@
|
||||
<h2 id="-value-binding-pattern-">值绑定模式(Value-Binding Pattern)</h2>
|
||||
<p>值绑定模式绑定匹配的值到一个变量或常量。当绑定匹配值给常量时,用关键字<code>let</code>,绑定给变量时,用关键之<code>var</code>。</p>
|
||||
<p>标识符模式包含在值绑定模式中,绑定新的变量或常量到匹配的值。例如,你可以分解一个元组的元素,并把每个元素绑定到相应的标识符模式中。</p>
|
||||
<pre><code>let point = (3, 2)
|
||||
<pre><code class="lang-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)).")
|
||||
}
|
||||
// prints "The point is at (3, 2).”
|
||||
</code></pre><p>在上面这个例子中,<code>let</code>将元组模式<code>(x, y)</code>分配到各个标识符模式。因为这种行为,<code>switch</code>语句中<code>case let (x, y):</code>和<code>case (let x, let y):</code>匹配的值是一样的。</p>
|
||||
</code></pre>
|
||||
<p>在上面这个例子中,<code>let</code>将元组模式<code>(x, y)</code>分配到各个标识符模式。因为这种行为,<code>switch</code>语句中<code>case let (x, y):</code>和<code>case (let x, let y):</code>匹配的值是一样的。</p>
|
||||
<blockquote>
|
||||
<p>值绑定(Value Binding)模式语法<br><em>值绑定模式</em> → <strong>var</strong> <a href="..\chapter3\07_Patterns.html#pattern"><em>模式</em></a> | <strong>let</strong> <a href="..\chapter3\07_Patterns.html#pattern"><em>模式</em></a> </p>
|
||||
</blockquote>
|
||||
@ -649,16 +651,18 @@ case let (x, y):
|
||||
<p>元组模式是逗号分隔的列表,包含一个或多个模式,并包含在一对圆括号中。元组模式匹配相应元组类型的值。</p>
|
||||
<p>你可以使用类型注释来限制一个元组模式来匹配某种元组类型。例如,在常量申明<code>let (x, y): (Int, Int) = (1, 2)</code>中的元组模式<code>(x, y): (Int, Int)</code>,只匹配两个元素都是<code>Int</code>这种类型的元组。如果仅需要限制一个元组模式中的某几个元素,只需要直接对这几个元素提供类型注释即可。例如,在<code>let (x: String, y)</code>中的元组模式,只要某个元组类型是包含两个元素,且第一个元素类型是<code>String</code>,则被匹配。</p>
|
||||
<p>当元组模式被用在<code>for-in</code>语句或者变量或常量申明时,它可以包含通配符模式,标识符模式或者其他包含这两种模式的模式。例如,下面这段代码是不正确的,因为<code>(x, 0)</code>中的元素<code>0</code>是一个表达式模式:</p>
|
||||
<pre><code>let points = [(0, 0), (1, 0), (1, 1), (2, 0), (2, 1)]
|
||||
<pre><code class="lang-swift">let points = [(0, 0), (1, 0), (1, 1), (2, 0), (2, 1)]
|
||||
// This code isn't valid.
|
||||
for (x, 0) in points {
|
||||
/* ... */
|
||||
}
|
||||
</code></pre><p>对于只包含一个元素的元组,括号是不起作用的。模式匹配那个单个元素的类型。例如,下面是等效的:</p>
|
||||
<pre><code>let a = 2 // a: Int = 2
|
||||
</code></pre>
|
||||
<p>对于只包含一个元素的元组,括号是不起作用的。模式匹配那个单个元素的类型。例如,下面是等效的:</p>
|
||||
<pre><code class="lang-swift">let a = 2 // a: Int = 2
|
||||
let (a) = 2 // a: Int = 2
|
||||
let (a): Int = 2 // a: Int = 2
|
||||
</code></pre><blockquote>
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>元组模式语法<br><em>元组模式</em> → <strong>(</strong> <a href="..\chapter3\07_Patterns.html#tuple_pattern_element_list"><em>元组模式元素列表</em></a> <em>可选</em> <strong>)</strong><br><em>元组模式元素列表</em> → <a href="..\chapter3\07_Patterns.html#tuple_pattern_element"><em>元组模式元素</em></a> | <a href="..\chapter3\07_Patterns.html#tuple_pattern_element"><em>元组模式元素</em></a> <strong>,</strong> <a href="..\chapter3\07_Patterns.html#tuple_pattern_element_list"><em>元组模式元素列表</em></a><br><em>元组模式元素</em> → <a href="..\chapter3\07_Patterns.html#pattern"><em>模式</em></a> </p>
|
||||
</blockquote>
|
||||
<p><a name="enumeration_case_pattern"></a></p>
|
||||
@ -671,9 +675,10 @@ let (a): Int = 2 // a: Int = 2
|
||||
<p><a name="type-casting_patterns"></a></p>
|
||||
<h2 id="-type-casting-patterns-">类型转换模式(Type-Casting Patterns)</h2>
|
||||
<p>有两种类型转换模式,<code>is</code>模式和<code>as</code>模式。这两种模式均只出现在<code>switch</code>语句中的<code>case</code>标签中。<code>is</code>模式和<code>as</code>模式有以下形式:</p>
|
||||
<pre><code>is type
|
||||
pattern as type
|
||||
</code></pre><p><code>is</code>模式匹配一个值,如果这个值的类型在运行时(runtime)和<code>is</code>模式右边的指定类型(或者那个类型的子类)是一致的。<code>is</code>模式和<code>is</code>操作符一样,它们都进行类型转换,但是抛弃了返回的类型。</p>
|
||||
<blockquote>
|
||||
<p>is <code>type</code><br><code>pattern</code> as <code>type</code></p>
|
||||
</blockquote>
|
||||
<p><code>is</code>模式匹配一个值,如果这个值的类型在运行时(runtime)和<code>is</code>模式右边的指定类型(或者那个类型的子类)是一致的。<code>is</code>模式和<code>is</code>操作符一样,它们都进行类型转换,但是抛弃了返回的类型。</p>
|
||||
<p><code>as</code>模式匹配一个值,如果这个值的类型在运行时(runtime)和<code>as</code>模式右边的指定类型(或者那个类型的子类)是一致的。一旦匹配成功,匹配的值的类型被转换成<code>as</code>模式左边指定的模式。</p>
|
||||
<p>关于使用<code>switch</code>语句来匹配<code>is</code>模式和<code>as</code>模式值的例子,请参阅<code>Type Casting for Any and AnyObject</code>。</p>
|
||||
<blockquote>
|
||||
@ -683,7 +688,7 @@ pattern as type
|
||||
<h2 id="-expression-pattern-">表达式模式(Expression Pattern)</h2>
|
||||
<p>表达式模式代表了一个表达式的值。这个模式只出现在<code>switch</code>语句中的<code>case</code>标签中。</p>
|
||||
<p>由表达式模式所代表的表达式用Swift标准库中的<code>~=</code>操作符与输入表达式的值进行比较。如果<code>~=</code>操作符返回<code>true</code>,则匹配成功。默认情况下,<code>~=</code>操作符使用<code>==</code>操作符来比较两个相同类型的值。它也可以匹配一个整数值与一个<code>Range</code>对象中的整数范围,正如下面这个例子所示:</p>
|
||||
<pre><code>let point = (1, 2)
|
||||
<pre><code class="lang-swift">let point = (1, 2)
|
||||
switch point {
|
||||
case (0, 0):
|
||||
println("(0, 0) is at the origin.")
|
||||
@ -693,8 +698,9 @@ default:
|
||||
println("The point is at (\(point.0), \(point.1)).")
|
||||
}
|
||||
// prints "(1, 2) is near the origin.”
|
||||
</code></pre><p>你可以重载<code>~=</code>操作符来提供自定义的表达式行为。例如,你可以重写上面的例子,以实现用字符串表达的点来比较<code>point</code>表达式。</p>
|
||||
<pre><code>// Overload the ~= operator to match a string with an integer
|
||||
</code></pre>
|
||||
<p>你可以重载<code>~=</code>操作符来提供自定义的表达式行为。例如,你可以重写上面的例子,以实现用字符串表达的点来比较<code>point</code>表达式。</p>
|
||||
<pre><code class="lang-swift">// Overload the ~= operator to match a string with an integer
|
||||
func ~=(pattern: String, value: Int) -> Bool {
|
||||
return pattern == "\(value)"
|
||||
}
|
||||
@ -707,7 +713,8 @@ default:
|
||||
println("The point is at (\(point.0), \(point.1)).")
|
||||
}
|
||||
// prints "(1, 2) is near the origin.”
|
||||
</code></pre><blockquote>
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>表达式模式语法<br><em>表达式模式</em> → <a href="..\chapter3\04_Expressions.html#expression"><em>表达式</em></a> </p>
|
||||
</blockquote>
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="3.9" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="3.9" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,11 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_95">
|
||||
<section class="normal" id="section-gitbook_69">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:fd5788</p>
|
||||
<p>校对:yankuangshi</p>
|
||||
<p>翻译:<a href="https://github.com/fd5788" target="_blank">fd5788</a><br>校对:<a href="https://github.com/yankuangshi" target="_blank">yankuangshi</a>, <a href="https://github.com/stanzhai" target="_blank">stanzhai</a></p>
|
||||
</blockquote>
|
||||
<h1 id="-">泛型参数</h1>
|
||||
<hr>
|
||||
@ -605,22 +604,27 @@
|
||||
<p><a name="generic_parameter"></a></p>
|
||||
<h2 id="-">泛型形参子句</h2>
|
||||
<p>泛型形参子句指定泛型类型或函数的类型形参,以及这些参数的关联约束和要求。泛型形参子句用尖括号(<>)包住,并且有以下两种形式:</p>
|
||||
<pre><code><generic parameter list>
|
||||
<generic parameter list where requirements >
|
||||
</code></pre><p>泛型形参列表中泛型形参用逗号分开,每一个采用以下形式:</p>
|
||||
<pre><code>type parameter : constrain
|
||||
</code></pre><p>泛型形参由两部分组成:类型形参及其后的可选约束。类型形参只是占位符类型(如T,U,V,KeyType,ValueType等)的名字而已。你可以在泛型类型、函数的其余部分或者构造器声明,以及函数或构造器的签名中使用它。</p>
|
||||
<blockquote>
|
||||
<p><<code>generic parameter list</code>><br><<code>generic parameter list</code> where <code>requirements</code>></p>
|
||||
</blockquote>
|
||||
<p>泛型形参列表中泛型形参用逗号分开,每一个采用以下形式:</p>
|
||||
<blockquote>
|
||||
<p><code>type parameter</code> : <code>constrain</code></p>
|
||||
</blockquote>
|
||||
<p>泛型形参由两部分组成:类型形参及其后的可选约束。类型形参只是占位符类型(如T,U,V,KeyType,ValueType等)的名字而已。你可以在泛型类型、函数的其余部分或者构造器声明,以及函数或构造器的签名中使用它。</p>
|
||||
<p>约束用于指明该类型形参继承自某个类或者遵守某个协议或协议的一部分。例如,在下面的泛型中,泛型形参<code>T: Comparable</code>表示任何用于替代类型形参<code>T</code>的类型实参必须满足<code>Comparable</code>协议。</p>
|
||||
<pre><code>func simpleMin<T: COmparable>(x: T, y: T) -> T {
|
||||
<pre><code class="lang-swift">func simpleMin<T: COmparable>(x: T, y: T) -> T {
|
||||
if x < y {
|
||||
return y
|
||||
}
|
||||
return x
|
||||
}
|
||||
</code></pre><p>如,<code>Int</code>和<code>Double</code>均满足<code>Comparable</code>协议,该函数接受任何一种类型。与泛型类型相反,调用泛型函数或构造器时不需要指定泛型实参子句。类型实参由传递给函数或构造器的实参推断而出。</p>
|
||||
<pre><code>simpleMin(17, 42) // T is inferred to be Int
|
||||
</code></pre>
|
||||
<p>如,<code>Int</code>和<code>Double</code>均满足<code>Comparable</code>协议,该函数接受任何一种类型。与泛型类型相反,调用泛型函数或构造器时不需要指定泛型实参子句。类型实参由传递给函数或构造器的实参推断而出。</p>
|
||||
<pre><code class="lang-swift">simpleMin(17, 42) // T is inferred to be Int
|
||||
simpleMin(3.14159, 2.71828) // T is inferred to be Double
|
||||
</code></pre><h2 id="where-">Where 子句</h2>
|
||||
</code></pre>
|
||||
<h2 id="where-">Where 子句</h2>
|
||||
<p>要想对类型形参及其关联类型指定额外要求,可以在泛型形参列表之后添加<code>where</code>子句。<code>where</code>子句由关键字<code>where</code>及其后的用逗号分割的多个要求组成。</p>
|
||||
<p><code>where</code>子句中的要求用于指明该类型形参继承自某个类或遵守某个协议或协议的一部分。尽管<code>where</code>子句有助于表达类型形参上的简单约束(如<code>T: Comparable</code>等同于<code>T where T: Comparable</code>,等等),但是依然可以用来对类型形参及其关联约束提供更复杂的约束。如,<code><T where T: C, T: P></code>表示泛型类型<code>T</code>继承自类<code>C</code>且遵守协议<code>P</code>。</p>
|
||||
<p>如上所述,可以强制约束类型形参的关联类型遵守某个协议。<code><T: Generator where T.Element: Equatable></code>表示<code>T</code>遵守<code>Generator</code>协议,而且<code>T</code>的关联类型<code>T.Element</code>遵守<code>Eauatable</code>协议(<code>T</code>有关联类型是因为<code>Generator</code>声明了<code>Element</code>,而<code>T</code>遵守<code>Generator</code>协议)。</p>
|
||||
@ -634,15 +638,19 @@ simpleMin(3.14159, 2.71828) // T is inferred to be Double
|
||||
<p><a name="generic_argument"></a></p>
|
||||
<h2 id="-">泛型实参子句</h2>
|
||||
<p>泛型实参子句指定<em>泛型类型</em>的类型实参。泛型实参子句用尖括号(<>)包住,形式如下:</p>
|
||||
<pre><code>< generic argument list >
|
||||
</code></pre><p>泛型实参列表中类型实参有逗号分开。类型实参是实际具体类型的名字,用来替代泛型类型的泛型形参子句中的相应的类型形参。从而得到泛型类型的一个特化版本。如,Swift标准库的泛型字典类型定义如下:</p>
|
||||
<pre><code>struct Dictionary<KeyTypel: Hashable, ValueType>: Collection, DictionaryLiteralConvertible {
|
||||
<blockquote>
|
||||
<p><<code>generic argument list</code>></p>
|
||||
</blockquote>
|
||||
<p>泛型实参列表中类型实参有逗号分开。类型实参是实际具体类型的名字,用来替代泛型类型的泛型形参子句中的相应的类型形参。从而得到泛型类型的一个特化版本。如,Swift标准库的泛型字典类型定义如下:</p>
|
||||
<pre><code class="lang-swift">struct Dictionary<KeyTypel: Hashable, ValueType>: Collection, DictionaryLiteralConvertible {
|
||||
/* .. */
|
||||
}
|
||||
</code></pre><p>泛型<code>Dictionary</code>类型的特化版本,<code>Dictionary<String, Int></code>就是用具体的<code>String</code>和<code>Int</code>类型替代泛型类型<code>KeyType: Hashable</code>和<code>ValueType</code>产生的。每一个类型实参必须满足它所替代的泛型形参的所有约束,包括任何<code>where</code>子句所指定的额外的要求。上面的例子中,类型形参<code>KeyType</code>要求满足<code>Hashable</code>协议,因此<code>String</code>也必须满足<code>Hashable</code>协议。</p>
|
||||
</code></pre>
|
||||
<p>泛型<code>Dictionary</code>类型的特化版本,<code>Dictionary<String, Int></code>就是用具体的<code>String</code>和<code>Int</code>类型替代泛型类型<code>KeyType: Hashable</code>和<code>ValueType</code>产生的。每一个类型实参必须满足它所替代的泛型形参的所有约束,包括任何<code>where</code>子句所指定的额外的要求。上面的例子中,类型形参<code>KeyType</code>要求满足<code>Hashable</code>协议,因此<code>String</code>也必须满足<code>Hashable</code>协议。</p>
|
||||
<p>可以用本身就是泛型类型的特化版本的类型实参替代类型形参(假设已满足合适的约束和要求)。例如,为了生成一个元素类型是整型数组的数组,可以用数组的特化版本<code>Array<Int></code>替代泛型类型<code>Array<T></code>的类型形参<code>T</code>来实现。</p>
|
||||
<pre><code>let arrayOfArrays: Array<Array<Int>> = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
|
||||
</code></pre><p>如<a href="#generic_parameter">泛型形参子句</a>所述,不能用泛型实参子句来指定泛型函数或构造器的类型实参。</p>
|
||||
<pre><code class="lang-swift">let arrayOfArrays: Array<Array<Int>> = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
|
||||
</code></pre>
|
||||
<p>如<a href="#generic_parameter">泛型形参子句</a>所述,不能用泛型实参子句来指定泛型函数或构造器的类型实参。</p>
|
||||
<blockquote>
|
||||
<p>泛型实参子句语法<br><em>(泛型参数子句Generic Argument Clause)</em> → <strong><</strong> <a href="GenericParametersAndArguments.html#generic_argument_list"><em>泛型参数列表</em></a> <strong>></strong><br><em>泛型参数列表</em> → <a href="GenericParametersAndArguments.html#generic_argument"><em>泛型参数</em></a> | <a href="GenericParametersAndArguments.html#generic_argument"><em>泛型参数</em></a> <strong>,</strong> <a href="GenericParametersAndArguments.html#generic_argument_list"><em>泛型参数列表</em></a><br><em>泛型参数</em> → <a href="..\chapter3\03_Types.html#type"><em>类型</em></a> </p>
|
||||
</blockquote>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="3.5" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="3.5" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -587,11 +587,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_97">
|
||||
<section class="normal" id="section-gitbook_73">
|
||||
|
||||
<blockquote>
|
||||
<p>翻译:coverxit</p>
|
||||
<p>校对:numbbbbb, coverxit</p>
|
||||
<p>翻译:<a href="https://github.com/coverxit" target="_blank">coverxit</a><br>校对:<a href="https://github.com/numbbbbb" target="_blank">numbbbbb</a>, <a href="https://github.com/coverxit" target="_blank">coverxit</a>, <a href="https://github.com/stanzhai" target="_blank">stanzhai</a></p>
|
||||
</blockquote>
|
||||
<h1 id="-">语句</h1>
|
||||
<hr>
|
||||
@ -606,162 +605,113 @@
|
||||
<p>循环语句用于重复执行代码块;分支语句用于执行满足特定条件的代码块;控制传递语句则用于修改代码的执行顺序。在稍后的叙述中,将会详细地介绍每一种类型的控制流语句。</p>
|
||||
<p>是否将分号(<code>;</code>)添加到语句的结尾处是可选的。但若要在同一行内写多条独立语句,请务必使用分号。</p>
|
||||
<blockquote>
|
||||
<p>GRAMMAR OF A STATEMENT</p>
|
||||
<p><em>statement</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression" target="_blank"><em>expression</em></a><strong>;</strong> <em>opt</em></p>
|
||||
<p><em>statement</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/declaration" target="_blank"><em>declaration</em></a><strong>;</strong> <em>opt</em></p>
|
||||
<p><em>statement</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/loop-statement" target="_blank"><em>loop-statement</em></a><strong>;</strong> <em>opt</em></p>
|
||||
<p><em>statement</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/branch-statement" target="_blank"><em>branch-statement</em></a><strong>;</strong> <em>opt</em></p>
|
||||
<p><em>statement</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/labeled-statement" target="_blank"><em>labeled-statement</em></a></p>
|
||||
<p><em>statement</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/control-transfer-statement" target="_blank"><em>control-transfer-statement</em></a><strong>;</strong> <em>opt</em></p>
|
||||
<p><em>statement</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/statement" target="_blank"><em>statment</em></a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/statements" target="_blank"><em>statements</em></a><strong>;</strong> <em>opt</em></p>
|
||||
<p>语句语法<br><em>语句</em> → <a href="..\chapter3\04_Expressions.html#expression"><em>表达式</em></a> <strong>;</strong> <em>可选</em><br><em>语句</em> → <a href="..\chapter3\05_Declarations.html#declaration"><em>声明</em></a> <strong>;</strong> <em>可选</em><br><em>语句</em> → <a href="..\chapter3\10_Statements.html#loop_statement"><em>循环语句</em></a> <strong>;</strong> <em>可选</em><br><em>语句</em> → <a href="..\chapter3\10_Statements.html#branch_statement"><em>分支语句</em></a> <strong>;</strong> <em>可选</em><br><em>语句</em> → <a href="..\chapter3\10_Statements.html#labeled_statement"><em>标记语句(Labeled Statement)</em></a><br><em>语句</em> → <a href="..\chapter3\10_Statements.html#control_transfer_statement"><em>控制转移语句</em></a> <strong>;</strong> <em>可选</em><br><em>多条语句(Statements)</em> → <a href="..\chapter3\10_Statements.html#statement"><em>语句</em></a> <a href="..\chapter3\10_Statements.html#statements"><em>多条语句(Statements)</em></a> <em>可选</em> </p>
|
||||
</blockquote>
|
||||
<p><a name="loop_statements"></a></p>
|
||||
<h2 id="-">循环语句</h2>
|
||||
<p>取决于特定的循环条件,循环语句允许重复执行代码块。Swift 提供四种类型的循环语句:<code>for</code>语句、<code>for-in</code>语句、<code>while</code>语句和<code>do-while</code>语句。</p>
|
||||
<p>通过<code>break</code>语句和<code>continue</code>语句可以改变循环语句的控制流。有关这两条语句,详情参见 <a href="#break_statement">Break 语句</a>和 <a href="#continue_statement">Continue 语句</a>。</p>
|
||||
<blockquote>
|
||||
<p>GRAMMAR OF A LOOP STATEMENT</p>
|
||||
<p><em>loop-statement</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/for-statement" target="_blank"><em>for-statement</em></a></p>
|
||||
<p><em>loop-statement</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/for-in-statement" target="_blank"><em>for-in-statement</em></a></p>
|
||||
<p><em>loop-statement</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/while-statement" target="_blank"><em>while-statement</em></a></p>
|
||||
<p><em>loop-statement</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/do-while-statement" target="_blank"><em>do-while-statement</em></a></p>
|
||||
<p>循环语句语法<br><em>循环语句</em> → <a href="..\chapter3\10_Statements.html#for_statement"><em>for语句</em></a><br><em>循环语句</em> → <a href="..\chapter3\10_Statements.html#for_in_statement"><em>for-in语句</em></a><br><em>循环语句</em> → <a href="..\chapter3\10_Statements.html#wheetatype类型ile_statement"><em>while语句</em></a><br><em>循环语句</em> → <a href="..\chapter3\10_Statements.html#do_while_statement"><em>do-while语句</em></a> </p>
|
||||
</blockquote>
|
||||
<h3 id="for-">For 语句</h3>
|
||||
<p><code>for</code>语句允许在重复执行代码块的同时,递增一个计数器。</p>
|
||||
<p><code>for</code>语句的形式如下:</p>
|
||||
<pre><code class="lang-swift">for `initialzation`; `condition`; `increment` {
|
||||
`statements`
|
||||
}
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>for <code>initialzation</code>; <code>condition</code>; <code>increment</code> {<br> <code>statements</code><br>} </p>
|
||||
</blockquote>
|
||||
<p><em>initialzation</em>、<em>condition</em> 和 <em>increment</em> 之间的分号,以及包围循环体 <em>statements</em> 的大括号都是不可省略的。</p>
|
||||
<p><code>for</code>语句的执行流程如下:</p>
|
||||
<ol>
|
||||
<li><p><em>initialzation</em> 只会被执行一次,通常用于声明和初始化在接下来的循环中需要使用的变量。</p>
|
||||
</li>
|
||||
<li><p>计算 <em>condition</em> 表达式:
|
||||
如果为<code>true</code>,<em>statements</em> 将会被执行,然后转到第3步。如果为<code>false</code>,<em>statements</em> 和 <em>increment</em> 都不会被执行,<code>for</code>至此执行完毕。</p>
|
||||
</li>
|
||||
<li><p>计算 <em>increment</em> 表达式,然后转到第2步。</p>
|
||||
</li>
|
||||
<li><em>initialzation</em> 只会被执行一次,通常用于声明和初始化在接下来的循环中需要使用的变量。</li>
|
||||
<li>计算 <em>condition</em> 表达式:
|
||||
如果为<code>true</code>,<em>statements</em> 将会被执行,然后转到第3步。如果为<code>false</code>,<em>statements</em> 和 <em>increment</em> 都不会被执行,<code>for</code>至此执行完毕。</li>
|
||||
<li>计算 <em>increment</em> 表达式,然后转到第2步。</li>
|
||||
</ol>
|
||||
<p>定义在 <em>initialzation</em> 中的变量仅在<code>for</code>语句的作用域以内有效。<em>condition</em> 表达式的值的类型必须遵循<code>LogicValue</code>协议。</p>
|
||||
<blockquote>
|
||||
<p>GRAMMAR OF A FOR STATEMENT</p>
|
||||
<p><em>for-statement</em> → <strong>for</strong> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/for-init" target="_blank"><em>for-init</em></a> <em>opt</em> <strong>;</strong> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression" target="_blank"><em>expression</em></a> <em>opt</em> <strong>;</strong> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression" target="_blank"><em>expression</em></a> <em>opt</em> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/code-block" target="_blank"><em>code-block</em></a></p>
|
||||
<p><em>for-statement</em> → <strong>for (</strong> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/for-init" target="_blank"><em>for-init</em></a> <em>opt</em> <strong>;</strong> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression" target="_blank"><em>expression</em></a> <em>opt</em> <strong>;</strong> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression" target="_blank"><em>expression</em></a> <em>opt</em> <strong>)</strong> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/code-block" target="_blank"><em>code-block</em></a></p>
|
||||
<p><em>for-statement</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/variable-declaration" target="_blank"><em>variable-declaration</em></a> | <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression-list" target="_blank"><em>expression-list</em></a></p>
|
||||
<p>For 循环语法<br><em>for语句</em> → <strong>for</strong> <a href="..\chapter3\10_Statements.html#for_init"><em>for初始条件</em></a> <em>可选</em> <strong>;</strong> <a href="..\chapter3\04_Expressions.html#expression"><em>表达式</em></a> <em>可选</em> <strong>;</strong> <a href="..\chapter3\04_Expressions.html#expression"><em>表达式</em></a> <em>可选</em> <a href="..\chapter3\05_Declarations.html#code_block"><em>代码块</em></a><br><em>for语句</em> → <strong>for</strong> <strong>(</strong> <a href="..\chapter3\10_Statements.html#for_init"><em>for初始条件</em></a> <em>可选</em> <strong>;</strong> <a href="..\chapter3\04_Expressions.html#expression"><em>表达式</em></a> <em>可选</em> <strong>;</strong> <a href="..\chapter3\04_Expressions.html#expression"><em>表达式</em></a> <em>可选</em> <strong>)</strong> <a href="..\chapter3\05_Declarations.html#code_block"><em>代码块</em></a><br><em>for初始条件</em> → <a href="..\chapter3\05_Declarations.html#variable_declaration"><em>变量声明</em></a> | <a href="..\chapter3\04_Expressions.html#expression_list"><em>表达式列表</em></a> </p>
|
||||
</blockquote>
|
||||
<h3 id="for-in-">For-In 语句</h3>
|
||||
<p><code>for-in</code>语句允许在重复执行代码块的同时,迭代集合(或遵循<code>Sequence</code>协议的任意类型)中的每一项。</p>
|
||||
<p><code>for-in</code>语句的形式如下:</p>
|
||||
<pre><code class="lang-swift">for `item` in `collection` {
|
||||
`statements`
|
||||
}
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>for <code>item</code> in <code>collection</code> {<br> <code>statements</code><br>} </p>
|
||||
</blockquote>
|
||||
<p><code>for-in</code>语句在循环开始前会调用 <em>collection</em> 表达式的<code>generate</code>方法来获取一个生成器类型(这是一个遵循<code>Generator</code>协议的类型)的值。接下来循环开始,调用 <em>collection</em> 表达式的<code>next</code>方法。如果其返回值不是<code>None</code>,它将会被赋给 <em>item</em>,然后执行 <em>statements</em>,执行完毕后回到循环开始处;否则,将不会赋值给 <em>item</em> 也不会执行 <em>statements</em>,<code>for-in</code>至此执行完毕。</p>
|
||||
<blockquote>
|
||||
<p>GRAMMAR OF A FOR-IN STATEMENT</p>
|
||||
<p><em>for-in-statement</em> → <strong>for</strong> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Patterns.html#//apple_ref/swift/grammar/pattern" target="_blank"><em>pattern</em></a> <strong>in</strong> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression" target="_blank"><em>expression</em></a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/code-block" target="_blank"><em>code-block</em></a></p>
|
||||
<p>For-In 循环语法<br><em>for-in语句</em> → <strong>for</strong> <a href="..\chapter3\07_Patterns.html#pattern"><em>模式</em></a> <strong>in</strong> <a href="..\chapter3\04_Expressions.html#expression"><em>表达式</em></a> <a href="..\chapter3\05_Declarations.html#code_block"><em>代码块</em></a> </p>
|
||||
</blockquote>
|
||||
<h3 id="while-">While 语句</h3>
|
||||
<p><code>while</code>语句允许重复执行代码块。</p>
|
||||
<p><code>while</code>语句的形式如下:</p>
|
||||
<pre><code class="lang-swift">while `condition` {
|
||||
`statements`
|
||||
}
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>while <code>condition</code> {<br> <code>statements</code><br>} </p>
|
||||
</blockquote>
|
||||
<p><code>while</code>语句的执行流程如下:</p>
|
||||
<ol>
|
||||
<li><p>计算 <em>condition</em> 表达式:
|
||||
如果为真<code>true</code>,转到第2步。如果为<code>false</code>,<code>while</code>至此执行完毕。</p>
|
||||
</li>
|
||||
<li><p>执行 <em>statements</em> ,然后转到第1步。</p>
|
||||
</li>
|
||||
<li>计算 <em>condition</em> 表达式:
|
||||
如果为真<code>true</code>,转到第2步。如果为<code>false</code>,<code>while</code>至此执行完毕。</li>
|
||||
<li>执行 <em>statements</em> ,然后转到第1步。</li>
|
||||
</ol>
|
||||
<p>由于 <em>condition</em> 的值在 <em>statements</em> 执行前就已计算出,因此<code>while</code>语句中的 <em>statements</em> 可能会被执行若干次,也可能不会被执行。</p>
|
||||
<p><em>condition</em> 表达式的值的类型必须遵循<code>LogicValue</code>协议。同时,<em>condition</em> 表达式也可以使用可选绑定,详情参见<a href="../chapter2/01_The_Basics.html#optional_binding">可选绑定</a>。</p>
|
||||
<blockquote>
|
||||
<p>GRAMMAR OF A WHILE STATEMENT</p>
|
||||
<p><em>while-statement</em> → <strong>while</strong> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/while-condition" target="_blank"><em>while-condition</em></a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/code-block" target="_blank"><em>code-block</em></a></p>
|
||||
<p><em>while-condition</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression" target="_blank"><em>expression</em></a> | <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/declaration" target="_blank"><em>declaration</em></a></p>
|
||||
<p>While 循环语法<br><em>while语句</em> → <strong>while</strong> <a href="..\chapter3\10_Statements.html#while_condition"><em>while条件</em></a> <a href="..\chapter3\05_Declarations.html#code_block"><em>代码块</em></a><br><em>while条件</em> → <a href="..\chapter3\04_Expressions.html#expression"><em>表达式</em></a> | <a href="..\chapter3\05_Declarations.html#declaration"><em>声明</em></a> </p>
|
||||
</blockquote>
|
||||
<h3 id="do-while-">Do-While 语句</h3>
|
||||
<p><code>do-while</code>语句允许代码块被执行一次或多次。</p>
|
||||
<p><code>do-while</code>语句的形式如下:</p>
|
||||
<pre><code class="lang-swift">do {
|
||||
`statements`
|
||||
} while `condition`
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>do {<br> <code>statements</code><br>} while <code>condition</code> </p>
|
||||
</blockquote>
|
||||
<p><code>do-while</code>语句的执行流程如下:</p>
|
||||
<ol>
|
||||
<li><p>执行 <em>statements</em>,然后转到第2步。</p>
|
||||
</li>
|
||||
<li><p>计算 <em>condition</em> 表达式:
|
||||
如果为<code>true</code>,转到第1步。如果为<code>false</code>,<code>do-while</code>至此执行完毕。</p>
|
||||
</li>
|
||||
<li>执行 <em>statements</em>,然后转到第2步。</li>
|
||||
<li>计算 <em>condition</em> 表达式:
|
||||
如果为<code>true</code>,转到第1步。如果为<code>false</code>,<code>do-while</code>至此执行完毕。</li>
|
||||
</ol>
|
||||
<p>由于 <em>condition</em> 表达式的值是在 <em>statements</em> 执行后才计算出,因此<code>do-while</code>语句中的 <em>statements</em> 至少会被执行一次。</p>
|
||||
<p><em>condition</em> 表达式的值的类型必须遵循<code>LogicValue</code>协议。同时,<em>condition</em> 表达式也可以使用可选绑定,详情参见<a href="../chapter2/01_The_Basics.html#optional_binding">可选绑定</a>。</p>
|
||||
<blockquote>
|
||||
<p>GRAMMAR OF A DO-WHILE STATEMENT</p>
|
||||
<p><em>do-while-statement</em> → <strong>do</strong> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/code-block" target="_blank"><em>code-block</em></a> <strong>while</strong> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/while-condition" target="_blank"><em>while-condition</em></a></p>
|
||||
<p>Do-While 循环语法<br><em>do-while语句</em> → <strong>do</strong> <a href="..\chapter3\05_Declarations.html#code_block"><em>代码块</em></a> <strong>while</strong> <a href="..\chapter3\10_Statements.html#while_condition"><em>while条件</em></a> </p>
|
||||
</blockquote>
|
||||
<p><a name="branch_statements"></a></p>
|
||||
<h2 id="-">分支语句</h2>
|
||||
<p>取决于一个或者多个条件的值,分支语句允许程序执行指定部分的代码。显然,分支语句中条件的值将会决定如何分支以及执行哪一块代码。Swift 提供两种类型的分支语句:<code>if</code>语句和<code>switch</code>语句。</p>
|
||||
<p><code>switch</code>语句中的控制流可以用<code>break</code>语句修改,详情请见<a href="#break_statement">Break 语句</a>。</p>
|
||||
<blockquote>
|
||||
<p>GRAMMAR OF A BRANCH STATEMENT</p>
|
||||
<p><em>branch-statement</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/if-statement" target="_blank"><em>if-statement</em></a></p>
|
||||
<p><em>branch-statement</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/switch-statement" target="_blank"><em>switch-statement</em></a></p>
|
||||
<p>分支语句语法<br><em>分支语句</em> → <a href="..\chapter3\10_Statements.html#if_statement"><em>if语句</em></a><br><em>分支语句</em> → <a href="..\chapter3\10_Statements.html#switch_statement"><em>switch语句</em></a> </p>
|
||||
</blockquote>
|
||||
<h3 id="if-">If 语句</h3>
|
||||
<p>取决于一个或多个条件的值,<code>if</code>语句将决定执行哪一块代码。</p>
|
||||
<p><code>if</code>语句有两种标准形式,在这两种形式里都必须有大括号。</p>
|
||||
<p>第一种形式是当且仅当条件为真时执行代码,像下面这样:</p>
|
||||
<pre><code class="lang-swift">if `condition` {
|
||||
`statements`
|
||||
}
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>if <code>condition</code> {<br> <code>statements</code><br>} </p>
|
||||
</blockquote>
|
||||
<p>第二种形式是在第一种形式的基础上添加 <em>else 语句</em>,当只有一个 else 语句时,像下面这样:</p>
|
||||
<pre><code class="lang-swift">if `condition` {
|
||||
`statements to execute if condition is true`
|
||||
<blockquote>
|
||||
<p>if <code>condition</code> {
|
||||
<code>statements to execute if condition is true</code>
|
||||
} else {
|
||||
`statements to execute if condition is false`
|
||||
}
|
||||
</code></pre>
|
||||
<code>statements to execute if condition is false</code>
|
||||
}</p>
|
||||
</blockquote>
|
||||
<p>同时,else 语句也可包含<code>if</code>语句,从而形成一条链来测试更多的条件,像下面这样:</p>
|
||||
<pre><code class="lang-swift">if `condition 1` {
|
||||
`statements to execute if condition 1 is true`
|
||||
} else if `condition 2` {
|
||||
`statements to execute if condition 2 is true`
|
||||
}
|
||||
else {
|
||||
`statements to execute if both conditions are false`
|
||||
}
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>if <code>condition 1</code> {<br> <code>statements to execute if condition 1 is true</code><br>} else if <code>condition 2</code> {<br> <code>statements to execute if condition 2 is true</code><br>}<br>else {<br> <code>statements to execute if both conditions are false</code><br>} </p>
|
||||
</blockquote>
|
||||
<p><code>if</code>语句中条件的值的类型必须遵循<code>LogicValue</code>协议。同时,条件也可以使用可选绑定,详情参见<a href="../chapter2/01_The_Basics.html#optional_binding">可选绑定</a>。</p>
|
||||
<blockquote>
|
||||
<p>GRAMMAR OF AN IF STATEMENT</p>
|
||||
<p><em>if-statement</em> → <strong>if</strong> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/if-condition" target="_blank"><em>if-condition</em></a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/code-block" target="_blank"><em>code-block</em></a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/else-clause" target="_blank"><em>else-clause</em></a> <em>opt</em></p>
|
||||
<p><em>if-condition</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression" target="_blank"><em>expression</em></a> | <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/declaration" target="_blank"><em>declaration</em></a></p>
|
||||
<p><em>else-clause</em> → <strong>else</strong> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/code-block" target="_blank"><em>code-block</em></a> | <strong>else</strong> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/if-statement" target="_blank"><em>if-statement</em></a> <em>opt</em></p>
|
||||
<p>If语句语法<br><em>if语句</em> → <strong>if</strong> <a href="..\chapter3\10_Statements.html#if_condition"><em>if条件</em></a> <a href="..\chapter3\05_Declarations.html#code_block"><em>代码块</em></a> <a href="..\chapter3\10_Statements.html#else_clause"><em>else子句(Clause)</em></a> <em>可选</em><br><em>if条件</em> → <a href="..\chapter3\04_Expressions.html#expression"><em>表达式</em></a> | <a href="..\chapter3\05_Declarations.html#declaration"><em>声明</em></a><br><em>else子句(Clause)</em> → <strong>else</strong> <a href="..\chapter3\05_Declarations.html#code_block"><em>代码块</em></a> | <strong>else</strong> <a href="..\chapter3\10_Statements.html#if_statement"><em>if语句</em></a> </p>
|
||||
</blockquote>
|
||||
<h3 id="switch-">Switch 语句</h3>
|
||||
<p>取决于<code>switch</code>语句的<em>控制表达式(control expression)</em>,<code>switch</code>语句将决定执行哪一块代码。</p>
|
||||
<p><code>switch</code>语句的形式如下:</p>
|
||||
<pre><code class="lang-swift">switch `control expression` {
|
||||
case `pattern 1`:
|
||||
`statements`
|
||||
case `pattern 2` where `condition`:
|
||||
`statements`
|
||||
case `pattern 3` where `condition`,
|
||||
`pattern 4` where `condition`:
|
||||
`statements`
|
||||
default:
|
||||
`statements`
|
||||
}
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>switch <code>control expression</code> {<br> case <code>pattern 1</code>:<br> <code>statements</code><br> case <code>pattern 2</code> where <code>condition</code>:<br> <code>statements</code><br> case <code>pattern 3</code> where <code>condition</code>,<br> <code>pattern 4</code> where <code>condition</code>:<br> <code>statements</code><br> default:<br> <code>statements</code><br>} </p>
|
||||
</blockquote>
|
||||
<p><code>switch</code>语句的<em>控制表达式(control expression)</em>会首先被计算,然后与每一个 case 的模式(pattern)进行匹配。如果匹配成功,程序将会执行对应的 case 分支里的 <em>statements</em>。另外,每一个 case 分支都不能为空,也就是说在每一个 case 分支中至少有一条语句。如果你不想在匹配到的 case 分支中执行代码,只需在该分支里写一条<code>break</code>语句即可。</p>
|
||||
<p>可以用作控制表达式的值是十分灵活的,除了标量类型(scalar types,如<code>Int</code>、<code>Character</code>)外,你可以使用任何类型的值,包括浮点数、字符串、元组、自定义类的实例和可选(optional)类型,甚至是枚举类型中的成员值和指定的范围(range)等。关于在<code>switch</code>语句中使用这些类型,详情参见<a href="../chapter2/05_Control_Flow.html">控制流</a>一章的 <a href="../chapter2/05_Control_Flow.html#switch">Switch</a>。</p>
|
||||
<p>你可以在模式后面添加一个起保护作用的表达式(guard expression)。<em>起保护作用的表达式</em>是这样构成的:关键字<code>where</code>后面跟着一个作为额外测试条件的表达式。因此,当且仅当<em>控制表达式</em>匹配一个<em>case</em>的某个模式且起保护作用的表达式为真时,对应 case 分支中的 <em>statements</em> 才会被执行。在下面的例子中,<em>控制表达式</em>只会匹配含两个相等元素的元组,如<code>(1, 1)</code>:</p>
|
||||
@ -775,16 +725,7 @@ else {
|
||||
<h4 id="-fall-through-">不存在隐式的贯穿(fall through)</h4>
|
||||
<p>当匹配的 case 分支中的代码执行完毕后,程序会终止<code>switch</code>语句,而不会继续执行下一个 case 分支。这就意味着,如果你想执行下一个 case 分支,需要显式地在你需要的 case 分支里使用<code>fallthrough</code>语句。关于<code>fallthrough</code>语句的更多信息,详情参见 <a href="#fallthrough_statement">Fallthrough 语句</a>。</p>
|
||||
<blockquote>
|
||||
<p>GRAMMAR OF A SWITCH STATEMENT</p>
|
||||
<p><em>switch-statement</em> → <strong>switch</strong> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression" target="_blank"><em>expression</em></a> <strong>{</strong> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/switch-cases" target="_blank"><em>switch-cases</em></a> <em>opt</em> <strong>}</strong></p>
|
||||
<p><em>switch-cases</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/switch-case" target="_blank"><em>switch-case</em></a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/switch-cases" target="_blank"><em>switch-cases</em></a> <em>opt</em></p>
|
||||
<p><em>switch-case</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/case-label" target="_blank"><em>case-label</em></a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/statements" target="_blank"><em>statement</em></a> | <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/default-label" target="_blank"><em>default-label</em></a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/statements" target="_blank"><em>statements</em></a></p>
|
||||
<p><em>switch-case</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/case-label" target="_blank"><em>case-label</em></a> <strong>;</strong> | <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/default-label" target="_blank"><em>default-label</em></a> <strong>;</strong></p>
|
||||
<p><em>case-label</em> → <strong>case</strong> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/case-item-list" target="_blank"><em>case-item-list</em></a> <strong>:</strong></p>
|
||||
<p><em>case-item-list</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Patterns.html#//apple_ref/swift/grammar/pattern" target="_blank"><em>pattern</em></a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/guard-clause" target="_blank"><em>guard-clause</em></a> <em>opt</em> | <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Patterns.html#//apple_ref/swift/grammar/pattern" target="_blank"><em>pattern</em></a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/guard-clause" target="_blank"><em>guard-clause</em></a> <em>opt</em>, <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/case-item-list" target="_blank"><em>case-item-list</em></a></p>
|
||||
<p><em>default-label</em> → <strong>default :</strong></p>
|
||||
<p><em>guard-clause</em> → <strong>where</strong> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/guard-expression" target="_blank"><em>guard-expression</em></a></p>
|
||||
<p><em>guard-expression</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression" target="_blank"><em>expression</em></a></p>
|
||||
<p>Switch语句语法<br><em>switch语句</em> → <strong>switch</strong> <a href="..\chapter3\04_Expressions.html#expression"><em>表达式</em></a> <strong>{</strong> <a href="..\chapter3\10_Statements.html#switch_cases"><em>SwitchCase列表</em></a> <em>可选</em> <strong>}</strong><br><em>SwitchCase列表</em> → <a href="..\chapter3\10_Statements.html#switch_case"><em>SwitchCase</em></a> <a href="..\chapter3\10_Statements.html#switch_cases"><em>SwitchCase列表</em></a> <em>可选</em><br><em>SwitchCase</em> → <a href="..\chapter3\10_Statements.html#case_label"><em>case标签</em></a> <a href="..\chapter3\10_Statements.html#statements"><em>多条语句(Statements)</em></a> | <a href="..\chapter3\10_Statements.html#default_label"><em>default标签</em></a> <a href="..\chapter3\10_Statements.html#statements"><em>多条语句(Statements)</em></a><br><em>SwitchCase</em> → <a href="..\chapter3\10_Statements.html#case_label"><em>case标签</em></a> <strong>;</strong> | <a href="..\chapter3\10_Statements.html#default_label"><em>default标签</em></a> <strong>;</strong><br><em>case标签</em> → <strong>case</strong> <a href="..\chapter3\10_Statements.html#case_item_list"><em>case项列表</em></a> <strong>:</strong><br><em>case项列表</em> → <a href="..\chapter3\07_Patterns.html#pattern"><em>模式</em></a> <a href="..\chapter3\10_Statements.html#guard_clause"><em>guard-clause</em></a> <em>可选</em> | <a href="..\chapter3\07_Patterns.html#pattern"><em>模式</em></a> <a href="..\chapter3\10_Statements.html#guard_clause"><em>guard-clause</em></a> <em>可选</em> <strong>,</strong> <a href="..\chapter3\10_Statements.html#case_item_list"><em>case项列表</em></a><br><em>default标签</em> → <strong>default</strong> <strong>:</strong><br><em>guard-clause</em> → <strong>where</strong> <a href="..\chapter3\10_Statements.html#guard_expression"><em>guard-expression</em></a><br><em>guard-expression</em> → <a href="..\chapter3\04_Expressions.html#expression"><em>表达式</em></a> </p>
|
||||
</blockquote>
|
||||
<p><a name="labeled_statement"></a>
|
||||
<a name="control_transfer_statements"></a> 带标签的语句</p>
|
||||
@ -792,48 +733,39 @@ else {
|
||||
<p>标签的作用域是该标签所标记的语句之后的所有语句。你可以不使用带标签的语句,但只要使用它,标签名就必唯一。</p>
|
||||
<p>关于使用带标签的语句的例子,详情参见<a href="../chapter2/05_Control_Flow.html">控制流</a>一章的<a href="../chapter2/05_Control_Flow.html#labeled_statements">带标签的语句</a>。</p>
|
||||
<blockquote>
|
||||
<p>GRAMMAR OF A LABELED STATEMENT</p>
|
||||
<p><em>labeled-statement</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/statement-label" target="_blank"><em>statement-label</em></a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/loop-statement" target="_blank"><em>loop-statement</em></a> | <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/statement-label" target="_blank"><em>statement-label</em></a> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/switch-statement" target="_blank"><em>switch-statement</em></a></p>
|
||||
<p><em>statement-label</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/label-name" target="_blank"><em>label-name</em></a> <strong>:</strong></p>
|
||||
<p><em>label-name</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/identifier" target="_blank"><em>identifier</em></a></p>
|
||||
<p>标记语句语法<br><em>标记语句(Labeled Statement)</em> → <a href="..\chapter3\10_Statements.html#statement_label"><em>语句标签</em></a> <a href="..\chapter3\10_Statements.html#loop_statement"><em>循环语句</em></a> | <a href="..\chapter3\10_Statements.html#statement_label"><em>语句标签</em></a> <a href="..\chapter3\10_Statements.html#switch_statement"><em>switch语句</em></a><br><em>语句标签</em> → <a href="..\chapter3\10_Statements.html#label_name"><em>标签名称</em></a> <strong>:</strong><br><em>标签名称</em> → <a href="..\chapter3\02_Lexical_Structure.html#identifier"><em>标识符</em></a> </p>
|
||||
</blockquote>
|
||||
<h2 id="-">控制传递语句</h2>
|
||||
<p>通过无条件地把控制权从一片代码传递到另一片代码,控制传递语句能够改变代码执行的顺序。Swift 提供四种类型的控制传递语句:<code>break</code>语句、<code>continue</code>语句、<code>fallthrough</code>语句和<code>return</code>语句。</p>
|
||||
<blockquote>
|
||||
<p>GRAMMAR OF A CONTROL TRANSER STATEMENT</p>
|
||||
<p><em>control-transfer-statement</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/break-statement" target="_blank"><em>break-statement</em></a></p>
|
||||
<p><em>control-transfer-statement</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/continue-statement" target="_blank"><em>continue-statement</em></a></p>
|
||||
<p><em>control-transfer-statement</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/fallthrough-statement" target="_blank"><em>fallthrough-statement</em></a></p>
|
||||
<p><em>control-transfer-statement</em> → <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/return-statement" target="_blank"><em>return-statement</em></a></p>
|
||||
<p>控制传递语句(Control Transfer Statement) 语法<br><em>控制传递语句</em> → <a href="..\chapter3\10_Statements.html#break_statement"><em>break语句</em></a><br><em>控制传递语句</em> → <a href="..\chapter3\10_Statements.html#continue_statement"><em>continue语句</em></a><br><em>控制传递语句</em> → <a href="..\chapter3\10_Statements.html#fallthrough_statement"><em>fallthrough语句</em></a><br><em>控制传递语句</em> → <a href="..\chapter3\10_Statements.html#return_statement"><em>return语句</em></a> </p>
|
||||
</blockquote>
|
||||
<p><a name="break_statement"></a></p>
|
||||
<h3 id="break-">Break 语句</h3>
|
||||
<p><code>break</code>语句用于终止循环或<code>switch</code>语句的执行。使用<code>break</code>语句时,可以只写<code>break</code>这个关键词,也可以在<code>break</code>后面跟上标签名(label name),像下面这样:</p>
|
||||
<pre><code class="lang-swift">break
|
||||
break `label name`
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>break<br>break <code>label name</code></p>
|
||||
</blockquote>
|
||||
<p>当<code>break</code>语句后面带标签名时,可用于终止由这个标签标记的循环或<code>switch</code>语句的执行。</p>
|
||||
<p>而当只写<code>break</code>时,则会终止<code>switch</code>语句或上下文中包含<code>break</code>语句的最内层循环的执行。</p>
|
||||
<p>在这两种情况下,控制权都会被传递给循环或<code>switch</code>语句外面的第一行语句。</p>
|
||||
<p>关于使用<code>break</code>语句的例子,详情参见<a href="../chapter2/05_Control_Flow.html">控制流</a>一章的 <a href="../chapter2/05_Control_Flow.html#break">Break</a> 和<a href="../chapter2/05_Control_Flow.html#labeled_statements">带标签的语句</a>。</p>
|
||||
<blockquote>
|
||||
<p>GRAMMAR OF A BREAK STATEMENT</p>
|
||||
<p><em>break-statement</em> → <strong>break</strong> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/label-name" target="_blank"><em>label-name</em></a> <em>opt</em></p>
|
||||
<p>Break 语句语法<br><em>break语句</em> → <strong>break</strong> <a href="..\chapter3\10_Statements.html#label_name"><em>标签名称</em></a> <em>可选</em> </p>
|
||||
</blockquote>
|
||||
<p><a name="continue_statement"></a></p>
|
||||
<h3 id="continue-">Continue 语句</h3>
|
||||
<p><code>continue</code>语句用于终止循环中当前迭代的执行,但不会终止该循环的执行。使用<code>continue</code>语句时,可以只写<code>continue</code>这个关键词,也可以在<code>continue</code>后面跟上标签名(label name),像下面这样:</p>
|
||||
<pre><code class="lang-swift">continue
|
||||
continue `label name`
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>continue<br>continue <code>label name</code> </p>
|
||||
</blockquote>
|
||||
<p>当<code>continue</code>语句后面带标签名时,可用于终止由这个标签标记的循环中当前迭代的执行。</p>
|
||||
<p>而当只写<code>break</code>时,可用于终止上下文中包含<code>continue</code>语句的最内层循环中当前迭代的执行。</p>
|
||||
<p>在这两种情况下,控制权都会被传递给循环外面的第一行语句。</p>
|
||||
<p>在<code>for</code>语句中,<code>continue</code>语句执行后,<em>increment</em> 表达式还是会被计算,这是因为每次循环体执行完毕后 <em>increment</em> 表达式都会被计算。</p>
|
||||
<p>关于使用<code>continue</code>语句的例子,详情参见<a href="../chapter2/05_Control_Flow.html">控制流</a>一章的 <a href="../chapter2/05_Control_Flow.html#continue">Continue</a> 和<a href="../chapter2/05_Control_Flow.html#labeled_statements">带标签的语句</a>。</p>
|
||||
<blockquote>
|
||||
<p>GRAMMAR OF A CONTINUE STATEMENT</p>
|
||||
<p><em>continue-statement</em> → <strong>continue</strong> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/label-name" target="_blank"><em>label-name</em></a> <em>opt</em></p>
|
||||
<p>Continue 语句语法<br><em>continue语句</em> → <strong>continue</strong> <a href="..\chapter3\10_Statements.html#label_name"><em>标签名称</em></a> <em>可选</em> </p>
|
||||
</blockquote>
|
||||
<p><a name="fallthrough_statement"></a></p>
|
||||
<h3 id="fallthrough-">Fallthrough 语句</h3>
|
||||
@ -841,20 +773,18 @@ continue `label name`
|
||||
<p><code>fallthrough</code>语句可出现在<code>switch</code>语句中的任意 case 里,但不能出现在最后一个 case 分支中。同时,<code>fallthrough</code>语句也不能把控制权传递给使用了可选绑定的 case 分支。</p>
|
||||
<p>关于在<code>switch</code>语句中使用<code>fallthrough</code>语句的例子,详情参见<a href="../chapter2/05_Control_Flow.html">控制流</a>一章的<a href="../chapter2/05_Control_Flow.html#control_transfer_statements">控制传递语句</a>。</p>
|
||||
<blockquote>
|
||||
<p>GRAMMAR OF A FALLTHROUGH STATEMENT</p>
|
||||
<p><em>continue-statement</em> → <strong>fallthrough</strong></p>
|
||||
<p>Fallthrough 语句语法<br><em>fallthrough语句</em> → <strong>fallthrough</strong> </p>
|
||||
</blockquote>
|
||||
<h3 id="return-">Return 语句</h3>
|
||||
<p><code>return</code>语句用于在函数或方法的实现中将控制权传递给调用者,接着程序将会从调用者的位置继续向下执行。</p>
|
||||
<p>使用<code>return</code>语句时,可以只写<code>return</code>这个关键词,也可以在<code>return</code>后面跟上表达式,像下面这样:</p>
|
||||
<pre><code class="lang-swift">return
|
||||
return `expression`
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>return<br>return <code>expression</code> </p>
|
||||
</blockquote>
|
||||
<p>当<code>return</code>语句后面带表达式时,表达式的值将会返回给调用者。如果表达式值的类型与调用者期望的类型不匹配,Swift 则会在返回表达式的值之前将表达式值的类型转换为调用者期望的类型。</p>
|
||||
<p>而当只写<code>return</code>时,仅仅是将控制权从该函数或方法传递给调用者,而不返回一个值。(这就是说,该函数或方法的返回类型为<code>Void</code>或<code>()</code>)</p>
|
||||
<blockquote>
|
||||
<p>GRAMMAR OF A RETURN STATEMENT</p>
|
||||
<p><em>return-statement</em> → <strong>return</strong> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression" target="_blank"><em>expression</em></a> <em>opt</em></p>
|
||||
<p>Return 语句语法<br><em>return语句</em> → <strong>return</strong> <a href="..\chapter3\04_Expressions.html#expression"><em>表达式</em></a> <em>可选</em> </p>
|
||||
</blockquote>
|
||||
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="3" data-basepath=".." data-revision="1402750255397">
|
||||
<div class="book" data-level="3" data-basepath=".." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
|
||||
105
index.html
105
index.html
@ -10,7 +10,7 @@
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="robots" content="index, follow">
|
||||
<meta name="author" content="">
|
||||
<meta name="description" content="Swift 中文翻译组:364279588(要求对翻译感兴趣)">
|
||||
<meta name="description" content="Swift 兴趣交流群:307017261Swift 开发者社区">
|
||||
<meta name="keywords" content="gitbook,github" >
|
||||
<meta name="generator" content="www.gitbook.io">
|
||||
|
||||
@ -44,7 +44,7 @@
|
||||
|
||||
|
||||
|
||||
<div class="book" data-level="0" data-basepath="." data-revision="1402750255397">
|
||||
<div class="book" data-level="0" data-basepath="." data-revision="1402809661159">
|
||||
<div class="book-header">
|
||||
<!-- Actions Left -->
|
||||
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
|
||||
@ -585,12 +585,10 @@
|
||||
|
||||
<div class="page-inner">
|
||||
|
||||
<section class="normal" id="section-gitbook_29">
|
||||
<section class="normal" id="section-gitbook_3">
|
||||
|
||||
<blockquote>
|
||||
<p>Swift 中文翻译组:364279588(要求对翻译感兴趣)</p>
|
||||
<p>Swift 兴趣交流群:307017261</p>
|
||||
<p><a href="http://swiftist.org" target="_blank">Swift 开发者社区</a></p>
|
||||
<p>Swift 兴趣交流群:307017261<br><a href="http://swiftist.org" target="_blank">Swift 开发者社区</a></p>
|
||||
</blockquote>
|
||||
<p><!-- --></p>
|
||||
<blockquote>
|
||||
@ -608,45 +606,62 @@
|
||||
<p>团队里每个人都有自己的事情,上班、上学、创业,但是我们只用了9天就完成整本书的翻译。我不知道大家付出了多少,牺牲了多少,但是我知道,他们的付出必将被这些文字记录下来,即使再过10年,20年,依然熠熠生辉,永不被人遗忘。</p>
|
||||
<p>全体人员名单(排名不分先后):</p>
|
||||
<ul>
|
||||
<li>numbbbbb</li>
|
||||
<li>coverxit</li>
|
||||
<li>wh1100717</li>
|
||||
<li>TimothyYe</li>
|
||||
<li>honghaoz</li>
|
||||
<li>Hawstein</li>
|
||||
<li>JaySurplus</li>
|
||||
<li>stanzhai</li>
|
||||
<li>lyuka</li>
|
||||
<li>geek5nan</li>
|
||||
<li>xielingwang</li>
|
||||
<li>yankuangshi</li>
|
||||
<li>dabing1022</li>
|
||||
<li>siemenliu</li>
|
||||
<li>fd5788</li>
|
||||
<li>youkugems</li>
|
||||
<li>haolloyin</li>
|
||||
<li>superkam</li>
|
||||
<li>vclwei</li>
|
||||
<li>sg552</li>
|
||||
<li>bzsy</li>
|
||||
<li>pyanfield</li>
|
||||
<li>ericzyh</li>
|
||||
<li>088haizi</li>
|
||||
<li>viztor</li>
|
||||
<li>pp-prog</li>
|
||||
<li>baocaixiong</li>
|
||||
<li>marsprince</li>
|
||||
<li>shinyzhu</li>
|
||||
<li>happyming</li>
|
||||
<li>menlongsheng</li>
|
||||
<li>zq54zquan</li>
|
||||
<li>Evilcome</li>
|
||||
<li>lslxdx</li>
|
||||
<li>yeahdongcn</li>
|
||||
<li>zqp</li>
|
||||
<li>xiehurricane</li>
|
||||
<li>Jasonbroker</li>
|
||||
<li>aquaporcus</li>
|
||||
<li><a href="https://github.com/numbbbbb" target="_blank">numbbbbb</a></li>
|
||||
<li><a href="https://github.com/stanzhai" target="_blank">stanzhai</a></li>
|
||||
<li><a href="https://github.com/coverxit" target="_blank">coverxit</a></li>
|
||||
<li><a href="https://github.com/wh1100717" target="_blank">wh1100717</a></li>
|
||||
<li><a href="https://github.com/TimothyYe" target="_blank">TimothyYe</a></li>
|
||||
<li><a href="https://github.com/honghaoz" target="_blank">honghaoz</a></li>
|
||||
<li><a href="https://github.com/lyuka" target="_blank">lyuka</a></li>
|
||||
<li><a href="https://github.com/JaySurplus" target="_blank">JaySurplus</a></li>
|
||||
<li><a href="https://github.com/Hawstein" target="_blank">Hawstein</a></li>
|
||||
<li><a href="https://github.com/geek5nan" target="_blank">geek5nan</a></li>
|
||||
<li><a href="https://github.com/yankuangshi" target="_blank">yankuangshi</a></li>
|
||||
<li><a href="https://github.com/xielingwang" target="_blank">xielingwang</a></li>
|
||||
<li><a href="https://github.com/yulingtianxia" target="_blank">yulingtianxia</a></li>
|
||||
<li><a href="https://github.com/twlkyao" target="_blank">twlkyao</a></li>
|
||||
<li><a href="https://github.com/dabing1022" target="_blank">dabing1022</a></li>
|
||||
<li><a href="https://github.com/vclwei" target="_blank">vclwei</a></li>
|
||||
<li><a href="https://github.com/fd5788" target="_blank">fd5788</a></li>
|
||||
<li><a href="https://github.com/siemenliu" target="_blank">siemenliu</a></li>
|
||||
<li><a href="https://github.com/youkugems" target="_blank">youkugems</a></li>
|
||||
<li><a href="https://github.com/haolloyin" target="_blank">haolloyin</a></li>
|
||||
<li><a href="https://github.com/wxstars" target="_blank">wxstars</a></li>
|
||||
<li><a href="https://github.com/IceskYsl" target="_blank">IceskYsl</a></li>
|
||||
<li><a href="https://github.com/sg552" target="_blank">sg552</a></li>
|
||||
<li><a href="https://github.com/superkam" target="_blank">superkam</a></li>
|
||||
<li><a href="https://github.com/zac1st1k" target="_blank">zac1st1k</a></li>
|
||||
<li><a href="https://github.com/bzsy" target="_blank">bzsy</a></li>
|
||||
<li><a href="https://github.com/pyanfield" target="_blank">pyanfield</a></li>
|
||||
<li><a href="https://github.com/ericzyh" target="_blank">ericzyh</a></li>
|
||||
<li><a href="https://github.com/peiyucn" target="_blank">peiyucn</a></li>
|
||||
<li><a href="https://github.com/sunfiled" target="_blank">sunfiled</a></li>
|
||||
<li><a href="https://github.com/lzw120" target="_blank">lzw120</a></li>
|
||||
<li><a href="https://github.com/viztor" target="_blank">viztor</a></li>
|
||||
<li><a href="https://github.com/wongzigii" target="_blank">wongzigii</a></li>
|
||||
<li><a href="https://github.com/umcsdon" target="_blank">umcsdon</a></li>
|
||||
<li><a href="https://github.com/zq54zquan" target="_blank">zq54zquan</a></li>
|
||||
<li><a href="https://github.com/xiehurricane" target="_blank">xiehurricane</a></li>
|
||||
<li><a href="https://github.com/Jasonbroker" target="_blank">Jasonbroker</a></li>
|
||||
<li><a href="https://github.com/tualatrix" target="_blank">tualatrix</a></li>
|
||||
<li><a href="https://github.com/pp-prog" target="_blank">pp-prog</a></li>
|
||||
<li><a href="https://github.com/088haizi" target="_blank">088haizi</a></li>
|
||||
<li><a href="https://github.com/baocaixiong" target="_blank">baocaixiong</a></li>
|
||||
<li><a href="https://github.com/yeahdongcn" target="_blank">yeahdongcn</a></li>
|
||||
<li><a href="https://github.com/shinyzhu" target="_blank">shinyzhu</a></li>
|
||||
<li><a href="https://github.com/lslxdx" target="_blank">lslxdx</a></li>
|
||||
<li><a href="https://github.com/Evilcome" target="_blank">Evilcome</a></li>
|
||||
<li><a href="https://github.com/zqp" target="_blank">zqp</a></li>
|
||||
<li><a href="https://github.com/NicePiao" target="_blank">NicePiao</a></li>
|
||||
<li><a href="https://github.com/LunaticM" target="_blank">LunaticM</a></li>
|
||||
<li><a href="https://github.com/menlongsheng" target="_blank">menlongsheng</a></li>
|
||||
<li><a href="https://github.com/lifedim" target="_blank">lifedim</a></li>
|
||||
<li><a href="https://github.com/happyming" target="_blank">happyming</a></li>
|
||||
<li><a href="https://github.com/bruce0505" target="_blank">bruce0505</a></li>
|
||||
<li><a href="https://github.com/Lin-H" target="_blank">Lin-H</a></li>
|
||||
<li><a href="https://github.com/takalard" target="_blank">takalard</a></li>
|
||||
<li><a href="https://github.com/dabing1022" target="_blank">dabing1022</a></li>
|
||||
<li><a href="https://github.com/marsprince" target="_blank">marsprince</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
CACHE MANIFEST
|
||||
# Revision 1402750255398
|
||||
# Revision 1402809661160
|
||||
|
||||
CACHE:
|
||||
index.html
|
||||
@ -14,11 +14,11 @@ chapter2/05_Control_Flow.html
|
||||
chapter2/06_Functions.html
|
||||
chapter2/07_Closures.html
|
||||
chapter2/08_Enumerations.html
|
||||
chapter2/09_Classes_and_Structures.html
|
||||
chapter2/10_Properties.html
|
||||
chapter2/11_Methods.html
|
||||
chapter2/12_Subscripts.html
|
||||
chapter2/02_Basic_Operators.html
|
||||
chapter2/10_Properties.html
|
||||
chapter2/09_Classes_and_Structures.html
|
||||
chapter2/14_Initialization.html
|
||||
chapter2/15_Deinitialization.html
|
||||
chapter2/16_Automatic_Reference_Counting.html
|
||||
@ -34,12 +34,12 @@ chapter3/06_Attributes.html
|
||||
chapter3/01_About_the_Language_Reference.html
|
||||
chapter3/03_Types.html
|
||||
chapter3/04_Expressions.html
|
||||
chapter3/02_Lexical_Structure.html
|
||||
chapter3/05_Declarations.html
|
||||
chapter3/02_Lexical_Structure.html
|
||||
chapter3/07_Patterns.html
|
||||
chapter3/08_Generic_Parameters_and_Arguments.html
|
||||
chapter3/10_Statements.html
|
||||
chapter3/09_Summary_of_the_Grammar.html
|
||||
chapter3/10_Statements.html
|
||||
chapter3/chapter3.html
|
||||
gitbook/app.js
|
||||
gitbook/fonts/anonymouspro/400.woff
|
||||
|
||||
File diff suppressed because one or more lines are too long
BIN
source/.DS_Store
vendored
BIN
source/.DS_Store
vendored
Binary file not shown.
100
source/README.md
100
source/README.md
@ -1,7 +1,4 @@
|
||||
> Swift 中文翻译组:364279588(要求对翻译感兴趣)
|
||||
|
||||
> Swift 兴趣交流群:307017261
|
||||
|
||||
> Swift 兴趣交流群:307017261
|
||||
> [Swift 开发者社区](http://swiftist.org)
|
||||
|
||||
<!-- -->
|
||||
@ -29,42 +26,59 @@ lifedim说他平时12点就会睡,1点47分,发给我校对后的文档。
|
||||
|
||||
全体人员名单(排名不分先后):
|
||||
|
||||
- numbbbbb
|
||||
- coverxit
|
||||
- wh1100717
|
||||
- TimothyYe
|
||||
- honghaoz
|
||||
- Hawstein
|
||||
- JaySurplus
|
||||
- stanzhai
|
||||
- lyuka
|
||||
- geek5nan
|
||||
- xielingwang
|
||||
- yankuangshi
|
||||
- dabing1022
|
||||
- siemenliu
|
||||
- fd5788
|
||||
- youkugems
|
||||
- haolloyin
|
||||
- superkam
|
||||
- vclwei
|
||||
- sg552
|
||||
- bzsy
|
||||
- pyanfield
|
||||
- ericzyh
|
||||
- 088haizi
|
||||
- viztor
|
||||
- pp-prog
|
||||
- baocaixiong
|
||||
- marsprince
|
||||
- shinyzhu
|
||||
- happyming
|
||||
- menlongsheng
|
||||
- zq54zquan
|
||||
- Evilcome
|
||||
- lslxdx
|
||||
- yeahdongcn
|
||||
- zqp
|
||||
- xiehurricane
|
||||
- Jasonbroker
|
||||
- aquaporcus
|
||||
- [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)
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
> 翻译:numbbbbb
|
||||
> 校对:yeahdongcn
|
||||
|
||||
# 关于 Swift
|
||||
-----------------
|
||||
|
||||
Swift 是一种新的编程语言,用于编写 iOS 和 OS X 应用。Swift 结合了 C 和 Objective-C 的优点并且不受 C 兼容性的限制。Swift 采用安全的编程模式并添加了很多新特性,这将使编程更简单,更灵活,也更有趣。Swift 是基于成熟而且倍受喜爱的 Cocoa 和 Cocoa Touch 框架,它的降临将重新定义软件开发。
|
||||
|
||||
Swift 的开发从很久之前就开始了。为了给 Swift 打好基础,苹果公司改进了编译器,调试器和框架结构。我们使用自动引用计数(Automatic Reference Counting, ARC)来简化内存管理。我们在 Foundation 和 Cocoa 的基础上构建框架栈并将其标准化。Objective-C 本身支持块、集合语法和模块,所以框架可以轻松支持现代编程语言技术。正是得益于这些基础工作,我们现在才能发布这样一个用于未来苹果软件开发的新语言。
|
||||
|
||||
Objective-C 开发者对 Swift 并不会感到陌生。它采用了 Objective-C 的命名参数以及动态对象模型,可以无缝对接到现有的 Cocoa 框架,并且可以兼容 Objective-C 代码。在此基础之上,Swift 还有许多新特性并且支持过程式编程和面向对象编程。
|
||||
|
||||
Swift 对于初学者来说也很友好。它是第一个既满足工业标准又像脚本语言一样充满表现力和趣味的编程语言。它支持代码预览,这个革命性的特性可以允许程序员在不编译和运行应用程序的前提下运行 Swift 代码并实时查看结果。
|
||||
|
||||
Swift 将现代编程语言的精华和苹果工程师文化的智慧结合了起来。编译器对性能进行了优化,编程语言对开发进行了优化,两者互不干扰,鱼与熊掌兼得。Swift 既可以用于开发 “hello, world” 这样的小程序,也可以用于开发一套完整的操作系统。所有的这些特性让 Swift 对于开发者和苹果来说都是一项值得的投资。
|
||||
|
||||
Swift 是编写 iOS 和 OS X 应用的极佳手段,并将伴随着新的特性和功能持续演进。我们对 Swift 充满信心,你还在等什么!
|
||||
> 翻译:[numbbbbb](https://github.com/numbbbbb)
|
||||
> 校对:[yeahdongcn](https://github.com/yeahdongcn)
|
||||
|
||||
# 关于 Swift
|
||||
-----------------
|
||||
|
||||
Swift 是一种新的编程语言,用于编写 iOS 和 OS X 应用。Swift 结合了 C 和 Objective-C 的优点并且不受 C 兼容性的限制。Swift 采用安全的编程模式并添加了很多新特性,这将使编程更简单,更灵活,也更有趣。Swift 是基于成熟而且倍受喜爱的 Cocoa 和 Cocoa Touch 框架,它的降临将重新定义软件开发。
|
||||
|
||||
Swift 的开发从很久之前就开始了。为了给 Swift 打好基础,苹果公司改进了编译器,调试器和框架结构。我们使用自动引用计数(Automatic Reference Counting, ARC)来简化内存管理。我们在 Foundation 和 Cocoa 的基础上构建框架栈并将其标准化。Objective-C 本身支持块、集合语法和模块,所以框架可以轻松支持现代编程语言技术。正是得益于这些基础工作,我们现在才能发布这样一个用于未来苹果软件开发的新语言。
|
||||
|
||||
Objective-C 开发者对 Swift 并不会感到陌生。它采用了 Objective-C 的命名参数以及动态对象模型,可以无缝对接到现有的 Cocoa 框架,并且可以兼容 Objective-C 代码。在此基础之上,Swift 还有许多新特性并且支持过程式编程和面向对象编程。
|
||||
|
||||
Swift 对于初学者来说也很友好。它是第一个既满足工业标准又像脚本语言一样充满表现力和趣味的编程语言。它支持代码预览,这个革命性的特性可以允许程序员在不编译和运行应用程序的前提下运行 Swift 代码并实时查看结果。
|
||||
|
||||
Swift 将现代编程语言的精华和苹果工程师文化的智慧结合了起来。编译器对性能进行了优化,编程语言对开发进行了优化,两者互不干扰,鱼与熊掌兼得。Swift 既可以用于开发 “hello, world” 这样的小程序,也可以用于开发一套完整的操作系统。所有的这些特性让 Swift 对于开发者和苹果来说都是一项值得的投资。
|
||||
|
||||
Swift 是编写 iOS 和 OS X 应用的极佳手段,并将伴随着新的特性和功能持续演进。我们对 Swift 充满信心,你还在等什么!
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
# 欢迎使用 Swift
|
||||
|
||||
在本章中您将了解 Swift 的特性和开发历史,并对 Swift 有一个初步的了解。
|
||||
|
||||
# 欢迎使用 Swift
|
||||
|
||||
在本章中您将了解 Swift 的特性和开发历史,并对 Swift 有一个初步的了解。
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,457 +1,457 @@
|
||||
> 翻译:xielingwang
|
||||
> 校对:Evilcome
|
||||
|
||||
# 基本运算符
|
||||
-----------------
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [术语](#terminology)
|
||||
- [赋值运算符](#assignment_operator)
|
||||
- [数值运算符](#arithmetic_operators)
|
||||
- [组合赋值运算符(Compound Assignment Operators)](#compound_assignment_operators)
|
||||
- [比较运算符](#comparison_operators)
|
||||
- [三元条件运算符(Ternary Conditional Operator)](#ternary_conditional_operator)
|
||||
- [区间运算符](#range_operators)
|
||||
- [逻辑运算符](#logical_operators)
|
||||
|
||||
运算符是检查,改变,合并值的特殊符号或短语。例如,加号`+`将两个数相加(如`let i = 1 + 2`)。复杂些的运行算例如逻辑与运算符`&&`(如`if enteredDoorCode && passedRetinaScan`),又或让 i 值加1的便捷运算符自增运算符`++i`等。
|
||||
|
||||
Swift 支持大部分标准 C 语言的运算符,且改进许多特性来减少常规编码错误。如,赋值符(`=`)不返回值,以防止把想要判断相等运算符(`==`)的地方写成赋值符导致的错误。数值运算符(`+`,`-`,`*`,`/`,`%`等)会检测并不允许值溢出,以此来避免保存变量时由于变量大于或小于其类型所能承载的范围时导致的异常结果。当然允许你使用 Swift 的溢出运算符来实现溢出。详情参见[溢出运算符](23_Advanced_Operators.html#overflow_operators)。
|
||||
|
||||
区别于 C 语言,在 Swift 中你可以对浮点数进行取余运算(`%`),Swift 还提供了 C 语言没有的表达两数之间的值的区间运算符,(`a..b`和`a...b`),这方便我们表达一个区间内的数值。
|
||||
|
||||
本章节只描述了 Swift 中的基本运算符,[高级运算符](23_Advanced_Operators.html)包含了高级运算符,及如何自定义运算符,及如何进行自定义类型的运算符重载。
|
||||
|
||||
<a name="terminology"></a>
|
||||
## 术语
|
||||
|
||||
运算符有一元,二元和三元运算符。
|
||||
|
||||
- 一元运算符对单一操作对象操作(如`-a`)。一元运算符分前置符和后置运算符,前置运算符需紧排操作对象之前(如`!b`),后置运算符需紧跟操作对象之后(如`i++`)。
|
||||
- 二元运算符操作两个操作对象(如`2 + 3`),是中置的,因为它们出现在两个操作对象之间。
|
||||
- 三元运算符操作三个操作对象,和 C 语言一样,Swift 只有一个三元运算符,就是三元条件运算符(`a ? b : c`)。
|
||||
|
||||
受运算符影响的值叫操作数,在表达式`1 + 2`中,加号`+`是二元运算符,它的两个操作数是值`1`和`2`。
|
||||
|
||||
<a name="assignment_operator"></a>
|
||||
## 赋值运算符
|
||||
|
||||
赋值运算(`a = b`),表示用`b`的值来初始化或更新`a`的值:
|
||||
|
||||
```swift
|
||||
let b = 10
|
||||
var a = 5
|
||||
a = b
|
||||
// a 现在等于 10
|
||||
```
|
||||
|
||||
如果赋值的右边是一个多元组,它的元素可以马上被分解多个变量或变量:
|
||||
|
||||
```swiflt
|
||||
let (x, y) = (1, 2)
|
||||
// 现在 x 等于 1, y 等于 2
|
||||
```
|
||||
|
||||
与 C 语言和 Objective-C 不同,Swift 的赋值操作并不返回任何值。所以以下代码是错误的:
|
||||
|
||||
```swift
|
||||
if x = y {
|
||||
// 此句错误, 因为 x = y 并不返回任何值
|
||||
}
|
||||
```
|
||||
|
||||
这个特性使得你无法把(`==`)错写成(`=`)了,由于`if x = y`是错误代码,Swift 从底层帮你避免了这些代码错误。
|
||||
|
||||
<a name="arithmetic_operators"></a>
|
||||
## 数值运算
|
||||
|
||||
Swift 让所有数值类型都支持了基本的四则运算:
|
||||
|
||||
- 加法(`+`)
|
||||
- 减法(`-`)
|
||||
- 乘法(`*`)
|
||||
- 除法(`/`)
|
||||
|
||||
```swift
|
||||
1 + 2 // 等于 3
|
||||
5 - 3 // 等于 2
|
||||
2 * 3 // 等于 6
|
||||
10.0 / 2.5 // 等于 4.0
|
||||
```
|
||||
|
||||
与 C 语言和 Objective-C 不同的是,Swift 默认不允许在数值运算中出现溢出情况。但你可以使用 Swift 的溢出运算符来达到你有目的的溢出(如`a &+ b`)。详情参见[溢出运算符](23_Advanced_Operators.html#overflow_operators)。
|
||||
|
||||
加法运算符也用于`String`的拼接:
|
||||
|
||||
```swift
|
||||
"hello, " + "world" // 等于 "hello, world"
|
||||
```
|
||||
|
||||
两个`Character`值或一个`String`和一个`Character`值,相加会生成一个新的`String`值:
|
||||
|
||||
```swift
|
||||
let dog: Character = "d"
|
||||
let cow: Character = "c"
|
||||
let dogCow = dog + cow
|
||||
// 译者注: 原来的引号内是很可爱的小狗和小牛, 但win os下不支持表情字符, 所以改成了普通字符
|
||||
// dogCow 现在是 "dc"
|
||||
```
|
||||
|
||||
详情参见[字符,字符串的拼接](03_Strings_and_Characters.html#concatenating_strings_and_characters)。
|
||||
|
||||
### 求余运算
|
||||
|
||||
求余运算(`a % b`)是计算`b`的多少倍刚刚好可以容入`a`,返回多出来的那部分(余数)。
|
||||
|
||||
>注意:
|
||||
求余运算(`%`)在其他语言也叫取模运算。然而严格说来,我们看该运算符对负数的操作结果,"求余"比"取模"更合适些。
|
||||
|
||||
我们来谈谈取余是怎么回事,计算`9 % 4`,你先计算出`4`的多少倍会刚好可以容入`9`中:
|
||||
|
||||

|
||||
|
||||
2倍,非常好,那余数是1(用橙色标出)
|
||||
|
||||
在 Swift 中这么来表达:
|
||||
|
||||
```swift
|
||||
9 % 4 // 等于 1
|
||||
```
|
||||
|
||||
为了得到`a % b`的结果,`%`计算了以下等式,并输出`余数`作为结果:
|
||||
|
||||
*a = (b × 倍数) + 余数*
|
||||
|
||||
当`倍数`取最大值的时候,就会刚好可以容入`a`中。
|
||||
|
||||
把`9`和`4`代入等式中,我们得`1`:
|
||||
|
||||
```swift
|
||||
9 = (4 × 2) + 1
|
||||
```
|
||||
|
||||
同样的方法,我来们计算 `-9 % 4`:
|
||||
|
||||
```swift
|
||||
-9 % 4 // 等于 -1
|
||||
```
|
||||
|
||||
把`-9`和`4`代入等式,`-2`是取到的最大整数:
|
||||
|
||||
```swift
|
||||
-9 = (4 × -2) + -1
|
||||
```
|
||||
|
||||
余数是`-1`。
|
||||
|
||||
在对负数`b`求余时,`b`的符号会被忽略。这意味着 `a % b` 和 `a % -b`的结果是相同的。
|
||||
|
||||
### 浮点数求余计算
|
||||
|
||||
不同于 C 语言和 Objective-C,Swift 中是可以对浮点数进行求余的。
|
||||
|
||||
```swift
|
||||
8 % 2.5 // 等于 0.5
|
||||
```
|
||||
|
||||
这个例子中,`8`除于`2.5`等于`3`余`0.5`,所以结果是一个`Double`值`0.5`。
|
||||
|
||||

|
||||
|
||||
### 自增和自增运算
|
||||
|
||||
和 C 语言一样,Swift 也提供了方便对变量本身加1或减1的自增(`++`)和自减(`--`)的运算符。其操作对象可以是整形和浮点型。
|
||||
|
||||
```swift
|
||||
var i = 0
|
||||
++i // 现在 i = 1
|
||||
```
|
||||
|
||||
每调用一次`++i`,`i`的值就会加1。实际上,`++i`是`i = i + 1`的简写,而`--i`是`i = i - 1`的简写。
|
||||
|
||||
`++`和`--`既是前置又是后置运算。`++i`,`i++`,`--i`和`i--`都是有效的写法。
|
||||
|
||||
我们需要注意的是这些运算符修改了`i`后有一个返回值。如果你只想修改`i`的值,那你就可以忽略这个返回值。但如果你想使用返回值,你就需要留意前置和后置操作的返回值是不同的。
|
||||
|
||||
- 当`++`前置的时候,先自増再返回。
|
||||
|
||||
- 当`++`后置的时候,先返回再自增。
|
||||
|
||||
例如:
|
||||
|
||||
```swift
|
||||
var a = 0
|
||||
let b = ++a // a 和 b 现在都是 1
|
||||
let c = a++ // a 现在 2, 但 c 是 a 自增前的值 1
|
||||
```
|
||||
|
||||
上述例子,`let b = ++a`先把`a`加1了再返回`a`的值。所以`a`和`b`都是新值`1`。
|
||||
|
||||
而`let c = a++`,是先返回了`a`的值,然后`a`才加1。所以`c`得到了`a`的旧值1,而`a`加1后变成2。
|
||||
|
||||
除非你需要使用`i++`的特性,不然推荐你使用`++i`和`--i`,因为先修改后返回这样的行为更符合我们的逻辑。
|
||||
|
||||
|
||||
### 一元负号
|
||||
|
||||
数值的正负号可以使用前缀`-`(即一元负号)来切换:
|
||||
|
||||
```swift
|
||||
let three = 3
|
||||
let minusThree = -three // minusThree 等于 -3
|
||||
let plusThree = -minusThree // plusThree 等于 3, 或 "负负3"
|
||||
```
|
||||
|
||||
一元负号(`-`)写在操作数之前,中间没有空格。
|
||||
|
||||
### 一元正号
|
||||
|
||||
一元正号(`+`)不做任何改变地返回操作数的值。
|
||||
|
||||
```swift
|
||||
let minusSix = -6
|
||||
let alsoMinusSix = +minusSix // alsoMinusSix 等于 -6
|
||||
```
|
||||
虽然一元`+`做无用功,但当你在使用一元负号来表达负数时,你可以使用一元正号来表达正数,如此你的代码会具有对称美。
|
||||
|
||||
|
||||
<a name="compound_assignment_operators"></a>
|
||||
## 复合赋值(Compound Assignment Operators)
|
||||
|
||||
如同强大的 C 语言,Swift 也提供把其他运算符和赋值运算(`=`)组合的复合赋值运算符,加赋运算(`+=`)是其中一个例子:
|
||||
|
||||
```swift
|
||||
var a = 1
|
||||
a += 2 // a 现在是 3
|
||||
```
|
||||
|
||||
表达式`a += 2`是`a = a + 2`的简写,一个加赋运算就把加法和赋值两件事完成了。
|
||||
|
||||
>注意:
|
||||
复合赋值运算没有返回值,`let b = a += 2`这类代码是错误。这不同于上面提到的自增和自减运算符。
|
||||
|
||||
在[表达式](../chapter3/04_Expressions.html)章节里有复合运算符的完整列表。
|
||||
|
||||
<a name="comparison_operators"></a>
|
||||
## 比较运算
|
||||
|
||||
所有标准 C 语言中的比较运算都可以在 Swift 中使用。
|
||||
|
||||
- 等于(`a == b`)
|
||||
- 不等于(`a != b`)
|
||||
- 大于(`a > b`)
|
||||
- 小于(`a < b`)
|
||||
- 大于等于(`a >= b`)
|
||||
- 小于等于(`a <= b`)
|
||||
|
||||
> 注意:
|
||||
Swift 也提供恒等`===`和不恒等`!==`这两个比较符来判断两个对象是否引用同一个对象实例。更多细节在[类与结构](09_Classes_and_Structures.html)。
|
||||
|
||||
每个比较运算都返回了一个标识表达式是否成立的布尔值:
|
||||
|
||||
```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`条件:
|
||||
|
||||
```swift
|
||||
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"
|
||||
```
|
||||
|
||||
关于`if`语句,请看[控制流](05_Control_Flow.html)。
|
||||
|
||||
<a name="ternary_conditional_operator"></a>
|
||||
## 三元条件运算(Ternary Conditional Operator)
|
||||
|
||||
三元条件运算的特殊在于它是有三个操作数的运算符,它的原型是 `问题 ? 答案1 : 答案2`。它简洁地表达根据`问题`成立与否作出二选一的操作。如果`问题`成立,返回`答案1`的结果; 如果不成立,返回`答案2`的结果。
|
||||
|
||||
使用三元条件运算简化了以下代码:
|
||||
|
||||
```swift
|
||||
if question: {
|
||||
answer1
|
||||
} else {
|
||||
answer2
|
||||
}
|
||||
```
|
||||
|
||||
这里有个计算表格行高的例子。如果有表头,那行高应比内容高度要高出50像素; 如果没有表头,只需高出20像素。
|
||||
|
||||
```swift
|
||||
let contentHeight = 40
|
||||
let hasHeader = true
|
||||
let rowHeight = contentHeight + (hasHeader ? 50 : 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`语句中改变。
|
||||
|
||||
三元条件运算提供有效率且便捷的方式来表达二选一的选择。需要注意的事,过度使用三元条件运算就会由简洁的代码变成难懂的代码。我们应避免在一个组合语句使用多个三元条件运算符。
|
||||
|
||||
<a name="range_operators"></a>
|
||||
## 区间运算符
|
||||
|
||||
Swift 提供了两个方便表达一个区间的值的运算符。
|
||||
|
||||
### 闭区间运算符
|
||||
闭区间运算符(`a...b`)定义一个包含从`a`到`b`(包括`a`和`b`)的所有值的区间。
|
||||
|
||||
闭区间运算符在迭代一个区间的所有值时是非常有用的,如在`for-in`循环中:
|
||||
|
||||
```swift
|
||||
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
|
||||
```
|
||||
|
||||
关于`for-in`,请看[控制流](05_Control_Flow.html)。
|
||||
|
||||
### 半闭区间
|
||||
|
||||
半闭区间(`a..b`)定义一个从`a`到`b`但不包括`b`的区间。
|
||||
之所以称为半闭区间,是因为该区间包含第一个值而不包括最后的值。
|
||||
|
||||
半闭区间的实用性在于当你使用一个0始的列表(如数组)时,非常方便地从0数到列表的长度。
|
||||
|
||||
```swift
|
||||
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)。
|
||||
|
||||
<a name="logical_operators"></a>
|
||||
## 逻辑运算
|
||||
|
||||
逻辑运算的操作对象是逻辑布尔值。Swift 支持基于 C 语言的三个标准逻辑运算。
|
||||
|
||||
- 逻辑非(`!a`)
|
||||
- 逻辑与(`a && b`)
|
||||
- 逻辑或(`a || b`)
|
||||
|
||||
### 逻辑非
|
||||
|
||||
逻辑非运算(`!a`)对一个布尔值取反,使得`true`变`false`,`false`变`true`。
|
||||
|
||||
它是一个前置运算符,需出现在操作数之前,且不加空格。读作`非 a`,然后我们看以下例子:
|
||||
|
||||
```swift
|
||||
let allowedEntry = false
|
||||
if !allowedEntry {
|
||||
println("ACCESS DENIED")
|
||||
}
|
||||
// 输出 "ACCESS DENIED"
|
||||
```
|
||||
|
||||
`if !allowedEntry`语句可以读作 "如果 非 alowed entry。",接下一行代码只有在如果 "非 allow entry" 为`true`,即`allowEntry`为`false`时被执行。
|
||||
|
||||
在示例代码中,小心地选择布尔常量或变量有助于代码的可读性,并且避免使用双重逻辑非运算,或混乱的逻辑语句。
|
||||
|
||||
### 逻辑与
|
||||
逻辑与(`a && b`)表达了只有`a`和`b`的值都为`true`时,整个表达式的值才会是`true`。
|
||||
|
||||
只要任意一个值为`false`,整个表达式的值就为`false`。事实上,如果第一个值为`false`,那么是不去计算第二个值的,因为它已经不可能影响整个表达式的结果了。这被称做 "短路计算(short-circuit evaluation)"。
|
||||
|
||||
以下例子,只有两个`Bool`值都为`true`值的时候才允许进入:
|
||||
|
||||
```swift
|
||||
let enteredDoorCode = true
|
||||
let passedRetinaScan = false
|
||||
if enteredDoorCode && passedRetinaScan {
|
||||
println("Welcome!")
|
||||
} else {
|
||||
println("ACCESS DENIED")
|
||||
}
|
||||
// 输出 "ACCESS DENIED"
|
||||
```
|
||||
|
||||
### 逻辑或
|
||||
逻辑或(`a || b`)是一个由两个连续的`|`组成的中置运算符。它表示了两个逻辑表达式的其中一个为`true`,整个表达式就为`true`。
|
||||
|
||||
同逻辑与运算类似,逻辑或也是"短路计算"的,当左端的表达式为`true`时,将不计算右边的表达式了,因为它不可能改变整个表达式的值了。
|
||||
|
||||
以下示例代码中,第一个布尔值(`hasDoorKey`)为`false`,但第二个值(`knowsOverridePassword`)为`true`,所以整个表达是`true`,于是允许进入:
|
||||
|
||||
```swift
|
||||
let hasDoorKey = false
|
||||
let knowsOverridePassword = true
|
||||
if hasDoorKey || knowsOverridePassword {
|
||||
println("Welcome!")
|
||||
} else {
|
||||
println("ACCESS DENIED")
|
||||
}
|
||||
// 输出 "Welcome!"
|
||||
```
|
||||
|
||||
### 组合逻辑
|
||||
|
||||
我们可以组合多个逻辑运算来表达一个复合逻辑:
|
||||
|
||||
```swift
|
||||
if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
|
||||
println("Welcome!")
|
||||
} else {
|
||||
println("ACCESS DENIED")
|
||||
}
|
||||
// 输出 "Welcome!"
|
||||
```
|
||||
|
||||
这个例子使用了含多个`&&`和`||`的复合逻辑。但无论怎样,`&&`和`||`始终只能操作两个值。所以这实际是三个简单逻辑连续操作的结果。我们来解读一下:
|
||||
|
||||
如果我们输入了正确的密码并通过了视网膜扫描; 或者我们有一把有效的钥匙; 又或者我们知道紧急情况下重置的密码,我们就能把门打开进入。
|
||||
|
||||
前两种情况,我们都不满足,所以前两个简单逻辑的结果是`false`,但是我们是知道紧急情况下重置的密码的,所以整个复杂表达式的值还是`true`。
|
||||
|
||||
### 使用括号来明确优先级
|
||||
|
||||
为了一个复杂表达式更容易读懂,在合适的地方使用括号来明确优先级是很有效的,虽然它并非必要的。在上个关于门的权限的例子中,我们给第一个部分加个括号,使用它看起来逻辑更明确:
|
||||
|
||||
```swift
|
||||
if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword {
|
||||
println("Welcome!")
|
||||
} else {
|
||||
println("ACCESS DENIED")
|
||||
}
|
||||
// 输出 "Welcome!"
|
||||
```
|
||||
|
||||
这括号使得前两个值被看成整个逻辑表达中独立的一个部分。虽然有括号和没括号的输出结果是一样的,但对于读代码的人来说有括号的代码更清晰。可读性比简洁性更重要,请在可以让你代码变清晰地地方加个括号吧!
|
||||
> 翻译:[xielingwang](https://github.com/xielingwang)
|
||||
> 校对:[Evilcome](https://github.com/Evilcome)
|
||||
|
||||
# 基本运算符
|
||||
-----------------
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [术语](#terminology)
|
||||
- [赋值运算符](#assignment_operator)
|
||||
- [数值运算符](#arithmetic_operators)
|
||||
- [组合赋值运算符(Compound Assignment Operators)](#compound_assignment_operators)
|
||||
- [比较运算符](#comparison_operators)
|
||||
- [三元条件运算符(Ternary Conditional Operator)](#ternary_conditional_operator)
|
||||
- [区间运算符](#range_operators)
|
||||
- [逻辑运算符](#logical_operators)
|
||||
|
||||
运算符是检查,改变,合并值的特殊符号或短语。例如,加号`+`将两个数相加(如`let i = 1 + 2`)。复杂些的运行算例如逻辑与运算符`&&`(如`if enteredDoorCode && passedRetinaScan`),又或让 i 值加1的便捷运算符自增运算符`++i`等。
|
||||
|
||||
Swift 支持大部分标准 C 语言的运算符,且改进许多特性来减少常规编码错误。如,赋值符(`=`)不返回值,以防止把想要判断相等运算符(`==`)的地方写成赋值符导致的错误。数值运算符(`+`,`-`,`*`,`/`,`%`等)会检测并不允许值溢出,以此来避免保存变量时由于变量大于或小于其类型所能承载的范围时导致的异常结果。当然允许你使用 Swift 的溢出运算符来实现溢出。详情参见[溢出运算符](23_Advanced_Operators.html#overflow_operators)。
|
||||
|
||||
区别于 C 语言,在 Swift 中你可以对浮点数进行取余运算(`%`),Swift 还提供了 C 语言没有的表达两数之间的值的区间运算符,(`a..b`和`a...b`),这方便我们表达一个区间内的数值。
|
||||
|
||||
本章节只描述了 Swift 中的基本运算符,[高级运算符](23_Advanced_Operators.html)包含了高级运算符,及如何自定义运算符,及如何进行自定义类型的运算符重载。
|
||||
|
||||
<a name="terminology"></a>
|
||||
## 术语
|
||||
|
||||
运算符有一元,二元和三元运算符。
|
||||
|
||||
- 一元运算符对单一操作对象操作(如`-a`)。一元运算符分前置符和后置运算符,前置运算符需紧排操作对象之前(如`!b`),后置运算符需紧跟操作对象之后(如`i++`)。
|
||||
- 二元运算符操作两个操作对象(如`2 + 3`),是中置的,因为它们出现在两个操作对象之间。
|
||||
- 三元运算符操作三个操作对象,和 C 语言一样,Swift 只有一个三元运算符,就是三元条件运算符(`a ? b : c`)。
|
||||
|
||||
受运算符影响的值叫操作数,在表达式`1 + 2`中,加号`+`是二元运算符,它的两个操作数是值`1`和`2`。
|
||||
|
||||
<a name="assignment_operator"></a>
|
||||
## 赋值运算符
|
||||
|
||||
赋值运算(`a = b`),表示用`b`的值来初始化或更新`a`的值:
|
||||
|
||||
```swift
|
||||
let b = 10
|
||||
var a = 5
|
||||
a = b
|
||||
// a 现在等于 10
|
||||
```
|
||||
|
||||
如果赋值的右边是一个多元组,它的元素可以马上被分解多个变量或变量:
|
||||
|
||||
```swiflt
|
||||
let (x, y) = (1, 2)
|
||||
// 现在 x 等于 1, y 等于 2
|
||||
```
|
||||
|
||||
与 C 语言和 Objective-C 不同,Swift 的赋值操作并不返回任何值。所以以下代码是错误的:
|
||||
|
||||
```swift
|
||||
if x = y {
|
||||
// 此句错误, 因为 x = y 并不返回任何值
|
||||
}
|
||||
```
|
||||
|
||||
这个特性使得你无法把(`==`)错写成(`=`)了,由于`if x = y`是错误代码,Swift 从底层帮你避免了这些代码错误。
|
||||
|
||||
<a name="arithmetic_operators"></a>
|
||||
## 数值运算
|
||||
|
||||
Swift 让所有数值类型都支持了基本的四则运算:
|
||||
|
||||
- 加法(`+`)
|
||||
- 减法(`-`)
|
||||
- 乘法(`*`)
|
||||
- 除法(`/`)
|
||||
|
||||
```swift
|
||||
1 + 2 // 等于 3
|
||||
5 - 3 // 等于 2
|
||||
2 * 3 // 等于 6
|
||||
10.0 / 2.5 // 等于 4.0
|
||||
```
|
||||
|
||||
与 C 语言和 Objective-C 不同的是,Swift 默认不允许在数值运算中出现溢出情况。但你可以使用 Swift 的溢出运算符来达到你有目的的溢出(如`a &+ b`)。详情参见[溢出运算符](23_Advanced_Operators.html#overflow_operators)。
|
||||
|
||||
加法运算符也用于`String`的拼接:
|
||||
|
||||
```swift
|
||||
"hello, " + "world" // 等于 "hello, world"
|
||||
```
|
||||
|
||||
两个`Character`值或一个`String`和一个`Character`值,相加会生成一个新的`String`值:
|
||||
|
||||
```swift
|
||||
let dog: Character = "d"
|
||||
let cow: Character = "c"
|
||||
let dogCow = dog + cow
|
||||
// 译者注: 原来的引号内是很可爱的小狗和小牛, 但win os下不支持表情字符, 所以改成了普通字符
|
||||
// dogCow 现在是 "dc"
|
||||
```
|
||||
|
||||
详情参见[字符,字符串的拼接](03_Strings_and_Characters.html#concatenating_strings_and_characters)。
|
||||
|
||||
### 求余运算
|
||||
|
||||
求余运算(`a % b`)是计算`b`的多少倍刚刚好可以容入`a`,返回多出来的那部分(余数)。
|
||||
|
||||
>注意:
|
||||
求余运算(`%`)在其他语言也叫取模运算。然而严格说来,我们看该运算符对负数的操作结果,"求余"比"取模"更合适些。
|
||||
|
||||
我们来谈谈取余是怎么回事,计算`9 % 4`,你先计算出`4`的多少倍会刚好可以容入`9`中:
|
||||
|
||||

|
||||
|
||||
2倍,非常好,那余数是1(用橙色标出)
|
||||
|
||||
在 Swift 中这么来表达:
|
||||
|
||||
```swift
|
||||
9 % 4 // 等于 1
|
||||
```
|
||||
|
||||
为了得到`a % b`的结果,`%`计算了以下等式,并输出`余数`作为结果:
|
||||
|
||||
*a = (b × 倍数) + 余数*
|
||||
|
||||
当`倍数`取最大值的时候,就会刚好可以容入`a`中。
|
||||
|
||||
把`9`和`4`代入等式中,我们得`1`:
|
||||
|
||||
```swift
|
||||
9 = (4 × 2) + 1
|
||||
```
|
||||
|
||||
同样的方法,我来们计算 `-9 % 4`:
|
||||
|
||||
```swift
|
||||
-9 % 4 // 等于 -1
|
||||
```
|
||||
|
||||
把`-9`和`4`代入等式,`-2`是取到的最大整数:
|
||||
|
||||
```swift
|
||||
-9 = (4 × -2) + -1
|
||||
```
|
||||
|
||||
余数是`-1`。
|
||||
|
||||
在对负数`b`求余时,`b`的符号会被忽略。这意味着 `a % b` 和 `a % -b`的结果是相同的。
|
||||
|
||||
### 浮点数求余计算
|
||||
|
||||
不同于 C 语言和 Objective-C,Swift 中是可以对浮点数进行求余的。
|
||||
|
||||
```swift
|
||||
8 % 2.5 // 等于 0.5
|
||||
```
|
||||
|
||||
这个例子中,`8`除于`2.5`等于`3`余`0.5`,所以结果是一个`Double`值`0.5`。
|
||||
|
||||

|
||||
|
||||
### 自增和自增运算
|
||||
|
||||
和 C 语言一样,Swift 也提供了方便对变量本身加1或减1的自增(`++`)和自减(`--`)的运算符。其操作对象可以是整形和浮点型。
|
||||
|
||||
```swift
|
||||
var i = 0
|
||||
++i // 现在 i = 1
|
||||
```
|
||||
|
||||
每调用一次`++i`,`i`的值就会加1。实际上,`++i`是`i = i + 1`的简写,而`--i`是`i = i - 1`的简写。
|
||||
|
||||
`++`和`--`既是前置又是后置运算。`++i`,`i++`,`--i`和`i--`都是有效的写法。
|
||||
|
||||
我们需要注意的是这些运算符修改了`i`后有一个返回值。如果你只想修改`i`的值,那你就可以忽略这个返回值。但如果你想使用返回值,你就需要留意前置和后置操作的返回值是不同的。
|
||||
|
||||
- 当`++`前置的时候,先自増再返回。
|
||||
|
||||
- 当`++`后置的时候,先返回再自增。
|
||||
|
||||
例如:
|
||||
|
||||
```swift
|
||||
var a = 0
|
||||
let b = ++a // a 和 b 现在都是 1
|
||||
let c = a++ // a 现在 2, 但 c 是 a 自增前的值 1
|
||||
```
|
||||
|
||||
上述例子,`let b = ++a`先把`a`加1了再返回`a`的值。所以`a`和`b`都是新值`1`。
|
||||
|
||||
而`let c = a++`,是先返回了`a`的值,然后`a`才加1。所以`c`得到了`a`的旧值1,而`a`加1后变成2。
|
||||
|
||||
除非你需要使用`i++`的特性,不然推荐你使用`++i`和`--i`,因为先修改后返回这样的行为更符合我们的逻辑。
|
||||
|
||||
|
||||
### 一元负号
|
||||
|
||||
数值的正负号可以使用前缀`-`(即一元负号)来切换:
|
||||
|
||||
```swift
|
||||
let three = 3
|
||||
let minusThree = -three // minusThree 等于 -3
|
||||
let plusThree = -minusThree // plusThree 等于 3, 或 "负负3"
|
||||
```
|
||||
|
||||
一元负号(`-`)写在操作数之前,中间没有空格。
|
||||
|
||||
### 一元正号
|
||||
|
||||
一元正号(`+`)不做任何改变地返回操作数的值。
|
||||
|
||||
```swift
|
||||
let minusSix = -6
|
||||
let alsoMinusSix = +minusSix // alsoMinusSix 等于 -6
|
||||
```
|
||||
虽然一元`+`做无用功,但当你在使用一元负号来表达负数时,你可以使用一元正号来表达正数,如此你的代码会具有对称美。
|
||||
|
||||
|
||||
<a name="compound_assignment_operators"></a>
|
||||
## 复合赋值(Compound Assignment Operators)
|
||||
|
||||
如同强大的 C 语言,Swift 也提供把其他运算符和赋值运算(`=`)组合的复合赋值运算符,加赋运算(`+=`)是其中一个例子:
|
||||
|
||||
```swift
|
||||
var a = 1
|
||||
a += 2 // a 现在是 3
|
||||
```
|
||||
|
||||
表达式`a += 2`是`a = a + 2`的简写,一个加赋运算就把加法和赋值两件事完成了。
|
||||
|
||||
>注意:
|
||||
复合赋值运算没有返回值,`let b = a += 2`这类代码是错误。这不同于上面提到的自增和自减运算符。
|
||||
|
||||
在[表达式](../chapter3/04_Expressions.html)章节里有复合运算符的完整列表。
|
||||
|
||||
<a name="comparison_operators"></a>
|
||||
## 比较运算
|
||||
|
||||
所有标准 C 语言中的比较运算都可以在 Swift 中使用。
|
||||
|
||||
- 等于(`a == b`)
|
||||
- 不等于(`a != b`)
|
||||
- 大于(`a > b`)
|
||||
- 小于(`a < b`)
|
||||
- 大于等于(`a >= b`)
|
||||
- 小于等于(`a <= b`)
|
||||
|
||||
> 注意:
|
||||
Swift 也提供恒等`===`和不恒等`!==`这两个比较符来判断两个对象是否引用同一个对象实例。更多细节在[类与结构](09_Classes_and_Structures.html)。
|
||||
|
||||
每个比较运算都返回了一个标识表达式是否成立的布尔值:
|
||||
|
||||
```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`条件:
|
||||
|
||||
```swift
|
||||
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"
|
||||
```
|
||||
|
||||
关于`if`语句,请看[控制流](05_Control_Flow.html)。
|
||||
|
||||
<a name="ternary_conditional_operator"></a>
|
||||
## 三元条件运算(Ternary Conditional Operator)
|
||||
|
||||
三元条件运算的特殊在于它是有三个操作数的运算符,它的原型是 `问题 ? 答案1 : 答案2`。它简洁地表达根据`问题`成立与否作出二选一的操作。如果`问题`成立,返回`答案1`的结果; 如果不成立,返回`答案2`的结果。
|
||||
|
||||
使用三元条件运算简化了以下代码:
|
||||
|
||||
```swift
|
||||
if question: {
|
||||
answer1
|
||||
} else {
|
||||
answer2
|
||||
}
|
||||
```
|
||||
|
||||
这里有个计算表格行高的例子。如果有表头,那行高应比内容高度要高出50像素; 如果没有表头,只需高出20像素。
|
||||
|
||||
```swift
|
||||
let contentHeight = 40
|
||||
let hasHeader = true
|
||||
let rowHeight = contentHeight + (hasHeader ? 50 : 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`语句中改变。
|
||||
|
||||
三元条件运算提供有效率且便捷的方式来表达二选一的选择。需要注意的事,过度使用三元条件运算就会由简洁的代码变成难懂的代码。我们应避免在一个组合语句使用多个三元条件运算符。
|
||||
|
||||
<a name="range_operators"></a>
|
||||
## 区间运算符
|
||||
|
||||
Swift 提供了两个方便表达一个区间的值的运算符。
|
||||
|
||||
### 闭区间运算符
|
||||
闭区间运算符(`a...b`)定义一个包含从`a`到`b`(包括`a`和`b`)的所有值的区间。
|
||||
|
||||
闭区间运算符在迭代一个区间的所有值时是非常有用的,如在`for-in`循环中:
|
||||
|
||||
```swift
|
||||
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
|
||||
```
|
||||
|
||||
关于`for-in`,请看[控制流](05_Control_Flow.html)。
|
||||
|
||||
### 半闭区间
|
||||
|
||||
半闭区间(`a..b`)定义一个从`a`到`b`但不包括`b`的区间。
|
||||
之所以称为半闭区间,是因为该区间包含第一个值而不包括最后的值。
|
||||
|
||||
半闭区间的实用性在于当你使用一个0始的列表(如数组)时,非常方便地从0数到列表的长度。
|
||||
|
||||
```swift
|
||||
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)。
|
||||
|
||||
<a name="logical_operators"></a>
|
||||
## 逻辑运算
|
||||
|
||||
逻辑运算的操作对象是逻辑布尔值。Swift 支持基于 C 语言的三个标准逻辑运算。
|
||||
|
||||
- 逻辑非(`!a`)
|
||||
- 逻辑与(`a && b`)
|
||||
- 逻辑或(`a || b`)
|
||||
|
||||
### 逻辑非
|
||||
|
||||
逻辑非运算(`!a`)对一个布尔值取反,使得`true`变`false`,`false`变`true`。
|
||||
|
||||
它是一个前置运算符,需出现在操作数之前,且不加空格。读作`非 a`,然后我们看以下例子:
|
||||
|
||||
```swift
|
||||
let allowedEntry = false
|
||||
if !allowedEntry {
|
||||
println("ACCESS DENIED")
|
||||
}
|
||||
// 输出 "ACCESS DENIED"
|
||||
```
|
||||
|
||||
`if !allowedEntry`语句可以读作 "如果 非 alowed entry。",接下一行代码只有在如果 "非 allow entry" 为`true`,即`allowEntry`为`false`时被执行。
|
||||
|
||||
在示例代码中,小心地选择布尔常量或变量有助于代码的可读性,并且避免使用双重逻辑非运算,或混乱的逻辑语句。
|
||||
|
||||
### 逻辑与
|
||||
逻辑与(`a && b`)表达了只有`a`和`b`的值都为`true`时,整个表达式的值才会是`true`。
|
||||
|
||||
只要任意一个值为`false`,整个表达式的值就为`false`。事实上,如果第一个值为`false`,那么是不去计算第二个值的,因为它已经不可能影响整个表达式的结果了。这被称做 "短路计算(short-circuit evaluation)"。
|
||||
|
||||
以下例子,只有两个`Bool`值都为`true`值的时候才允许进入:
|
||||
|
||||
```swift
|
||||
let enteredDoorCode = true
|
||||
let passedRetinaScan = false
|
||||
if enteredDoorCode && passedRetinaScan {
|
||||
println("Welcome!")
|
||||
} else {
|
||||
println("ACCESS DENIED")
|
||||
}
|
||||
// 输出 "ACCESS DENIED"
|
||||
```
|
||||
|
||||
### 逻辑或
|
||||
逻辑或(`a || b`)是一个由两个连续的`|`组成的中置运算符。它表示了两个逻辑表达式的其中一个为`true`,整个表达式就为`true`。
|
||||
|
||||
同逻辑与运算类似,逻辑或也是"短路计算"的,当左端的表达式为`true`时,将不计算右边的表达式了,因为它不可能改变整个表达式的值了。
|
||||
|
||||
以下示例代码中,第一个布尔值(`hasDoorKey`)为`false`,但第二个值(`knowsOverridePassword`)为`true`,所以整个表达是`true`,于是允许进入:
|
||||
|
||||
```swift
|
||||
let hasDoorKey = false
|
||||
let knowsOverridePassword = true
|
||||
if hasDoorKey || knowsOverridePassword {
|
||||
println("Welcome!")
|
||||
} else {
|
||||
println("ACCESS DENIED")
|
||||
}
|
||||
// 输出 "Welcome!"
|
||||
```
|
||||
|
||||
### 组合逻辑
|
||||
|
||||
我们可以组合多个逻辑运算来表达一个复合逻辑:
|
||||
|
||||
```swift
|
||||
if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
|
||||
println("Welcome!")
|
||||
} else {
|
||||
println("ACCESS DENIED")
|
||||
}
|
||||
// 输出 "Welcome!"
|
||||
```
|
||||
|
||||
这个例子使用了含多个`&&`和`||`的复合逻辑。但无论怎样,`&&`和`||`始终只能操作两个值。所以这实际是三个简单逻辑连续操作的结果。我们来解读一下:
|
||||
|
||||
如果我们输入了正确的密码并通过了视网膜扫描; 或者我们有一把有效的钥匙; 又或者我们知道紧急情况下重置的密码,我们就能把门打开进入。
|
||||
|
||||
前两种情况,我们都不满足,所以前两个简单逻辑的结果是`false`,但是我们是知道紧急情况下重置的密码的,所以整个复杂表达式的值还是`true`。
|
||||
|
||||
### 使用括号来明确优先级
|
||||
|
||||
为了一个复杂表达式更容易读懂,在合适的地方使用括号来明确优先级是很有效的,虽然它并非必要的。在上个关于门的权限的例子中,我们给第一个部分加个括号,使用它看起来逻辑更明确:
|
||||
|
||||
```swift
|
||||
if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword {
|
||||
println("Welcome!")
|
||||
} else {
|
||||
println("ACCESS DENIED")
|
||||
}
|
||||
// 输出 "Welcome!"
|
||||
```
|
||||
|
||||
这括号使得前两个值被看成整个逻辑表达中独立的一个部分。虽然有括号和没括号的输出结果是一样的,但对于读代码的人来说有括号的代码更清晰。可读性比简洁性更重要,请在可以让你代码变清晰地地方加个括号吧!
|
||||
|
||||
@ -1,411 +1,411 @@
|
||||
> 翻译:wh1100717
|
||||
> 校对:Hawstein
|
||||
|
||||
# 字符串和字符(Strings and Characters)
|
||||
-----------------
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [字符串字面量](#string_literals)
|
||||
- [初始化空字符串](#initializing_an_empty_string)
|
||||
- [字符串可变性](#string_mutability)
|
||||
- [字符串是值类型](#strings_are_value_types)
|
||||
- [使用字符](#working_with_characters)
|
||||
- [计算字符数量](#counting_characters)
|
||||
- [连接字符串和字符](#concatenating_strings_and_characters)
|
||||
- [字符串插值](#string_interpolation)
|
||||
- [比较字符串](#comparing_strings)
|
||||
- [字符串大小写](#uppercase_and_lowercase_strings)
|
||||
- [Unicode](#unicode)
|
||||
|
||||
`String`是例如“hello, world”,“海贼王” 这样的有序的`Character`(字符)类型的值的集合,通过`String`类型来表示。
|
||||
|
||||
Swift 的`String`和`Character`类型提供了一个快速的,兼容 Unicode 的方式来处理代码中的文本信息。
|
||||
创建和操作字符串的语法与 C 语言中字符串操作相似,轻量并且易读。
|
||||
字符串连接操作只需要简单地通过`+`号将两个字符串相连即可。
|
||||
与 Swift 中其他值一样,能否更改字符串的值,取决于其被定义为常量还是变量。
|
||||
|
||||
尽管语法简易,但`String`类型是一种快速、现代化的字符串实现。
|
||||
每一个字符串都是由独立编码的 Unicode 字符组成,并提供了以不同 Unicode 表示(representations)来访问这些字符的支持。
|
||||
|
||||
Swift 可以在常量、变量、字面量和表达式中进行字符串插值操作,可以轻松创建用于展示、存储和打印的自定义字符串。
|
||||
|
||||
> 注意:
|
||||
Swift 的`String`类型与 Foundation `NSString`类进行了无缝桥接。如果您利用 Cocoa 或 Cocoa Touch 中的 Foundation 框架进行工作。所有`NSString` API 都可以调用您创建的任意`String`类型的值。除此之外,还可以使用本章介绍的`String`特性。您也可以在任意要求传入`NSString`实例作为参数的 API 中使用`String`类型的值作为替代。
|
||||
>更多关于在 Foundation 和 Cocoa 中使用`String`的信息请查看 [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="string_literals"></a>
|
||||
## 字符串字面量(String Literals)
|
||||
|
||||
您可以在您的代码中包含一段预定义的字符串值作为字符串字面量。
|
||||
字符串字面量是由双引号 ("") 包裹着的具有固定顺序的文本字符集。
|
||||
|
||||
字符串字面量可以用于为常量和变量提供初始值。
|
||||
|
||||
```swift
|
||||
let someString = "Some string literal value"
|
||||
```
|
||||
|
||||
> 注意:
|
||||
`someString`变量通过字符串字面量进行初始化,Swift 因此推断该变量为`String`类型。
|
||||
|
||||
字符串字面量可以包含以下特殊字符:
|
||||
|
||||
* 转义字符`\0`(空字符)、`\\`(反斜线)、`\t`(水平制表符)、`\n`(换行符)、`\r`(回车符)、`\"`(双引号)、`\'`(单引号)。
|
||||
* 单字节 Unicode 标量,写成`\xnn`,其中`nn`为两位十六进制数。
|
||||
* 双字节 Unicode 标量,写成`\unnnn`,其中`nnnn`为四位十六进制数。
|
||||
* 四字节 Unicode 标量,写成`\Unnnnnnnn`,其中`nnnnnnnn`为八位十六进制数。
|
||||
|
||||
下面的代码为各种特殊字符的使用示例。
|
||||
`wiseWords`常量包含了两个转移特殊字符 (双括号);
|
||||
`dollarSign`、`blackHeart`和`sparklingHeart`常量演示了三种不同格式的 Unicode 标量:
|
||||
|
||||
```swift
|
||||
let wiseWords = "\"我是要成为海贼王的男人\" - 路飞"
|
||||
// "我是要成为海贼王的男人" - 路飞
|
||||
let dollarSign = "\x24" // $, Unicode 标量 U+0024
|
||||
let blackHeart = "\u2665" // ♥, Unicode 标量 U+2665
|
||||
let sparklingHeart = "\U0001F496" // 💖, Unicode 标量 U+1F496
|
||||
```
|
||||
|
||||
<a name="initializing_an_empty_string"></a>
|
||||
## 初始化空字符串 (Initializing an Empty String)
|
||||
|
||||
为了构造一个很长的字符串,可以创建一个空字符串作为初始值。
|
||||
可以将空的字符串字面量赋值给变量,也可以初始化一个新的`String`实例:
|
||||
|
||||
```swift
|
||||
var emptyString = "" // 空字符串字面量
|
||||
var anotherEmptyString = String() // 初始化 String 实例
|
||||
// 两个字符串均为空并等价。
|
||||
```
|
||||
|
||||
您可以通过检查其`Boolean`类型的`isEmpty`属性来判断该字符串是否为空:
|
||||
|
||||
```swift
|
||||
if emptyString.isEmpty {
|
||||
println("什么都没有")
|
||||
}
|
||||
// 打印输出:"什么都没有"
|
||||
```
|
||||
|
||||
<a name="string_mutability"></a>
|
||||
## 字符串可变性 (String Mutability)
|
||||
|
||||
您可以通过将一个特定字符串分配给一个变量来对其进行修改,或者分配给一个常量来保证其不会被修改:
|
||||
|
||||
```swift
|
||||
var variableString = "Horse"
|
||||
variableString += " and carriage"
|
||||
// variableString 现在为 "Horse and carriage"
|
||||
let constantString = "Highlander"
|
||||
constantString += " and another Highlander"
|
||||
// 这会报告一个编译错误 (compile-time error) - 常量不可以被修改。
|
||||
```
|
||||
|
||||
> 注意:
|
||||
在 Objective-C 和 Cocoa 中,您通过选择两个不同的类(`NSString`和`NSMutableString`)来指定该字符串是否可以被修改,Swift 中的字符串是否可以修改仅通过定义的是变量还是常量来决定,实现了多种类型可变性操作的统一。
|
||||
|
||||
<a name="strings_are_value_types"></a>
|
||||
## 字符串是值类型(Strings Are Value Types)
|
||||
|
||||
Swift 的`String`类型是值类型。
|
||||
如果您创建了一个新的字符串,那么当其进行常量、变量赋值操作或在函数/方法中传递时,会进行值拷贝。
|
||||
任何情况下,都会对已有字符串值创建新副本,并对该新副本进行传递或赋值操作。
|
||||
值类型在 [结构体和枚举是值类型](09_Classes_and_Structures.html#structures_and_enumerations_are_value_types) 中进行了说明。
|
||||
|
||||
> 注意:
|
||||
与 Cocoa 中的`NSString`不同,当您在 Cocoa 中创建了一个`NSString`实例,并将其传递给一个函数/方法,或者赋值给一个变量,您传递或赋值的是该`NSString`实例的一个引用,除非您特别要求进行值拷贝,否则字符串不会生成新的副本来进行赋值操作。
|
||||
|
||||
Swift 默认字符串拷贝的方式保证了在函数/方法中传递的是字符串的值。
|
||||
很明显无论该值来自于哪里,都是您独自拥有的。
|
||||
您可以放心您传递的字符串本身不会被更改。
|
||||
|
||||
在实际编译时,Swift 编译器会优化字符串的使用,使实际的复制只发生在绝对必要的情况下,这意味着您将字符串作为值类型的同时可以获得极高的性能。
|
||||
|
||||
<a name="working_with_characters"></a>
|
||||
## 使用字符(Working with Characters)
|
||||
|
||||
Swift 的`String`类型表示特定序列的`Character`(字符) 类型值的集合。
|
||||
每一个字符值代表一个 Unicode 字符。
|
||||
您可利用`for-in`循环来遍历字符串中的每一个字符:
|
||||
|
||||
```swift
|
||||
for character in "Dog!🐶" {
|
||||
println(character)
|
||||
}
|
||||
// D
|
||||
// o
|
||||
// g
|
||||
// !
|
||||
// 🐶
|
||||
```
|
||||
|
||||
for-in 循环在 [For Loops](05_Control_Flow.html#for_loops) 中进行了详细描述。
|
||||
|
||||
另外,通过标明一个`Character`类型注解并通过字符字面量进行赋值,可以建立一个独立的字符常量或变量:
|
||||
|
||||
```swift
|
||||
let yenSign: Character = "¥"
|
||||
```
|
||||
|
||||
<a name="counting_characters"></a>
|
||||
## 计算字符数量 (Counting Characters)
|
||||
|
||||
通过调用全局`countElements`函数,并将字符串作为参数进行传递,可以获取该字符串的字符数量。
|
||||
|
||||
```swift
|
||||
let unusualMenagerie = "Koala 🐨, Snail 🐌, Penguin 🐧, Dromedary 🐪"
|
||||
println("unusualMenagerie has \(countElements(unusualMenagerie)) characters")
|
||||
// 打印输出:"unusualMenagerie has 40 characters"
|
||||
```
|
||||
|
||||
> 注意:
|
||||
不同的 Unicode 字符以及相同 Unicode 字符的不同表示方式可能需要不同数量的内存空间来存储。所以 Swift 中的字符在一个字符串中并不一定占用相同的内存空间。因此字符串的长度不得不通过迭代字符串中每一个字符的长度来进行计算。如果您正在处理一个长字符串,需要注意`countElements`函数必须遍历字符串中的字符以精准计算字符串的长度。
|
||||
> 另外需要注意的是通过`countElements`返回的字符数量并不总是与包含相同字符的`NSString`的`length`属性相同。`NSString`的`length`属性是基于利用 UTF-16 表示的十六位代码单元数字,而不是基于 Unicode 字符。为了解决这个问题,`NSString`的`length`属性在被 Swift 的`String`访问时会成为`utf16count`。
|
||||
|
||||
<a name="concatenating_strings_and_characters"></a>
|
||||
## 连接字符串和字符 (Concatenating Strings and Characters)
|
||||
|
||||
字符串和字符的值可以通过加法运算符(`+`)相加在一起并创建一个新的字符串值:
|
||||
|
||||
```swift
|
||||
let string1 = "hello"
|
||||
let string2 = " there"
|
||||
let character1: Character = "!"
|
||||
let character2: Character = "?"
|
||||
|
||||
let stringPlusCharacter = string1 + character1 // 等于 "hello!"
|
||||
let stringPlusString = string1 + string2 // 等于 "hello there"
|
||||
let characterPlusString = character1 + string1 // 等于 "!hello"
|
||||
let characterPlusCharacter = character1 + character2 // 等于 "!?"
|
||||
```
|
||||
|
||||
您也可以通过加法赋值运算符 (`+=`) 将一个字符串或者字符添加到一个已经存在字符串变量上:
|
||||
|
||||
```swift
|
||||
var instruction = "look over"
|
||||
instruction += string2
|
||||
// instruction 现在等于 "look over there"
|
||||
|
||||
var welcome = "good morning"
|
||||
welcome += character1
|
||||
// welcome 现在等于 "good morning!"
|
||||
```
|
||||
|
||||
> 注意:
|
||||
您不能将一个字符串或者字符添加到一个已经存在的字符变量上,因为字符变量只能包含一个字符。
|
||||
|
||||
<a name="string_interpolation"></a>
|
||||
## 字符串插值 (String Interpolation)
|
||||
|
||||
字符串插值是一种构建新字符串的方式,可以在其中包含常量、变量、字面量和表达式。
|
||||
您插入的字符串字面量的每一项都被包裹在以反斜线为前缀的圆括号中:
|
||||
|
||||
```swift
|
||||
let multiplier = 3
|
||||
let message = "\(multiplier) 乘以 2.5 是 \(Double(multiplier) * 2.5)"
|
||||
// message 是 "3 乘以 2.5 是 7.5"
|
||||
```
|
||||
|
||||
在上面的例子中,`multiplier`作为`\(multiplier)`被插入到一个字符串字面量中。
|
||||
当创建字符串执行插值计算时此占位符会被替换为`multiplier`实际的值。
|
||||
|
||||
`multiplier`的值也作为字符串中后面表达式的一部分。
|
||||
该表达式计算`Double(multiplier) * 2.5`的值并将结果 (7.5) 插入到字符串中。
|
||||
在这个例子中,表达式写为`\(Double(multiplier) * 2.5)`并包含在字符串字面量中。
|
||||
|
||||
> 注意:
|
||||
插值字符串中写在括号中的表达式不能包含非转义双引号 (`"`) 和反斜杠 (`\`),并且不能包含回车或换行符。
|
||||
|
||||
<a name="comparing_strings"></a>
|
||||
## 比较字符串 (Comparing Strings)
|
||||
|
||||
Swift 提供了三种方式来比较字符串的值:字符串相等、前缀相等和后缀相等。
|
||||
|
||||
<a name="string_equality"></a>
|
||||
### 字符串相等 (String Equality)
|
||||
|
||||
如果两个字符串以同一顺序包含完全相同的字符,则认为两者字符串相等:
|
||||
|
||||
```swift
|
||||
let quotation = "我们是一样一样滴."
|
||||
let sameQuotation = "我们是一样一样滴."
|
||||
if quotation == sameQuotation {
|
||||
println("这两个字符串被认为是相同的")
|
||||
}
|
||||
// 打印输出:"这两个字符串被认为是相同的"
|
||||
```
|
||||
|
||||
<a name="prefix_and_suffix_equality"></a>
|
||||
### 前缀/后缀相等 (Prefix and Suffix Equality)
|
||||
|
||||
通过调用字符串的`hasPrefix`/`hasSuffix`方法来检查字符串是否拥有特定前缀/后缀。
|
||||
两个方法均需要以字符串作为参数传入并传出`Boolean`值。
|
||||
两个方法均执行基本字符串和前缀/后缀字符串之间逐个字符的比较操作。
|
||||
|
||||
下面的例子以一个字符串数组表示莎士比亚话剧《罗密欧与朱丽叶》中前两场的场景位置:
|
||||
|
||||
```swift
|
||||
let romeoAndJuliet = [
|
||||
"Act 1 Scene 1: Verona, A public place",
|
||||
"Act 1 Scene 2: Capulet's mansion",
|
||||
"Act 1 Scene 3: A room in Capulet's mansion",
|
||||
"Act 1 Scene 4: A street outside Capulet's mansion",
|
||||
"Act 1 Scene 5: The Great Hall in Capulet's mansion",
|
||||
"Act 2 Scene 1: Outside Capulet's mansion",
|
||||
"Act 2 Scene 2: Capulet's orchard",
|
||||
"Act 2 Scene 3: Outside Friar Lawrence's cell",
|
||||
"Act 2 Scene 4: A street in Verona",
|
||||
"Act 2 Scene 5: Capulet's mansion",
|
||||
"Act 2 Scene 6: Friar Lawrence's cell"
|
||||
]
|
||||
```
|
||||
|
||||
您可以利用`hasPrefix`方法来计算话剧中第一幕的场景数:
|
||||
|
||||
```swift
|
||||
var act1SceneCount = 0
|
||||
for scene in romeoAndJuliet {
|
||||
if scene.hasPrefix("Act 1 ") {
|
||||
++act1SceneCount
|
||||
}
|
||||
}
|
||||
println("There are \(act1SceneCount) scenes in Act 1")
|
||||
// 打印输出:"There are 5 scenes in Act 1"
|
||||
```
|
||||
|
||||
相似地,您可以用`hasSuffix`方法来计算发生在不同地方的场景数:
|
||||
|
||||
```swift
|
||||
var mansionCount = 0
|
||||
var cellCount = 0
|
||||
for scene in romeoAndJuliet {
|
||||
if scene.hasSuffix("Capulet's mansion") {
|
||||
++mansionCount
|
||||
} else if scene.hasSuffix("Friar Lawrence's cell") {
|
||||
++cellCount
|
||||
}
|
||||
}
|
||||
println("\(mansionCount) mansion scenes; \(cellCount) cell scenes")
|
||||
// 打印输出:"6 mansion scenes; 2 cell scenes”
|
||||
```
|
||||
|
||||
<a name="uppercase_and_lowercase_strings"></a>
|
||||
### 大写和小写字符串(Uppercase and Lowercase Strings)
|
||||
|
||||
您可以通过字符串的`uppercaseString`和`lowercaseString`属性来访问大写/小写版本的字符串。
|
||||
|
||||
```swift
|
||||
let normal = "Could you help me, please?"
|
||||
let shouty = normal.uppercaseString
|
||||
// shouty 值为 "COULD YOU HELP ME, PLEASE?"
|
||||
let whispered = normal.lowercaseString
|
||||
// whispered 值为 "could you help me, please?"
|
||||
```
|
||||
|
||||
<a name="unicode"></a>
|
||||
## Unicode
|
||||
|
||||
Unicode 是一个国际标准,用于文本的编码和表示。
|
||||
它使您可以用标准格式表示来自任意语言几乎所有的字符,并能够对文本文件或网页这样的外部资源中的字符进行读写操作。
|
||||
|
||||
Swift 的字符串和字符类型是完全兼容 Unicode 标准的,它支持如下所述的一系列不同的 Unicode 编码。
|
||||
|
||||
<a name="unicode_terminology"></a>
|
||||
### Unicode 术语(Unicode Terminology)
|
||||
|
||||
Unicode 中每一个字符都可以被解释为一个或多个 unicode 标量。
|
||||
字符的 unicode 标量是一个唯一的21位数字(和名称),例如`U+0061`表示小写的拉丁字母A ("a"),`U+1F425`表示小鸡表情 ("🐥")
|
||||
|
||||
当 Unicode 字符串被写进文本文件或其他存储结构当中,这些 unicode 标量将会按照 Unicode 定义的集中格式之一进行编码。其包括`UTF-8`(以8位代码单元进行编码) 和`UTF-16`(以16位代码单元进行编码)。
|
||||
|
||||
<a name="unicode_representations_of_strings"></a>
|
||||
### 字符串的 Unicode 表示(Unicode Representations of Strings)
|
||||
|
||||
Swift 提供了几种不同的方式来访问字符串的 Unicode 表示。
|
||||
|
||||
您可以利用`for-in`来对字符串进行遍历,从而以 Unicode 字符的方式访问每一个字符值。
|
||||
该过程在 [使用字符](#working_with_characters) 中进行了描述。
|
||||
|
||||
另外,能够以其他三种 Unicode 兼容的方式访问字符串的值:
|
||||
|
||||
* UTF-8 代码单元集合 (利用字符串的`utf8`属性进行访问)
|
||||
* UTF-16 代码单元集合 (利用字符串的`utf16`属性进行访问)
|
||||
* 21位的 Unicode 标量值集合 (利用字符串的`unicodeScalars`属性进行访问)
|
||||
|
||||
下面由`D``o``g``!`和`🐶`(`DOG FACE`,Unicode 标量为`U+1F436`)组成的字符串中的每一个字符代表着一种不同的表示:
|
||||
|
||||
```swift
|
||||
let dogString = "Dog!🐶"
|
||||
```
|
||||
|
||||
<a name="UTF-8"></a>
|
||||
### UTF-8
|
||||
|
||||
您可以通过遍历字符串的`utf8`属性来访问它的`UTF-8`表示。
|
||||
其为`UTF8View`类型的属性,`UTF8View`是无符号8位 (`UInt8`) 值的集合,每一个`UInt8`值都是一个字符的 UTF-8 表示:
|
||||
|
||||
```swift
|
||||
for codeUnit in dogString.utf8 {
|
||||
print("\(codeUnit) ")
|
||||
}
|
||||
print("\n")
|
||||
// 68 111 103 33 240 159 144 182
|
||||
```
|
||||
|
||||
上面的例子中,前四个10进制代码单元值 (68, 111, 103, 33) 代表了字符`D` `o` `g`和`!`,它们的 UTF-8 表示与 ASCII 表示相同。
|
||||
后四个代码单元值 (240, 159, 144, 182) 是`DOG FACE`的4字节 UTF-8 表示。
|
||||
|
||||
<a name="UTF-16"></a>
|
||||
### UTF-16
|
||||
|
||||
您可以通过遍历字符串的`utf16`属性来访问它的`UTF-16`表示。
|
||||
其为`UTF16View`类型的属性,`UTF16View`是无符号16位 (`UInt16`) 值的集合,每一个`UInt16`都是一个字符的 UTF-16 表示:
|
||||
|
||||
```swift
|
||||
for codeUnit in dogString.utf16 {
|
||||
print("\(codeUnit) ")
|
||||
}
|
||||
print("\n")
|
||||
// 68 111 103 33 55357 56374
|
||||
```
|
||||
|
||||
同样,前四个代码单元值 (68, 111, 103, 33) 代表了字符`D` `o` `g`和`!`,它们的 UTF-16 代码单元和 UTF-8 完全相同。
|
||||
|
||||
第五和第六个代码单元值 (55357 和 56374) 是`DOG FACE`字符的UTF-16 表示。
|
||||
第一个值为`U+D83D`(十进制值为 55357),第二个值为`U+DC36`(十进制值为 56374)。
|
||||
|
||||
<a name="unicode_scalars"></a>
|
||||
### Unicode 标量 (Unicode Scalars)
|
||||
|
||||
您可以通过遍历字符串的`unicodeScalars`属性来访问它的 Unicode 标量表示。
|
||||
其为`UnicodeScalarView`类型的属性, `UnicodeScalarView`是`UnicodeScalar`的集合。
|
||||
`UnicodeScalar`是21位的 Unicode 代码点。
|
||||
|
||||
每一个`UnicodeScalar`拥有一个值属性,可以返回对应的21位数值,用`UInt32`来表示。
|
||||
|
||||
```swift
|
||||
for scalar in dogString.unicodeScalars {
|
||||
print("\(scalar.value) ")
|
||||
}
|
||||
print("\n")
|
||||
// 68 111 103 33 128054
|
||||
```
|
||||
|
||||
同样,前四个代码单元值 (68, 111, 103, 33) 代表了字符`D` `o` `g`和`!`。
|
||||
第五位数值,128054,是一个十六进制1F436的十进制表示。
|
||||
其等同于`DOG FACE`的Unicode 标量 U+1F436。
|
||||
|
||||
作为查询字符值属性的一种替代方法,每个`UnicodeScalar`值也可以用来构建一个新的字符串值,比如在字符串插值中使用:
|
||||
|
||||
```swift
|
||||
for scalar in dogString.unicodeScalars {
|
||||
println("\(scalar) ")
|
||||
}
|
||||
// D
|
||||
// o
|
||||
// g
|
||||
// !
|
||||
// 🐶
|
||||
```
|
||||
> 翻译:[wh1100717](https://github.com/wh1100717)
|
||||
> 校对:[Hawstein](https://github.com/Hawstein)
|
||||
|
||||
# 字符串和字符(Strings and Characters)
|
||||
-----------------
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [字符串字面量](#string_literals)
|
||||
- [初始化空字符串](#initializing_an_empty_string)
|
||||
- [字符串可变性](#string_mutability)
|
||||
- [字符串是值类型](#strings_are_value_types)
|
||||
- [使用字符](#working_with_characters)
|
||||
- [计算字符数量](#counting_characters)
|
||||
- [连接字符串和字符](#concatenating_strings_and_characters)
|
||||
- [字符串插值](#string_interpolation)
|
||||
- [比较字符串](#comparing_strings)
|
||||
- [字符串大小写](#uppercase_and_lowercase_strings)
|
||||
- [Unicode](#unicode)
|
||||
|
||||
`String`是例如“hello, world”,“海贼王” 这样的有序的`Character`(字符)类型的值的集合,通过`String`类型来表示。
|
||||
|
||||
Swift 的`String`和`Character`类型提供了一个快速的,兼容 Unicode 的方式来处理代码中的文本信息。
|
||||
创建和操作字符串的语法与 C 语言中字符串操作相似,轻量并且易读。
|
||||
字符串连接操作只需要简单地通过`+`号将两个字符串相连即可。
|
||||
与 Swift 中其他值一样,能否更改字符串的值,取决于其被定义为常量还是变量。
|
||||
|
||||
尽管语法简易,但`String`类型是一种快速、现代化的字符串实现。
|
||||
每一个字符串都是由独立编码的 Unicode 字符组成,并提供了以不同 Unicode 表示(representations)来访问这些字符的支持。
|
||||
|
||||
Swift 可以在常量、变量、字面量和表达式中进行字符串插值操作,可以轻松创建用于展示、存储和打印的自定义字符串。
|
||||
|
||||
> 注意:
|
||||
Swift 的`String`类型与 Foundation `NSString`类进行了无缝桥接。如果您利用 Cocoa 或 Cocoa Touch 中的 Foundation 框架进行工作。所有`NSString` API 都可以调用您创建的任意`String`类型的值。除此之外,还可以使用本章介绍的`String`特性。您也可以在任意要求传入`NSString`实例作为参数的 API 中使用`String`类型的值作为替代。
|
||||
>更多关于在 Foundation 和 Cocoa 中使用`String`的信息请查看 [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="string_literals"></a>
|
||||
## 字符串字面量(String Literals)
|
||||
|
||||
您可以在您的代码中包含一段预定义的字符串值作为字符串字面量。
|
||||
字符串字面量是由双引号 ("") 包裹着的具有固定顺序的文本字符集。
|
||||
|
||||
字符串字面量可以用于为常量和变量提供初始值。
|
||||
|
||||
```swift
|
||||
let someString = "Some string literal value"
|
||||
```
|
||||
|
||||
> 注意:
|
||||
`someString`变量通过字符串字面量进行初始化,Swift 因此推断该变量为`String`类型。
|
||||
|
||||
字符串字面量可以包含以下特殊字符:
|
||||
|
||||
* 转义字符`\0`(空字符)、`\\`(反斜线)、`\t`(水平制表符)、`\n`(换行符)、`\r`(回车符)、`\"`(双引号)、`\'`(单引号)。
|
||||
* 单字节 Unicode 标量,写成`\xnn`,其中`nn`为两位十六进制数。
|
||||
* 双字节 Unicode 标量,写成`\unnnn`,其中`nnnn`为四位十六进制数。
|
||||
* 四字节 Unicode 标量,写成`\Unnnnnnnn`,其中`nnnnnnnn`为八位十六进制数。
|
||||
|
||||
下面的代码为各种特殊字符的使用示例。
|
||||
`wiseWords`常量包含了两个转移特殊字符 (双括号);
|
||||
`dollarSign`、`blackHeart`和`sparklingHeart`常量演示了三种不同格式的 Unicode 标量:
|
||||
|
||||
```swift
|
||||
let wiseWords = "\"我是要成为海贼王的男人\" - 路飞"
|
||||
// "我是要成为海贼王的男人" - 路飞
|
||||
let dollarSign = "\x24" // $, Unicode 标量 U+0024
|
||||
let blackHeart = "\u2665" // ♥, Unicode 标量 U+2665
|
||||
let sparklingHeart = "\U0001F496" // 💖, Unicode 标量 U+1F496
|
||||
```
|
||||
|
||||
<a name="initializing_an_empty_string"></a>
|
||||
## 初始化空字符串 (Initializing an Empty String)
|
||||
|
||||
为了构造一个很长的字符串,可以创建一个空字符串作为初始值。
|
||||
可以将空的字符串字面量赋值给变量,也可以初始化一个新的`String`实例:
|
||||
|
||||
```swift
|
||||
var emptyString = "" // 空字符串字面量
|
||||
var anotherEmptyString = String() // 初始化 String 实例
|
||||
// 两个字符串均为空并等价。
|
||||
```
|
||||
|
||||
您可以通过检查其`Boolean`类型的`isEmpty`属性来判断该字符串是否为空:
|
||||
|
||||
```swift
|
||||
if emptyString.isEmpty {
|
||||
println("什么都没有")
|
||||
}
|
||||
// 打印输出:"什么都没有"
|
||||
```
|
||||
|
||||
<a name="string_mutability"></a>
|
||||
## 字符串可变性 (String Mutability)
|
||||
|
||||
您可以通过将一个特定字符串分配给一个变量来对其进行修改,或者分配给一个常量来保证其不会被修改:
|
||||
|
||||
```swift
|
||||
var variableString = "Horse"
|
||||
variableString += " and carriage"
|
||||
// variableString 现在为 "Horse and carriage"
|
||||
let constantString = "Highlander"
|
||||
constantString += " and another Highlander"
|
||||
// 这会报告一个编译错误 (compile-time error) - 常量不可以被修改。
|
||||
```
|
||||
|
||||
> 注意:
|
||||
在 Objective-C 和 Cocoa 中,您通过选择两个不同的类(`NSString`和`NSMutableString`)来指定该字符串是否可以被修改,Swift 中的字符串是否可以修改仅通过定义的是变量还是常量来决定,实现了多种类型可变性操作的统一。
|
||||
|
||||
<a name="strings_are_value_types"></a>
|
||||
## 字符串是值类型(Strings Are Value Types)
|
||||
|
||||
Swift 的`String`类型是值类型。
|
||||
如果您创建了一个新的字符串,那么当其进行常量、变量赋值操作或在函数/方法中传递时,会进行值拷贝。
|
||||
任何情况下,都会对已有字符串值创建新副本,并对该新副本进行传递或赋值操作。
|
||||
值类型在 [结构体和枚举是值类型](09_Classes_and_Structures.html#structures_and_enumerations_are_value_types) 中进行了说明。
|
||||
|
||||
> 注意:
|
||||
与 Cocoa 中的`NSString`不同,当您在 Cocoa 中创建了一个`NSString`实例,并将其传递给一个函数/方法,或者赋值给一个变量,您传递或赋值的是该`NSString`实例的一个引用,除非您特别要求进行值拷贝,否则字符串不会生成新的副本来进行赋值操作。
|
||||
|
||||
Swift 默认字符串拷贝的方式保证了在函数/方法中传递的是字符串的值。
|
||||
很明显无论该值来自于哪里,都是您独自拥有的。
|
||||
您可以放心您传递的字符串本身不会被更改。
|
||||
|
||||
在实际编译时,Swift 编译器会优化字符串的使用,使实际的复制只发生在绝对必要的情况下,这意味着您将字符串作为值类型的同时可以获得极高的性能。
|
||||
|
||||
<a name="working_with_characters"></a>
|
||||
## 使用字符(Working with Characters)
|
||||
|
||||
Swift 的`String`类型表示特定序列的`Character`(字符) 类型值的集合。
|
||||
每一个字符值代表一个 Unicode 字符。
|
||||
您可利用`for-in`循环来遍历字符串中的每一个字符:
|
||||
|
||||
```swift
|
||||
for character in "Dog!🐶" {
|
||||
println(character)
|
||||
}
|
||||
// D
|
||||
// o
|
||||
// g
|
||||
// !
|
||||
// 🐶
|
||||
```
|
||||
|
||||
for-in 循环在 [For Loops](05_Control_Flow.html#for_loops) 中进行了详细描述。
|
||||
|
||||
另外,通过标明一个`Character`类型注解并通过字符字面量进行赋值,可以建立一个独立的字符常量或变量:
|
||||
|
||||
```swift
|
||||
let yenSign: Character = "¥"
|
||||
```
|
||||
|
||||
<a name="counting_characters"></a>
|
||||
## 计算字符数量 (Counting Characters)
|
||||
|
||||
通过调用全局`countElements`函数,并将字符串作为参数进行传递,可以获取该字符串的字符数量。
|
||||
|
||||
```swift
|
||||
let unusualMenagerie = "Koala 🐨, Snail 🐌, Penguin 🐧, Dromedary 🐪"
|
||||
println("unusualMenagerie has \(countElements(unusualMenagerie)) characters")
|
||||
// 打印输出:"unusualMenagerie has 40 characters"
|
||||
```
|
||||
|
||||
> 注意:
|
||||
不同的 Unicode 字符以及相同 Unicode 字符的不同表示方式可能需要不同数量的内存空间来存储。所以 Swift 中的字符在一个字符串中并不一定占用相同的内存空间。因此字符串的长度不得不通过迭代字符串中每一个字符的长度来进行计算。如果您正在处理一个长字符串,需要注意`countElements`函数必须遍历字符串中的字符以精准计算字符串的长度。
|
||||
> 另外需要注意的是通过`countElements`返回的字符数量并不总是与包含相同字符的`NSString`的`length`属性相同。`NSString`的`length`属性是基于利用 UTF-16 表示的十六位代码单元数字,而不是基于 Unicode 字符。为了解决这个问题,`NSString`的`length`属性在被 Swift 的`String`访问时会成为`utf16count`。
|
||||
|
||||
<a name="concatenating_strings_and_characters"></a>
|
||||
## 连接字符串和字符 (Concatenating Strings and Characters)
|
||||
|
||||
字符串和字符的值可以通过加法运算符(`+`)相加在一起并创建一个新的字符串值:
|
||||
|
||||
```swift
|
||||
let string1 = "hello"
|
||||
let string2 = " there"
|
||||
let character1: Character = "!"
|
||||
let character2: Character = "?"
|
||||
|
||||
let stringPlusCharacter = string1 + character1 // 等于 "hello!"
|
||||
let stringPlusString = string1 + string2 // 等于 "hello there"
|
||||
let characterPlusString = character1 + string1 // 等于 "!hello"
|
||||
let characterPlusCharacter = character1 + character2 // 等于 "!?"
|
||||
```
|
||||
|
||||
您也可以通过加法赋值运算符 (`+=`) 将一个字符串或者字符添加到一个已经存在字符串变量上:
|
||||
|
||||
```swift
|
||||
var instruction = "look over"
|
||||
instruction += string2
|
||||
// instruction 现在等于 "look over there"
|
||||
|
||||
var welcome = "good morning"
|
||||
welcome += character1
|
||||
// welcome 现在等于 "good morning!"
|
||||
```
|
||||
|
||||
> 注意:
|
||||
您不能将一个字符串或者字符添加到一个已经存在的字符变量上,因为字符变量只能包含一个字符。
|
||||
|
||||
<a name="string_interpolation"></a>
|
||||
## 字符串插值 (String Interpolation)
|
||||
|
||||
字符串插值是一种构建新字符串的方式,可以在其中包含常量、变量、字面量和表达式。
|
||||
您插入的字符串字面量的每一项都被包裹在以反斜线为前缀的圆括号中:
|
||||
|
||||
```swift
|
||||
let multiplier = 3
|
||||
let message = "\(multiplier) 乘以 2.5 是 \(Double(multiplier) * 2.5)"
|
||||
// message 是 "3 乘以 2.5 是 7.5"
|
||||
```
|
||||
|
||||
在上面的例子中,`multiplier`作为`\(multiplier)`被插入到一个字符串字面量中。
|
||||
当创建字符串执行插值计算时此占位符会被替换为`multiplier`实际的值。
|
||||
|
||||
`multiplier`的值也作为字符串中后面表达式的一部分。
|
||||
该表达式计算`Double(multiplier) * 2.5`的值并将结果 (7.5) 插入到字符串中。
|
||||
在这个例子中,表达式写为`\(Double(multiplier) * 2.5)`并包含在字符串字面量中。
|
||||
|
||||
> 注意:
|
||||
插值字符串中写在括号中的表达式不能包含非转义双引号 (`"`) 和反斜杠 (`\`),并且不能包含回车或换行符。
|
||||
|
||||
<a name="comparing_strings"></a>
|
||||
## 比较字符串 (Comparing Strings)
|
||||
|
||||
Swift 提供了三种方式来比较字符串的值:字符串相等、前缀相等和后缀相等。
|
||||
|
||||
<a name="string_equality"></a>
|
||||
### 字符串相等 (String Equality)
|
||||
|
||||
如果两个字符串以同一顺序包含完全相同的字符,则认为两者字符串相等:
|
||||
|
||||
```swift
|
||||
let quotation = "我们是一样一样滴."
|
||||
let sameQuotation = "我们是一样一样滴."
|
||||
if quotation == sameQuotation {
|
||||
println("这两个字符串被认为是相同的")
|
||||
}
|
||||
// 打印输出:"这两个字符串被认为是相同的"
|
||||
```
|
||||
|
||||
<a name="prefix_and_suffix_equality"></a>
|
||||
### 前缀/后缀相等 (Prefix and Suffix Equality)
|
||||
|
||||
通过调用字符串的`hasPrefix`/`hasSuffix`方法来检查字符串是否拥有特定前缀/后缀。
|
||||
两个方法均需要以字符串作为参数传入并传出`Boolean`值。
|
||||
两个方法均执行基本字符串和前缀/后缀字符串之间逐个字符的比较操作。
|
||||
|
||||
下面的例子以一个字符串数组表示莎士比亚话剧《罗密欧与朱丽叶》中前两场的场景位置:
|
||||
|
||||
```swift
|
||||
let romeoAndJuliet = [
|
||||
"Act 1 Scene 1: Verona, A public place",
|
||||
"Act 1 Scene 2: Capulet's mansion",
|
||||
"Act 1 Scene 3: A room in Capulet's mansion",
|
||||
"Act 1 Scene 4: A street outside Capulet's mansion",
|
||||
"Act 1 Scene 5: The Great Hall in Capulet's mansion",
|
||||
"Act 2 Scene 1: Outside Capulet's mansion",
|
||||
"Act 2 Scene 2: Capulet's orchard",
|
||||
"Act 2 Scene 3: Outside Friar Lawrence's cell",
|
||||
"Act 2 Scene 4: A street in Verona",
|
||||
"Act 2 Scene 5: Capulet's mansion",
|
||||
"Act 2 Scene 6: Friar Lawrence's cell"
|
||||
]
|
||||
```
|
||||
|
||||
您可以利用`hasPrefix`方法来计算话剧中第一幕的场景数:
|
||||
|
||||
```swift
|
||||
var act1SceneCount = 0
|
||||
for scene in romeoAndJuliet {
|
||||
if scene.hasPrefix("Act 1 ") {
|
||||
++act1SceneCount
|
||||
}
|
||||
}
|
||||
println("There are \(act1SceneCount) scenes in Act 1")
|
||||
// 打印输出:"There are 5 scenes in Act 1"
|
||||
```
|
||||
|
||||
相似地,您可以用`hasSuffix`方法来计算发生在不同地方的场景数:
|
||||
|
||||
```swift
|
||||
var mansionCount = 0
|
||||
var cellCount = 0
|
||||
for scene in romeoAndJuliet {
|
||||
if scene.hasSuffix("Capulet's mansion") {
|
||||
++mansionCount
|
||||
} else if scene.hasSuffix("Friar Lawrence's cell") {
|
||||
++cellCount
|
||||
}
|
||||
}
|
||||
println("\(mansionCount) mansion scenes; \(cellCount) cell scenes")
|
||||
// 打印输出:"6 mansion scenes; 2 cell scenes”
|
||||
```
|
||||
|
||||
<a name="uppercase_and_lowercase_strings"></a>
|
||||
### 大写和小写字符串(Uppercase and Lowercase Strings)
|
||||
|
||||
您可以通过字符串的`uppercaseString`和`lowercaseString`属性来访问大写/小写版本的字符串。
|
||||
|
||||
```swift
|
||||
let normal = "Could you help me, please?"
|
||||
let shouty = normal.uppercaseString
|
||||
// shouty 值为 "COULD YOU HELP ME, PLEASE?"
|
||||
let whispered = normal.lowercaseString
|
||||
// whispered 值为 "could you help me, please?"
|
||||
```
|
||||
|
||||
<a name="unicode"></a>
|
||||
## Unicode
|
||||
|
||||
Unicode 是一个国际标准,用于文本的编码和表示。
|
||||
它使您可以用标准格式表示来自任意语言几乎所有的字符,并能够对文本文件或网页这样的外部资源中的字符进行读写操作。
|
||||
|
||||
Swift 的字符串和字符类型是完全兼容 Unicode 标准的,它支持如下所述的一系列不同的 Unicode 编码。
|
||||
|
||||
<a name="unicode_terminology"></a>
|
||||
### Unicode 术语(Unicode Terminology)
|
||||
|
||||
Unicode 中每一个字符都可以被解释为一个或多个 unicode 标量。
|
||||
字符的 unicode 标量是一个唯一的21位数字(和名称),例如`U+0061`表示小写的拉丁字母A ("a"),`U+1F425`表示小鸡表情 ("🐥")
|
||||
|
||||
当 Unicode 字符串被写进文本文件或其他存储结构当中,这些 unicode 标量将会按照 Unicode 定义的集中格式之一进行编码。其包括`UTF-8`(以8位代码单元进行编码) 和`UTF-16`(以16位代码单元进行编码)。
|
||||
|
||||
<a name="unicode_representations_of_strings"></a>
|
||||
### 字符串的 Unicode 表示(Unicode Representations of Strings)
|
||||
|
||||
Swift 提供了几种不同的方式来访问字符串的 Unicode 表示。
|
||||
|
||||
您可以利用`for-in`来对字符串进行遍历,从而以 Unicode 字符的方式访问每一个字符值。
|
||||
该过程在 [使用字符](#working_with_characters) 中进行了描述。
|
||||
|
||||
另外,能够以其他三种 Unicode 兼容的方式访问字符串的值:
|
||||
|
||||
* UTF-8 代码单元集合 (利用字符串的`utf8`属性进行访问)
|
||||
* UTF-16 代码单元集合 (利用字符串的`utf16`属性进行访问)
|
||||
* 21位的 Unicode 标量值集合 (利用字符串的`unicodeScalars`属性进行访问)
|
||||
|
||||
下面由`D``o``g``!`和`🐶`(`DOG FACE`,Unicode 标量为`U+1F436`)组成的字符串中的每一个字符代表着一种不同的表示:
|
||||
|
||||
```swift
|
||||
let dogString = "Dog!🐶"
|
||||
```
|
||||
|
||||
<a name="UTF-8"></a>
|
||||
### UTF-8
|
||||
|
||||
您可以通过遍历字符串的`utf8`属性来访问它的`UTF-8`表示。
|
||||
其为`UTF8View`类型的属性,`UTF8View`是无符号8位 (`UInt8`) 值的集合,每一个`UInt8`值都是一个字符的 UTF-8 表示:
|
||||
|
||||
```swift
|
||||
for codeUnit in dogString.utf8 {
|
||||
print("\(codeUnit) ")
|
||||
}
|
||||
print("\n")
|
||||
// 68 111 103 33 240 159 144 182
|
||||
```
|
||||
|
||||
上面的例子中,前四个10进制代码单元值 (68, 111, 103, 33) 代表了字符`D` `o` `g`和`!`,它们的 UTF-8 表示与 ASCII 表示相同。
|
||||
后四个代码单元值 (240, 159, 144, 182) 是`DOG FACE`的4字节 UTF-8 表示。
|
||||
|
||||
<a name="UTF-16"></a>
|
||||
### UTF-16
|
||||
|
||||
您可以通过遍历字符串的`utf16`属性来访问它的`UTF-16`表示。
|
||||
其为`UTF16View`类型的属性,`UTF16View`是无符号16位 (`UInt16`) 值的集合,每一个`UInt16`都是一个字符的 UTF-16 表示:
|
||||
|
||||
```swift
|
||||
for codeUnit in dogString.utf16 {
|
||||
print("\(codeUnit) ")
|
||||
}
|
||||
print("\n")
|
||||
// 68 111 103 33 55357 56374
|
||||
```
|
||||
|
||||
同样,前四个代码单元值 (68, 111, 103, 33) 代表了字符`D` `o` `g`和`!`,它们的 UTF-16 代码单元和 UTF-8 完全相同。
|
||||
|
||||
第五和第六个代码单元值 (55357 和 56374) 是`DOG FACE`字符的UTF-16 表示。
|
||||
第一个值为`U+D83D`(十进制值为 55357),第二个值为`U+DC36`(十进制值为 56374)。
|
||||
|
||||
<a name="unicode_scalars"></a>
|
||||
### Unicode 标量 (Unicode Scalars)
|
||||
|
||||
您可以通过遍历字符串的`unicodeScalars`属性来访问它的 Unicode 标量表示。
|
||||
其为`UnicodeScalarView`类型的属性, `UnicodeScalarView`是`UnicodeScalar`的集合。
|
||||
`UnicodeScalar`是21位的 Unicode 代码点。
|
||||
|
||||
每一个`UnicodeScalar`拥有一个值属性,可以返回对应的21位数值,用`UInt32`来表示。
|
||||
|
||||
```swift
|
||||
for scalar in dogString.unicodeScalars {
|
||||
print("\(scalar.value) ")
|
||||
}
|
||||
print("\n")
|
||||
// 68 111 103 33 128054
|
||||
```
|
||||
|
||||
同样,前四个代码单元值 (68, 111, 103, 33) 代表了字符`D` `o` `g`和`!`。
|
||||
第五位数值,128054,是一个十六进制1F436的十进制表示。
|
||||
其等同于`DOG FACE`的Unicode 标量 U+1F436。
|
||||
|
||||
作为查询字符值属性的一种替代方法,每个`UnicodeScalar`值也可以用来构建一个新的字符串值,比如在字符串插值中使用:
|
||||
|
||||
```swift
|
||||
for scalar in dogString.unicodeScalars {
|
||||
println("\(scalar) ")
|
||||
}
|
||||
// D
|
||||
// o
|
||||
// g
|
||||
// !
|
||||
// 🐶
|
||||
```
|
||||
|
||||
@ -1,438 +1,428 @@
|
||||
> 翻译:zqp
|
||||
> 校对:shinyzhu
|
||||
|
||||
# 集合类型 (Collection Types)
|
||||
-----------------
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [数组(Arrays)](#arrays)
|
||||
- [字典(Dictionaries)](#dictionaries)
|
||||
- [集合的可变性(Mutability of Collections)](#mutability_of_collections)
|
||||
|
||||
Swift 语言提供经典的数组和字典两种集合类型来存储集合数据。数组用来按顺序存储相同类型的数据。字典虽然无序存储相同类型数据值但是需要由独有的标识符引用和寻址(就是键值对)。
|
||||
|
||||
Swift 语言里的数组和字典中存储的数据值类型必须明确。 这意味着我们不能把不正确的数据类型插入其中。 同时这也说明我们完全可以对获取出的值类型非常自信。 Swift 对显式类型集合的使用确保了我们的代码对工作所需要的类型非常清楚,也让我们在开发中可以早早地找到任何的类型不匹配错误。
|
||||
|
||||
> 注意:
|
||||
Swift 的数组结构在被声明成常量和变量或者被传入函数与方法中时会相对于其他类型展现出不同的特性。 获取更多信息请参见[集合的可变性](#mutability_of_collections)与[集合在赋值和复制中的行为](09_Classes_and_Structures.html#assignment_and_copy_behavior_for_collection_types)章节。
|
||||
|
||||
<a name="arrays"></a>
|
||||
## 数组
|
||||
|
||||
数组使用有序列表存储同一类型的多个值。相同的值可以多次出现在一个数组的不同位置中。
|
||||
|
||||
Swift 数组特定于它所存储元素的类型。这与 Objective-C 的 NSArray 和 NSMutableArray 不同,这两个类可以存储任意类型的对象,并且不提供所返回对象的任何特别信息。在 Swift 中,数据值在被存储进入某个数组之前类型必须明确,方法是通过显式的类型标注或类型推断,而且不是必须是`class`类型。例如: 如果我们创建了一个`Int`值类型的数组,我们不能往其中插入任何不是`Int`类型的数据。 Swift 中的数组是类型安全的,并且它们中包含的类型必须明确。
|
||||
|
||||
<a name="array_type_shorthand_syntax"></a>
|
||||
### 数组的简单语法
|
||||
|
||||
写 Swift 数组应该遵循像`Array<SomeType>`这样的形式,其中`SomeType`是这个数组中唯一允许存在的数据类型。 我们也可以使用像`SomeType[]`这样的简单语法。 尽管两种形式在功能上是一样的,但是推荐较短的那种,而且在本文中都会使用这种形式来使用数组。
|
||||
|
||||
<a name="array_literals"></a>
|
||||
### 数组构造语句
|
||||
|
||||
我们可以使用字面量来进行数组构造,这是一种用一个或者多个数值构造数组的简单方法。字面量是一系列由逗号分割并由方括号包含的数值。
|
||||
`[value 1, value 2, value 3]`。
|
||||
|
||||
下面这个例子创建了一个叫做`shoppingList`并且存储字符串的数组:
|
||||
|
||||
```swift
|
||||
var shoppingList: String[] = ["Eggs", "Milk"]
|
||||
// shoppingList 已经被构造并且拥有两个初始项。
|
||||
```
|
||||
|
||||
`shoppingList`变量被声明为“字符串值类型的数组“,记作`String[]`。 因为这个数组被规定只有`String`一种数据结构,所以只有`String`类型可以在其中被存取。 在这里,`shoppinglist`数组由两个`String`值(`"Eggs"` 和`"Milk"`)构造,并且由字面量定义。
|
||||
|
||||
> 注意:
|
||||
> `Shoppinglist`数组被声明为变量(`var`关键字创建)而不是常量(`let`创建)是因为以后可能会有更多的数据项被插入其中。
|
||||
|
||||
在这个例子中,字面量仅仅包含两个`String`值。匹配了该数组的变量声明(只能包含`String`的数组),所以这个字面量的分配过程就是允许用两个初始项来构造`shoppinglist`。
|
||||
|
||||
由于 Swift 的类型推断机制,当我们用字面量构造只拥有相同类型值数组的时候,我们不必把数组的类型定义清楚。 `shoppinglist`的构造也可以这样写:
|
||||
|
||||
```swift
|
||||
var shoppingList = ["Eggs", "Milk"]
|
||||
```
|
||||
|
||||
因为所有字面量中的值都是相同的类型,Swift 可以推断出`String[]`是`shoppinglist`中变量的正确类型。
|
||||
|
||||
<a name="accessing_and_modifying_an_array"></a>
|
||||
### 访问和修改数组
|
||||
|
||||
我们可以通过数组的方法和属性来访问和修改数组,或者下标语法。
|
||||
还可以使用数组的只读属性`count`来获取数组中的数据项数量。
|
||||
|
||||
```swift
|
||||
println("The shopping list contains \(shoppingList.count) items.")
|
||||
// 输出"The shopping list contains 2 items."(这个数组有2个项)
|
||||
```
|
||||
|
||||
使用布尔项`isEmpty`来作为检查`count`属性的值是否为 0 的捷径。
|
||||
|
||||
```swift
|
||||
if shoppingList.isEmpty {
|
||||
println("The shopping list is empty.")
|
||||
} else {
|
||||
println("The shopping list is not empty.")
|
||||
}
|
||||
// 打印 "The shopping list is not empty."(shoppinglist不是空的)
|
||||
```
|
||||
|
||||
也可以使用`append`方法在数组后面添加新的数据项:
|
||||
|
||||
```swift
|
||||
shoppingList.append("Flour")
|
||||
// shoppingList 现在有3个数据项,有人在摊煎饼
|
||||
```
|
||||
|
||||
除此之外,使用加法赋值运算符(`+=`)也可以直接在数组后面添加数据项:
|
||||
|
||||
```swift
|
||||
shoppingList += "Baking Powder"
|
||||
// shoppingList 现在有四项了
|
||||
```
|
||||
|
||||
我们也可以使用加法赋值运算符(`+=`)直接添加拥有相同类型数据的数组。
|
||||
|
||||
```swift
|
||||
shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
|
||||
// shoppingList 现在有7项了
|
||||
```
|
||||
|
||||
可以直接使用下标语法来获取数组中的数据项,把我们需要的数据项的索引值放在直接放在数组名称的方括号中:
|
||||
|
||||
```swift
|
||||
var firstItem = shoppingList[0]
|
||||
// 第一项是 "Eggs"
|
||||
```
|
||||
|
||||
注意第一项在数组中的索引值是`0`而不是`1`。 Swift 中的数组索引总是从零开始。
|
||||
|
||||
我们也可以用下标来改变某个已有索引值对应的数据值:
|
||||
|
||||
```swift
|
||||
shoppingList[0] = "Six eggs"
|
||||
// 其中的第一项现在是 "Six eggs" 而不是 "Eggs"
|
||||
```
|
||||
|
||||
还可以利用下标来一次改变一系列数据值,即使新数据和原有数据的数量是不一样的。下面的例子把`"Chocolate Spread"`,`"Cheese"`,和`"Butter"`替换为`"Bananas"`和 `"Apples"`:
|
||||
|
||||
```swift
|
||||
shoppingList[4...6] = ["Bananas", "Apples"]
|
||||
// shoppingList 现在有六项
|
||||
```
|
||||
|
||||
<<<<<<< HEAD
|
||||
> 注意:
|
||||
>我们不能使用下标语法在数组尾部添加新项。如果我们试着用这种方法对索引越界的数据进行检索或者设置新值的操作,我们会引发一个运行期错误。我们可以使用索引值和数组的`count`属性进行比较来在使用某个索引之前先检验是否有效。除了当`count`等于 0 时(说明这是个空数组),最大索引值一直是`count - 1`,因为数组都是零起索引。
|
||||
=======
|
||||
> 注意:
|
||||
>
|
||||
>我们不能使用下标语法在数组尾部添加新项。如果我们试着用这种方法对索引越界的数据进行检索或者设置新值的操作,我们会引发一个运行时错误。我们可以使用索引值和数组的`count`属性进行比较来在使用某个索引之前先检验是否有效。除了当`count`等于 0 时(说明这是个空数组),最大索引值一直是`count - 1`,因为数组都是零起索引。
|
||||
>>>>>>> a516af6a531a104ec88da0d236ecf389a5ec72af
|
||||
|
||||
调用数组的`insert(atIndex:)`方法来在某个具体索引值之前添加数据项:
|
||||
|
||||
```swift
|
||||
shoppingList.insert("Maple Syrup", atIndex: 0)
|
||||
// shoppingList 现在有7项
|
||||
// "Maple Syrup" 现在是这个列表中的第一项
|
||||
```
|
||||
|
||||
这次`insert`函数调用把值为`"Maple Syrup"`的新数据项插入列表的最开始位置,并且使用`0`作为索引值。
|
||||
|
||||
类似的我们可以使用`removeAtIndex`方法来移除数组中的某一项。这个方法把数组在特定索引值中存储的数据项移除并且返回这个被移除的数据项(我们不需要的时候就可以无视它):
|
||||
|
||||
```swift
|
||||
let mapleSyrup = shoppingList.removeAtIndex(0)
|
||||
// 索引值为0的数据项被移除
|
||||
// shoppingList 现在只有6项,而且不包括Maple Syrup
|
||||
// mapleSyrup常量的值等于被移除数据项的值 "Maple Syrup"
|
||||
```
|
||||
|
||||
数据项被移除后数组中的空出项会被自动填补,所以现在索引值为`0`的数据项的值再次等于`"Six eggs"`:
|
||||
|
||||
```swift
|
||||
firstItem = shoppingList[0]
|
||||
// firstItem 现在等于 "Six eggs"
|
||||
```
|
||||
|
||||
如果我们只想把数组中的最后一项移除,可以使用`removeLast`方法而不是`removeAtIndex`方法来避免我们需要获取数组的`count`属性。就像后者一样,前者也会返回被移除的数据项:
|
||||
|
||||
```swift
|
||||
let apples = shoppingList.removeLast()
|
||||
// 数组的最后一项被移除了
|
||||
// shoppingList现在只有5项,不包括cheese
|
||||
// apples 常量的值现在等于"Apples" 字符串
|
||||
```
|
||||
|
||||
<a name="iterating_over_an_array"></a>
|
||||
### 数组的遍历
|
||||
|
||||
我们可以使用`for-in`循环来遍历所有数组中的数据项:
|
||||
|
||||
```swift
|
||||
for item in shoppingList {
|
||||
println(item)
|
||||
}
|
||||
// Six eggs
|
||||
// Milk
|
||||
// Flour
|
||||
// Baking Powder
|
||||
// Bananas
|
||||
```
|
||||
|
||||
如果我们同时需要每个数据项的值和索引值,可以使用全局`enumerate`函数来进行数组遍历。`enumerate`返回一个由每一个数据项索引值和数据值组成的元组。我们可以把这个元组分解成临时常量或者变量来进行遍历:
|
||||
|
||||
```swift
|
||||
for (index, value) in enumerate(shoppingList) {
|
||||
println("Item \(index + 1): \(value)")
|
||||
}
|
||||
// Item 1: Six eggs
|
||||
// Item 2: Milk
|
||||
// Item 3: Flour
|
||||
// Item 4: Baking Powder
|
||||
// Item 5: Bananas
|
||||
```
|
||||
|
||||
更多关于`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="dictionaries"></a>
|
||||
## 字典
|
||||
|
||||
字典是一种存储多个相同类型的值的容器。每个值(value)都关联唯一的键(key),键作为字典中的这个值数据的标识符。和数组中的数据项不同,字典中的数据项并没有具体顺序。我们在需要通过标识符(键)访问数据的时候使用字典,这种方法很大程度上和我们在现实世界中使用字典查字义的方法一样。
|
||||
|
||||
Swift 的字典使用时需要具体规定可以存储键和值类型。不同于 Objective-C 的`NSDictionary`和`NSMutableDictionary` 类可以使用任何类型的对象来作键和值并且不提供任何关于这些对象的本质信息。在 Swift 中,在某个特定字典中可以存储的键和值必须提前定义清楚,方法是通过显性类型标注或者类型推断。
|
||||
|
||||
Swift 的字典使用`Dictionary<KeyType, ValueType>`定义,其中`KeyType`是字典中键的数据类型,`ValueType`是字典中对应于这些键所存储值的数据类型。
|
||||
|
||||
`KeyType`的唯一限制就是可哈希的,这样可以保证它是独一无二的,所有的 Swift 基本类型(例如`String`,`Int`, `Double`和`Bool`)都是默认可哈希的,并且所有这些类型都可以在字典中当做键使用。未关联值的枚举成员(参见[枚举](08_Enumerations.html))也是默认可哈希的。
|
||||
|
||||
<a name="dictionary_literals"></a>
|
||||
## 字典字面量
|
||||
|
||||
我们可以使用字典字面量来构造字典,它们和我们刚才介绍过的数组字面量拥有相似语法。一个字典字面量是一个定义拥有一个或者多个键值对的字典集合的简单语句。
|
||||
|
||||
一个键值对是一个`key`和一个`value`的结合体。在字典字面量中,每一个键值对的键和值都由冒号分割。这些键值对构成一个列表,其中这些键值对由方括号包含并且由逗号分割:
|
||||
|
||||
```swift
|
||||
[key 1: value 1, key 2: value 2, key 3: value 3]
|
||||
```
|
||||
|
||||
下面的例子创建了一个存储国际机场名称的字典。在这个字典中键是三个字母的国际航空运输相关代码,值是机场名称:
|
||||
|
||||
```swift
|
||||
var airports: Dictionary<String, String> = ["TYO": "Tokyo", "DUB": "Dublin"]
|
||||
```
|
||||
|
||||
`airports`字典被定义为一种`Dictionary<String, String>`,它意味着这个字典的键和值都是`String`类型。
|
||||
|
||||
> 注意:
|
||||
> `airports`字典被声明为变量(用`var`关键字)而不是常量(`let`关键字)因为后来更多的机场信息会被添加到这个示例字典中。
|
||||
|
||||
`airports`字典使用字典字面量初始化,包含两个键值对。第一对的键是`TYO`,值是`Tokyo`。第二对的键是`DUB`,值是`Dublin`。
|
||||
|
||||
这个字典语句包含了两个`String: String`类型的键值对。它们对应`airports`变量声明的类型(一个只有`String`键和`String`值的字典)所以这个字典字面量是构造两个初始数据项的`airport`字典。
|
||||
|
||||
和数组一样,如果我们使用字面量构造字典就不用把类型定义清楚。`airports`的也可以用这种方法简短定义:
|
||||
|
||||
```swift
|
||||
var airports = ["TYO": "Tokyo", "DUB": "Dublin"]
|
||||
```
|
||||
|
||||
因为这个语句中所有的键和值都分别是相同的数据类型,Swift 可以推断出`Dictionary<String, String>`是`airports`字典的正确类型。
|
||||
|
||||
<a name="accessing_and_modifying_a_dictionary"></a>
|
||||
### 读取和修改字典
|
||||
|
||||
我们可以通过字典的方法和属性来读取和修改字典,或者使用下标语法。和数组一样,我们可以通过字典的只读属性`count`来获取某个字典的数据项数量:
|
||||
|
||||
```swift
|
||||
println("The dictionary of airports contains \(airports.count) items.")
|
||||
// 打印 "The dictionary of airports contains 2 items."(这个字典有两个数据项)
|
||||
```
|
||||
|
||||
我们也可以在字典中使用下标语法来添加新的数据项。可以使用一个合适类型的 key 作为下标索引,并且分配新的合适类型的值:
|
||||
|
||||
```swift
|
||||
airports["LHR"] = "London"
|
||||
// airports 字典现在有三个数据项
|
||||
```
|
||||
|
||||
我们也可以使用下标语法来改变特定键对应的值:
|
||||
|
||||
```swift
|
||||
airports["LHR"] = "London Heathrow"
|
||||
// "LHR"对应的值 被改为 "London Heathrow
|
||||
```
|
||||
|
||||
作为另一种下标方法,字典的`updateValue(forKey:)`方法可以设置或者更新特定键对应的值。就像上面所示的示例,`updateValue(forKey:)`方法在这个键不存在对应值的时候设置值或者在存在时更新已存在的值。和上面的下标方法不一样,这个方法返回更新值之前的原值。这样方便我们检查更新是否成功。
|
||||
|
||||
`updateValue(forKey:)`函数会返回包含一个字典值类型的可选值。举例来说:对于存储`String`值的字典,这个函数会返回一个`String?`或者“可选 `String`”类型的值。如果值存在,则这个可选值值等于被替换的值,否则将会是`nil`。
|
||||
|
||||
```swift
|
||||
if let oldValue = airports.updateValue("Dublin Internation", forKey: "DUB") {
|
||||
println("The old value for DUB was \(oldValue).")
|
||||
}
|
||||
// 输出 "The old value for DUB was Dublin."(DUB原值是dublin)
|
||||
```
|
||||
|
||||
我们也可以使用下标语法来在字典中检索特定键对应的值。由于使用一个没有值的键这种情况是有可能发生的,可选类型返回这个键存在的相关值,否则就返回`nil`:
|
||||
|
||||
```swift
|
||||
if let airportName = airports["DUB"] {
|
||||
println("The name of the airport is \(airportName).")
|
||||
} else {
|
||||
println("That airport is not in the airports dictionary.")
|
||||
}
|
||||
// 打印 "The name of the airport is Dublin Internation."(机场的名字是都柏林国际)
|
||||
```
|
||||
|
||||
我们还可以使用下标语法来通过给某个键的对应值赋值为`nil`来从字典里移除一个键值对:
|
||||
|
||||
```swift
|
||||
airports["APL"] = "Apple Internation"
|
||||
// "Apple Internation"不是真的 APL机场, 删除它
|
||||
airports["APL"] = nil
|
||||
// APL现在被移除了
|
||||
```
|
||||
|
||||
另外,`removeValueForKey`方法也可以用来在字典中移除键值对。这个方法在键值对存在的情况下会移除该键值对并且返回被移除的value或者在没有值的情况下返回`nil`:
|
||||
|
||||
```swift
|
||||
if let removedValue = airports.removeValueForKey("DUB") {
|
||||
println("The removed airport's name is \(removedValue).")
|
||||
} else {
|
||||
println("The airports dictionary does not contain a value for DUB.")
|
||||
}
|
||||
// prints "The removed airport's name is Dublin International."
|
||||
```
|
||||
|
||||
<a name="iterating_over_a_dictionary"></a>
|
||||
### 字典遍历
|
||||
|
||||
我们可以使用`for-in`循环来遍历某个字典中的键值对。每一个字典中的数据项都由`(key, value)`元组形式返回,并且我们可以使用临时常量或者变量来分解这些元组:
|
||||
|
||||
```swift
|
||||
for (airportCode, airportName) in airports {
|
||||
println("\(airportCode): \(airportName)")
|
||||
}
|
||||
// TYO: Tokyo
|
||||
// LHR: London Heathrow
|
||||
```
|
||||
`for-in`循环请参见[For 循环](05_Control_Flow.html#for_loops)。
|
||||
|
||||
我们也可以通过访问它的`keys`或者`values`属性(都是可遍历集合)检索一个字典的键或者值:
|
||||
|
||||
```swift
|
||||
for airportCode in airports.keys {
|
||||
println("Airport code: \(airportCode)")
|
||||
}
|
||||
// Airport code: TYO
|
||||
// Airport code: LHR
|
||||
```
|
||||
|
||||
```swift
|
||||
for airportName in airports.values {
|
||||
println("Airport name: \(airportName)")
|
||||
}
|
||||
// Airport name: Tokyo
|
||||
// Airport name: London Heathrow
|
||||
```
|
||||
|
||||
如果我们只是需要使用某个字典的键集合或者值集合来作为某个接受`Array`实例 API 的参数,可以直接使用`keys`或者`values`属性直接构造一个新数组:
|
||||
|
||||
```swift
|
||||
let airportCodes = Array(airports.keys)
|
||||
// airportCodes is ["TYO", "LHR"]
|
||||
```
|
||||
|
||||
```swift
|
||||
let airportNames = Array(airports.values)
|
||||
// airportNames is ["Tokyo", "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 数组在大小被固定的时候依然可以做的很棒。
|
||||
|
||||
Swift 数组的可变性行为同时影响了数组实例如何被分配和修改,想获取更多信息,请参见[集合在赋值和复制中的行为](09_Classes_and_Structures.html#assignment_and_copy_behavior_for_collection_types)。
|
||||
|
||||
> 注意:
|
||||
> 在我们不需要改变数组大小的时候创建不可变数组是很好的习惯。如此 Swift 编译器可以优化我们创建的集合。
|
||||
|
||||
> 翻译:[zqp](https://github.com/zqp)
|
||||
> 校对:[shinyzhu](https://github.com/shinyzhu), [stanzhai](https://github.com/stanzhai)
|
||||
|
||||
# 集合类型 (Collection Types)
|
||||
-----------------
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [数组(Arrays)](#arrays)
|
||||
- [字典(Dictionaries)](#dictionaries)
|
||||
- [集合的可变性(Mutability of Collections)](#mutability_of_collections)
|
||||
|
||||
Swift 语言提供经典的数组和字典两种集合类型来存储集合数据。数组用来按顺序存储相同类型的数据。字典虽然无序存储相同类型数据值但是需要由独有的标识符引用和寻址(就是键值对)。
|
||||
|
||||
Swift 语言里的数组和字典中存储的数据值类型必须明确。 这意味着我们不能把不正确的数据类型插入其中。 同时这也说明我们完全可以对获取出的值类型非常自信。 Swift 对显式类型集合的使用确保了我们的代码对工作所需要的类型非常清楚,也让我们在开发中可以早早地找到任何的类型不匹配错误。
|
||||
|
||||
> 注意:
|
||||
Swift 的数组结构在被声明成常量和变量或者被传入函数与方法中时会相对于其他类型展现出不同的特性。 获取更多信息请参见[集合的可变性](#mutability_of_collections)与[集合在赋值和复制中的行为](09_Classes_and_Structures.html#assignment_and_copy_behavior_for_collection_types)章节。
|
||||
|
||||
<a name="arrays"></a>
|
||||
## 数组
|
||||
|
||||
数组使用有序列表存储同一类型的多个值。相同的值可以多次出现在一个数组的不同位置中。
|
||||
|
||||
Swift 数组特定于它所存储元素的类型。这与 Objective-C 的 NSArray 和 NSMutableArray 不同,这两个类可以存储任意类型的对象,并且不提供所返回对象的任何特别信息。在 Swift 中,数据值在被存储进入某个数组之前类型必须明确,方法是通过显式的类型标注或类型推断,而且不是必须是`class`类型。例如: 如果我们创建了一个`Int`值类型的数组,我们不能往其中插入任何不是`Int`类型的数据。 Swift 中的数组是类型安全的,并且它们中包含的类型必须明确。
|
||||
|
||||
<a name="array_type_shorthand_syntax"></a>
|
||||
### 数组的简单语法
|
||||
|
||||
写 Swift 数组应该遵循像`Array<SomeType>`这样的形式,其中`SomeType`是这个数组中唯一允许存在的数据类型。 我们也可以使用像`SomeType[]`这样的简单语法。 尽管两种形式在功能上是一样的,但是推荐较短的那种,而且在本文中都会使用这种形式来使用数组。
|
||||
|
||||
<a name="array_literals"></a>
|
||||
### 数组构造语句
|
||||
|
||||
我们可以使用字面量来进行数组构造,这是一种用一个或者多个数值构造数组的简单方法。字面量是一系列由逗号分割并由方括号包含的数值。
|
||||
`[value 1, value 2, value 3]`。
|
||||
|
||||
下面这个例子创建了一个叫做`shoppingList`并且存储字符串的数组:
|
||||
|
||||
```swift
|
||||
var shoppingList: String[] = ["Eggs", "Milk"]
|
||||
// shoppingList 已经被构造并且拥有两个初始项。
|
||||
```
|
||||
|
||||
`shoppingList`变量被声明为“字符串值类型的数组“,记作`String[]`。 因为这个数组被规定只有`String`一种数据结构,所以只有`String`类型可以在其中被存取。 在这里,`shoppinglist`数组由两个`String`值(`"Eggs"` 和`"Milk"`)构造,并且由字面量定义。
|
||||
|
||||
> 注意:
|
||||
> `Shoppinglist`数组被声明为变量(`var`关键字创建)而不是常量(`let`创建)是因为以后可能会有更多的数据项被插入其中。
|
||||
|
||||
在这个例子中,字面量仅仅包含两个`String`值。匹配了该数组的变量声明(只能包含`String`的数组),所以这个字面量的分配过程就是允许用两个初始项来构造`shoppinglist`。
|
||||
|
||||
由于 Swift 的类型推断机制,当我们用字面量构造只拥有相同类型值数组的时候,我们不必把数组的类型定义清楚。 `shoppinglist`的构造也可以这样写:
|
||||
|
||||
```swift
|
||||
var shoppingList = ["Eggs", "Milk"]
|
||||
```
|
||||
|
||||
因为所有字面量中的值都是相同的类型,Swift 可以推断出`String[]`是`shoppinglist`中变量的正确类型。
|
||||
|
||||
<a name="accessing_and_modifying_an_array"></a>
|
||||
### 访问和修改数组
|
||||
|
||||
我们可以通过数组的方法和属性来访问和修改数组,或者下标语法。
|
||||
还可以使用数组的只读属性`count`来获取数组中的数据项数量。
|
||||
|
||||
```swift
|
||||
println("The shopping list contains \(shoppingList.count) items.")
|
||||
// 输出"The shopping list contains 2 items."(这个数组有2个项)
|
||||
```
|
||||
|
||||
使用布尔项`isEmpty`来作为检查`count`属性的值是否为 0 的捷径。
|
||||
|
||||
```swift
|
||||
if shoppingList.isEmpty {
|
||||
println("The shopping list is empty.")
|
||||
} else {
|
||||
println("The shopping list is not empty.")
|
||||
}
|
||||
// 打印 "The shopping list is not empty."(shoppinglist不是空的)
|
||||
```
|
||||
|
||||
也可以使用`append`方法在数组后面添加新的数据项:
|
||||
|
||||
```swift
|
||||
shoppingList.append("Flour")
|
||||
// shoppingList 现在有3个数据项,有人在摊煎饼
|
||||
```
|
||||
|
||||
除此之外,使用加法赋值运算符(`+=`)也可以直接在数组后面添加数据项:
|
||||
|
||||
```swift
|
||||
shoppingList += "Baking Powder"
|
||||
// shoppingList 现在有四项了
|
||||
```
|
||||
|
||||
我们也可以使用加法赋值运算符(`+=`)直接添加拥有相同类型数据的数组。
|
||||
|
||||
```swift
|
||||
shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
|
||||
// shoppingList 现在有7项了
|
||||
```
|
||||
|
||||
可以直接使用下标语法来获取数组中的数据项,把我们需要的数据项的索引值放在直接放在数组名称的方括号中:
|
||||
|
||||
```swift
|
||||
var firstItem = shoppingList[0]
|
||||
// 第一项是 "Eggs"
|
||||
```
|
||||
|
||||
注意第一项在数组中的索引值是`0`而不是`1`。 Swift 中的数组索引总是从零开始。
|
||||
|
||||
我们也可以用下标来改变某个已有索引值对应的数据值:
|
||||
|
||||
```swift
|
||||
shoppingList[0] = "Six eggs"
|
||||
// 其中的第一项现在是 "Six eggs" 而不是 "Eggs"
|
||||
```
|
||||
|
||||
还可以利用下标来一次改变一系列数据值,即使新数据和原有数据的数量是不一样的。下面的例子把`"Chocolate Spread"`,`"Cheese"`,和`"Butter"`替换为`"Bananas"`和 `"Apples"`:
|
||||
|
||||
```swift
|
||||
shoppingList[4...6] = ["Bananas", "Apples"]
|
||||
// shoppingList 现在有六项
|
||||
```
|
||||
|
||||
> 注意:
|
||||
>我们不能使用下标语法在数组尾部添加新项。如果我们试着用这种方法对索引越界的数据进行检索或者设置新值的操作,我们会引发一个运行期错误。我们可以使用索引值和数组的`count`属性进行比较来在使用某个索引之前先检验是否有效。除了当`count`等于 0 时(说明这是个空数组),最大索引值一直是`count - 1`,因为数组都是零起索引。
|
||||
|
||||
调用数组的`insert(atIndex:)`方法来在某个具体索引值之前添加数据项:
|
||||
|
||||
```swift
|
||||
shoppingList.insert("Maple Syrup", atIndex: 0)
|
||||
// shoppingList 现在有7项
|
||||
// "Maple Syrup" 现在是这个列表中的第一项
|
||||
```
|
||||
|
||||
这次`insert`函数调用把值为`"Maple Syrup"`的新数据项插入列表的最开始位置,并且使用`0`作为索引值。
|
||||
|
||||
类似的我们可以使用`removeAtIndex`方法来移除数组中的某一项。这个方法把数组在特定索引值中存储的数据项移除并且返回这个被移除的数据项(我们不需要的时候就可以无视它):
|
||||
|
||||
```swift
|
||||
let mapleSyrup = shoppingList.removeAtIndex(0)
|
||||
// 索引值为0的数据项被移除
|
||||
// shoppingList 现在只有6项,而且不包括Maple Syrup
|
||||
// mapleSyrup常量的值等于被移除数据项的值 "Maple Syrup"
|
||||
```
|
||||
|
||||
数据项被移除后数组中的空出项会被自动填补,所以现在索引值为`0`的数据项的值再次等于`"Six eggs"`:
|
||||
|
||||
```swift
|
||||
firstItem = shoppingList[0]
|
||||
// firstItem 现在等于 "Six eggs"
|
||||
```
|
||||
|
||||
如果我们只想把数组中的最后一项移除,可以使用`removeLast`方法而不是`removeAtIndex`方法来避免我们需要获取数组的`count`属性。就像后者一样,前者也会返回被移除的数据项:
|
||||
|
||||
```swift
|
||||
let apples = shoppingList.removeLast()
|
||||
// 数组的最后一项被移除了
|
||||
// shoppingList现在只有5项,不包括cheese
|
||||
// apples 常量的值现在等于"Apples" 字符串
|
||||
```
|
||||
|
||||
<a name="iterating_over_an_array"></a>
|
||||
### 数组的遍历
|
||||
|
||||
我们可以使用`for-in`循环来遍历所有数组中的数据项:
|
||||
|
||||
```swift
|
||||
for item in shoppingList {
|
||||
println(item)
|
||||
}
|
||||
// Six eggs
|
||||
// Milk
|
||||
// Flour
|
||||
// Baking Powder
|
||||
// Bananas
|
||||
```
|
||||
|
||||
如果我们同时需要每个数据项的值和索引值,可以使用全局`enumerate`函数来进行数组遍历。`enumerate`返回一个由每一个数据项索引值和数据值组成的元组。我们可以把这个元组分解成临时常量或者变量来进行遍历:
|
||||
|
||||
```swift
|
||||
for (index, value) in enumerate(shoppingList) {
|
||||
println("Item \(index + 1): \(value)")
|
||||
}
|
||||
// Item 1: Six eggs
|
||||
// Item 2: Milk
|
||||
// Item 3: Flour
|
||||
// Item 4: Baking Powder
|
||||
// Item 5: Bananas
|
||||
```
|
||||
|
||||
更多关于`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="dictionaries"></a>
|
||||
## 字典
|
||||
|
||||
字典是一种存储多个相同类型的值的容器。每个值(value)都关联唯一的键(key),键作为字典中的这个值数据的标识符。和数组中的数据项不同,字典中的数据项并没有具体顺序。我们在需要通过标识符(键)访问数据的时候使用字典,这种方法很大程度上和我们在现实世界中使用字典查字义的方法一样。
|
||||
|
||||
Swift 的字典使用时需要具体规定可以存储键和值类型。不同于 Objective-C 的`NSDictionary`和`NSMutableDictionary` 类可以使用任何类型的对象来作键和值并且不提供任何关于这些对象的本质信息。在 Swift 中,在某个特定字典中可以存储的键和值必须提前定义清楚,方法是通过显性类型标注或者类型推断。
|
||||
|
||||
Swift 的字典使用`Dictionary<KeyType, ValueType>`定义,其中`KeyType`是字典中键的数据类型,`ValueType`是字典中对应于这些键所存储值的数据类型。
|
||||
|
||||
`KeyType`的唯一限制就是可哈希的,这样可以保证它是独一无二的,所有的 Swift 基本类型(例如`String`,`Int`, `Double`和`Bool`)都是默认可哈希的,并且所有这些类型都可以在字典中当做键使用。未关联值的枚举成员(参见[枚举](08_Enumerations.html))也是默认可哈希的。
|
||||
|
||||
<a name="dictionary_literals"></a>
|
||||
## 字典字面量
|
||||
|
||||
我们可以使用字典字面量来构造字典,它们和我们刚才介绍过的数组字面量拥有相似语法。一个字典字面量是一个定义拥有一个或者多个键值对的字典集合的简单语句。
|
||||
|
||||
一个键值对是一个`key`和一个`value`的结合体。在字典字面量中,每一个键值对的键和值都由冒号分割。这些键值对构成一个列表,其中这些键值对由方括号包含并且由逗号分割:
|
||||
|
||||
```swift
|
||||
[key 1: value 1, key 2: value 2, key 3: value 3]
|
||||
```
|
||||
|
||||
下面的例子创建了一个存储国际机场名称的字典。在这个字典中键是三个字母的国际航空运输相关代码,值是机场名称:
|
||||
|
||||
```swift
|
||||
var airports: Dictionary<String, String> = ["TYO": "Tokyo", "DUB": "Dublin"]
|
||||
```
|
||||
|
||||
`airports`字典被定义为一种`Dictionary<String, String>`,它意味着这个字典的键和值都是`String`类型。
|
||||
|
||||
> 注意:
|
||||
> `airports`字典被声明为变量(用`var`关键字)而不是常量(`let`关键字)因为后来更多的机场信息会被添加到这个示例字典中。
|
||||
|
||||
`airports`字典使用字典字面量初始化,包含两个键值对。第一对的键是`TYO`,值是`Tokyo`。第二对的键是`DUB`,值是`Dublin`。
|
||||
|
||||
这个字典语句包含了两个`String: String`类型的键值对。它们对应`airports`变量声明的类型(一个只有`String`键和`String`值的字典)所以这个字典字面量是构造两个初始数据项的`airport`字典。
|
||||
|
||||
和数组一样,如果我们使用字面量构造字典就不用把类型定义清楚。`airports`的也可以用这种方法简短定义:
|
||||
|
||||
```swift
|
||||
var airports = ["TYO": "Tokyo", "DUB": "Dublin"]
|
||||
```
|
||||
|
||||
因为这个语句中所有的键和值都分别是相同的数据类型,Swift 可以推断出`Dictionary<String, String>`是`airports`字典的正确类型。
|
||||
|
||||
<a name="accessing_and_modifying_a_dictionary"></a>
|
||||
### 读取和修改字典
|
||||
|
||||
我们可以通过字典的方法和属性来读取和修改字典,或者使用下标语法。和数组一样,我们可以通过字典的只读属性`count`来获取某个字典的数据项数量:
|
||||
|
||||
```swift
|
||||
println("The dictionary of airports contains \(airports.count) items.")
|
||||
// 打印 "The dictionary of airports contains 2 items."(这个字典有两个数据项)
|
||||
```
|
||||
|
||||
我们也可以在字典中使用下标语法来添加新的数据项。可以使用一个合适类型的 key 作为下标索引,并且分配新的合适类型的值:
|
||||
|
||||
```swift
|
||||
airports["LHR"] = "London"
|
||||
// airports 字典现在有三个数据项
|
||||
```
|
||||
|
||||
我们也可以使用下标语法来改变特定键对应的值:
|
||||
|
||||
```swift
|
||||
airports["LHR"] = "London Heathrow"
|
||||
// "LHR"对应的值 被改为 "London Heathrow
|
||||
```
|
||||
|
||||
作为另一种下标方法,字典的`updateValue(forKey:)`方法可以设置或者更新特定键对应的值。就像上面所示的示例,`updateValue(forKey:)`方法在这个键不存在对应值的时候设置值或者在存在时更新已存在的值。和上面的下标方法不一样,这个方法返回更新值之前的原值。这样方便我们检查更新是否成功。
|
||||
|
||||
`updateValue(forKey:)`函数会返回包含一个字典值类型的可选值。举例来说:对于存储`String`值的字典,这个函数会返回一个`String?`或者“可选 `String`”类型的值。如果值存在,则这个可选值值等于被替换的值,否则将会是`nil`。
|
||||
|
||||
```swift
|
||||
if let oldValue = airports.updateValue("Dublin Internation", forKey: "DUB") {
|
||||
println("The old value for DUB was \(oldValue).")
|
||||
}
|
||||
// 输出 "The old value for DUB was Dublin."(DUB原值是dublin)
|
||||
```
|
||||
|
||||
我们也可以使用下标语法来在字典中检索特定键对应的值。由于使用一个没有值的键这种情况是有可能发生的,可选类型返回这个键存在的相关值,否则就返回`nil`:
|
||||
|
||||
```swift
|
||||
if let airportName = airports["DUB"] {
|
||||
println("The name of the airport is \(airportName).")
|
||||
} else {
|
||||
println("That airport is not in the airports dictionary.")
|
||||
}
|
||||
// 打印 "The name of the airport is Dublin Internation."(机场的名字是都柏林国际)
|
||||
```
|
||||
|
||||
我们还可以使用下标语法来通过给某个键的对应值赋值为`nil`来从字典里移除一个键值对:
|
||||
|
||||
```swift
|
||||
airports["APL"] = "Apple Internation"
|
||||
// "Apple Internation"不是真的 APL机场, 删除它
|
||||
airports["APL"] = nil
|
||||
// APL现在被移除了
|
||||
```
|
||||
|
||||
另外,`removeValueForKey`方法也可以用来在字典中移除键值对。这个方法在键值对存在的情况下会移除该键值对并且返回被移除的value或者在没有值的情况下返回`nil`:
|
||||
|
||||
```swift
|
||||
if let removedValue = airports.removeValueForKey("DUB") {
|
||||
println("The removed airport's name is \(removedValue).")
|
||||
} else {
|
||||
println("The airports dictionary does not contain a value for DUB.")
|
||||
}
|
||||
// prints "The removed airport's name is Dublin International."
|
||||
```
|
||||
|
||||
<a name="iterating_over_a_dictionary"></a>
|
||||
### 字典遍历
|
||||
|
||||
我们可以使用`for-in`循环来遍历某个字典中的键值对。每一个字典中的数据项都由`(key, value)`元组形式返回,并且我们可以使用临时常量或者变量来分解这些元组:
|
||||
|
||||
```swift
|
||||
for (airportCode, airportName) in airports {
|
||||
println("\(airportCode): \(airportName)")
|
||||
}
|
||||
// TYO: Tokyo
|
||||
// LHR: London Heathrow
|
||||
```
|
||||
|
||||
`for-in`循环请参见[For 循环](05_Control_Flow.html#for_loops)。
|
||||
|
||||
我们也可以通过访问它的`keys`或者`values`属性(都是可遍历集合)检索一个字典的键或者值:
|
||||
|
||||
```swift
|
||||
for airportCode in airports.keys {
|
||||
println("Airport code: \(airportCode)")
|
||||
}
|
||||
// Airport code: TYO
|
||||
// Airport code: LHR
|
||||
|
||||
for airportName in airports.values {
|
||||
println("Airport name: \(airportName)")
|
||||
}
|
||||
// Airport name: Tokyo
|
||||
// Airport name: London Heathrow
|
||||
```
|
||||
|
||||
如果我们只是需要使用某个字典的键集合或者值集合来作为某个接受`Array`实例 API 的参数,可以直接使用`keys`或者`values`属性直接构造一个新数组:
|
||||
|
||||
```swift
|
||||
let airportCodes = Array(airports.keys)
|
||||
// airportCodes is ["TYO", "LHR"]
|
||||
|
||||
let airportNames = Array(airports.values)
|
||||
// airportNames is ["Tokyo", "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 数组在大小被固定的时候依然可以做的很棒。
|
||||
|
||||
Swift 数组的可变性行为同时影响了数组实例如何被分配和修改,想获取更多信息,请参见[集合在赋值和复制中的行为](09_Classes_and_Structures.html#assignment_and_copy_behavior_for_collection_types)。
|
||||
|
||||
> 注意:
|
||||
> 在我们不需要改变数组大小的时候创建不可变数组是很好的习惯。如此 Swift 编译器可以优化我们创建的集合。
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
||||
> 翻译:honghaoz
|
||||
> 校对:LunaticM
|
||||
> 翻译:[honghaoz](https://github.com/honghaoz)
|
||||
> 校对:[LunaticM](https://github.com/LunaticM)
|
||||
|
||||
# 函数(Functions)
|
||||
-----------------
|
||||
@ -12,9 +12,9 @@
|
||||
- [函数类型(Function Types)](#Function_Types)
|
||||
- [函数嵌套(Nested Functions)](#Nested_Functions)
|
||||
|
||||
函数是用来完成特定任务的独立的代码块。你给一个函数起一个合适的名字,用来标示函数做什么,并且当函数需要执行的时候,这个名字会被“调用”。
|
||||
函数是用来完成特定任务的独立的代码块。你给一个函数起一个合适的名字,用来标识函数做什么,并且当函数需要执行的时候,这个名字会被“调用”。
|
||||
|
||||
Swift 统一的函数语法足够灵活,可以用来表示任何函数,包括从最简单的没有参数名字的 C 风格函数,到复杂的带局部和外部参数名的 Objective-C 风格函数。参数可以提供默认值,以简化函数调用。参数也可以即当做传入参数,也当做传出参数,也就是说,一旦函数执行结束,传入的参数值可以被修改。
|
||||
Swift 统一的函数语法足够灵活,可以用来表示任何函数,包括从最简单的没有参数名字的 C 风格函数,到复杂的带局部和外部参数名的 Objective-C 风格函数。参数可以提供默认值,以简化函数调用。参数也可以既当做传入参数,也当做传出参数,也就是说,一旦函数执行结束,传入的参数值可以被修改。
|
||||
|
||||
在 Swift 中,每个函数都有一种类型,包括函数的参数值类型和返回值类型。你可以把函数类型当做任何其他普通变量类型一样处理,这样就可以更简单地把函数当做别的函数的参数,也可以从其他函数中返回函数。函数的定义可以写在在其他函数定义中,这样可以在嵌套函数范围内实现功能封装。
|
||||
|
||||
|
||||
@ -1,375 +1,375 @@
|
||||
> 翻译:wh1100717
|
||||
> 校对:lyuka
|
||||
|
||||
# 闭包(Closures)
|
||||
-----------------
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [闭包表达式(Closure Expressions)](#closure_expressions)
|
||||
- [尾随闭包(Trailing Closures)](#trailing_closures)
|
||||
- [值捕获(Capturing Values)](#capturing_values)
|
||||
- [闭包是引用类型(Closures Are Reference Types)](#closures_are_reference_types)
|
||||
|
||||
闭包是自包含的函数代码块,可以在代码中被传递和使用。
|
||||
Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的 lambdas 函数比较相似。
|
||||
|
||||
闭包可以捕获和存储其所在上下文中任意常量和变量的引用。
|
||||
这就是所谓的闭合并包裹着这些常量和变量,俗称闭包。Swift 会为您管理在捕获过程中涉及到的所有内存操作。
|
||||
|
||||
> 注意:
|
||||
> 如果您不熟悉捕获(capturing)这个概念也不用担心,您可以在 [值捕获](#capturing_values) 章节对其进行详细了解。
|
||||
|
||||
在[函数](../chapter2/06_Functions.html) 章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之一:
|
||||
|
||||
* 全局函数是一个有名字但不会捕获任何值的闭包
|
||||
* 嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包
|
||||
* 闭包表达式是一个利用轻量级语法所写的可以捕获其上下文中变量或常量值的匿名闭包
|
||||
|
||||
Swift 的闭包表达式拥有简洁的风格,并鼓励在常见场景中进行语法优化,主要优化如下:
|
||||
|
||||
* 利用上下文推断参数和返回值类型
|
||||
* 隐式返回单表达式闭包,即单表达式闭包可以省略`return`关键字
|
||||
* 参数名称缩写
|
||||
* 尾随(Trailing)闭包语法
|
||||
|
||||
<a name="closure_expressions"></a>
|
||||
## 闭包表达式(Closure Expressions)
|
||||
|
||||
|
||||
[嵌套函数](../chapter2/06_Functions.html#nested_function) 是一个在较复杂函数中方便进行命名和定义自包含代码模块的方式。当然,有时候撰写小巧的没有完整定义和命名的类函数结构也是很有用处的,尤其是在您处理一些函数并需要将另外一些函数作为该函数的参数时。
|
||||
|
||||
闭包表达式是一种利用简洁语法构建内联闭包的方式。
|
||||
闭包表达式提供了一些语法优化,使得撰写闭包变得简单明了。
|
||||
下面闭包表达式的例子通过使用几次迭代展示了`sort`函数定义和语法优化的方式。
|
||||
每一次迭代都用更简洁的方式描述了相同的功能。
|
||||
|
||||
<a name="the_sort_function"></a>
|
||||
### sort 函数(The Sort Function)
|
||||
|
||||
Swift 标准库提供了`sort`函数,会根据您提供的基于输出类型排序的闭包函数将已知类型数组中的值进行排序。
|
||||
一旦排序完成,函数会返回一个与原数组大小相同的新数组,该数组中包含已经正确排序的同类型元素。
|
||||
|
||||
下面的闭包表达式示例使用`sort`函数对一个`String`类型的数组进行字母逆序排序,以下是初始数组值:
|
||||
|
||||
```swift
|
||||
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
|
||||
```
|
||||
|
||||
`sort`函数需要传入两个参数:
|
||||
|
||||
* 已知类型的数组
|
||||
* 闭包函数,该闭包函数需要传入与数组类型相同的两个值,并返回一个布尔类型值来告诉`sort`函数当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值前面,排序闭包函数需要返回`true`,反之返回`false`。
|
||||
|
||||
该例子对一个`String`类型的数组进行排序,因此排序闭包函数类型需为`(String, String) -> Bool`。
|
||||
|
||||
提供排序闭包函数的一种方式是撰写一个符合其类型要求的普通函数,并将其作为`sort`函数的第二个参数传入:
|
||||
|
||||
```swift
|
||||
func backwards(s1: String, s2: String) -> Bool {
|
||||
return s1 > s2
|
||||
}
|
||||
var reversed = sort(names, backwards)
|
||||
// reversed 为 ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
|
||||
```
|
||||
|
||||
如果第一个字符串 (`s1`) 大于第二个字符串 (`s2`),`backwards`函数返回`true`,表示在新的数组中`s1`应该出现在`s2`前。
|
||||
对于字符串中的字符来说,“大于” 表示 “按照字母顺序较晚出现”。
|
||||
这意味着字母`"B"`大于字母`"A"`,字符串`"Tom"`大于字符串`"Tim"`。
|
||||
其将进行字母逆序排序,`"Barry"`将会排在`"Alex"`之后。
|
||||
|
||||
然而,这是一个相当冗长的方式,本质上只是写了一个单表达式函数 (a > b)。
|
||||
在下面的例子中,利用闭合表达式语法可以更好的构造一个内联排序闭包。
|
||||
|
||||
<a name="closure_expression_syntax"></a>
|
||||
### 闭包表达式语法(Closure Expression Syntax)
|
||||
|
||||
闭包表达式语法有如下一般形式:
|
||||
|
||||
```swift
|
||||
{ (parameters) -> returnType in
|
||||
statements
|
||||
}
|
||||
```
|
||||
|
||||
闭包表达式语法可以使用常量、变量和`inout`类型作为参数,不提供默认值。
|
||||
也可以在参数列表的最后使用可变参数。
|
||||
元组也可以作为参数和返回值。
|
||||
|
||||
下面的例子展示了之前`backwards`函数对应的闭包表达式版本的代码:
|
||||
|
||||
```swift
|
||||
reversed = sort(names, { (s1: String, s2: String) -> Bool in
|
||||
return s1 > s2
|
||||
})
|
||||
```
|
||||
|
||||
需要注意的是内联闭包参数和返回值类型声明与`backwards`函数类型声明相同。
|
||||
在这两种方式中,都写成了`(s1: String, s2: String) -> Bool`。
|
||||
然而在内联闭包表达式中,函数和返回值类型都写在大括号内,而不是大括号外。
|
||||
|
||||
闭包的函数体部分由关键字`in`引入。
|
||||
该关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始。
|
||||
|
||||
因为这个闭包的函数体部分如此短以至于可以将其改写成一行代码:
|
||||
|
||||
```swift
|
||||
reversed = sort(names, { (s1: String, s2: String) -> Bool in return s1 > s2 } )
|
||||
```
|
||||
|
||||
这说明`sort`函数的整体调用保持不变,一对圆括号仍然包裹住了函数中整个参数集合。而其中一个参数现在变成了内联闭包(相比于`backwards`版本的代码)。
|
||||
|
||||
<a name="inferring_type_from_context"></a>
|
||||
### 根据上下文推断类型(Inferring Type From Context)
|
||||
|
||||
因为排序闭包函数是作为`sort`函数的参数进行传入的,Swift可以推断其参数和返回值的类型。
|
||||
`sort`期望第二个参数是类型为`(String, String) -> Bool`的函数,因此实际上`String`,`String`和`Bool`类型并不需要作为闭包表达式定义中的一部分。
|
||||
因为所有的类型都可以被正确推断,返回箭头 (`->`) 和围绕在参数周围的括号也可以被省略:
|
||||
|
||||
```swift
|
||||
reversed = sort(names, { s1, s2 in return s1 > s2 } )
|
||||
```
|
||||
|
||||
实际上任何情况下,通过内联闭包表达式构造的闭包作为参数传递给函数时,都可以推断出闭包的参数和返回值类型,这意味着您几乎不需要利用完整格式构造任何内联闭包。
|
||||
|
||||
<a name="implicit_returns_from_single_expression_closures"></a>
|
||||
### 单表达式闭包隐式返回(Implicit Return From Single-Expression Clossures)
|
||||
|
||||
单行表达式闭包可以通过隐藏`return`关键字来隐式返回单行表达式的结果,如上版本的例子可以改写为:
|
||||
|
||||
```swift
|
||||
reversed = sort(names, { s1, s2 in s1 > s2 } )
|
||||
```
|
||||
|
||||
在这个例子中,`sort`函数的第二个参数函数类型明确了闭包必须返回一个`Bool`类型值。
|
||||
因为闭包函数体只包含了一个单一表达式 (`s1 > s2`),该表达式返回`Bool`类型值,因此这里没有歧义,`return`关键字可以省略。
|
||||
|
||||
<a name="shorthand_argument_names"></a>
|
||||
### 参数名称缩写(Shorthand Argument Names)
|
||||
|
||||
Swift 自动为内联函数提供了参数名称缩写功能,您可以直接通过`$0`,`$1`,`$2`来顺序调用闭包的参数。
|
||||
|
||||
如果您在闭包表达式中使用参数名称缩写,您可以在闭包参数列表中省略对其的定义,并且对应参数名称缩写的类型会通过函数类型进行推断。
|
||||
`in`关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成:
|
||||
|
||||
```swift
|
||||
reversed = sort(names, { $0 > $1 } )
|
||||
```
|
||||
|
||||
在这个例子中,`$0`和`$1`表示闭包中第一个和第二个`String`类型的参数。
|
||||
|
||||
<a name="operator_functions"></a>
|
||||
### 运算符函数(Operator Functions)
|
||||
|
||||
实际上还有一种更简短的方式来撰写上面例子中的闭包表达式。
|
||||
Swift 的`String`类型定义了关于大于号 (`>`) 的字符串实现,其作为一个函数接受两个`String`类型的参数并返回`Bool`类型的值。
|
||||
而这正好与`sort`函数的第二个参数需要的函数类型相符合。
|
||||
因此,您可以简单地传递一个大于号,Swift可以自动推断出您想使用大于号的字符串函数实现:
|
||||
|
||||
```swift
|
||||
reversed = sort(names, >)
|
||||
```
|
||||
|
||||
更多关于运算符表达式的内容请查看 [运算符函数](../chapter2/23_Advanced_Operators.html#operator_functions)。
|
||||
|
||||
<a name="trailing_closures"></a>
|
||||
## 尾随闭包(Trailing Closures)
|
||||
|
||||
|
||||
如果您需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用尾随闭包来增强函数的可读性。
|
||||
尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。
|
||||
|
||||
```swift
|
||||
func someFunctionThatTakesAClosure(closure: () -> ()) {
|
||||
// 函数体部分
|
||||
}
|
||||
|
||||
// 以下是不使用尾随闭包进行函数调用
|
||||
someFunctionThatTakesAClosure({
|
||||
// 闭包主体部分
|
||||
})
|
||||
|
||||
// 以下是使用尾随闭包进行函数调用
|
||||
someFunctionThatTakesAClosure() {
|
||||
// 闭包主体部分
|
||||
}
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> 如果函数只需要闭包表达式一个参数,当您使用尾随闭包时,您甚至可以把`()`省略掉。
|
||||
|
||||
在上例中作为`sort`函数参数的字符串排序闭包可以改写为:
|
||||
|
||||
```swift
|
||||
reversed = sort(names) { $0 > $1 }
|
||||
```
|
||||
|
||||
当闭包非常长以至于不能在一行中进行书写时,尾随闭包变得非常有用。
|
||||
举例来说,Swift 的`Array`类型有一个`map`方法,其获取一个闭包表达式作为其唯一参数。
|
||||
数组中的每一个元素调用一次该闭包函数,并返回该元素所映射的值(也可以是不同类型的值)。
|
||||
具体的映射方式和返回值类型由闭包来指定。
|
||||
|
||||
当提供给数组闭包函数后,`map`方法将返回一个新的数组,数组中包含了与原数组一一对应的映射后的值。
|
||||
|
||||
下例介绍了如何在`map`方法中使用尾随闭包将`Int`类型数组`[16,58,510]`转换为包含对应`String`类型的数组`["OneSix", "FiveEight", "FiveOneZero"]`:
|
||||
|
||||
```swift
|
||||
let digitNames = [
|
||||
0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four",
|
||||
5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
|
||||
]
|
||||
let numbers = [16, 58, 510]
|
||||
```
|
||||
|
||||
如上代码创建了一个数字位和它们名字映射的英文版本字典。
|
||||
同时定义了一个准备转换为字符串的整型数组。
|
||||
|
||||
您现在可以通过传递一个尾随闭包给`numbers`的`map`方法来创建对应的字符串版本数组。
|
||||
需要注意的时调用`numbers.map`不需要在`map`后面包含任何括号,因为其只需要传递闭包表达式这一个参数,并且该闭包表达式参数通过尾随方式进行撰写:
|
||||
|
||||
```swift
|
||||
let strings = numbers.map {
|
||||
(var number) -> String in
|
||||
var output = ""
|
||||
while number > 0 {
|
||||
output = digitNames[number % 10]! + output
|
||||
number /= 10
|
||||
}
|
||||
return output
|
||||
}
|
||||
// strings 常量被推断为字符串类型数组,即 String[]
|
||||
// 其值为 ["OneSix", "FiveEight", "FiveOneZero"]
|
||||
```
|
||||
|
||||
`map`在数组中为每一个元素调用了闭包表达式。
|
||||
您不需要指定闭包的输入参数`number`的类型,因为可以通过要映射的数组类型进行推断。
|
||||
|
||||
闭包`number`参数被声明为一个变量参数(变量的具体描述请参看[常量参数和变量参数](../chapter2/06_Functions.html#constant_and_variable_parameters)),因此可以在闭包函数体内对其进行修改。闭包表达式制定了返回类型为`String`,以表明存储映射值的新数组类型为`String`。
|
||||
|
||||
闭包表达式在每次被调用的时候创建了一个字符串并返回。
|
||||
其使用求余运算符 (number % 10) 计算最后一位数字并利用`digitNames`字典获取所映射的字符串。
|
||||
|
||||
> 注意:
|
||||
> 字典`digitNames`下标后跟着一个叹号 (!),因为字典下标返回一个可选值 (optional value),表明即使该 key 不存在也不会查找失败。
|
||||
> 在上例中,它保证了`number % 10`可以总是作为一个`digitNames`字典的有效下标 key。
|
||||
> 因此叹号可以用于强制解析 (force-unwrap) 存储在可选下标项中的`String`类型值。
|
||||
|
||||
从`digitNames`字典中获取的字符串被添加到输出的前部,逆序建立了一个字符串版本的数字。
|
||||
(在表达式`number % 10`中,如果number为16,则返回6,58返回8,510返回0)。
|
||||
|
||||
`number`变量之后除以10。
|
||||
因为其是整数,在计算过程中未除尽部分被忽略。
|
||||
因此 16变成了1,58变成了5,510变成了51。
|
||||
|
||||
整个过程重复进行,直到`number /= 10`为0,这时闭包会将字符串输出,而`map`函数则会将字符串添加到所映射的数组中。
|
||||
|
||||
上例中尾随闭包语法在函数后整洁封装了具体的闭包功能,而不再需要将整个闭包包裹在`map`函数的括号内。
|
||||
|
||||
<a name="capturing_values"></a>
|
||||
## 捕获值(Capturing Values)
|
||||
|
||||
|
||||
闭包可以在其定义的上下文中捕获常量或变量。
|
||||
即使定义这些常量和变量的原域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。
|
||||
|
||||
Swift最简单的闭包形式是嵌套函数,也就是定义在其他函数的函数体内的函数。
|
||||
嵌套函数可以捕获其外部函数所有的参数以及定义的常量和变量。
|
||||
|
||||
下例为一个叫做`makeIncrementor`的函数,其包含了一个叫做`incrementor`嵌套函数。
|
||||
嵌套函数`incrementor`从上下文中捕获了两个值,`runningTotal`和`amount`。
|
||||
之后`makeIncrementor`将`incrementor`作为闭包返回。
|
||||
每次调用`incrementor`时,其会以`amount`作为增量增加`runningTotal`的值。
|
||||
|
||||
```swift
|
||||
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
|
||||
var runningTotal = 0
|
||||
func incrementor() -> Int {
|
||||
runningTotal += amount
|
||||
return runningTotal
|
||||
}
|
||||
return incrementor
|
||||
}
|
||||
```
|
||||
|
||||
`makeIncrementor`返回类型为`() -> Int`。
|
||||
这意味着其返回的是一个函数,而不是一个简单类型值。
|
||||
该函数在每次调用时不接受参数只返回一个`Int`类型的值。
|
||||
关于函数返回其他函数的内容,请查看[函数类型作为返回类型](../chapter2/06_Functions.html#function_types_as_return_types)。
|
||||
|
||||
`makeIncrementor`函数定义了一个整型变量`runningTotal`(初始为0) 用来存储当前跑步总数。
|
||||
该值通过`incrementor`返回。
|
||||
|
||||
`makeIncrementor`有一个`Int`类型的参数,其外部命名为`forIncrement`, 内部命名为`amount`,表示每次`incrementor`被调用时`runningTotal`将要增加的量。
|
||||
|
||||
`incrementor`函数用来执行实际的增加操作。
|
||||
该函数简单地使`runningTotal`增加`amount`,并将其返回。
|
||||
|
||||
如果我们单独看这个函数,会发现看上去不同寻常:
|
||||
|
||||
```swift
|
||||
func incrementor() -> Int {
|
||||
runningTotal += amount
|
||||
return runningTotal
|
||||
}
|
||||
```
|
||||
|
||||
`incrementor`函数并没有获取任何参数,但是在函数体内访问了`runningTotal`和`amount`变量。这是因为其通过捕获在包含它的函数体内已经存在的`runningTotal`和`amount`变量而实现。
|
||||
|
||||
由于没有修改`amount`变量,`incrementor`实际上捕获并存储了该变量的一个副本,而该副本随着`incrementor`一同被存储。
|
||||
|
||||
然而,因为每次调用该函数的时候都会修改`runningTotal`的值,`incrementor`捕获了当前`runningTotal`变量的引用,而不是仅仅复制该变量的初始值。捕获一个引用保证了当`makeIncrementor`结束时候并不会消失,也保证了当下一次执行`incrementor`函数时,`runningTotal`可以继续增加。
|
||||
|
||||
> 注意:
|
||||
> Swift 会决定捕获引用还是拷贝值。
|
||||
> 您不需要标注`amount`或者`runningTotal`来声明在嵌入的`incrementor`函数中的使用方式。
|
||||
> Swift 同时也处理`runingTotal`变量的内存管理操作,如果不再被`incrementor`函数使用,则会被清除。
|
||||
|
||||
下面代码为一个使用`makeIncrementor`的例子:
|
||||
|
||||
```swift
|
||||
let incrementByTen = makeIncrementor(forIncrement: 10)
|
||||
```
|
||||
|
||||
该例子定义了一个叫做`incrementByTen`的常量,该常量指向一个每次调用会加10的`incrementor`函数。
|
||||
调用这个函数多次可以得到以下结果:
|
||||
|
||||
```swift
|
||||
incrementByTen()
|
||||
// 返回的值为10
|
||||
incrementByTen()
|
||||
// 返回的值为20
|
||||
incrementByTen()
|
||||
// 返回的值为30
|
||||
```
|
||||
|
||||
如果您创建了另一个`incrementor`,其会有一个属于自己的独立的`runningTotal`变量的引用。
|
||||
下面的例子中,`incrementBySevne`捕获了一个新的`runningTotal`变量,该变量和`incrementByTen`中捕获的变量没有任何联系:
|
||||
|
||||
```swift
|
||||
let incrementBySeven = makeIncrementor(forIncrement: 7)
|
||||
incrementBySeven()
|
||||
// 返回的值为7
|
||||
incrementByTen()
|
||||
// 返回的值为40
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> 如果您将闭包赋值给一个类实例的属性,并且该闭包通过指向该实例或其成员来捕获了该实例,您将创建一个在闭包和实例间的强引用环。
|
||||
> Swift 使用捕获列表来打破这种强引用环。更多信息,请参考 [闭包引起的循环强引用](../chapter2/16_Automatic_Reference_Counting.html#strong_reference_cycles_for_closures)。
|
||||
|
||||
<a name="closures_are_reference_types"></a>
|
||||
## 闭包是引用类型(Closures Are Reference Types)
|
||||
|
||||
上面的例子中,`incrementBySeven`和`incrementByTen`是常量,但是这些常量指向的闭包仍然可以增加其捕获的变量值。
|
||||
这是因为函数和闭包都是引用类型。
|
||||
|
||||
无论您将函数/闭包赋值给一个常量还是变量,您实际上都是将常量/变量的值设置为对应函数/闭包的引用。
|
||||
上面的例子中,`incrementByTen`指向闭包的引用是一个常量,而并非闭包内容本身。
|
||||
|
||||
这也意味着如果您将闭包赋值给了两个不同的常量/变量,两个值都会指向同一个闭包:
|
||||
|
||||
```swift
|
||||
let alsoIncrementByTen = incrementByTen
|
||||
alsoIncrementByTen()
|
||||
// 返回的值为50
|
||||
```
|
||||
> 翻译:[wh1100717](https://github.com/wh1100717)
|
||||
> 校对:[lyuka](https://github.com/lyuka)
|
||||
|
||||
# 闭包(Closures)
|
||||
-----------------
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [闭包表达式(Closure Expressions)](#closure_expressions)
|
||||
- [尾随闭包(Trailing Closures)](#trailing_closures)
|
||||
- [值捕获(Capturing Values)](#capturing_values)
|
||||
- [闭包是引用类型(Closures Are Reference Types)](#closures_are_reference_types)
|
||||
|
||||
闭包是自包含的函数代码块,可以在代码中被传递和使用。
|
||||
Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的 lambdas 函数比较相似。
|
||||
|
||||
闭包可以捕获和存储其所在上下文中任意常量和变量的引用。
|
||||
这就是所谓的闭合并包裹着这些常量和变量,俗称闭包。Swift 会为您管理在捕获过程中涉及到的所有内存操作。
|
||||
|
||||
> 注意:
|
||||
> 如果您不熟悉捕获(capturing)这个概念也不用担心,您可以在 [值捕获](#capturing_values) 章节对其进行详细了解。
|
||||
|
||||
在[函数](../chapter2/06_Functions.html) 章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之一:
|
||||
|
||||
* 全局函数是一个有名字但不会捕获任何值的闭包
|
||||
* 嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包
|
||||
* 闭包表达式是一个利用轻量级语法所写的可以捕获其上下文中变量或常量值的匿名闭包
|
||||
|
||||
Swift 的闭包表达式拥有简洁的风格,并鼓励在常见场景中进行语法优化,主要优化如下:
|
||||
|
||||
* 利用上下文推断参数和返回值类型
|
||||
* 隐式返回单表达式闭包,即单表达式闭包可以省略`return`关键字
|
||||
* 参数名称缩写
|
||||
* 尾随(Trailing)闭包语法
|
||||
|
||||
<a name="closure_expressions"></a>
|
||||
## 闭包表达式(Closure Expressions)
|
||||
|
||||
|
||||
[嵌套函数](../chapter2/06_Functions.html#nested_function) 是一个在较复杂函数中方便进行命名和定义自包含代码模块的方式。当然,有时候撰写小巧的没有完整定义和命名的类函数结构也是很有用处的,尤其是在您处理一些函数并需要将另外一些函数作为该函数的参数时。
|
||||
|
||||
闭包表达式是一种利用简洁语法构建内联闭包的方式。
|
||||
闭包表达式提供了一些语法优化,使得撰写闭包变得简单明了。
|
||||
下面闭包表达式的例子通过使用几次迭代展示了`sort`函数定义和语法优化的方式。
|
||||
每一次迭代都用更简洁的方式描述了相同的功能。
|
||||
|
||||
<a name="the_sort_function"></a>
|
||||
### sort 函数(The Sort Function)
|
||||
|
||||
Swift 标准库提供了`sort`函数,会根据您提供的基于输出类型排序的闭包函数将已知类型数组中的值进行排序。
|
||||
一旦排序完成,函数会返回一个与原数组大小相同的新数组,该数组中包含已经正确排序的同类型元素。
|
||||
|
||||
下面的闭包表达式示例使用`sort`函数对一个`String`类型的数组进行字母逆序排序,以下是初始数组值:
|
||||
|
||||
```swift
|
||||
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
|
||||
```
|
||||
|
||||
`sort`函数需要传入两个参数:
|
||||
|
||||
* 已知类型的数组
|
||||
* 闭包函数,该闭包函数需要传入与数组类型相同的两个值,并返回一个布尔类型值来告诉`sort`函数当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值前面,排序闭包函数需要返回`true`,反之返回`false`。
|
||||
|
||||
该例子对一个`String`类型的数组进行排序,因此排序闭包函数类型需为`(String, String) -> Bool`。
|
||||
|
||||
提供排序闭包函数的一种方式是撰写一个符合其类型要求的普通函数,并将其作为`sort`函数的第二个参数传入:
|
||||
|
||||
```swift
|
||||
func backwards(s1: String, s2: String) -> Bool {
|
||||
return s1 > s2
|
||||
}
|
||||
var reversed = sort(names, backwards)
|
||||
// reversed 为 ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
|
||||
```
|
||||
|
||||
如果第一个字符串 (`s1`) 大于第二个字符串 (`s2`),`backwards`函数返回`true`,表示在新的数组中`s1`应该出现在`s2`前。
|
||||
对于字符串中的字符来说,“大于” 表示 “按照字母顺序较晚出现”。
|
||||
这意味着字母`"B"`大于字母`"A"`,字符串`"Tom"`大于字符串`"Tim"`。
|
||||
其将进行字母逆序排序,`"Barry"`将会排在`"Alex"`之后。
|
||||
|
||||
然而,这是一个相当冗长的方式,本质上只是写了一个单表达式函数 (a > b)。
|
||||
在下面的例子中,利用闭合表达式语法可以更好的构造一个内联排序闭包。
|
||||
|
||||
<a name="closure_expression_syntax"></a>
|
||||
### 闭包表达式语法(Closure Expression Syntax)
|
||||
|
||||
闭包表达式语法有如下一般形式:
|
||||
|
||||
```swift
|
||||
{ (parameters) -> returnType in
|
||||
statements
|
||||
}
|
||||
```
|
||||
|
||||
闭包表达式语法可以使用常量、变量和`inout`类型作为参数,不提供默认值。
|
||||
也可以在参数列表的最后使用可变参数。
|
||||
元组也可以作为参数和返回值。
|
||||
|
||||
下面的例子展示了之前`backwards`函数对应的闭包表达式版本的代码:
|
||||
|
||||
```swift
|
||||
reversed = sort(names, { (s1: String, s2: String) -> Bool in
|
||||
return s1 > s2
|
||||
})
|
||||
```
|
||||
|
||||
需要注意的是内联闭包参数和返回值类型声明与`backwards`函数类型声明相同。
|
||||
在这两种方式中,都写成了`(s1: String, s2: String) -> Bool`。
|
||||
然而在内联闭包表达式中,函数和返回值类型都写在大括号内,而不是大括号外。
|
||||
|
||||
闭包的函数体部分由关键字`in`引入。
|
||||
该关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始。
|
||||
|
||||
因为这个闭包的函数体部分如此短以至于可以将其改写成一行代码:
|
||||
|
||||
```swift
|
||||
reversed = sort(names, { (s1: String, s2: String) -> Bool in return s1 > s2 } )
|
||||
```
|
||||
|
||||
这说明`sort`函数的整体调用保持不变,一对圆括号仍然包裹住了函数中整个参数集合。而其中一个参数现在变成了内联闭包(相比于`backwards`版本的代码)。
|
||||
|
||||
<a name="inferring_type_from_context"></a>
|
||||
### 根据上下文推断类型(Inferring Type From Context)
|
||||
|
||||
因为排序闭包函数是作为`sort`函数的参数进行传入的,Swift可以推断其参数和返回值的类型。
|
||||
`sort`期望第二个参数是类型为`(String, String) -> Bool`的函数,因此实际上`String`,`String`和`Bool`类型并不需要作为闭包表达式定义中的一部分。
|
||||
因为所有的类型都可以被正确推断,返回箭头 (`->`) 和围绕在参数周围的括号也可以被省略:
|
||||
|
||||
```swift
|
||||
reversed = sort(names, { s1, s2 in return s1 > s2 } )
|
||||
```
|
||||
|
||||
实际上任何情况下,通过内联闭包表达式构造的闭包作为参数传递给函数时,都可以推断出闭包的参数和返回值类型,这意味着您几乎不需要利用完整格式构造任何内联闭包。
|
||||
|
||||
<a name="implicit_returns_from_single_expression_closures"></a>
|
||||
### 单表达式闭包隐式返回(Implicit Return From Single-Expression Clossures)
|
||||
|
||||
单行表达式闭包可以通过隐藏`return`关键字来隐式返回单行表达式的结果,如上版本的例子可以改写为:
|
||||
|
||||
```swift
|
||||
reversed = sort(names, { s1, s2 in s1 > s2 } )
|
||||
```
|
||||
|
||||
在这个例子中,`sort`函数的第二个参数函数类型明确了闭包必须返回一个`Bool`类型值。
|
||||
因为闭包函数体只包含了一个单一表达式 (`s1 > s2`),该表达式返回`Bool`类型值,因此这里没有歧义,`return`关键字可以省略。
|
||||
|
||||
<a name="shorthand_argument_names"></a>
|
||||
### 参数名称缩写(Shorthand Argument Names)
|
||||
|
||||
Swift 自动为内联函数提供了参数名称缩写功能,您可以直接通过`$0`,`$1`,`$2`来顺序调用闭包的参数。
|
||||
|
||||
如果您在闭包表达式中使用参数名称缩写,您可以在闭包参数列表中省略对其的定义,并且对应参数名称缩写的类型会通过函数类型进行推断。
|
||||
`in`关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成:
|
||||
|
||||
```swift
|
||||
reversed = sort(names, { $0 > $1 } )
|
||||
```
|
||||
|
||||
在这个例子中,`$0`和`$1`表示闭包中第一个和第二个`String`类型的参数。
|
||||
|
||||
<a name="operator_functions"></a>
|
||||
### 运算符函数(Operator Functions)
|
||||
|
||||
实际上还有一种更简短的方式来撰写上面例子中的闭包表达式。
|
||||
Swift 的`String`类型定义了关于大于号 (`>`) 的字符串实现,其作为一个函数接受两个`String`类型的参数并返回`Bool`类型的值。
|
||||
而这正好与`sort`函数的第二个参数需要的函数类型相符合。
|
||||
因此,您可以简单地传递一个大于号,Swift可以自动推断出您想使用大于号的字符串函数实现:
|
||||
|
||||
```swift
|
||||
reversed = sort(names, >)
|
||||
```
|
||||
|
||||
更多关于运算符表达式的内容请查看 [运算符函数](../chapter2/23_Advanced_Operators.html#operator_functions)。
|
||||
|
||||
<a name="trailing_closures"></a>
|
||||
## 尾随闭包(Trailing Closures)
|
||||
|
||||
|
||||
如果您需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用尾随闭包来增强函数的可读性。
|
||||
尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。
|
||||
|
||||
```swift
|
||||
func someFunctionThatTakesAClosure(closure: () -> ()) {
|
||||
// 函数体部分
|
||||
}
|
||||
|
||||
// 以下是不使用尾随闭包进行函数调用
|
||||
someFunctionThatTakesAClosure({
|
||||
// 闭包主体部分
|
||||
})
|
||||
|
||||
// 以下是使用尾随闭包进行函数调用
|
||||
someFunctionThatTakesAClosure() {
|
||||
// 闭包主体部分
|
||||
}
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> 如果函数只需要闭包表达式一个参数,当您使用尾随闭包时,您甚至可以把`()`省略掉。
|
||||
|
||||
在上例中作为`sort`函数参数的字符串排序闭包可以改写为:
|
||||
|
||||
```swift
|
||||
reversed = sort(names) { $0 > $1 }
|
||||
```
|
||||
|
||||
当闭包非常长以至于不能在一行中进行书写时,尾随闭包变得非常有用。
|
||||
举例来说,Swift 的`Array`类型有一个`map`方法,其获取一个闭包表达式作为其唯一参数。
|
||||
数组中的每一个元素调用一次该闭包函数,并返回该元素所映射的值(也可以是不同类型的值)。
|
||||
具体的映射方式和返回值类型由闭包来指定。
|
||||
|
||||
当提供给数组闭包函数后,`map`方法将返回一个新的数组,数组中包含了与原数组一一对应的映射后的值。
|
||||
|
||||
下例介绍了如何在`map`方法中使用尾随闭包将`Int`类型数组`[16,58,510]`转换为包含对应`String`类型的数组`["OneSix", "FiveEight", "FiveOneZero"]`:
|
||||
|
||||
```swift
|
||||
let digitNames = [
|
||||
0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four",
|
||||
5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
|
||||
]
|
||||
let numbers = [16, 58, 510]
|
||||
```
|
||||
|
||||
如上代码创建了一个数字位和它们名字映射的英文版本字典。
|
||||
同时定义了一个准备转换为字符串的整型数组。
|
||||
|
||||
您现在可以通过传递一个尾随闭包给`numbers`的`map`方法来创建对应的字符串版本数组。
|
||||
需要注意的时调用`numbers.map`不需要在`map`后面包含任何括号,因为其只需要传递闭包表达式这一个参数,并且该闭包表达式参数通过尾随方式进行撰写:
|
||||
|
||||
```swift
|
||||
let strings = numbers.map {
|
||||
(var number) -> String in
|
||||
var output = ""
|
||||
while number > 0 {
|
||||
output = digitNames[number % 10]! + output
|
||||
number /= 10
|
||||
}
|
||||
return output
|
||||
}
|
||||
// strings 常量被推断为字符串类型数组,即 String[]
|
||||
// 其值为 ["OneSix", "FiveEight", "FiveOneZero"]
|
||||
```
|
||||
|
||||
`map`在数组中为每一个元素调用了闭包表达式。
|
||||
您不需要指定闭包的输入参数`number`的类型,因为可以通过要映射的数组类型进行推断。
|
||||
|
||||
闭包`number`参数被声明为一个变量参数(变量的具体描述请参看[常量参数和变量参数](../chapter2/06_Functions.html#constant_and_variable_parameters)),因此可以在闭包函数体内对其进行修改。闭包表达式制定了返回类型为`String`,以表明存储映射值的新数组类型为`String`。
|
||||
|
||||
闭包表达式在每次被调用的时候创建了一个字符串并返回。
|
||||
其使用求余运算符 (number % 10) 计算最后一位数字并利用`digitNames`字典获取所映射的字符串。
|
||||
|
||||
> 注意:
|
||||
> 字典`digitNames`下标后跟着一个叹号 (!),因为字典下标返回一个可选值 (optional value),表明即使该 key 不存在也不会查找失败。
|
||||
> 在上例中,它保证了`number % 10`可以总是作为一个`digitNames`字典的有效下标 key。
|
||||
> 因此叹号可以用于强制解析 (force-unwrap) 存储在可选下标项中的`String`类型值。
|
||||
|
||||
从`digitNames`字典中获取的字符串被添加到输出的前部,逆序建立了一个字符串版本的数字。
|
||||
(在表达式`number % 10`中,如果number为16,则返回6,58返回8,510返回0)。
|
||||
|
||||
`number`变量之后除以10。
|
||||
因为其是整数,在计算过程中未除尽部分被忽略。
|
||||
因此 16变成了1,58变成了5,510变成了51。
|
||||
|
||||
整个过程重复进行,直到`number /= 10`为0,这时闭包会将字符串输出,而`map`函数则会将字符串添加到所映射的数组中。
|
||||
|
||||
上例中尾随闭包语法在函数后整洁封装了具体的闭包功能,而不再需要将整个闭包包裹在`map`函数的括号内。
|
||||
|
||||
<a name="capturing_values"></a>
|
||||
## 捕获值(Capturing Values)
|
||||
|
||||
|
||||
闭包可以在其定义的上下文中捕获常量或变量。
|
||||
即使定义这些常量和变量的原域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。
|
||||
|
||||
Swift最简单的闭包形式是嵌套函数,也就是定义在其他函数的函数体内的函数。
|
||||
嵌套函数可以捕获其外部函数所有的参数以及定义的常量和变量。
|
||||
|
||||
下例为一个叫做`makeIncrementor`的函数,其包含了一个叫做`incrementor`嵌套函数。
|
||||
嵌套函数`incrementor`从上下文中捕获了两个值,`runningTotal`和`amount`。
|
||||
之后`makeIncrementor`将`incrementor`作为闭包返回。
|
||||
每次调用`incrementor`时,其会以`amount`作为增量增加`runningTotal`的值。
|
||||
|
||||
```swift
|
||||
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
|
||||
var runningTotal = 0
|
||||
func incrementor() -> Int {
|
||||
runningTotal += amount
|
||||
return runningTotal
|
||||
}
|
||||
return incrementor
|
||||
}
|
||||
```
|
||||
|
||||
`makeIncrementor`返回类型为`() -> Int`。
|
||||
这意味着其返回的是一个函数,而不是一个简单类型值。
|
||||
该函数在每次调用时不接受参数只返回一个`Int`类型的值。
|
||||
关于函数返回其他函数的内容,请查看[函数类型作为返回类型](../chapter2/06_Functions.html#function_types_as_return_types)。
|
||||
|
||||
`makeIncrementor`函数定义了一个整型变量`runningTotal`(初始为0) 用来存储当前跑步总数。
|
||||
该值通过`incrementor`返回。
|
||||
|
||||
`makeIncrementor`有一个`Int`类型的参数,其外部命名为`forIncrement`, 内部命名为`amount`,表示每次`incrementor`被调用时`runningTotal`将要增加的量。
|
||||
|
||||
`incrementor`函数用来执行实际的增加操作。
|
||||
该函数简单地使`runningTotal`增加`amount`,并将其返回。
|
||||
|
||||
如果我们单独看这个函数,会发现看上去不同寻常:
|
||||
|
||||
```swift
|
||||
func incrementor() -> Int {
|
||||
runningTotal += amount
|
||||
return runningTotal
|
||||
}
|
||||
```
|
||||
|
||||
`incrementor`函数并没有获取任何参数,但是在函数体内访问了`runningTotal`和`amount`变量。这是因为其通过捕获在包含它的函数体内已经存在的`runningTotal`和`amount`变量而实现。
|
||||
|
||||
由于没有修改`amount`变量,`incrementor`实际上捕获并存储了该变量的一个副本,而该副本随着`incrementor`一同被存储。
|
||||
|
||||
然而,因为每次调用该函数的时候都会修改`runningTotal`的值,`incrementor`捕获了当前`runningTotal`变量的引用,而不是仅仅复制该变量的初始值。捕获一个引用保证了当`makeIncrementor`结束时候并不会消失,也保证了当下一次执行`incrementor`函数时,`runningTotal`可以继续增加。
|
||||
|
||||
> 注意:
|
||||
> Swift 会决定捕获引用还是拷贝值。
|
||||
> 您不需要标注`amount`或者`runningTotal`来声明在嵌入的`incrementor`函数中的使用方式。
|
||||
> Swift 同时也处理`runingTotal`变量的内存管理操作,如果不再被`incrementor`函数使用,则会被清除。
|
||||
|
||||
下面代码为一个使用`makeIncrementor`的例子:
|
||||
|
||||
```swift
|
||||
let incrementByTen = makeIncrementor(forIncrement: 10)
|
||||
```
|
||||
|
||||
该例子定义了一个叫做`incrementByTen`的常量,该常量指向一个每次调用会加10的`incrementor`函数。
|
||||
调用这个函数多次可以得到以下结果:
|
||||
|
||||
```swift
|
||||
incrementByTen()
|
||||
// 返回的值为10
|
||||
incrementByTen()
|
||||
// 返回的值为20
|
||||
incrementByTen()
|
||||
// 返回的值为30
|
||||
```
|
||||
|
||||
如果您创建了另一个`incrementor`,其会有一个属于自己的独立的`runningTotal`变量的引用。
|
||||
下面的例子中,`incrementBySevne`捕获了一个新的`runningTotal`变量,该变量和`incrementByTen`中捕获的变量没有任何联系:
|
||||
|
||||
```swift
|
||||
let incrementBySeven = makeIncrementor(forIncrement: 7)
|
||||
incrementBySeven()
|
||||
// 返回的值为7
|
||||
incrementByTen()
|
||||
// 返回的值为40
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> 如果您将闭包赋值给一个类实例的属性,并且该闭包通过指向该实例或其成员来捕获了该实例,您将创建一个在闭包和实例间的强引用环。
|
||||
> Swift 使用捕获列表来打破这种强引用环。更多信息,请参考 [闭包引起的循环强引用](../chapter2/16_Automatic_Reference_Counting.html#strong_reference_cycles_for_closures)。
|
||||
|
||||
<a name="closures_are_reference_types"></a>
|
||||
## 闭包是引用类型(Closures Are Reference Types)
|
||||
|
||||
上面的例子中,`incrementBySeven`和`incrementByTen`是常量,但是这些常量指向的闭包仍然可以增加其捕获的变量值。
|
||||
这是因为函数和闭包都是引用类型。
|
||||
|
||||
无论您将函数/闭包赋值给一个常量还是变量,您实际上都是将常量/变量的值设置为对应函数/闭包的引用。
|
||||
上面的例子中,`incrementByTen`指向闭包的引用是一个常量,而并非闭包内容本身。
|
||||
|
||||
这也意味着如果您将闭包赋值给了两个不同的常量/变量,两个值都会指向同一个闭包:
|
||||
|
||||
```swift
|
||||
let alsoIncrementByTen = incrementByTen
|
||||
alsoIncrementByTen()
|
||||
// 返回的值为50
|
||||
```
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
> 翻译:yankuangshi
|
||||
> 校对:shinyzhu
|
||||
> 翻译:[yankuangshi](https://github.com/yankuangshi)
|
||||
> 校对:[shinyzhu](https://github.com/shinyzhu)
|
||||
|
||||
# 枚举(Enumerations)
|
||||
---
|
||||
@ -8,14 +8,14 @@
|
||||
|
||||
- [枚举语法(Enumeration Syntax)](#enumeration_syntax)
|
||||
- [匹配枚举值与`Swith`语句(Matching Enumeration Values with a Switch Statement)](#matching_enumeration_values_with_a_switch_statement)
|
||||
- [实例值(Associated Values)](#associated_values)
|
||||
- [相关值(Associated Values)](#associated_values)
|
||||
- [原始值(Raw Values)](#raw_values)
|
||||
|
||||
枚举定义了一个通用类型的一组相关的值,使你可以在你的代码中以一个安全的方式来使用这些值。
|
||||
|
||||
如果你熟悉 C 语言,你就会知道,在 C 语言中枚举指定相关名称为一组整型值。Swift 中的枚举更加灵活,不必给每一个枚举成员提供一个值。如果一个值(被认为是“原始”值)被提供给每个枚举成员,则该值可以是一个字符串,一个字符,或是一个整型值或浮点值。
|
||||
|
||||
此外,枚举成员可以指定任何类型的实例值存储到枚举成员值中,就像其他语言中的联合体(unions)和变体(variants)。你可以定义一组通用的相关成员作为枚举的一部分,每一组都有不同的一组与它相关的适当类型的数值。
|
||||
此外,枚举成员可以指定任何类型的相关值存储到枚举成员值中,就像其他语言中的联合体(unions)和变体(variants)。你可以定义一组通用的相关成员作为枚举的一部分,每一组都有不同的一组与它相关的适当类型的数值。
|
||||
|
||||
在 Swift 中,枚举类型是一等(first-class)类型。它们采用了很多传统上只被类(class)所支持的特征,例如计算型属性(computed properties),用于提供关于枚举当前值的附加信息, 实例方法(instance methods),用于提供和枚举所代表的值相关联的功能。枚举也可以定义构造函数(initializers)来提供一个初始成员值;可以在原始的实现基础上扩展它们的功能;可以遵守协议(protocols)来提供标准的功能。
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
使用`enum`关键词并且把它们的整个定义放在一对大括号内:
|
||||
|
||||
```swift
|
||||
enum SomeEumeration {
|
||||
enum SomeEnumeration {
|
||||
// enumeration definition goes here
|
||||
}
|
||||
```
|
||||
@ -112,11 +112,11 @@ default:
|
||||
```
|
||||
|
||||
<a name="associated_values"></a>
|
||||
## 实例值(Associated Values)
|
||||
## 相关值(Associated Values)
|
||||
|
||||
上一小节的例子演示了一个枚举的成员是如何被定义(分类)的。你可以为`Planet.Earth`设置一个常量或则变量,并且在之后查看这个值。然而,有时候会很有用如果能够把其他类型的实例值和成员值一起存储起来。这能让你随着成员值存储额外的自定义信息,并且当每次你在代码中利用该成员时允许这个信息产生变化。
|
||||
上一小节的例子演示了一个枚举的成员是如何被定义(分类)的。你可以为`Planet.Earth`设置一个常量或则变量,并且在之后查看这个值。然而,有时候会很有用如果能够把其他类型的相关值和成员值一起存储起来。这能让你存储成员值之外的自定义信息,并且当你每次在代码中使用该成员时允许这个信息产生变化。
|
||||
|
||||
你可以定义 Swift 的枚举存储任何类型的实例值,如果需要的话,每个成员的数据类型可以是各不相同的。枚举的这种特性跟其他语言中的可辨识联合(discriminated unions),标签联合(tagged unions),或者变体(variants)相似。
|
||||
你可以定义 Swift 的枚举存储任何类型的相关值,如果需要的话,每个成员的数据类型可以是各不相同的。枚举的这种特性跟其他语言中的可辨识联合(discriminated unions),标签联合(tagged unions),或者变体(variants)相似。
|
||||
|
||||
例如,假设一个库存跟踪系统需要利用两种不同类型的条形码来跟踪商品。有些商品上标有 UPC-A 格式的一维码,它使用数字 0 到 9。每一个条形码都有一个代表“数字系统”的数字,该数字后接 10 个代表“标识符”的数字。最后一个数字是“检查”位,用来验证代码是否被正确扫描:
|
||||
|
||||
@ -139,9 +139,9 @@ enum Barcode {
|
||||
|
||||
以上代码可以这么理解:
|
||||
|
||||
“定义一个名为`Barcode`的枚举类型,它可以是`UPCA`的一个实例值(`Int`,`Int`,`Int`),或者`QRCode`的一个字符串类型(`String`)实例值。”
|
||||
“定义一个名为`Barcode`的枚举类型,它可以是`UPCA`的一个相关值(`Int`,`Int`,`Int`),或者`QRCode`的一个字符串类型(`String`)相关值。”
|
||||
|
||||
这个定义不提供任何`Int`或`String`的实际值,它只是定义了,当`Barcode`常量和变量等于`Barcode.UPCA`或`Barcode.QRCode`时,实例值的类型。
|
||||
这个定义不提供任何`Int`或`String`的实际值,它只是定义了,当`Barcode`常量和变量等于`Barcode.UPCA`或`Barcode.QRCode`时,相关值的类型。
|
||||
|
||||
然后可以使用任何一种条码类型创建新的条码,如:
|
||||
|
||||
@ -149,7 +149,7 @@ enum Barcode {
|
||||
var productBarcode = Barcode.UPCA(8, 85909_51226, 3)
|
||||
```
|
||||
|
||||
以上例子创建了一个名为`productBarcode`的新变量,并且赋给它一个`Barcode.UPCA`的实例元组值`(8, 8590951226, 3)`。提供的“标识符”值在整数字中有一个下划线,使其便于阅读条形码。
|
||||
以上例子创建了一个名为`productBarcode`的新变量,并且赋给它一个`Barcode.UPCA`的相关元组值`(8, 8590951226, 3)`。提供的“标识符”值在整数字中有一个下划线,使其便于阅读条形码。
|
||||
|
||||
同一个商品可以被分配给一个不同类型的条形码,如:
|
||||
|
||||
@ -157,9 +157,9 @@ var productBarcode = Barcode.UPCA(8, 85909_51226, 3)
|
||||
productBarcode = .QRCode("ABCDEFGHIJKLMNOP")
|
||||
```
|
||||
|
||||
这时,原始的`Barcode.UPCA`和其整数值被新的`Barcode.QRCode`和其字符串值所替代。条形码的常量和变量可以存储一个`.UPCA`或者一个`.QRCode`(连同它的实例值),但是在任何指定时间只能存储其中之一。
|
||||
这时,原始的`Barcode.UPCA`和其整数值被新的`Barcode.QRCode`和其字符串值所替代。条形码的常量和变量可以存储一个`.UPCA`或者一个`.QRCode`(连同它的相关值),但是在任何指定时间只能存储其中之一。
|
||||
|
||||
像以前那样,不同的条形码类型可以使用一个 switch 语句来检查,然而这次实例值可以被提取作为 switch 语句的一部分。你可以在`switch`的 case 分支代码中提取每个实例值作为一个常量(用`let`前缀)或者作为一个变量(用`var`前缀)来使用:
|
||||
像以前那样,不同的条形码类型可以使用一个 switch 语句来检查,然而这次相关值可以被提取作为 switch 语句的一部分。你可以在`switch`的 case 分支代码中提取每个相关值作为一个常量(用`let`前缀)或者作为一个变量(用`var`前缀)来使用:
|
||||
|
||||
```swift
|
||||
switch productBarcode {
|
||||
@ -171,7 +171,7 @@ case .QRCode(let productCode):
|
||||
// 输出 "QR code with value of ABCDEFGHIJKLMNOP.”
|
||||
```
|
||||
|
||||
如果一个枚举成员的所有实例值被提取为常量,或者它们全部被提取为变量,为了简洁,你可以只放置一个`var`或者`let`标注在成员名称前:
|
||||
如果一个枚举成员的所有相关值被提取为常量,或者它们全部被提取为变量,为了简洁,你可以只放置一个`var`或者`let`标注在成员名称前:
|
||||
|
||||
```swift
|
||||
switch productBarcode {
|
||||
@ -186,7 +186,7 @@ case let .QRCode(productCode):
|
||||
<a name="raw_values"></a>
|
||||
## 原始值(Raw Values)
|
||||
|
||||
在实例值小节的条形码例子中演示了一个枚举的成员如何声明它们存储不同类型的实例值。作为实例值的替代,枚举成员可以被默认值(称为原始值)预先填充,其中这些原始值具有相同的类型。
|
||||
在[Associated Values](#raw_values)小节的条形码例子中演示了一个枚举的成员如何声明它们存储不同类型的相关值。作为相关值的替代,枚举成员可以被默认值(称为原始值)预先填充,其中这些原始值具有相同的类型。
|
||||
|
||||
这里是一个枚举成员存储原始 ASCII 值的例子:
|
||||
|
||||
@ -198,9 +198,9 @@ enum ASCIIControlCharacter: Character {
|
||||
}
|
||||
```
|
||||
|
||||
在这里,称为`ASCIIControlCharacter`的枚举的原始值类型被定义为字符型`Character`,并被设置了一些比较常见的 ASCII 控制字符。字符值的描述请详见字符串和字符`Strings and Characters`部分。
|
||||
在这里,称为`ASCIIControlCharacter`的枚举的原始值类型被定义为字符型`Character`,并被设置了一些比较常见的 ASCII 控制字符。字符值的描述请详见字符串和字符[`Strings and Characters`](03_Strings_and_Characters.html)部分。
|
||||
|
||||
注意,原始值和实例值是不相同的。当你开始在你的代码中定义枚举的时候原始值是被预先填充的值,像上述三个 ASCII 码。对于一个特定的枚举成员,它的原始值始终是相同的。实例值是当你在创建一个基于枚举成员的新常量或变量时才会被设置,并且每次当你这么做得时候,它的值可以是不同的。
|
||||
注意,原始值和相关值是不相同的。当你开始在你的代码中定义枚举的时候原始值是被预先填充的值,像上述三个 ASCII 码。对于一个特定的枚举成员,它的原始值始终是相同的。相关值是当你在创建一个基于枚举成员的新常量或变量时才会被设置,并且每次当你这么做得时候,它的值可以是不同的。
|
||||
|
||||
原始值可以是字符串,字符,或者任何整型值或浮点型值。每个原始值在它的枚举声明中必须是唯一的。当整型值被用于原始值,如果其他枚举成员没有值时,它们会自动递增。
|
||||
|
||||
|
||||
@ -1,444 +1,443 @@
|
||||
> 翻译:JaySurplus
|
||||
> 校对:sg552
|
||||
|
||||
# 类和结构体
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [类和结构体对比](#comparing_classes_and_structures)
|
||||
- [结构体和枚举是值类型](#structures_and_enumerations_are_value_types)
|
||||
- [类是引用类型](#classes_are_reference_types)
|
||||
- [类和结构体的选择](#choosing_between_classes_and_structures)
|
||||
- [集合(collection)类型的赋值与复制行为](#assignment_and_copy_behavior_for_collection_types)
|
||||
|
||||
类和结构体是人们构建代码所用的一种通用且灵活的构造体。为了在类和结构体中实现各种功能,我们必须要严格按照对于常量,变量以及函数所规定的语法规则来定义属性和添加方法。
|
||||
|
||||
与其他编程语言所不同的是,Swift 并不要求你为自定义类和结构去创建独立的接口和实现文件。你所要做的是在一个单一文件中定义一个类或者结构体,系统将会自动生成面向其它代码的外部接口。
|
||||
|
||||
> 注意:
|
||||
通常一个`类`的实例被称为`对象`。然而在Swift 中,类和结构体的关系要比在其他语言中更加的密切,本章中所讨论的大部分功能都可以用在类和结构体上。因此,我们会主要使用`实例`而不是`对象`。
|
||||
|
||||
<a name="comparing_classes_and_structures"></a>
|
||||
###类和结构体对比
|
||||
Swift 中类和结构体有很多共同点。共同处在于:
|
||||
|
||||
<<<<<<< HEAD
|
||||
* 定义属性用于储存值
|
||||
* 定义方法用于提供功能
|
||||
* 定义附属脚本用于访问值
|
||||
* 定义构造器用于生成初始化值
|
||||
* 通过扩展以增加默认实现的功能
|
||||
* 符合协议以对某类提供标准功能
|
||||
=======
|
||||
* 定义属性用于储存值
|
||||
* 定义方法用于提供功能
|
||||
* 定义下标脚本用于访问值
|
||||
* 定义构造器用于生成初始化值
|
||||
* 通过扩展以增加默认实现的功能
|
||||
* 符合协议以对某类提供标准功能
|
||||
>>>>>>> a516af6a531a104ec88da0d236ecf389a5ec72af
|
||||
|
||||
更多信息请参见 [属性](10_Properties.html),[方法](11_Methods.html),[下标脚本](12_Subscripts.html),[初始过程](14_Initialization.html),[扩展](20_Extensions.html),和[协议](21_Protocols.html)。
|
||||
|
||||
与结构体相比,类还有如下的附加功能:
|
||||
|
||||
|
||||
* 继承允许一个类继承另一个类的特征
|
||||
* 类型转换允许在运行时检查和解释一个类实例的类型
|
||||
* 解构器允许一个类实例释放任何其所被分配的资源
|
||||
* 引用计数允许对一个类的多次引用
|
||||
|
||||
更多信息请参见[继承](http://),[类型转换](http://),[初始化](http://),和[自动引用计数](http://)。
|
||||
|
||||
> 注意:
|
||||
结构体总是通过被复制的方式在代码中传递,因此请不要使用引用计数。
|
||||
|
||||
### 定义
|
||||
类和结构体有着类似的定义方式。我们通过关键字`class`和`struct`来分别表示类和结构体,并在一对大括号中定义它们的具体内容:
|
||||
|
||||
```swift
|
||||
class SomeClass {
|
||||
// class definition goes here
|
||||
}
|
||||
struct SomeStructure {
|
||||
// structure definition goes here
|
||||
}
|
||||
```
|
||||
|
||||
> 注意:
|
||||
在你每次定义一个新类或者结构体的时候,实际上你是有效地定义了一个新的 Swift 类型。因此请使用 `UpperCamelCase` 这种方式来命名(如 `SomeClass` 和`SomeStructure`等),以便符合标准Swift 类型的大写命名风格(如`String`,`Int`和`Bool`)。相反的,请使用`lowerCamelCase`这种方式为属性和方法命名(如`framerate`和`incrementCount`),以便和类区分。
|
||||
|
||||
以下是定义结构体和定义类的示例:
|
||||
|
||||
```swift
|
||||
struct Resolution {
|
||||
var width = 0
|
||||
var heigth = 0
|
||||
}
|
||||
class VideoMode {
|
||||
var resolution = Resolution()
|
||||
var interlaced = false
|
||||
var frameRate = 0.0
|
||||
var name: String?
|
||||
}
|
||||
```
|
||||
|
||||
在上面的示例中我们定义了一个名为`Resolution`的结构体,用来描述一个显示器的像素分辨率。这个结构体包含了两个名为`width`和`height`的储存属性。储存属性是捆绑和储存在类或结构体中的常量或变量。当这两个属性被初始化为整数`0`的时候,它们会被推断为`Int`类型。
|
||||
|
||||
在上面的示例中我们还定义了一个名为`VideoMode`的类,用来描述一个视频显示器的特定模式。这个类包含了四个储存属性变量。第一个是`分辨率`,它被初始化为一个新的`Resolution`结构体的实例,具有`Resolution`的属性类型。新`VideoMode`实例同时还会初始化其它三个属性,它们分别是,初始值为`false`(意为“non-interlaced video”)的`inteflaced`,回放帧率初始值为`0.0`的`frameRate`和值为可选`String`的`name`。`name`属性会被自动赋予一个默认值`nil`,意为“没有`name`值”,因它是一个可选类型。
|
||||
|
||||
### 类和结构体实例
|
||||
`Resolution`结构体和`VideoMode`类的定义仅描述了什么是`Resolution`和`VideoMode`。它们并没有描述一个特定的分辨率(resolution)或者视频模式(video mode)。为了描述一个特定的分辨率或者视频模式,我们需要生成一个它们的实例。
|
||||
|
||||
生成结构体和类实例的语法非常相似:
|
||||
|
||||
```swift
|
||||
let someResolution = Resolution()
|
||||
let someVideoMode = VideoMode()
|
||||
```
|
||||
|
||||
结构体和类都使用构造器语法来生成新的实例。构造器语法的最简单形式是在结构体或者类的类型名称后跟随一个空括弧,如`Resolution()`或`VideoMode()`。通过这种方式所创建的类或者结构体实例,其属均会被初始化为默认值。[构造过程](14_Initialization.html)章节会对类和结构体的初始化进行更详细的讨论。
|
||||
|
||||
|
||||
### 属性访问
|
||||
通过使用*点语法*(*dot syntax*),你可以访问实例中所含有的属性。其语法规则是,实例名后面紧跟属性名,两者通过点号(.)连接:
|
||||
|
||||
```swift
|
||||
println("The width of someResolution is \(someResolution.width)")
|
||||
// 输出 "The width of someResolution is 0"
|
||||
```
|
||||
|
||||
在上面的例子中,`someResolution.width`引用`someResolution`的`width`属性,返回`width`的初始值`0`。
|
||||
|
||||
你也可以访问子属性,如何`VideoMode`中`Resolution`属性的`width`属性:
|
||||
|
||||
```swift
|
||||
println("The width of someVideoMode is \(someVideoMode.resolution.width)")
|
||||
// 输出 "The width of someVideoMode is 0"
|
||||
```
|
||||
|
||||
你也可以使用点语法为属性变量赋值:
|
||||
|
||||
```swift
|
||||
someVideoMode.resolution.width = 12880
|
||||
println("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)
|
||||
|
||||
所有结构体都有一个自动生成的成员逐一构造器,用于初始化新结构体实例中成员的属性。新实例中各个属性的初始值可以通过属性的名称传递到成员逐一构造器之中:
|
||||
|
||||
```swift
|
||||
let vga = resolution(width:640, heigth: 480)
|
||||
```
|
||||
|
||||
与结构体不同,类实例没有默认的成员逐一构造器。[构造过程](14_Initialization.html)章节会对构造器进行更详细的讨论。
|
||||
|
||||
<a name="structures_and_enumerations_are_value_types"></a>
|
||||
## 结构体和枚举是值类型
|
||||
|
||||
值类型被赋予给一个变量,常数或者本身被传递给一个函数的时候,实际上操作的是其的拷贝。
|
||||
|
||||
在之前的章节中,我们已经大量使用了值类型。实际上,在 Swift 中,所有的基本类型:整数(Integer)、浮点数(floating-point)、布尔值(Booleans)、字符串(string)、数组(array)和字典(dictionaries),都是值类型,并且都是以结构体的形式在后台所实现。
|
||||
|
||||
在 Swift 中,所有的结构体和枚举都是值类型。这意味着它们的实例,以及实例中所包含的任何值类型属性,在代码中传递的时候都会被复制。
|
||||
|
||||
请看下面这个示例,其使用了前一个示例中`Resolution`结构体:
|
||||
|
||||
```swift
|
||||
let hd = Resolution(width: 1920, height: 1080)
|
||||
var cinema = hd
|
||||
```
|
||||
|
||||
在以上示例中,声明了一个名为`hd`的常量,其值为一个初始化为全高清视频分辨率(1920 像素宽,1080 像素高)的`Resolution`实例。
|
||||
|
||||
然后示例中又声明了一个名为`cinema`的变量,其值为之前声明的`hd`。因为`Resolution`是一个结构体,所以`cinema`的值其实是`hd`的一个拷贝副本,而不是`hd`本身。尽管`hd`和`cinema`有着相同的宽(width)和高(height)属性,但是在后台中,它们是两个完全不同的实例。
|
||||
|
||||
下面,为了符合数码影院放映的需求(2048 像素宽,1080 像素高),`cinema`的`width`属性需要作如下修改:
|
||||
|
||||
```swift
|
||||
cinema.width = 2048
|
||||
```
|
||||
|
||||
这里,将会显示`cinema`的`width`属性确已改为了`2048`:
|
||||
|
||||
```swift
|
||||
println("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")
|
||||
// 输出 "hd is still 1920 pixels wide"
|
||||
```
|
||||
|
||||
在将`hd`赋予给`cinema`的时候,实际上是将`hd`中所储存的`值(values)`进行拷贝,然后将拷贝的数据储存到新的`cinema`实例中。结果就是两个完全独立的实例碰巧包含有相同的数值。由于两者相互独立,因此将`cinema`的`width`修改为`2048`并不会影响`hd`中的宽(width)。
|
||||
|
||||
枚举也遵循相同的行为准则:
|
||||
|
||||
```swift
|
||||
enum CompassPoint {
|
||||
case North, South, East, West
|
||||
}
|
||||
var currentDirection = CompassPoint.West
|
||||
let rememberedDirection = currentDirection
|
||||
currentDirection = .East
|
||||
if rememberDirection == .West {
|
||||
println("The remembered direction is still .West")
|
||||
}
|
||||
// 输出 "The remembered direction is still .West"
|
||||
```
|
||||
|
||||
上例中`rememberedDirection`被赋予了`currentDirection`的值(value),实际上它被赋予的是值(value)的一个拷贝。赋值过程结束后再修改`currentDirection`的值并不影响`rememberedDirection`所储存的原始值(value)的拷贝。
|
||||
|
||||
<a name="classes_are_reference_types"></a>
|
||||
## 类是引用类型
|
||||
与值类型不同,引用类型在被赋予到一个变量,常量或者被传递到一个函数时,操作的并不是其拷贝。因此,引用的是已存在的实例本身而不是其拷贝。
|
||||
|
||||
请看下面这个示例,其使用了之前定义的`VideoMode`类:
|
||||
|
||||
```swift
|
||||
let tenEighty = VideoMode()
|
||||
tenEighty.resolution = hd
|
||||
tenEighty.interlaced = true
|
||||
tenEighty.name = "1080i"
|
||||
tenEighty.frameRate = 25.0
|
||||
```
|
||||
|
||||
以上示例中,声明了一个名为`tenEighty`的常量,其引用了一个`VideoMode`类的新实例。在之前的示例中,这个视频模式(video mode)被赋予了HD分辨率(1920*1080)的一个拷贝(`hd`)。同时设置为交错(interlaced),命名为`“1080i”`。最后,其帧率是`25.0`帧每秒。
|
||||
|
||||
然后,`tenEighty` 被赋予名为`alsoTenEighty`的新常量,同时对`alsoTenEighty`的帧率进行修改:
|
||||
|
||||
```swift
|
||||
let alsoTenEighty = tenEighty
|
||||
alsoTenEighty.frameRate = 30.0
|
||||
```
|
||||
|
||||
因为类是引用类型,所以`tenEight`和`alsoTenEight`实际上引用的是相同的`VideoMode`实例。换句话说,它们只是同一个实例的两种叫法。
|
||||
|
||||
下面,通过查看`tenEighty`的`frameRate`属性,我们会发现它正确的显示了基本`VideoMode`实例的新帧率,其值为`30.0`:
|
||||
|
||||
```swift
|
||||
println("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
|
||||
// 输出 "The frameRate property of theEighty is now 30.0"
|
||||
```
|
||||
|
||||
需要注意的是`tenEighty`和`alsoTenEighty`被声明为*常量((constants)*而不是变量。然而你依然可以改变`tenEighty.frameRate`和`alsoTenEighty.frameRate`,因为这两个常量本身不会改变。它们并不`储存`这个`VideoMode`实例,在后台仅仅是对`VideoMode`实例的引用。所以,改变的是被引用的基础`VideoMode`的`frameRate`参数,而不改变常量的值。
|
||||
|
||||
### 恒等运算符
|
||||
|
||||
因为类是引用类型,有可能有多个常量和变量在后台同时引用某一个类实例。(对于结构体和枚举来说,这并不成立。因为它们作值类型,在被赋予到常量,变量或者传递到函数时,总是会被拷贝。)
|
||||
|
||||
如果能够判定两个常量或者变量是否引用同一个类实例将会很有帮助。为了达到这个目的,Swift 内建了两个恒等运算符:
|
||||
|
||||
* 等价于 ( === )
|
||||
* 不等价于 ( !== )
|
||||
|
||||
以下是运用这两个运算符检测两个常量或者变量是否引用同一个实例:
|
||||
|
||||
```swift
|
||||
if tenEighty === alsoTenTighty {
|
||||
println("tenTighty and alsoTenEighty refer to the same Resolution instance.")
|
||||
}
|
||||
//输出 "tenEighty and alsoTenEighty refer to the same Resolution instance."
|
||||
```
|
||||
|
||||
请注意“等价于”(用三个等号表示,===) 与“等于”(用两个等号表示,==)的不同:
|
||||
|
||||
* “等价于”表示两个类类型(class type)的常量或者变量引用同一个类实例。
|
||||
* “等于”表示两个实例的值“相等”或“相同”,判定时要遵照类设计者定义定义的评判标准,因此相比于“相等”,这是一种更加合适的叫法。
|
||||
|
||||
当你在定义你的自定义类和结构体的时候,你有义务来决定判定两个实例“相等”的标准。在章节[运算符函数(Operator Functions)](23_Advanced_Operators.html#operator_functions)中将会详细介绍实现自定义“等于”和“不等于”运算符的流程。
|
||||
|
||||
### 指针
|
||||
如果你有 C,C++ 或者 Objective-C 语言的经验,那么你也许会知道这些语言使用指针来引用内存中的地址。一个 Swift 常量或者变量引用一个引用类型的实例与 C 语言中的指针类似,不同的是并不直接指向内存中的某个地址,而且也不要求你使用星号(*)来表明你在创建一个引用。Swift 中这些引用与其它的常量或变量的定义方式相同。
|
||||
|
||||
<a name="choosing_between_classes_and_structures"></a>
|
||||
## 类和结构体的选择
|
||||
在你的代码中,你可以使用类和结构体来定义你的自定义数据类型。
|
||||
|
||||
然而,结构体实例总是通过值传递,类实例总是通过引用传递。这意味两者适用不同的任务。当你的在考虑一个工程项目的数据构造和功能的时候,你需要决定每个数据构造是定义成类还是结构体。
|
||||
|
||||
按照通用的准则,当符合一条或多条以下条件时,请考虑构建结构体:
|
||||
|
||||
* 结构体的主要目的是用来封装少量相关简单数据值。
|
||||
* 有理由预计一个结构体实例在赋值或传递时,封装的数据将会被拷贝而不是被引用。
|
||||
* 任何在结构体中储存的值类型属性,也将会被拷贝,而不是被引用。
|
||||
* 结构体不需要去继承另一个已存在类型的属性或者行为。
|
||||
|
||||
合适的结构体候选者包括:
|
||||
|
||||
* 几何形状的大小,封装一个`width`属性和`height`属性,两者均为`Double`类型。
|
||||
* 一定范围内的路径,封装一个`start`属性和`length`属性,两者均为`Int`类型。
|
||||
* 三维坐标系内一点,封装`x`,`y`和`z`属性,三者均为`Double`类型。
|
||||
|
||||
在所有其它案例中,定义一个类,生成一个它的实例,并通过引用来管理和传递。实际中,这意味着绝大部分的自定义数据构造都应该是类,而非结构体。
|
||||
|
||||
<a name="assignment_and_copy_behavior_for_collection_types"></a>
|
||||
## 集合(Collection)类型的赋值和拷贝行为
|
||||
Swift 中`数组(Array)`和`字典(Dictionary)`类型均以结构体的形式实现。然而当数组被赋予一个常量或变量,或被传递给一个函数或方法时,其拷贝行为与字典和其它结构体有些许不同。
|
||||
|
||||
以下对`数组`和`结构体`的行为描述与对`NSArray`和`NSDictionary`的行为描述在本质上不同,后者是以类的形式实现,前者是以结构体的形式实现。`NSArray`和`NSDictionary`实例总是以对已有实例引用,而不是拷贝的方式被赋值和传递。
|
||||
|
||||
> 注意:
|
||||
以下是对于数组,字典,字符串和其它值的`拷贝`的描述。
|
||||
在你的代码中,拷贝好像是确实是在有拷贝行为的地方产生过。然而,在 Swift 的后台中,只有确有必要,`实际(actual)`拷贝才会被执行。Swift 管理所有的值拷贝以确保性能最优化的性能,所以你也没有必要去避免赋值以保证最优性能。(实际赋值由系统管理优化)
|
||||
|
||||
### 字典类型的赋值和拷贝行为
|
||||
无论何时将一个`字典`实例赋给一个常量或变量,或者传递给一个函数或方法,这个字典会即会在赋值或调用发生时被拷贝。在章节[结构体和枚举是值类型](#structures_and_enumerations_are_value_types)中将会对此过程进行详细介绍。
|
||||
|
||||
如果`字典`实例中所储存的键(keys)和/或值(values)是值类型(结构体或枚举),当赋值或调用发生时,它们都会被拷贝。相反,如果键(keys)和/或值(values)是引用类型,被拷贝的将会是引用,而不是被它们引用的类实例或函数。`字典`的键和值的拷贝行为与结构体所储存的属性的拷贝行为相同。
|
||||
|
||||
下面的示例定义了一个名为`ages`的字典,其中储存了四个人的名字和年龄。`ages`字典被赋予了一个名为`copiedAges`的新变量,同时`ages`在赋值的过程中被拷贝。赋值结束后,`ages`和`copiedAges`成为两个相互独立的字典。
|
||||
|
||||
```swift
|
||||
var ages = ["Peter": 23, "Wei": 35, "Anish": 65, "Katya": 19]
|
||||
var copiedAges = ages
|
||||
```
|
||||
|
||||
这个字典的键(keys)是`字符串(String)`类型,值(values)是`整(Int)`类型。这两种类型在Swift 中都是值类型(value types),所以当字典被拷贝时,两者都会被拷贝。
|
||||
|
||||
我们可以通过改变一个字典中的年龄值(age value),检查另一个字典中所对应的值,来证明`ages`字典确实是被拷贝了。如果在`copiedAges`字典中将`Peter`的值设为`24`,那么`ages`字典仍然会返回修改前的值`23`:
|
||||
|
||||
```swift
|
||||
copiedAges["Peter"] = 24
|
||||
println(ages["Peter"])
|
||||
// 输出 "23"
|
||||
```
|
||||
|
||||
### 数组的赋值和拷贝行为
|
||||
|
||||
在Swift 中,`数组(Arrays)`类型的赋值和拷贝行为要比`字典(Dictionary)`类型的复杂的多。当操作数组内容时,`数组(Array)`能提供接近C语言的的性能,并且拷贝行为只有在必要时才会发生。
|
||||
|
||||
如果你将一个`数组(Array)`实例赋给一个变量或常量,或者将其作为参数传递给函数或方法调用,在事件发生时数组的内容`不`会被拷贝。相反,数组公用相同的元素序列。当你在一个数组内修改某一元素,修改结果也会在另一数组显示。
|
||||
|
||||
对数组来说,拷贝行为仅仅当操作有可能修改数组`长度`时才会发生。这种行为包括了附加(appending),插入(inserting),删除(removing)或者使用范围下标(ranged subscript)去替换这一范围内的元素。只有当数组拷贝确要发生时,数组内容的行为规则与字典中键值的相同,参见章节[集合(collection)类型的赋值与复制行为](#assignment_and_copy_behavior_for_collection_types。
|
||||
|
||||
下面的示例将一个`整数(Int)`数组赋给了一个名为`a`的变量,继而又被赋给了变量`b`和`c`:
|
||||
|
||||
```swift
|
||||
var a = [1, 2, 3]
|
||||
var b = a
|
||||
var c = a
|
||||
```
|
||||
|
||||
我们可以在`a`,`b`,`c`上使用下标语法以得到数组的第一个元素:
|
||||
|
||||
```swift
|
||||
println(a[0])
|
||||
// 1
|
||||
println(b[0])
|
||||
// 1
|
||||
println(c[0])
|
||||
// 1
|
||||
```
|
||||
|
||||
如果通过下标语法修改数组中某一元素的值,那么`a`,`b`,`c`中的相应值都会发生改变。请注意当你用下标语法修改某一值时,并没有拷贝行为伴随发生,因为下表语法修改值时没有改变数组长度的可能:
|
||||
|
||||
```swift
|
||||
a[0] = 42
|
||||
println(a[0])
|
||||
// 42
|
||||
println(b[0])
|
||||
// 42
|
||||
println(c[0])
|
||||
// 42
|
||||
```
|
||||
|
||||
然而,当你给`a`附加新元素时,数组的长度`会`改变。
|
||||
当附加元素这一事件发生时,Swift 会创建这个数组的一个拷贝。从此以后,`a`将会是原数组的一个独立拷贝。
|
||||
|
||||
拷贝发生后,如果再修改`a`中元素值的话,`a`将会返回与`b`,`c`不同的结果,因为后两者引用的是原来的数组:
|
||||
|
||||
```swift
|
||||
a.append(4)
|
||||
a[0] = 777
|
||||
println(a[0])
|
||||
// 777
|
||||
println(b[0])
|
||||
// 42
|
||||
println(c[0])
|
||||
// 42
|
||||
```
|
||||
|
||||
### 确保数组的唯一性
|
||||
在操作一个数组,或将其传递给函数以及方法调用之前是很有必要先确定这个数组是有一个唯一拷贝的。通过在数组变量上调用`unshare`方法来确定数组引用的唯一性。(当数组赋给常量时,不能调用`unshare`方法)
|
||||
|
||||
如果一个数组被多个变量引用,在其中的一个变量上调用`unshare`方法,则会拷贝此数组,此时这个变量将会有属于它自己的独立数组拷贝。当数组仅被一个变量引用时,则不会有拷贝发生。
|
||||
|
||||
在上一个示例的最后,`b`和`c`都引用了同一个数组。此时在`b`上调用`unshare`方法则会将`b`变成一个唯一个拷贝:
|
||||
|
||||
```swift
|
||||
b.unshare()
|
||||
```
|
||||
|
||||
在`unshare`方法调用后再修改`b`中第一个元素的值,这三个数组(`a`,`b`,`c`)会返回不同的三个值:
|
||||
|
||||
```swift
|
||||
b[0] = -105
|
||||
println(a[0])
|
||||
// 77
|
||||
println(b[0])
|
||||
// -105
|
||||
println(c[0])
|
||||
// 42
|
||||
```
|
||||
|
||||
|
||||
### 判定两个数组是否共用相同元素
|
||||
|
||||
我们通过使用恒等运算符(identity operators)( === 和 !==)来判定两个数组或子数组共用相同的储存空间或元素。
|
||||
|
||||
下面这个示例使用了“等同(identical to)” 运算符(===) 来判定`b`和`c`是否共用相同的数组元素:
|
||||
|
||||
```swift
|
||||
if b === c {
|
||||
println("b and c still share the same array elements.")
|
||||
} else {
|
||||
println("b and c now refer to two independent sets of array elements.")
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
// 输出 "b and c now refer totwo independent sets of array elements."
|
||||
```
|
||||
|
||||
此外,我们还可以使用恒等运算符来判定两个子数组是否共用相同的元素。下面这个示例中,比较了`b`的两个相等的子数组,并且确定了这两个子数组都引用相同的元素:
|
||||
|
||||
```swift
|
||||
if b[0...1] === b[0...1] {
|
||||
println("These two subarrays share the same elements.")
|
||||
} else {
|
||||
println("These two subarrays do not share the same elements.")
|
||||
}
|
||||
// 输出 "These two subarrays share the same elements."
|
||||
```
|
||||
|
||||
### 强制复制数组
|
||||
|
||||
我们通过调用数组的`copy`方法进行强制显性复制。这个方法对数组进行了浅拷贝(shallow copy),并且返回一个包含此拷贝的新数组。
|
||||
|
||||
下面这个示例中定义了一个`names`数组,其包含了七个人名。还定义了一个`copiedNames`变量,用以储存在`names`上调用`copy`方法所返回的结果:
|
||||
|
||||
```swift
|
||||
var names = ["Mohsen", "Hilary", "Justyn", "Amy", "Rich", "Graham", "Vic"]
|
||||
var copiedNames = names.copy()
|
||||
```
|
||||
|
||||
我们可以通过修改一个数组中某元素,并且检查另一个数组中对应元素的方法来判定`names`数组确已被复制。如果你将`copiedNames`中第一个元素从"`Mohsen`"修改为"`Mo`",则`names`数组返回的仍是拷贝发生前的"`Mohsen`":
|
||||
|
||||
```swift
|
||||
copiedName[0] = "Mo"
|
||||
println(name[0])
|
||||
// 输出 "Mohsen"
|
||||
```
|
||||
|
||||
> 注意:
|
||||
如果你仅需要确保你对数组的引用是唯一引用,请调用`unshare`方法,而不是`copy`方法。`unshare`方法仅会在确有必要时才会创建数组拷贝。`copy`方法会在任何时候都创建一个新的拷贝,即使引用已经是唯一引用。
|
||||
|
||||
> 翻译:[JaySurplus](https://github.com/JaySurplus)
|
||||
> 校对:[sg552](https://github.com/sg552)
|
||||
|
||||
# 类和结构体
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [类和结构体对比](#comparing_classes_and_structures)
|
||||
- [结构体和枚举是值类型](#structures_and_enumerations_are_value_types)
|
||||
- [类是引用类型](#classes_are_reference_types)
|
||||
- [类和结构体的选择](#choosing_between_classes_and_structures)
|
||||
- [集合(collection)类型的赋值与复制行为](#assignment_and_copy_behavior_for_collection_types)
|
||||
|
||||
类和结构体是人们构建代码所用的一种通用且灵活的构造体。为了在类和结构体中实现各种功能,我们必须要严格按照对于常量,变量以及函数所规定的语法规则来定义属性和添加方法。
|
||||
|
||||
与其他编程语言所不同的是,Swift 并不要求你为自定义类和结构去创建独立的接口和实现文件。你所要做的是在一个单一文件中定义一个类或者结构体,系统将会自动生成面向其它代码的外部接口。
|
||||
|
||||
> 注意:
|
||||
通常一个`类`的实例被称为`对象`。然而在Swift 中,类和结构体的关系要比在其他语言中更加的密切,本章中所讨论的大部分功能都可以用在类和结构体上。因此,我们会主要使用`实例`而不是`对象`。
|
||||
|
||||
<a name="comparing_classes_and_structures"></a>
|
||||
###类和结构体对比
|
||||
|
||||
Swift 中类和结构体有很多共同点。共同处在于:
|
||||
|
||||
* 定义属性用于储存值
|
||||
* 定义方法用于提供功能
|
||||
* 定义附属脚本用于访问值
|
||||
* 定义构造器用于生成初始化值
|
||||
* 通过扩展以增加默认实现的功能
|
||||
* 符合协议以对某类提供标准功能
|
||||
|
||||
更多信息请参见 [属性](10_Properties.html),[方法](11_Methods.html),[下标脚本](12_Subscripts.html),[初始过程](14_Initialization.html),[扩展](20_Extensions.html),和[协议](21_Protocols.html)。
|
||||
|
||||
与结构体相比,类还有如下的附加功能:
|
||||
|
||||
* 继承允许一个类继承另一个类的特征
|
||||
* 类型转换允许在运行时检查和解释一个类实例的类型
|
||||
* 解构器允许一个类实例释放任何其所被分配的资源
|
||||
* 引用计数允许对一个类的多次引用
|
||||
|
||||
更多信息请参见[继承](http://),[类型转换](http://),[初始化](http://),和[自动引用计数](http://)。
|
||||
|
||||
> 注意:
|
||||
结构体总是通过被复制的方式在代码中传递,因此请不要使用引用计数。
|
||||
|
||||
### 定义
|
||||
|
||||
类和结构体有着类似的定义方式。我们通过关键字`class`和`struct`来分别表示类和结构体,并在一对大括号中定义它们的具体内容:
|
||||
|
||||
```swift
|
||||
class SomeClass {
|
||||
// class definition goes here
|
||||
}
|
||||
struct SomeStructure {
|
||||
// structure definition goes here
|
||||
}
|
||||
```
|
||||
|
||||
> 注意:
|
||||
在你每次定义一个新类或者结构体的时候,实际上你是有效地定义了一个新的 Swift 类型。因此请使用 `UpperCamelCase` 这种方式来命名(如 `SomeClass` 和`SomeStructure`等),以便符合标准Swift 类型的大写命名风格(如`String`,`Int`和`Bool`)。相反的,请使用`lowerCamelCase`这种方式为属性和方法命名(如`framerate`和`incrementCount`),以便和类区分。
|
||||
|
||||
以下是定义结构体和定义类的示例:
|
||||
|
||||
```swift
|
||||
struct Resolution {
|
||||
var width = 0
|
||||
var heigth = 0
|
||||
}
|
||||
class VideoMode {
|
||||
var resolution = Resolution()
|
||||
var interlaced = false
|
||||
var frameRate = 0.0
|
||||
var name: String?
|
||||
}
|
||||
```
|
||||
|
||||
在上面的示例中我们定义了一个名为`Resolution`的结构体,用来描述一个显示器的像素分辨率。这个结构体包含了两个名为`width`和`height`的储存属性。储存属性是捆绑和储存在类或结构体中的常量或变量。当这两个属性被初始化为整数`0`的时候,它们会被推断为`Int`类型。
|
||||
|
||||
在上面的示例中我们还定义了一个名为`VideoMode`的类,用来描述一个视频显示器的特定模式。这个类包含了四个储存属性变量。第一个是`分辨率`,它被初始化为一个新的`Resolution`结构体的实例,具有`Resolution`的属性类型。新`VideoMode`实例同时还会初始化其它三个属性,它们分别是,初始值为`false`(意为“non-interlaced video”)的`interlaced`,回放帧率初始值为`0.0`的`frameRate`和值为可选`String`的`name`。`name`属性会被自动赋予一个默认值`nil`,意为“没有`name`值”,因它是一个可选类型。
|
||||
|
||||
### 类和结构体实例
|
||||
|
||||
`Resolution`结构体和`VideoMode`类的定义仅描述了什么是`Resolution`和`VideoMode`。它们并没有描述一个特定的分辨率(resolution)或者视频模式(video mode)。为了描述一个特定的分辨率或者视频模式,我们需要生成一个它们的实例。
|
||||
|
||||
生成结构体和类实例的语法非常相似:
|
||||
|
||||
```swift
|
||||
let someResolution = Resolution()
|
||||
let someVideoMode = VideoMode()
|
||||
```
|
||||
|
||||
结构体和类都使用构造器语法来生成新的实例。构造器语法的最简单形式是在结构体或者类的类型名称后跟随一个空括弧,如`Resolution()`或`VideoMode()`。通过这种方式所创建的类或者结构体实例,其属性均会被初始化为默认值。[构造过程](14_Initialization.html)章节会对类和结构体的初始化进行更详细的讨论。
|
||||
|
||||
### 属性访问
|
||||
|
||||
通过使用*点语法*(*dot syntax*),你可以访问实例中所含有的属性。其语法规则是,实例名后面紧跟属性名,两者通过点号(.)连接:
|
||||
|
||||
```swift
|
||||
println("The width of someResolution is \(someResolution.width)")
|
||||
// 输出 "The width of someResolution is 0"
|
||||
```
|
||||
|
||||
在上面的例子中,`someResolution.width`引用`someResolution`的`width`属性,返回`width`的初始值`0`。
|
||||
|
||||
你也可以访问子属性,如何`VideoMode`中`Resolution`属性的`width`属性:
|
||||
|
||||
```swift
|
||||
println("The width of someVideoMode is \(someVideoMode.resolution.width)")
|
||||
// 输出 "The width of someVideoMode is 0"
|
||||
```
|
||||
|
||||
你也可以使用点语法为属性变量赋值:
|
||||
|
||||
```swift
|
||||
someVideoMode.resolution.width = 12880
|
||||
println("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)
|
||||
|
||||
所有结构体都有一个自动生成的成员逐一构造器,用于初始化新结构体实例中成员的属性。新实例中各个属性的初始值可以通过属性的名称传递到成员逐一构造器之中:
|
||||
|
||||
```swift
|
||||
let vga = resolution(width:640, heigth: 480)
|
||||
```
|
||||
|
||||
与结构体不同,类实例没有默认的成员逐一构造器。[构造过程](14_Initialization.html)章节会对构造器进行更详细的讨论。
|
||||
|
||||
<a name="structures_and_enumerations_are_value_types"></a>
|
||||
## 结构体和枚举是值类型
|
||||
|
||||
值类型被赋予给一个变量,常数或者本身被传递给一个函数的时候,实际上操作的是其的拷贝。
|
||||
|
||||
在之前的章节中,我们已经大量使用了值类型。实际上,在 Swift 中,所有的基本类型:整数(Integer)、浮点数(floating-point)、布尔值(Booleans)、字符串(string)、数组(array)和字典(dictionaries),都是值类型,并且都是以结构体的形式在后台所实现。
|
||||
|
||||
在 Swift 中,所有的结构体和枚举都是值类型。这意味着它们的实例,以及实例中所包含的任何值类型属性,在代码中传递的时候都会被复制。
|
||||
|
||||
请看下面这个示例,其使用了前一个示例中`Resolution`结构体:
|
||||
|
||||
```swift
|
||||
let hd = Resolution(width: 1920, height: 1080)
|
||||
var cinema = hd
|
||||
```
|
||||
|
||||
在以上示例中,声明了一个名为`hd`的常量,其值为一个初始化为全高清视频分辨率(1920 像素宽,1080 像素高)的`Resolution`实例。
|
||||
|
||||
然后示例中又声明了一个名为`cinema`的变量,其值为之前声明的`hd`。因为`Resolution`是一个结构体,所以`cinema`的值其实是`hd`的一个拷贝副本,而不是`hd`本身。尽管`hd`和`cinema`有着相同的宽(width)和高(height)属性,但是在后台中,它们是两个完全不同的实例。
|
||||
|
||||
下面,为了符合数码影院放映的需求(2048 像素宽,1080 像素高),`cinema`的`width`属性需要作如下修改:
|
||||
|
||||
```swift
|
||||
cinema.width = 2048
|
||||
```
|
||||
|
||||
这里,将会显示`cinema`的`width`属性确已改为了`2048`:
|
||||
|
||||
```swift
|
||||
println("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")
|
||||
// 输出 "hd is still 1920 pixels wide"
|
||||
```
|
||||
|
||||
在将`hd`赋予给`cinema`的时候,实际上是将`hd`中所储存的`值(values)`进行拷贝,然后将拷贝的数据储存到新的`cinema`实例中。结果就是两个完全独立的实例碰巧包含有相同的数值。由于两者相互独立,因此将`cinema`的`width`修改为`2048`并不会影响`hd`中的宽(width)。
|
||||
|
||||
枚举也遵循相同的行为准则:
|
||||
|
||||
```swift
|
||||
enum CompassPoint {
|
||||
case North, South, East, West
|
||||
}
|
||||
var currentDirection = CompassPoint.West
|
||||
let rememberedDirection = currentDirection
|
||||
currentDirection = .East
|
||||
if rememberDirection == .West {
|
||||
println("The remembered direction is still .West")
|
||||
}
|
||||
// 输出 "The remembered direction is still .West"
|
||||
```
|
||||
|
||||
上例中`rememberedDirection`被赋予了`currentDirection`的值(value),实际上它被赋予的是值(value)的一个拷贝。赋值过程结束后再修改`currentDirection`的值并不影响`rememberedDirection`所储存的原始值(value)的拷贝。
|
||||
|
||||
<a name="classes_are_reference_types"></a>
|
||||
## 类是引用类型
|
||||
|
||||
与值类型不同,引用类型在被赋予到一个变量,常量或者被传递到一个函数时,操作的并不是其拷贝。因此,引用的是已存在的实例本身而不是其拷贝。
|
||||
|
||||
请看下面这个示例,其使用了之前定义的`VideoMode`类:
|
||||
|
||||
```swift
|
||||
let tenEighty = VideoMode()
|
||||
tenEighty.resolution = hd
|
||||
tenEighty.interlaced = true
|
||||
tenEighty.name = "1080i"
|
||||
tenEighty.frameRate = 25.0
|
||||
```
|
||||
|
||||
以上示例中,声明了一个名为`tenEighty`的常量,其引用了一个`VideoMode`类的新实例。在之前的示例中,这个视频模式(video mode)被赋予了HD分辨率(1920*1080)的一个拷贝(`hd`)。同时设置为交错(interlaced),命名为`“1080i”`。最后,其帧率是`25.0`帧每秒。
|
||||
|
||||
然后,`tenEighty` 被赋予名为`alsoTenEighty`的新常量,同时对`alsoTenEighty`的帧率进行修改:
|
||||
|
||||
```swift
|
||||
let alsoTenEighty = tenEighty
|
||||
alsoTenEighty.frameRate = 30.0
|
||||
```
|
||||
|
||||
因为类是引用类型,所以`tenEight`和`alsoTenEight`实际上引用的是相同的`VideoMode`实例。换句话说,它们只是同一个实例的两种叫法。
|
||||
|
||||
下面,通过查看`tenEighty`的`frameRate`属性,我们会发现它正确的显示了基本`VideoMode`实例的新帧率,其值为`30.0`:
|
||||
|
||||
```swift
|
||||
println("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
|
||||
// 输出 "The frameRate property of theEighty is now 30.0"
|
||||
```
|
||||
|
||||
需要注意的是`tenEighty`和`alsoTenEighty`被声明为*常量((constants)*而不是变量。然而你依然可以改变`tenEighty.frameRate`和`alsoTenEighty.frameRate`,因为这两个常量本身不会改变。它们并不`储存`这个`VideoMode`实例,在后台仅仅是对`VideoMode`实例的引用。所以,改变的是被引用的基础`VideoMode`的`frameRate`参数,而不改变常量的值。
|
||||
|
||||
### 恒等运算符
|
||||
|
||||
因为类是引用类型,有可能有多个常量和变量在后台同时引用某一个类实例。(对于结构体和枚举来说,这并不成立。因为它们作值类型,在被赋予到常量,变量或者传递到函数时,总是会被拷贝。)
|
||||
|
||||
如果能够判定两个常量或者变量是否引用同一个类实例将会很有帮助。为了达到这个目的,Swift 内建了两个恒等运算符:
|
||||
|
||||
* 等价于 ( === )
|
||||
* 不等价于 ( !== )
|
||||
|
||||
以下是运用这两个运算符检测两个常量或者变量是否引用同一个实例:
|
||||
|
||||
```swift
|
||||
if tenEighty === alsoTenTighty {
|
||||
println("tenTighty and alsoTenEighty refer to the same Resolution instance.")
|
||||
}
|
||||
//输出 "tenEighty and alsoTenEighty refer to the same Resolution instance."
|
||||
```
|
||||
|
||||
请注意“等价于”(用三个等号表示,===) 与“等于”(用两个等号表示,==)的不同:
|
||||
|
||||
* “等价于”表示两个类类型(class type)的常量或者变量引用同一个类实例。
|
||||
* “等于”表示两个实例的值“相等”或“相同”,判定时要遵照类设计者定义定义的评判标准,因此相比于“相等”,这是一种更加合适的叫法。
|
||||
|
||||
当你在定义你的自定义类和结构体的时候,你有义务来决定判定两个实例“相等”的标准。在章节[运算符函数(Operator Functions)](23_Advanced_Operators.html#operator_functions)中将会详细介绍实现自定义“等于”和“不等于”运算符的流程。
|
||||
|
||||
### 指针
|
||||
|
||||
如果你有 C,C++ 或者 Objective-C 语言的经验,那么你也许会知道这些语言使用指针来引用内存中的地址。一个 Swift 常量或者变量引用一个引用类型的实例与 C 语言中的指针类似,不同的是并不直接指向内存中的某个地址,而且也不要求你使用星号(*)来表明你在创建一个引用。Swift 中这些引用与其它的常量或变量的定义方式相同。
|
||||
|
||||
<a name="choosing_between_classes_and_structures"></a>
|
||||
## 类和结构体的选择
|
||||
|
||||
在你的代码中,你可以使用类和结构体来定义你的自定义数据类型。
|
||||
|
||||
然而,结构体实例总是通过值传递,类实例总是通过引用传递。这意味两者适用不同的任务。当你的在考虑一个工程项目的数据构造和功能的时候,你需要决定每个数据构造是定义成类还是结构体。
|
||||
|
||||
按照通用的准则,当符合一条或多条以下条件时,请考虑构建结构体:
|
||||
|
||||
* 结构体的主要目的是用来封装少量相关简单数据值。
|
||||
* 有理由预计一个结构体实例在赋值或传递时,封装的数据将会被拷贝而不是被引用。
|
||||
* 任何在结构体中储存的值类型属性,也将会被拷贝,而不是被引用。
|
||||
* 结构体不需要去继承另一个已存在类型的属性或者行为。
|
||||
|
||||
合适的结构体候选者包括:
|
||||
|
||||
* 几何形状的大小,封装一个`width`属性和`height`属性,两者均为`Double`类型。
|
||||
* 一定范围内的路径,封装一个`start`属性和`length`属性,两者均为`Int`类型。
|
||||
* 三维坐标系内一点,封装`x`,`y`和`z`属性,三者均为`Double`类型。
|
||||
|
||||
在所有其它案例中,定义一个类,生成一个它的实例,并通过引用来管理和传递。实际中,这意味着绝大部分的自定义数据构造都应该是类,而非结构体。
|
||||
|
||||
<a name="assignment_and_copy_behavior_for_collection_types"></a>
|
||||
## 集合(Collection)类型的赋值和拷贝行为
|
||||
|
||||
Swift 中`数组(Array)`和`字典(Dictionary)`类型均以结构体的形式实现。然而当数组被赋予一个常量或变量,或被传递给一个函数或方法时,其拷贝行为与字典和其它结构体有些许不同。
|
||||
|
||||
以下对`数组`和`结构体`的行为描述与对`NSArray`和`NSDictionary`的行为描述在本质上不同,后者是以类的形式实现,前者是以结构体的形式实现。`NSArray`和`NSDictionary`实例总是以对已有实例引用,而不是拷贝的方式被赋值和传递。
|
||||
|
||||
> 注意:
|
||||
以下是对于数组,字典,字符串和其它值的`拷贝`的描述。
|
||||
在你的代码中,拷贝好像是确实是在有拷贝行为的地方产生过。然而,在 Swift 的后台中,只有确有必要,`实际(actual)`拷贝才会被执行。Swift 管理所有的值拷贝以确保性能最优化的性能,所以你也没有必要去避免赋值以保证最优性能。(实际赋值由系统管理优化)
|
||||
|
||||
### 字典类型的赋值和拷贝行为
|
||||
|
||||
无论何时将一个`字典`实例赋给一个常量或变量,或者传递给一个函数或方法,这个字典会即会在赋值或调用发生时被拷贝。在章节[结构体和枚举是值类型](#structures_and_enumerations_are_value_types)中将会对此过程进行详细介绍。
|
||||
|
||||
如果`字典`实例中所储存的键(keys)和/或值(values)是值类型(结构体或枚举),当赋值或调用发生时,它们都会被拷贝。相反,如果键(keys)和/或值(values)是引用类型,被拷贝的将会是引用,而不是被它们引用的类实例或函数。`字典`的键和值的拷贝行为与结构体所储存的属性的拷贝行为相同。
|
||||
|
||||
下面的示例定义了一个名为`ages`的字典,其中储存了四个人的名字和年龄。`ages`字典被赋予了一个名为`copiedAges`的新变量,同时`ages`在赋值的过程中被拷贝。赋值结束后,`ages`和`copiedAges`成为两个相互独立的字典。
|
||||
|
||||
```swift
|
||||
var ages = ["Peter": 23, "Wei": 35, "Anish": 65, "Katya": 19]
|
||||
var copiedAges = ages
|
||||
```
|
||||
|
||||
这个字典的键(keys)是`字符串(String)`类型,值(values)是`整(Int)`类型。这两种类型在Swift 中都是值类型(value types),所以当字典被拷贝时,两者都会被拷贝。
|
||||
|
||||
我们可以通过改变一个字典中的年龄值(age value),检查另一个字典中所对应的值,来证明`ages`字典确实是被拷贝了。如果在`copiedAges`字典中将`Peter`的值设为`24`,那么`ages`字典仍然会返回修改前的值`23`:
|
||||
|
||||
```swift
|
||||
copiedAges["Peter"] = 24
|
||||
println(ages["Peter"])
|
||||
// 输出 "23"
|
||||
```
|
||||
|
||||
### 数组的赋值和拷贝行为
|
||||
|
||||
在Swift 中,`数组(Arrays)`类型的赋值和拷贝行为要比`字典(Dictionary)`类型的复杂的多。当操作数组内容时,`数组(Array)`能提供接近C语言的的性能,并且拷贝行为只有在必要时才会发生。
|
||||
|
||||
如果你将一个`数组(Array)`实例赋给一个变量或常量,或者将其作为参数传递给函数或方法调用,在事件发生时数组的内容`不`会被拷贝。相反,数组公用相同的元素序列。当你在一个数组内修改某一元素,修改结果也会在另一数组显示。
|
||||
|
||||
对数组来说,拷贝行为仅仅当操作有可能修改数组`长度`时才会发生。这种行为包括了附加(appending),插入(inserting),删除(removing)或者使用范围下标(ranged subscript)去替换这一范围内的元素。只有当数组拷贝确要发生时,数组内容的行为规则与字典中键值的相同,参见章节[集合(collection)类型的赋值与复制行为](#assignment_and_copy_behavior_for_collection_types。
|
||||
|
||||
下面的示例将一个`整数(Int)`数组赋给了一个名为`a`的变量,继而又被赋给了变量`b`和`c`:
|
||||
|
||||
```swift
|
||||
var a = [1, 2, 3]
|
||||
var b = a
|
||||
var c = a
|
||||
```
|
||||
|
||||
我们可以在`a`,`b`,`c`上使用下标语法以得到数组的第一个元素:
|
||||
|
||||
```swift
|
||||
println(a[0])
|
||||
// 1
|
||||
println(b[0])
|
||||
// 1
|
||||
println(c[0])
|
||||
// 1
|
||||
```
|
||||
|
||||
如果通过下标语法修改数组中某一元素的值,那么`a`,`b`,`c`中的相应值都会发生改变。请注意当你用下标语法修改某一值时,并没有拷贝行为伴随发生,因为下表语法修改值时没有改变数组长度的可能:
|
||||
|
||||
```swift
|
||||
a[0] = 42
|
||||
println(a[0])
|
||||
// 42
|
||||
println(b[0])
|
||||
// 42
|
||||
println(c[0])
|
||||
// 42
|
||||
```
|
||||
|
||||
然而,当你给`a`附加新元素时,数组的长度`会`改变。
|
||||
当附加元素这一事件发生时,Swift 会创建这个数组的一个拷贝。从此以后,`a`将会是原数组的一个独立拷贝。
|
||||
|
||||
拷贝发生后,如果再修改`a`中元素值的话,`a`将会返回与`b`,`c`不同的结果,因为后两者引用的是原来的数组:
|
||||
|
||||
```swift
|
||||
a.append(4)
|
||||
a[0] = 777
|
||||
println(a[0])
|
||||
// 777
|
||||
println(b[0])
|
||||
// 42
|
||||
println(c[0])
|
||||
// 42
|
||||
```
|
||||
|
||||
### 确保数组的唯一性
|
||||
|
||||
在操作一个数组,或将其传递给函数以及方法调用之前是很有必要先确定这个数组是有一个唯一拷贝的。通过在数组变量上调用`unshare`方法来确定数组引用的唯一性。(当数组赋给常量时,不能调用`unshare`方法)
|
||||
|
||||
如果一个数组被多个变量引用,在其中的一个变量上调用`unshare`方法,则会拷贝此数组,此时这个变量将会有属于它自己的独立数组拷贝。当数组仅被一个变量引用时,则不会有拷贝发生。
|
||||
|
||||
在上一个示例的最后,`b`和`c`都引用了同一个数组。此时在`b`上调用`unshare`方法则会将`b`变成一个唯一个拷贝:
|
||||
|
||||
```swift
|
||||
b.unshare()
|
||||
```
|
||||
|
||||
在`unshare`方法调用后再修改`b`中第一个元素的值,这三个数组(`a`,`b`,`c`)会返回不同的三个值:
|
||||
|
||||
```swift
|
||||
b[0] = -105
|
||||
println(a[0])
|
||||
// 77
|
||||
println(b[0])
|
||||
// -105
|
||||
println(c[0])
|
||||
// 42
|
||||
```
|
||||
|
||||
|
||||
### 判定两个数组是否共用相同元素
|
||||
|
||||
我们通过使用恒等运算符(identity operators)( === 和 !==)来判定两个数组或子数组共用相同的储存空间或元素。
|
||||
|
||||
下面这个示例使用了“等同(identical to)” 运算符(===) 来判定`b`和`c`是否共用相同的数组元素:
|
||||
|
||||
```swift
|
||||
if b === c {
|
||||
println("b and c still share the same array elements.")
|
||||
} else {
|
||||
println("b and c now refer to two independent sets of array elements.")
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
// 输出 "b and c now refer totwo independent sets of array elements."
|
||||
```
|
||||
|
||||
此外,我们还可以使用恒等运算符来判定两个子数组是否共用相同的元素。下面这个示例中,比较了`b`的两个相等的子数组,并且确定了这两个子数组都引用相同的元素:
|
||||
|
||||
```swift
|
||||
if b[0...1] === b[0...1] {
|
||||
println("These two subarrays share the same elements.")
|
||||
} else {
|
||||
println("These two subarrays do not share the same elements.")
|
||||
}
|
||||
// 输出 "These two subarrays share the same elements."
|
||||
```
|
||||
|
||||
### 强制复制数组
|
||||
|
||||
我们通过调用数组的`copy`方法进行强制显性复制。这个方法对数组进行了浅拷贝(shallow copy),并且返回一个包含此拷贝的新数组。
|
||||
|
||||
下面这个示例中定义了一个`names`数组,其包含了七个人名。还定义了一个`copiedNames`变量,用以储存在`names`上调用`copy`方法所返回的结果:
|
||||
|
||||
```swift
|
||||
var names = ["Mohsen", "Hilary", "Justyn", "Amy", "Rich", "Graham", "Vic"]
|
||||
var copiedNames = names.copy()
|
||||
```
|
||||
|
||||
我们可以通过修改一个数组中某元素,并且检查另一个数组中对应元素的方法来判定`names`数组确已被复制。如果你将`copiedNames`中第一个元素从"`Mohsen`"修改为"`Mo`",则`names`数组返回的仍是拷贝发生前的"`Mohsen`":
|
||||
|
||||
```swift
|
||||
copiedName[0] = "Mo"
|
||||
println(name[0])
|
||||
// 输出 "Mohsen"
|
||||
```
|
||||
|
||||
> 注意:
|
||||
如果你仅需要确保你对数组的引用是唯一引用,请调用`unshare`方法,而不是`copy`方法。`unshare`方法仅会在确有必要时才会创建数组拷贝。`copy`方法会在任何时候都创建一个新的拷贝,即使引用已经是唯一引用。
|
||||
|
||||
|
||||
@ -1,428 +1,428 @@
|
||||
> 翻译:shinyzhu
|
||||
> 校对:pp-prog
|
||||
|
||||
# 属性 (Properties)
|
||||
---
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [存储属性(Stored Properties)](#stored_properties)
|
||||
- [计算属性(Computed Properties)](#computed_properties)
|
||||
- [属性监视器(Property Observers)](#property_observers)
|
||||
- [全局变量和局部变量(Global and Local Variables)](global_and_local_variables)
|
||||
- [类型属性(Type Properties)](#type_properties)
|
||||
|
||||
**属性**将值跟特定的类、结构或枚举关联。存储属性存储常量或变量作为实例的一部分,计算属性计算(而不是存储)一个值。计算属性可以用于类、结构体和枚举里,存储属性只能用于类和结构体。
|
||||
|
||||
存储属性和计算属性通常用于特定类型的实例,但是,属性也可以直接用于类型本身,这种属性称为类型属性。
|
||||
|
||||
另外,还可以定义属性监视器来监控属性值的变化,以此来触发一个自定义的操作。属性监视器可以添加到自己写的存储属性上,也可以添加到从父类继承的属性上。
|
||||
|
||||
<a name="stored_properties"></a>
|
||||
## 存储属性
|
||||
|
||||
简单来说,一个存储属性就是存储在特定类或结构体的实例里的一个常量或变量,存储属性可以是*变量存储属性*(用关键字`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)一节。
|
||||
|
||||
下面的例子定义了一个名为`FixedLengthRange`的结构体,它描述了一个在创建后无法修改值域宽度的区间:
|
||||
|
||||
```swift
|
||||
struct FixedLengthRange {
|
||||
var firstValue: Int
|
||||
let length: Int
|
||||
}
|
||||
var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
|
||||
// 该区间表示整数0,1,2
|
||||
rangeOfThreeItems.firstValue = 6
|
||||
// 该区间现在表示整数6,7,8
|
||||
```
|
||||
|
||||
`FixedLengthRange`的实例包含一个名为`firstValue`的变量存储属性和一个名为`length`的常量存储属性。在上面的例子中,`length`在创建实例的时候被赋值,因为它是一个常量存储属性,所以之后无法修改它的值。
|
||||
|
||||
<a name="stored_properties_of_constant_structure_instances"></a>
|
||||
### 常量和存储属性
|
||||
|
||||
如果创建了一个结构体的实例并赋值给一个常量,则无法修改实例的任何属性,即使定义了变量存储属性:
|
||||
|
||||
```swift
|
||||
let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
|
||||
// 该区间表示整数0,1,2,3
|
||||
rangeOfFourItems.firstValue = 6
|
||||
// 尽管 firstValue 是个变量属性,这里还是会报错
|
||||
```
|
||||
|
||||
因为`rangeOfFourItems`声明成了常量(用`let`关键字),即使`firstValue`是一个变量属性,也无法再修改它了。
|
||||
|
||||
这种行为是由于结构体(struct)属于*值类型*。当值类型的实例被声明为常量的时候,它的所有属性也就成了常量。
|
||||
|
||||
属于*引用类型*的类(class)则不一样,把一个引用类型的实例赋给一个常量后,仍然可以修改实例的变量属性。
|
||||
|
||||
<a name="lazy_stored_properties"></a>
|
||||
### 延迟存储属性
|
||||
|
||||
延迟存储属性是指当第一次被调用的时候才会计算其初始值的属性。在属性声明前使用`@lazy`来标示一个延迟存储属性。
|
||||
|
||||
> 注意:
|
||||
> 必须将延迟存储属性声明成变量(使用`var`关键字),因为属性的值在实例构造完成之前可能无法得到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟属性。
|
||||
|
||||
延迟属性很有用,当属性的值依赖于在实例的构造过程结束前无法知道具体值的外部因素时,或者当属性的值需要复杂或大量计算时,可以只在需要的时候来计算它。
|
||||
|
||||
下面的例子使用了延迟存储属性来避免复杂类的不必要的初始化。例子中定义了`DataImporter`和`DataManager`两个类,下面是部分代码:
|
||||
|
||||
```swift
|
||||
class DataImporter {
|
||||
/*
|
||||
DataImporter 是一个将外部文件中的数据导入的类。
|
||||
这个类的初始化会消耗不少时间。
|
||||
*/
|
||||
var fileName = "data.txt"
|
||||
// 这是提供数据导入功能
|
||||
}
|
||||
|
||||
class DataManager {
|
||||
@lazy var importer = DataImporter()
|
||||
var data = String[]()
|
||||
// 这是提供数据管理功能
|
||||
}
|
||||
|
||||
let manager = DataManager()
|
||||
manager.data += "Some data"
|
||||
manager.data += "Some more data"
|
||||
// DataImporter 实例的 importer 属性还没有被创建
|
||||
```
|
||||
|
||||
`DataManager`类包含一个名为`data`的存储属性,初始值是一个空的字符串(`String`)数组。虽然没有写出全部代码,`DataManager`类的目的是管理和提供对这个字符串数组的访问。
|
||||
|
||||
`DataManager`的一个功能是从文件导入数据,该功能由`DataImporter`类提供,`DataImporter`需要消耗不少时间完成初始化:因为它的实例在初始化时可能要打开文件,还要读取文件内容到内存。
|
||||
|
||||
`DataManager`也可能不从文件中导入数据。所以当`DataManager`的实例被创建时,没必要创建一个`DataImporter`的实例,更明智的是当用到`DataImporter`的时候才去创建它。
|
||||
|
||||
由于使用了`@lazy`,`importer`属性只有在第一次被访问的时候才被创建。比如访问它的属性`fileName`时:
|
||||
|
||||
```swift
|
||||
println(manager.importer.fileName)
|
||||
// DataImporter 实例的 importer 属性现在被创建了
|
||||
// 输出 "data.txt”
|
||||
```
|
||||
|
||||
<a name="stored_properties_and_instance_variables"></a>
|
||||
### 存储属性和实例变量
|
||||
|
||||
如果您有过 Objective-C 经验,应该知道有两种方式在类实例存储值和引用。对于属性来说,也可以使用实例变量作为属性值的后端存储。
|
||||
|
||||
Swift 编程语言中把这些理论统一用属性来实现。Swift 中的属性没有对应的实例变量,属性的后端存储也无法直接访问。这就避免了不同场景下访问方式的困扰,同时也将属性的定义简化成一个语句。
|
||||
一个类型中属性的全部信息——包括命名、类型和内存管理特征——都在唯一一个地方(类型定义中)定义。
|
||||
|
||||
<a name="computed_properties"></a>
|
||||
## 计算属性
|
||||
|
||||
除存储属性外,类、结构体和枚举可以定义*计算属性*,计算属性不直接存储值,而是提供一个 getter 来获取值,一个可选的 setter 来间接设置其他属性或变量的值。
|
||||
|
||||
```swift
|
||||
struct Point {
|
||||
var x = 0.0, y = 0.0
|
||||
}
|
||||
struct Size {
|
||||
var width = 0.0, height = 0.0
|
||||
}
|
||||
struct Rect {
|
||||
var origin = Point()
|
||||
var size = Size()
|
||||
var center: Point {
|
||||
get {
|
||||
let centerX = origin.x + (size.width / 2)
|
||||
let centerY = origin.y + (size.height / 2)
|
||||
return Point(x: centerX, y: centerY)
|
||||
}
|
||||
set(newCenter) {
|
||||
origin.x = newCenter.x - (size.width / 2)
|
||||
origin.y = newCenter.y - (size.height / 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
var square = Rect(origin: Point(x: 0.0, y: 0.0),
|
||||
size: Size(width: 10.0, height: 10.0))
|
||||
let initialSquareCenter = square.center
|
||||
square.center = Point(x: 15.0, y: 15.0)
|
||||
println("square.origin is now at (\(square.origin.x), \(square.origin.y))")
|
||||
// 输出 "square.origin is now at (10.0, 10.0)”
|
||||
```
|
||||
|
||||
这个例子定义了 3 个几何形状的结构体:
|
||||
|
||||
- `Point`封装了一个`(x, y)`的坐标
|
||||
- `Size`封装了一个`width`和`height`
|
||||
- `Rect`表示一个有原点和尺寸的矩形
|
||||
|
||||
`Rect`也提供了一个名为`center`的计算属性。一个矩形的中心点可以从原点和尺寸来算出,所以不需要将它以显式声明的`Point`来保存。`Rect`的计算属性`center`提供了自定义的 getter 和 setter 来获取和设置矩形的中心点,就像它有一个存储属性一样。
|
||||
|
||||
例子中接下来创建了一个名为`square`的`Rect`实例,初始值原点是`(0, 0)`,宽度高度都是`10`。如图所示蓝色正方形。
|
||||
|
||||
`square`的`center`属性可以通过点运算符(`square.center`)来访问,这会调用 getter 来获取属性的值。跟直接返回已经存在的值不同,getter 实际上通过计算然后返回一个新的`Point`来表示`square`的中心点。如代码所示,它正确返回了中心点`(5, 5)`。
|
||||
|
||||
`center`属性之后被设置了一个新的值`(15, 15)`,表示向右上方移动正方形到如图所示橙色正方形的位置。设置属性`center`的值会调用 setter 来修改属性`origin`的`x`和`y`的值,从而实现移动正方形到新的位置。
|
||||
|
||||
<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" />
|
||||
|
||||
<a name="shorthand_setter_declaration"></a>
|
||||
### 便捷 setter 声明
|
||||
|
||||
如果计算属性的 setter 没有定义表示新值的参数名,则可以使用默认名称`newValue`。下面是使用了便捷 setter 声明的`Rect`结构体代码:
|
||||
|
||||
```swift
|
||||
struct AlternativeRect {
|
||||
var origin = Point()
|
||||
var size = Size()
|
||||
var center: Point {
|
||||
get {
|
||||
let centerX = origin.x + (size.width / 2)
|
||||
let centerY = origin.y + (size.height / 2)
|
||||
return Point(x: centerX, y: centerY)
|
||||
}
|
||||
set {
|
||||
origin.x = newValue.x - (size.width / 2)
|
||||
origin.y = newValue.y - (size.height / 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<a name="readonly_computed_properties"></a>
|
||||
### 只读计算属性
|
||||
|
||||
只有 getter 没有 setter 的计算属性就是*只读计算属性*。只读计算属性总是返回一个值,可以通过点运算符访问,但不能设置新的值。
|
||||
|
||||
<<<<<<< HEAD
|
||||
> 注意:
|
||||
> 必须使用`var`关键字定义计算属性,包括只读计算属性,因为他们的值不是固定的。`let`关键字只用来声明常量属性,表示初始化后再也无法修改的值。
|
||||
=======
|
||||
> 注意:
|
||||
>
|
||||
> 必须使用`var`关键字定义计算属性,包括只读计算属性,因为它们的值不是固定的。`let`关键字只用来声明常量属性,表示初始化后再也无法修改的值。
|
||||
>>>>>>> a516af6a531a104ec88da0d236ecf389a5ec72af
|
||||
|
||||
只读计算属性的声明可以去掉`get`关键字和花括号:
|
||||
|
||||
```swift
|
||||
struct Cuboid {
|
||||
var width = 0.0, height = 0.0, depth = 0.0
|
||||
var volume: Double {
|
||||
return width * height * depth
|
||||
}
|
||||
}
|
||||
let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
|
||||
println("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`提供一个只读计算属性来让外部用户直接获取体积是很有用的。
|
||||
|
||||
<a name="property_observers"></a>
|
||||
## 属性监视器
|
||||
|
||||
*属性监视器*监控和响应属性值的变化,每次属性被设置值的时候都会调用属性监视器,甚至新的值和现在的值相同的时候也不例外。
|
||||
|
||||
可以为除了延迟存储属性之外的其他存储属性添加属性监视器,也可以通过重载属性的方式为继承的属性(包括存储属性和计算属性)添加属性监视器。属性重载请参考[继承](chapter/13_Inheritance.html)一章的[重载](chapter/13_Inheritance.html#overriding)。
|
||||
|
||||
> 注意:
|
||||
> 不需要为无法重载的计算属性添加属性监视器,因为可以通过 setter 直接监控和响应值的变化。
|
||||
|
||||
可以为属性添加如下的一个或全部监视器:
|
||||
|
||||
- `willSet`在设置新的值之前调用
|
||||
- `didSet`在新的值被设置之后立即调用
|
||||
|
||||
`willSet`监视器会将新的属性值作为固定参数传入,在`willSet`的实现代码中可以为这个参数指定一个名称,如果不指定则参数仍然可用,这时使用默认名称`newValue`表示。
|
||||
|
||||
类似地,`didSet`监视器会将旧的属性值作为参数传入,可以为该参数命名或者使用默认参数名`oldValue`。
|
||||
|
||||
<<<<<<< HEAD
|
||||
> 注意:
|
||||
> `willSet`和`didSet`监视器在属性初始化过程中不会被调用,他们只会当属性的值在初始化之外的地方被设置时被调用。
|
||||
=======
|
||||
> 注意:
|
||||
>
|
||||
> `willSet`和`didSet`监视器在属性初始化过程中不会被调用,它们只会当属性的值在初始化之外的地方被设置时被调用。
|
||||
>>>>>>> a516af6a531a104ec88da0d236ecf389a5ec72af
|
||||
|
||||
这里是一个`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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let stepCounter = StepCounter()
|
||||
stepCounter.totalSteps = 200
|
||||
// About to set totalSteps to 200
|
||||
// Added 200 steps
|
||||
stepCounter.totalSteps = 360
|
||||
// About to set totalSteps to 360
|
||||
// Added 160 steps
|
||||
stepCounter.totalSteps = 896
|
||||
// About to set totalSteps to 896
|
||||
// Added 536 steps
|
||||
```
|
||||
|
||||
`StepCounter`类定义了一个`Int`类型的属性`totalSteps`,它是一个存储属性,包含`willSet`和`didSet`监视器。
|
||||
|
||||
当`totalSteps`设置新值的时候,它的`willSet`和`didSet`监视器都会被调用,甚至当新的值和现在的值完全相同也会调用。
|
||||
|
||||
例子中的`willSet`监视器将表示新值的参数自定义为`newTotalSteps`,这个监视器只是简单的将新的值输出。
|
||||
|
||||
`didSet`监视器在`totalSteps`的值改变后被调用,它把新的值和旧的值进行对比,如果总的步数增加了,就输出一个消息表示增加了多少步。`didSet`没有提供自定义名称,所以默认值`oldValue`表示旧值的参数名。
|
||||
|
||||
> 注意:
|
||||
> 如果在`didSet`监视器里为属性赋值,这个值会替换监视器之前设置的值。
|
||||
|
||||
<a name="global_and_local_variables"></a>
|
||||
##全局变量和局部变量
|
||||
|
||||
计算属性和属性监视器所描述的模式也可以用于*全局变量*和*局部变量*,全局变量是在函数、方法、闭包或任何类型之外定义的变量,局部变量是在函数、方法或闭包内部定义的变量。
|
||||
|
||||
前面章节提到的全局或局部变量都属于存储型变量,跟存储属性类似,它提供特定类型的存储空间,并允许读取和写入。
|
||||
|
||||
另外,在全局或局部范围都可以定义计算型变量和为存储型变量定义监视器,计算型变量跟计算属性一样,返回一个计算的值而不是存储值,声明格式也完全一样。
|
||||
|
||||
> 注意:
|
||||
> 全局的常量或变量都是延迟计算的,跟[延迟存储属性](#lazy_stored_properties)相似,不同的地方在于,全局的常量或变量不需要标记`@lazy`特性。
|
||||
> 局部范围的常量或变量不会延迟计算。
|
||||
|
||||
<a name="type_properties"></a>
|
||||
##类型属性
|
||||
|
||||
实例的属性属于一个特定类型实例,每次类型实例化后都拥有自己的一套属性值,实例之间的属性相互独立。
|
||||
|
||||
也可以为类型本身定义属性,不管类型有多少个实例,这些属性都只有唯一一份。这种属性就是*类型属性*。
|
||||
|
||||
类型属性用于定义特定类型所有实例共享的数据,比如所有实例都能用的一个常量(就像 C 语言中的静态常量),或者所有实例都能访问的一个变量(就像 C 语言中的静态变量)。
|
||||
|
||||
对于值类型(指结构体和枚举)可以定义存储型和计算型类型属性,对于类(class)则只能定义计算型类型属性。
|
||||
|
||||
值类型的存储型类型属性可以是变量或常量,计算型类型属性跟实例的计算属性一样定义成变量属性。
|
||||
|
||||
> 注意:
|
||||
> 跟实例的存储属性不同,必须给存储型类型属性指定默认值,因为类型本身无法在初始化过程中使用构造器给类型属性赋值。
|
||||
|
||||
<a name="type_property_syntax"></a>
|
||||
###类型属性语法
|
||||
|
||||
在 C 或 Objective-C 中,静态常量和静态变量的定义是通过特定类型加上`global`关键字。在 Swift 编程语言中,类型属性是作为类型定义的一部分写在类型最外层的花括号内,因此它的作用范围也就在类型支持的范围内。
|
||||
|
||||
使用关键字`static`来定义值类型的类型属性,关键字`class`来为类(class)定义类型属性。下面的例子演示了存储型和计算型类型属性的语法:
|
||||
|
||||
```swift
|
||||
struct SomeStructure {
|
||||
static var storedTypeProperty = "Some value."
|
||||
static var computedTypeProperty: Int {
|
||||
// 这里返回一个 Int 值
|
||||
}
|
||||
}
|
||||
enum SomeEnumeration {
|
||||
static var storedTypeProperty = "Some value."
|
||||
static var computedTypeProperty: Int {
|
||||
// 这里返回一个 Int 值
|
||||
}
|
||||
}
|
||||
class SomeClass {
|
||||
class var computedTypeProperty: Int {
|
||||
// 这里返回一个 Int 值
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> 例子中的计算型类型属性是只读的,但也可以定义可读可写的计算型类型属性,跟实例计算属性的语法类似。
|
||||
|
||||
<a name="querying_and_setting_type_properties"></a>
|
||||
###获取和设置类型属性的值
|
||||
|
||||
跟实例的属性一样,类型属性的访问也是通过点运算符来进行,但是,类型属性是通过类型本身来获取和设置,而不是通过实例。比如:
|
||||
|
||||
```swift
|
||||
println(SomeClass.computedTypeProperty)
|
||||
// 输出 "42"
|
||||
|
||||
println(SomeStructure.storedTypeProperty)
|
||||
// 输出 "Some value."
|
||||
SomeStructure.storedTypeProperty = "Another value."
|
||||
println(SomeStructure.storedTypeProperty)
|
||||
// 输出 "Another value.”
|
||||
```
|
||||
|
||||
下面的例子定义了一个结构体,使用两个存储型类型属性来表示多个声道的声音电平值,每个声道有一个 0 到 10 之间的整数表示声音电平值。
|
||||
|
||||
后面的图表展示了如何联合使用两个声道来表示一个立体声的声音电平值。当声道的电平值是 0,没有一个灯会亮;当声道的电平值是 10,所有灯点亮。本图中,左声道的电平是 9,右声道的电平是 7。
|
||||
|
||||
<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`结构体来表示:
|
||||
|
||||
```swift
|
||||
struct AudioChannel {
|
||||
static let thresholdLevel = 10
|
||||
static var maxInputLevelForAllChannels = 0
|
||||
var currentLevel: Int = 0 {
|
||||
didSet {
|
||||
if currentLevel > AudioChannel.thresholdLevel {
|
||||
// 将新电平值设置为阀值
|
||||
currentLevel = AudioChannel.thresholdLevel
|
||||
}
|
||||
if currentLevel > AudioChannel.maxInputLevelForAllChannels {
|
||||
// 存储当前电平值作为新的最大输入电平
|
||||
AudioChannel.maxInputLevelForAllChannels = currentLevel
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
结构`AudioChannel`定义了 2 个存储型类型属性来实现上述功能。第一个是`thresholdLevel`,表示声音电平的最大上限阈值,它是一个取值为 10 的常量,对所有实例都可见,如果声音电平高于 10,则取最大上限值 10(见后面描述)。
|
||||
|
||||
第二个类型属性是变量存储型属性`maxInputLevelForAllChannels`,它用来表示所有`AudioChannel`实例的电平值的最大值,初始值是 0。
|
||||
|
||||
`AudioChannel`也定义了一个名为`currentLevel`的实例存储属性,表示当前声道现在的电平值,取值为 0 到 10。
|
||||
|
||||
属性`currentLevel`包含`didSet`属性监视器来检查每次新设置后的属性值,有如下两个检查:
|
||||
|
||||
- 如果`currentLevel`的新值大于允许的阈值`thresholdLevel`,属性监视器将`currentLevel`的值限定为阈值`thresholdLevel`。
|
||||
- 如果修正后的`currentLevel`值大于任何之前任意`AudioChannel`实例中的值,属性监视器将新值保存在静态属性`maxInputLevelForAllChannels`中。
|
||||
|
||||
> 注意:
|
||||
> 在第一个检查过程中,`didSet`属性监视器将`currentLevel`设置成了不同的值,但这时不会再次调用属性监视器。
|
||||
|
||||
可以使用结构体`AudioChannel`来创建表示立体声系统的两个声道`leftChannel`和`rightChannel`:
|
||||
|
||||
```swift
|
||||
var leftChannel = AudioChannel()
|
||||
var rightChannel = AudioChannel()
|
||||
```
|
||||
|
||||
如果将左声道的电平设置成 7,类型属性`maxInputLevelForAllChannels`也会更新成 7:
|
||||
|
||||
```swift
|
||||
leftChannel.currentLevel = 7
|
||||
println(leftChannel.currentLevel)
|
||||
// 输出 "7"
|
||||
println(AudioChannel.maxInputLevelForAllChannels)
|
||||
// 输出 "7"
|
||||
```
|
||||
|
||||
如果试图将右声道的电平设置成 11,则会将右声道的`currentLevel`修正到最大值 10,同时`maxInputLevelForAllChannels`的值也会更新到 10:
|
||||
|
||||
```swift
|
||||
rightChannel.currentLevel = 11
|
||||
println(rightChannel.currentLevel)
|
||||
// 输出 "10"
|
||||
println(AudioChannel.maxInputLevelForAllChannels)
|
||||
// 输出 "10"
|
||||
```
|
||||
> 翻译:[shinyzhu](https://github.com/shinyzhu)
|
||||
> 校对:[pp-prog](https://github.com/pp-prog)
|
||||
|
||||
# 属性 (Properties)
|
||||
---
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [存储属性(Stored Properties)](#stored_properties)
|
||||
- [计算属性(Computed Properties)](#computed_properties)
|
||||
- [属性监视器(Property Observers)](#property_observers)
|
||||
- [全局变量和局部变量(Global and Local Variables)](global_and_local_variables)
|
||||
- [类型属性(Type Properties)](#type_properties)
|
||||
|
||||
**属性**将值跟特定的类、结构或枚举关联。存储属性存储常量或变量作为实例的一部分,计算属性计算(而不是存储)一个值。计算属性可以用于类、结构体和枚举里,存储属性只能用于类和结构体。
|
||||
|
||||
存储属性和计算属性通常用于特定类型的实例,但是,属性也可以直接用于类型本身,这种属性称为类型属性。
|
||||
|
||||
另外,还可以定义属性监视器来监控属性值的变化,以此来触发一个自定义的操作。属性监视器可以添加到自己写的存储属性上,也可以添加到从父类继承的属性上。
|
||||
|
||||
<a name="stored_properties"></a>
|
||||
## 存储属性
|
||||
|
||||
简单来说,一个存储属性就是存储在特定类或结构体的实例里的一个常量或变量,存储属性可以是*变量存储属性*(用关键字`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)一节。
|
||||
|
||||
下面的例子定义了一个名为`FixedLengthRange`的结构体,它描述了一个在创建后无法修改值域宽度的区间:
|
||||
|
||||
```swift
|
||||
struct FixedLengthRange {
|
||||
var firstValue: Int
|
||||
let length: Int
|
||||
}
|
||||
var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
|
||||
// 该区间表示整数0,1,2
|
||||
rangeOfThreeItems.firstValue = 6
|
||||
// 该区间现在表示整数6,7,8
|
||||
```
|
||||
|
||||
`FixedLengthRange`的实例包含一个名为`firstValue`的变量存储属性和一个名为`length`的常量存储属性。在上面的例子中,`length`在创建实例的时候被赋值,因为它是一个常量存储属性,所以之后无法修改它的值。
|
||||
|
||||
<a name="stored_properties_of_constant_structure_instances"></a>
|
||||
### 常量和存储属性
|
||||
|
||||
如果创建了一个结构体的实例并赋值给一个常量,则无法修改实例的任何属性,即使定义了变量存储属性:
|
||||
|
||||
```swift
|
||||
let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
|
||||
// 该区间表示整数0,1,2,3
|
||||
rangeOfFourItems.firstValue = 6
|
||||
// 尽管 firstValue 是个变量属性,这里还是会报错
|
||||
```
|
||||
|
||||
因为`rangeOfFourItems`声明成了常量(用`let`关键字),即使`firstValue`是一个变量属性,也无法再修改它了。
|
||||
|
||||
这种行为是由于结构体(struct)属于*值类型*。当值类型的实例被声明为常量的时候,它的所有属性也就成了常量。
|
||||
|
||||
属于*引用类型*的类(class)则不一样,把一个引用类型的实例赋给一个常量后,仍然可以修改实例的变量属性。
|
||||
|
||||
<a name="lazy_stored_properties"></a>
|
||||
### 延迟存储属性
|
||||
|
||||
延迟存储属性是指当第一次被调用的时候才会计算其初始值的属性。在属性声明前使用`@lazy`来标示一个延迟存储属性。
|
||||
|
||||
> 注意:
|
||||
> 必须将延迟存储属性声明成变量(使用`var`关键字),因为属性的值在实例构造完成之前可能无法得到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟属性。
|
||||
|
||||
延迟属性很有用,当属性的值依赖于在实例的构造过程结束前无法知道具体值的外部因素时,或者当属性的值需要复杂或大量计算时,可以只在需要的时候来计算它。
|
||||
|
||||
下面的例子使用了延迟存储属性来避免复杂类的不必要的初始化。例子中定义了`DataImporter`和`DataManager`两个类,下面是部分代码:
|
||||
|
||||
```swift
|
||||
class DataImporter {
|
||||
/*
|
||||
DataImporter 是一个将外部文件中的数据导入的类。
|
||||
这个类的初始化会消耗不少时间。
|
||||
*/
|
||||
var fileName = "data.txt"
|
||||
// 这是提供数据导入功能
|
||||
}
|
||||
|
||||
class DataManager {
|
||||
@lazy var importer = DataImporter()
|
||||
var data = String[]()
|
||||
// 这是提供数据管理功能
|
||||
}
|
||||
|
||||
let manager = DataManager()
|
||||
manager.data += "Some data"
|
||||
manager.data += "Some more data"
|
||||
// DataImporter 实例的 importer 属性还没有被创建
|
||||
```
|
||||
|
||||
`DataManager`类包含一个名为`data`的存储属性,初始值是一个空的字符串(`String`)数组。虽然没有写出全部代码,`DataManager`类的目的是管理和提供对这个字符串数组的访问。
|
||||
|
||||
`DataManager`的一个功能是从文件导入数据,该功能由`DataImporter`类提供,`DataImporter`需要消耗不少时间完成初始化:因为它的实例在初始化时可能要打开文件,还要读取文件内容到内存。
|
||||
|
||||
`DataManager`也可能不从文件中导入数据。所以当`DataManager`的实例被创建时,没必要创建一个`DataImporter`的实例,更明智的是当用到`DataImporter`的时候才去创建它。
|
||||
|
||||
由于使用了`@lazy`,`importer`属性只有在第一次被访问的时候才被创建。比如访问它的属性`fileName`时:
|
||||
|
||||
```swift
|
||||
println(manager.importer.fileName)
|
||||
// DataImporter 实例的 importer 属性现在被创建了
|
||||
// 输出 "data.txt”
|
||||
```
|
||||
|
||||
<a name="stored_properties_and_instance_variables"></a>
|
||||
### 存储属性和实例变量
|
||||
|
||||
如果您有过 Objective-C 经验,应该知道有两种方式在类实例存储值和引用。对于属性来说,也可以使用实例变量作为属性值的后端存储。
|
||||
|
||||
Swift 编程语言中把这些理论统一用属性来实现。Swift 中的属性没有对应的实例变量,属性的后端存储也无法直接访问。这就避免了不同场景下访问方式的困扰,同时也将属性的定义简化成一个语句。
|
||||
一个类型中属性的全部信息——包括命名、类型和内存管理特征——都在唯一一个地方(类型定义中)定义。
|
||||
|
||||
<a name="computed_properties"></a>
|
||||
## 计算属性
|
||||
|
||||
除存储属性外,类、结构体和枚举可以定义*计算属性*,计算属性不直接存储值,而是提供一个 getter 来获取值,一个可选的 setter 来间接设置其他属性或变量的值。
|
||||
|
||||
```swift
|
||||
struct Point {
|
||||
var x = 0.0, y = 0.0
|
||||
}
|
||||
struct Size {
|
||||
var width = 0.0, height = 0.0
|
||||
}
|
||||
struct Rect {
|
||||
var origin = Point()
|
||||
var size = Size()
|
||||
var center: Point {
|
||||
get {
|
||||
let centerX = origin.x + (size.width / 2)
|
||||
let centerY = origin.y + (size.height / 2)
|
||||
return Point(x: centerX, y: centerY)
|
||||
}
|
||||
set(newCenter) {
|
||||
origin.x = newCenter.x - (size.width / 2)
|
||||
origin.y = newCenter.y - (size.height / 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
var square = Rect(origin: Point(x: 0.0, y: 0.0),
|
||||
size: Size(width: 10.0, height: 10.0))
|
||||
let initialSquareCenter = square.center
|
||||
square.center = Point(x: 15.0, y: 15.0)
|
||||
println("square.origin is now at (\(square.origin.x), \(square.origin.y))")
|
||||
// 输出 "square.origin is now at (10.0, 10.0)”
|
||||
```
|
||||
|
||||
这个例子定义了 3 个几何形状的结构体:
|
||||
|
||||
- `Point`封装了一个`(x, y)`的坐标
|
||||
- `Size`封装了一个`width`和`height`
|
||||
- `Rect`表示一个有原点和尺寸的矩形
|
||||
|
||||
`Rect`也提供了一个名为`center`的计算属性。一个矩形的中心点可以从原点和尺寸来算出,所以不需要将它以显式声明的`Point`来保存。`Rect`的计算属性`center`提供了自定义的 getter 和 setter 来获取和设置矩形的中心点,就像它有一个存储属性一样。
|
||||
|
||||
例子中接下来创建了一个名为`square`的`Rect`实例,初始值原点是`(0, 0)`,宽度高度都是`10`。如图所示蓝色正方形。
|
||||
|
||||
`square`的`center`属性可以通过点运算符(`square.center`)来访问,这会调用 getter 来获取属性的值。跟直接返回已经存在的值不同,getter 实际上通过计算然后返回一个新的`Point`来表示`square`的中心点。如代码所示,它正确返回了中心点`(5, 5)`。
|
||||
|
||||
`center`属性之后被设置了一个新的值`(15, 15)`,表示向右上方移动正方形到如图所示橙色正方形的位置。设置属性`center`的值会调用 setter 来修改属性`origin`的`x`和`y`的值,从而实现移动正方形到新的位置。
|
||||
|
||||
<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" />
|
||||
|
||||
<a name="shorthand_setter_declaration"></a>
|
||||
### 便捷 setter 声明
|
||||
|
||||
如果计算属性的 setter 没有定义表示新值的参数名,则可以使用默认名称`newValue`。下面是使用了便捷 setter 声明的`Rect`结构体代码:
|
||||
|
||||
```swift
|
||||
struct AlternativeRect {
|
||||
var origin = Point()
|
||||
var size = Size()
|
||||
var center: Point {
|
||||
get {
|
||||
let centerX = origin.x + (size.width / 2)
|
||||
let centerY = origin.y + (size.height / 2)
|
||||
return Point(x: centerX, y: centerY)
|
||||
}
|
||||
set {
|
||||
origin.x = newValue.x - (size.width / 2)
|
||||
origin.y = newValue.y - (size.height / 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<a name="readonly_computed_properties"></a>
|
||||
### 只读计算属性
|
||||
|
||||
只有 getter 没有 setter 的计算属性就是*只读计算属性*。只读计算属性总是返回一个值,可以通过点运算符访问,但不能设置新的值。
|
||||
|
||||
<<<<<<< HEAD
|
||||
> 注意:
|
||||
> 必须使用`var`关键字定义计算属性,包括只读计算属性,因为他们的值不是固定的。`let`关键字只用来声明常量属性,表示初始化后再也无法修改的值。
|
||||
=======
|
||||
> 注意:
|
||||
>
|
||||
> 必须使用`var`关键字定义计算属性,包括只读计算属性,因为它们的值不是固定的。`let`关键字只用来声明常量属性,表示初始化后再也无法修改的值。
|
||||
>>>>>>> a516af6a531a104ec88da0d236ecf389a5ec72af
|
||||
|
||||
只读计算属性的声明可以去掉`get`关键字和花括号:
|
||||
|
||||
```swift
|
||||
struct Cuboid {
|
||||
var width = 0.0, height = 0.0, depth = 0.0
|
||||
var volume: Double {
|
||||
return width * height * depth
|
||||
}
|
||||
}
|
||||
let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
|
||||
println("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`提供一个只读计算属性来让外部用户直接获取体积是很有用的。
|
||||
|
||||
<a name="property_observers"></a>
|
||||
## 属性监视器
|
||||
|
||||
*属性监视器*监控和响应属性值的变化,每次属性被设置值的时候都会调用属性监视器,甚至新的值和现在的值相同的时候也不例外。
|
||||
|
||||
可以为除了延迟存储属性之外的其他存储属性添加属性监视器,也可以通过重载属性的方式为继承的属性(包括存储属性和计算属性)添加属性监视器。属性重载请参考[继承](chapter/13_Inheritance.html)一章的[重载](chapter/13_Inheritance.html#overriding)。
|
||||
|
||||
> 注意:
|
||||
> 不需要为无法重载的计算属性添加属性监视器,因为可以通过 setter 直接监控和响应值的变化。
|
||||
|
||||
可以为属性添加如下的一个或全部监视器:
|
||||
|
||||
- `willSet`在设置新的值之前调用
|
||||
- `didSet`在新的值被设置之后立即调用
|
||||
|
||||
`willSet`监视器会将新的属性值作为固定参数传入,在`willSet`的实现代码中可以为这个参数指定一个名称,如果不指定则参数仍然可用,这时使用默认名称`newValue`表示。
|
||||
|
||||
类似地,`didSet`监视器会将旧的属性值作为参数传入,可以为该参数命名或者使用默认参数名`oldValue`。
|
||||
|
||||
<<<<<<< HEAD
|
||||
> 注意:
|
||||
> `willSet`和`didSet`监视器在属性初始化过程中不会被调用,他们只会当属性的值在初始化之外的地方被设置时被调用。
|
||||
=======
|
||||
> 注意:
|
||||
>
|
||||
> `willSet`和`didSet`监视器在属性初始化过程中不会被调用,它们只会当属性的值在初始化之外的地方被设置时被调用。
|
||||
>>>>>>> a516af6a531a104ec88da0d236ecf389a5ec72af
|
||||
|
||||
这里是一个`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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let stepCounter = StepCounter()
|
||||
stepCounter.totalSteps = 200
|
||||
// About to set totalSteps to 200
|
||||
// Added 200 steps
|
||||
stepCounter.totalSteps = 360
|
||||
// About to set totalSteps to 360
|
||||
// Added 160 steps
|
||||
stepCounter.totalSteps = 896
|
||||
// About to set totalSteps to 896
|
||||
// Added 536 steps
|
||||
```
|
||||
|
||||
`StepCounter`类定义了一个`Int`类型的属性`totalSteps`,它是一个存储属性,包含`willSet`和`didSet`监视器。
|
||||
|
||||
当`totalSteps`设置新值的时候,它的`willSet`和`didSet`监视器都会被调用,甚至当新的值和现在的值完全相同也会调用。
|
||||
|
||||
例子中的`willSet`监视器将表示新值的参数自定义为`newTotalSteps`,这个监视器只是简单的将新的值输出。
|
||||
|
||||
`didSet`监视器在`totalSteps`的值改变后被调用,它把新的值和旧的值进行对比,如果总的步数增加了,就输出一个消息表示增加了多少步。`didSet`没有提供自定义名称,所以默认值`oldValue`表示旧值的参数名。
|
||||
|
||||
> 注意:
|
||||
> 如果在`didSet`监视器里为属性赋值,这个值会替换监视器之前设置的值。
|
||||
|
||||
<a name="global_and_local_variables"></a>
|
||||
##全局变量和局部变量
|
||||
|
||||
计算属性和属性监视器所描述的模式也可以用于*全局变量*和*局部变量*,全局变量是在函数、方法、闭包或任何类型之外定义的变量,局部变量是在函数、方法或闭包内部定义的变量。
|
||||
|
||||
前面章节提到的全局或局部变量都属于存储型变量,跟存储属性类似,它提供特定类型的存储空间,并允许读取和写入。
|
||||
|
||||
另外,在全局或局部范围都可以定义计算型变量和为存储型变量定义监视器,计算型变量跟计算属性一样,返回一个计算的值而不是存储值,声明格式也完全一样。
|
||||
|
||||
> 注意:
|
||||
> 全局的常量或变量都是延迟计算的,跟[延迟存储属性](#lazy_stored_properties)相似,不同的地方在于,全局的常量或变量不需要标记`@lazy`特性。
|
||||
> 局部范围的常量或变量不会延迟计算。
|
||||
|
||||
<a name="type_properties"></a>
|
||||
##类型属性
|
||||
|
||||
实例的属性属于一个特定类型实例,每次类型实例化后都拥有自己的一套属性值,实例之间的属性相互独立。
|
||||
|
||||
也可以为类型本身定义属性,不管类型有多少个实例,这些属性都只有唯一一份。这种属性就是*类型属性*。
|
||||
|
||||
类型属性用于定义特定类型所有实例共享的数据,比如所有实例都能用的一个常量(就像 C 语言中的静态常量),或者所有实例都能访问的一个变量(就像 C 语言中的静态变量)。
|
||||
|
||||
对于值类型(指结构体和枚举)可以定义存储型和计算型类型属性,对于类(class)则只能定义计算型类型属性。
|
||||
|
||||
值类型的存储型类型属性可以是变量或常量,计算型类型属性跟实例的计算属性一样定义成变量属性。
|
||||
|
||||
> 注意:
|
||||
> 跟实例的存储属性不同,必须给存储型类型属性指定默认值,因为类型本身无法在初始化过程中使用构造器给类型属性赋值。
|
||||
|
||||
<a name="type_property_syntax"></a>
|
||||
###类型属性语法
|
||||
|
||||
在 C 或 Objective-C 中,静态常量和静态变量的定义是通过特定类型加上`global`关键字。在 Swift 编程语言中,类型属性是作为类型定义的一部分写在类型最外层的花括号内,因此它的作用范围也就在类型支持的范围内。
|
||||
|
||||
使用关键字`static`来定义值类型的类型属性,关键字`class`来为类(class)定义类型属性。下面的例子演示了存储型和计算型类型属性的语法:
|
||||
|
||||
```swift
|
||||
struct SomeStructure {
|
||||
static var storedTypeProperty = "Some value."
|
||||
static var computedTypeProperty: Int {
|
||||
// 这里返回一个 Int 值
|
||||
}
|
||||
}
|
||||
enum SomeEnumeration {
|
||||
static var storedTypeProperty = "Some value."
|
||||
static var computedTypeProperty: Int {
|
||||
// 这里返回一个 Int 值
|
||||
}
|
||||
}
|
||||
class SomeClass {
|
||||
class var computedTypeProperty: Int {
|
||||
// 这里返回一个 Int 值
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 注意:
|
||||
> 例子中的计算型类型属性是只读的,但也可以定义可读可写的计算型类型属性,跟实例计算属性的语法类似。
|
||||
|
||||
<a name="querying_and_setting_type_properties"></a>
|
||||
###获取和设置类型属性的值
|
||||
|
||||
跟实例的属性一样,类型属性的访问也是通过点运算符来进行,但是,类型属性是通过类型本身来获取和设置,而不是通过实例。比如:
|
||||
|
||||
```swift
|
||||
println(SomeClass.computedTypeProperty)
|
||||
// 输出 "42"
|
||||
|
||||
println(SomeStructure.storedTypeProperty)
|
||||
// 输出 "Some value."
|
||||
SomeStructure.storedTypeProperty = "Another value."
|
||||
println(SomeStructure.storedTypeProperty)
|
||||
// 输出 "Another value.”
|
||||
```
|
||||
|
||||
下面的例子定义了一个结构体,使用两个存储型类型属性来表示多个声道的声音电平值,每个声道有一个 0 到 10 之间的整数表示声音电平值。
|
||||
|
||||
后面的图表展示了如何联合使用两个声道来表示一个立体声的声音电平值。当声道的电平值是 0,没有一个灯会亮;当声道的电平值是 10,所有灯点亮。本图中,左声道的电平是 9,右声道的电平是 7。
|
||||
|
||||
<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`结构体来表示:
|
||||
|
||||
```swift
|
||||
struct AudioChannel {
|
||||
static let thresholdLevel = 10
|
||||
static var maxInputLevelForAllChannels = 0
|
||||
var currentLevel: Int = 0 {
|
||||
didSet {
|
||||
if currentLevel > AudioChannel.thresholdLevel {
|
||||
// 将新电平值设置为阀值
|
||||
currentLevel = AudioChannel.thresholdLevel
|
||||
}
|
||||
if currentLevel > AudioChannel.maxInputLevelForAllChannels {
|
||||
// 存储当前电平值作为新的最大输入电平
|
||||
AudioChannel.maxInputLevelForAllChannels = currentLevel
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
结构`AudioChannel`定义了 2 个存储型类型属性来实现上述功能。第一个是`thresholdLevel`,表示声音电平的最大上限阈值,它是一个取值为 10 的常量,对所有实例都可见,如果声音电平高于 10,则取最大上限值 10(见后面描述)。
|
||||
|
||||
第二个类型属性是变量存储型属性`maxInputLevelForAllChannels`,它用来表示所有`AudioChannel`实例的电平值的最大值,初始值是 0。
|
||||
|
||||
`AudioChannel`也定义了一个名为`currentLevel`的实例存储属性,表示当前声道现在的电平值,取值为 0 到 10。
|
||||
|
||||
属性`currentLevel`包含`didSet`属性监视器来检查每次新设置后的属性值,有如下两个检查:
|
||||
|
||||
- 如果`currentLevel`的新值大于允许的阈值`thresholdLevel`,属性监视器将`currentLevel`的值限定为阈值`thresholdLevel`。
|
||||
- 如果修正后的`currentLevel`值大于任何之前任意`AudioChannel`实例中的值,属性监视器将新值保存在静态属性`maxInputLevelForAllChannels`中。
|
||||
|
||||
> 注意:
|
||||
> 在第一个检查过程中,`didSet`属性监视器将`currentLevel`设置成了不同的值,但这时不会再次调用属性监视器。
|
||||
|
||||
可以使用结构体`AudioChannel`来创建表示立体声系统的两个声道`leftChannel`和`rightChannel`:
|
||||
|
||||
```swift
|
||||
var leftChannel = AudioChannel()
|
||||
var rightChannel = AudioChannel()
|
||||
```
|
||||
|
||||
如果将左声道的电平设置成 7,类型属性`maxInputLevelForAllChannels`也会更新成 7:
|
||||
|
||||
```swift
|
||||
leftChannel.currentLevel = 7
|
||||
println(leftChannel.currentLevel)
|
||||
// 输出 "7"
|
||||
println(AudioChannel.maxInputLevelForAllChannels)
|
||||
// 输出 "7"
|
||||
```
|
||||
|
||||
如果试图将右声道的电平设置成 11,则会将右声道的`currentLevel`修正到最大值 10,同时`maxInputLevelForAllChannels`的值也会更新到 10:
|
||||
|
||||
```swift
|
||||
rightChannel.currentLevel = 11
|
||||
println(rightChannel.currentLevel)
|
||||
// 输出 "10"
|
||||
println(AudioChannel.maxInputLevelForAllChannels)
|
||||
// 输出 "10"
|
||||
```
|
||||
|
||||
@ -1,309 +1,309 @@
|
||||
> 翻译:pp-prog
|
||||
> 校对:zqp
|
||||
|
||||
# 方法(Methods)
|
||||
-----------------
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [实例方法(Instance Methods](#instance_methods)
|
||||
- [类型方法(Type Methods)](#type_methods)
|
||||
|
||||
**方法**是与某些特定类型相关联的函数。类、结构体、枚举都可以定义实例方法;实例方法为给定类型的实例封装了具体的任务与功能。类、结构体、枚举也可以定义类型方法;类型方法与类型本身相关联。类型方法与 Objective-C 中的类方法(class methods)相似。
|
||||
|
||||
结构体和枚举能够定义方法是 Swift 与 C/Objective-C 的主要区别之一。在 Objective-C 中,类是唯一能定义方法的类型。但在 Swift 中,你不仅能选择是否要定义一个类/结构体/枚举,还能灵活的在你创建的类型(类/结构体/枚举)上定义方法。
|
||||
|
||||
<a name="instance_methods"></a>
|
||||
## 实例方法(Instance Methods)
|
||||
|
||||
**实例方法**是属于某个特定类、结构体或者枚举类型实例的方法。实例方法提供访问和修改实例属性的方法或提供与实例目的相关的功能,并以此来支撑实例的功能。实例方法的语法与函数完全一致,详情参见[函数](../charpter2/06_Functions.md)。
|
||||
|
||||
实例方法要写在它所属的类型的前后大括号之间。实例方法能够隐式访问它所属类型的所有的其他实例方法和属性。实例方法只能被它所属的类的某个特定实例调用。实例方法不能脱离于现存的实例而被调用。
|
||||
|
||||
下面的例子,定义一个很简单的类`Counter`,`Counter`能被用来对一个动作发生的次数进行计数:
|
||||
|
||||
```swift
|
||||
class Counter {
|
||||
var count = 0
|
||||
func increment() {
|
||||
count++
|
||||
}
|
||||
func incrementBy(amount: Int) {
|
||||
count += amount
|
||||
}
|
||||
func reset() {
|
||||
count = 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`Counter`类定义了三个实例方法:
|
||||
- `increment`让计数器按一递增;
|
||||
- `incrementBy(amount: Int)`让计数器按一个指定的整数值递增;
|
||||
- `reset`将计数器重置为0。
|
||||
|
||||
`Counter`这个类还声明了一个可变属性`count`,用它来保持对当前计数器值的追踪。
|
||||
|
||||
和调用属性一样,用点语法(dot syntax)调用实例方法:
|
||||
|
||||
```swift
|
||||
let counter = Counter()
|
||||
// 初始计数值是0
|
||||
counter.increment()
|
||||
// 计数值现在是1
|
||||
counter.incrementBy(5)
|
||||
// 计数值现在是6
|
||||
counter.reset()
|
||||
// 计数值现在是0
|
||||
```
|
||||
|
||||
<a name="local_and_external_parameter"></a>
|
||||
### 方法的局部参数名称和外部参数名称(Local and External Parameter Names for Methods)
|
||||
|
||||
函数参数可以同时有一个局部名称(在函数体内部使用)和一个外部名称(在调用函数时使用),详情参见[函数的外部参数名](06_Functions.html)。方法参数也一样(因为方法就是函数,只是这个函数与某个类型相关联了)。但是,方法和函数的局部名称和外部名称的默认行为是不一样的。
|
||||
|
||||
Swift 中的方法和 Objective-C 中的方法极其相似。像在 Objective-C 中一样,Swift 中方法的名称通常用一个介词指向方法的第一个参数,比如:`with`,`for`,`by`等等。前面的`Counter`类的例子中`incrementBy`方法就是这样的。介词的使用让方法在被调用时能像一个句子一样被解读。和函数参数不同,对于方法的参数,Swift 使用不同的默认处理方式,这可以让方法命名规范更容易写。
|
||||
|
||||
具体来说,Swift 默认仅给方法的第一个参数名称一个局部参数名称;默认同时给第二个和后续的参数名称局部参数名称和外部参数名称。这个约定与典型的命名和调用约定相适应,与你在写 Objective-C 的方法时很相似。这个约定还让表达式方法在调用时不需要再限定参数名称。
|
||||
|
||||
看看下面这个`Counter`的另一个版本(它定义了一个更复杂的`incrementBy`方法):
|
||||
|
||||
```swift
|
||||
class Counter {
|
||||
var count: Int = 0
|
||||
func incrementBy(amount: Int, numberOfTimes: Int) {
|
||||
count += amount * numberOfTimes
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`incrementBy`方法有两个参数: `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
|
||||
}
|
||||
```
|
||||
|
||||
这种默认行为使上面代码意味着:在 Swift 中定义方法使用了与 Objective-C 同样的语法风格,并且方法将以自然表达式的方式被调用。
|
||||
|
||||
<a name="modifying_external_parameter"></a>
|
||||
### 修改方法的外部参数名称(Modifying External Parameter Name Behavior for Methods)
|
||||
|
||||
有时为方法的第一个参数提供一个外部参数名称是非常有用的,尽管这不是默认的行为。你可以自己添加一个显式的外部名称或者用一个井号(`#`)作为第一个参数的前缀来把这个局部名称当作外部名称使用。
|
||||
|
||||
相反,如果你不想为方法的第二个及后续的参数提供一个外部名称,可以通过使用下划线(`_`)作为该参数的显式外部名称,这样做将覆盖默认行为。
|
||||
|
||||
<a name="self_property"></a>
|
||||
## `self`属性(The self Property)
|
||||
|
||||
类型的每一个实例都有一个隐含属性叫做`self`,`self`完全等同于该实例本身。你可以在一个实例的实例方法中使用这个隐含的`self`属性来引用当前实例。
|
||||
|
||||
上面例子中的`increment`方法还可以这样写:
|
||||
|
||||
```swift
|
||||
func increment() {
|
||||
self.count++
|
||||
}
|
||||
```
|
||||
|
||||
实际上,你不必在你的代码里面经常写`self`。不论何时,只要在一个方法中使用一个已知的属性或者方法名称,如果你没有明确的写`self`,Swift 假定你是指当前实例的属性或者方法。这种假定在上面的`Counter`中已经示范了:`Counter`中的三个实例方法中都使用的是`count`(而不是`self.count`)。
|
||||
|
||||
使用这条规则的主要场景是实例方法的某个参数名称与实例的某个属性名称相同的时候。在这种情况下,参数名称享有优先权,并且在引用属性时必须使用一种更严格的方式。这时你可以使用`self`属性来区分参数名称和属性名称。
|
||||
|
||||
下面的例子中,`self`消除方法参数`x`和实例属性`x`之间的歧义:
|
||||
|
||||
```swift
|
||||
struct Point {
|
||||
var x = 0.0, y = 0.0
|
||||
func isToTheRightOfX(x: Double) -> Bool {
|
||||
return self.x > x
|
||||
}
|
||||
}
|
||||
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")
|
||||
}
|
||||
// 输出 "This point is to the right of the line where x == 1.0"(这个点在x等于1.0这条线的右边)
|
||||
```
|
||||
|
||||
如果不使用`self`前缀,Swift 就认为两次使用的`x`都指的是名称为`x`的函数参数。
|
||||
|
||||
<a name="modifying_value_types"></a>
|
||||
### 在实例方法中修改值类型(Modifying Value Types from Within Instance Methods)
|
||||
|
||||
结构体和枚举是**值类型**。一般情况下,值类型的属性不能在它的实例方法中被修改。
|
||||
|
||||
但是,如果你确实需要在某个具体的方法中修改结构体或者枚举的属性,你可以选择`变异(mutating)`这个方法,然后方法就可以从方法内部改变它的属性;并且它做的任何改变在方法结束时还会保留在原始结构中。方法还可以给它隐含的`self`属性赋值一个全新的实例,这个新实例在方法结束后将替换原来的实例。
|
||||
|
||||
要使用`变异`方法, 将关键字`mutating` 放到方法的`func`关键字之前就可以了:
|
||||
|
||||
```swift
|
||||
struct Point {
|
||||
var x = 0.0, y = 0.0
|
||||
mutating func moveByX(deltaX: Double, y deltaY: Double) {
|
||||
x += deltaX
|
||||
y += deltaY
|
||||
}
|
||||
}
|
||||
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)"
|
||||
```
|
||||
|
||||
上面的`Point`结构体定义了一个变异方法(mutating method)`moveByX`,`moveByX`用来移动点。`moveByX`方法在被调用时修改了这个点,而不是返回一个新的点。方法定义时加上`mutating`关键字,这才让方法可以修改值类型的属性。
|
||||
|
||||
注意:不能在结构体类型常量上调用变异方法,因为常量的属性不能被改变,即使想改变的是常量的变量属性也不行,详情参见[存储属性和实例变量]("10_Properties.html")
|
||||
|
||||
```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>
|
||||
### 在变异方法中给self赋值(Assigning to self Within a Mutating Method)
|
||||
|
||||
变异方法能够赋给隐含属性`self`一个全新的实例。上面`Point`的例子可以用下面的方式改写:
|
||||
|
||||
```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)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
新版的变异方法`moveByX`创建了一个新的结构(它的 x 和 y 的值都被设定为目标值)。调用这个版本的方法和调用上个版本的最终结果是一样的。
|
||||
|
||||
枚举的变异方法可以把`self`设置为相同的枚举类型中不同的成员:
|
||||
|
||||
```swift
|
||||
enum TriStateSwitch {
|
||||
case Off, Low, High
|
||||
mutating func next() {
|
||||
switch self {
|
||||
case Off:
|
||||
self = Low
|
||||
case Low:
|
||||
self = High
|
||||
case High:
|
||||
self = Off
|
||||
}
|
||||
}
|
||||
}
|
||||
var ovenLight = TriStateSwitch.Low
|
||||
ovenLight.next()
|
||||
// ovenLight 现在等于 .High
|
||||
ovenLight.next()
|
||||
// ovenLight 现在等于 .Off
|
||||
```
|
||||
|
||||
上面的例子中定义了一个三态开关的枚举。每次调用`next`方法时,开关在不同的电源状态(`Off`,`Low`,`High`)之前循环切换。
|
||||
|
||||
<a name="type_methods"></a>
|
||||
## 类型方法(Type Methods)
|
||||
|
||||
实例方法是被类型的某个实例调用的方法。你也可以定义类型本身调用的方法,这种方法就叫做**类型方法**。声明类的类型方法,在方法的`func`关键字之前加上关键字`class`;声明结构体和枚举的类型方法,在方法的`func`关键字之前加上关键字`static`。
|
||||
|
||||
> 注意:
|
||||
> 在 Objective-C 里面,你只能为 Objective-C 的类定义类型方法(type-level methods)。在 Swift 中,你可以为所有的类、结构体和枚举定义类型方法:每一个类型方法都被它所支持的类型显式包含。
|
||||
|
||||
类型方法和实例方法一样用点语法调用。但是,你是在类型层面上调用这个方法,而不是在实例层面上调用。下面是如何在`SomeClass`类上调用类型方法的例子:
|
||||
|
||||
```swift
|
||||
class SomeClass {
|
||||
class func someTypeMethod() {
|
||||
// type method implementation goes here
|
||||
}
|
||||
}
|
||||
SomeClass.someTypeMethod()
|
||||
```
|
||||
|
||||
在类型方法的方法体(body)中,`self`指向这个类型本身,而不是类型的某个实例。对于结构体和枚举来说,这意味着你可以用`self`来消除静态属性和静态方法参数之间的歧义(类似于我们在前面处理实例属性和实例方法参数时做的那样)。
|
||||
|
||||
一般来说,任何未限定的方法和属性名称,将会来自于本类中另外的类型级别的方法和属性。一个类型方法可以调用本类中另一个类型方法的名称,而无需在方法名称前面加上类型名称的前缀。同样,结构体和枚举的类型方法也能够直接通过静态属性的名称访问静态属性,而不需要类型名称前缀。
|
||||
|
||||
下面的例子定义了一个名为`LevelTracker`结构体。它监测玩家的游戏发展情况(游戏的不同层次或阶段)。这是一个单人游戏,但也可以存储多个玩家在同一设备上的游戏信息。
|
||||
|
||||
游戏初始时,所有的游戏等级(除了等级 1)都被锁定。每次有玩家完成一个等级,这个等级就对这个设备上的所有玩家解锁。`LevelTracker`结构体用静态属性和方法监测游戏的哪个等级已经被解锁。它还监测每个玩家的当前等级。
|
||||
|
||||
```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
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`LevelTracker`监测玩家的已解锁的最高等级。这个值被存储在静态属性`highestUnlockedLevel`中。
|
||||
|
||||
`LevelTracker`还定义了两个类型方法与`highestUnlockedLevel`配合工作。第一个类型方法是`unlockLevel`:一旦新等级被解锁,它会更新`highestUnlockedLevel`的值。第二个类型方法是`levelIsUnlocked`:如果某个给定的等级已经被解锁,它将返回`true`。(注意:尽管我们没有使用类似`LevelTracker.highestUnlockedLevel`的写法,这个类型方法还是能够访问静态属性`highestUnlockedLevel`)
|
||||
|
||||
除了静态属性和类型方法,`LevelTracker`还监测每个玩家的进度。它用实例属性`currentLevel`来监测玩家当前的等级。
|
||||
|
||||
为了便于管理`currentLevel`属性,`LevelTracker`定义了实例方法`advanceToLevel`。这个方法会在更新`currentLevel`之前检查所请求的新等级是否已经解锁。`advanceToLevel`方法返回布尔值以指示是否能够设置`currentLevel`。
|
||||
|
||||
下面,`Player`类使用`LevelTracker`来监测和更新每个玩家的发展进度:
|
||||
|
||||
```swift
|
||||
class Player {
|
||||
var tracker = LevelTracker()
|
||||
let playerName: String
|
||||
func completedLevel(level: Int) {
|
||||
LevelTracker.unlockLevel(level + 1)
|
||||
tracker.advanceToLevel(level + 1)
|
||||
}
|
||||
init(name: String) {
|
||||
playerName = name
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`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)
|
||||
```
|
||||
|
||||
如果你创建了第二个玩家,并尝试让它开始一个没有被任何玩家解锁的等级,那么这次设置玩家当前等级的尝试将会失败:
|
||||
|
||||
```swift
|
||||
player = Player(name: "Beto")
|
||||
if player.tracker.advanceToLevel(6) {
|
||||
println("player is now on level 6")
|
||||
} else {
|
||||
println("level 6 has not yet been unlocked")
|
||||
}
|
||||
// 输出 "level 6 has not yet been unlocked"(等级6还没被解锁)
|
||||
```
|
||||
> 翻译:[pp-prog](https://github.com/pp-prog)
|
||||
> 校对:[zqp](https://github.com/zqp)
|
||||
|
||||
# 方法(Methods)
|
||||
-----------------
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [实例方法(Instance Methods](#instance_methods)
|
||||
- [类型方法(Type Methods)](#type_methods)
|
||||
|
||||
**方法**是与某些特定类型相关联的函数。类、结构体、枚举都可以定义实例方法;实例方法为给定类型的实例封装了具体的任务与功能。类、结构体、枚举也可以定义类型方法;类型方法与类型本身相关联。类型方法与 Objective-C 中的类方法(class methods)相似。
|
||||
|
||||
结构体和枚举能够定义方法是 Swift 与 C/Objective-C 的主要区别之一。在 Objective-C 中,类是唯一能定义方法的类型。但在 Swift 中,你不仅能选择是否要定义一个类/结构体/枚举,还能灵活的在你创建的类型(类/结构体/枚举)上定义方法。
|
||||
|
||||
<a name="instance_methods"></a>
|
||||
## 实例方法(Instance Methods)
|
||||
|
||||
**实例方法**是属于某个特定类、结构体或者枚举类型实例的方法。实例方法提供访问和修改实例属性的方法或提供与实例目的相关的功能,并以此来支撑实例的功能。实例方法的语法与函数完全一致,详情参见[函数](../charpter2/06_Functions.md)。
|
||||
|
||||
实例方法要写在它所属的类型的前后大括号之间。实例方法能够隐式访问它所属类型的所有的其他实例方法和属性。实例方法只能被它所属的类的某个特定实例调用。实例方法不能脱离于现存的实例而被调用。
|
||||
|
||||
下面的例子,定义一个很简单的类`Counter`,`Counter`能被用来对一个动作发生的次数进行计数:
|
||||
|
||||
```swift
|
||||
class Counter {
|
||||
var count = 0
|
||||
func increment() {
|
||||
count++
|
||||
}
|
||||
func incrementBy(amount: Int) {
|
||||
count += amount
|
||||
}
|
||||
func reset() {
|
||||
count = 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`Counter`类定义了三个实例方法:
|
||||
- `increment`让计数器按一递增;
|
||||
- `incrementBy(amount: Int)`让计数器按一个指定的整数值递增;
|
||||
- `reset`将计数器重置为0。
|
||||
|
||||
`Counter`这个类还声明了一个可变属性`count`,用它来保持对当前计数器值的追踪。
|
||||
|
||||
和调用属性一样,用点语法(dot syntax)调用实例方法:
|
||||
|
||||
```swift
|
||||
let counter = Counter()
|
||||
// 初始计数值是0
|
||||
counter.increment()
|
||||
// 计数值现在是1
|
||||
counter.incrementBy(5)
|
||||
// 计数值现在是6
|
||||
counter.reset()
|
||||
// 计数值现在是0
|
||||
```
|
||||
|
||||
<a name="local_and_external_parameter"></a>
|
||||
### 方法的局部参数名称和外部参数名称(Local and External Parameter Names for Methods)
|
||||
|
||||
函数参数可以同时有一个局部名称(在函数体内部使用)和一个外部名称(在调用函数时使用),详情参见[函数的外部参数名](06_Functions.html)。方法参数也一样(因为方法就是函数,只是这个函数与某个类型相关联了)。但是,方法和函数的局部名称和外部名称的默认行为是不一样的。
|
||||
|
||||
Swift 中的方法和 Objective-C 中的方法极其相似。像在 Objective-C 中一样,Swift 中方法的名称通常用一个介词指向方法的第一个参数,比如:`with`,`for`,`by`等等。前面的`Counter`类的例子中`incrementBy`方法就是这样的。介词的使用让方法在被调用时能像一个句子一样被解读。和函数参数不同,对于方法的参数,Swift 使用不同的默认处理方式,这可以让方法命名规范更容易写。
|
||||
|
||||
具体来说,Swift 默认仅给方法的第一个参数名称一个局部参数名称;默认同时给第二个和后续的参数名称局部参数名称和外部参数名称。这个约定与典型的命名和调用约定相适应,与你在写 Objective-C 的方法时很相似。这个约定还让表达式方法在调用时不需要再限定参数名称。
|
||||
|
||||
看看下面这个`Counter`的另一个版本(它定义了一个更复杂的`incrementBy`方法):
|
||||
|
||||
```swift
|
||||
class Counter {
|
||||
var count: Int = 0
|
||||
func incrementBy(amount: Int, numberOfTimes: Int) {
|
||||
count += amount * numberOfTimes
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`incrementBy`方法有两个参数: `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
|
||||
}
|
||||
```
|
||||
|
||||
这种默认行为使上面代码意味着:在 Swift 中定义方法使用了与 Objective-C 同样的语法风格,并且方法将以自然表达式的方式被调用。
|
||||
|
||||
<a name="modifying_external_parameter"></a>
|
||||
### 修改方法的外部参数名称(Modifying External Parameter Name Behavior for Methods)
|
||||
|
||||
有时为方法的第一个参数提供一个外部参数名称是非常有用的,尽管这不是默认的行为。你可以自己添加一个显式的外部名称或者用一个井号(`#`)作为第一个参数的前缀来把这个局部名称当作外部名称使用。
|
||||
|
||||
相反,如果你不想为方法的第二个及后续的参数提供一个外部名称,可以通过使用下划线(`_`)作为该参数的显式外部名称,这样做将覆盖默认行为。
|
||||
|
||||
<a name="self_property"></a>
|
||||
## `self`属性(The self Property)
|
||||
|
||||
类型的每一个实例都有一个隐含属性叫做`self`,`self`完全等同于该实例本身。你可以在一个实例的实例方法中使用这个隐含的`self`属性来引用当前实例。
|
||||
|
||||
上面例子中的`increment`方法还可以这样写:
|
||||
|
||||
```swift
|
||||
func increment() {
|
||||
self.count++
|
||||
}
|
||||
```
|
||||
|
||||
实际上,你不必在你的代码里面经常写`self`。不论何时,只要在一个方法中使用一个已知的属性或者方法名称,如果你没有明确的写`self`,Swift 假定你是指当前实例的属性或者方法。这种假定在上面的`Counter`中已经示范了:`Counter`中的三个实例方法中都使用的是`count`(而不是`self.count`)。
|
||||
|
||||
使用这条规则的主要场景是实例方法的某个参数名称与实例的某个属性名称相同的时候。在这种情况下,参数名称享有优先权,并且在引用属性时必须使用一种更严格的方式。这时你可以使用`self`属性来区分参数名称和属性名称。
|
||||
|
||||
下面的例子中,`self`消除方法参数`x`和实例属性`x`之间的歧义:
|
||||
|
||||
```swift
|
||||
struct Point {
|
||||
var x = 0.0, y = 0.0
|
||||
func isToTheRightOfX(x: Double) -> Bool {
|
||||
return self.x > x
|
||||
}
|
||||
}
|
||||
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")
|
||||
}
|
||||
// 输出 "This point is to the right of the line where x == 1.0"(这个点在x等于1.0这条线的右边)
|
||||
```
|
||||
|
||||
如果不使用`self`前缀,Swift 就认为两次使用的`x`都指的是名称为`x`的函数参数。
|
||||
|
||||
<a name="modifying_value_types"></a>
|
||||
### 在实例方法中修改值类型(Modifying Value Types from Within Instance Methods)
|
||||
|
||||
结构体和枚举是**值类型**。一般情况下,值类型的属性不能在它的实例方法中被修改。
|
||||
|
||||
但是,如果你确实需要在某个具体的方法中修改结构体或者枚举的属性,你可以选择`变异(mutating)`这个方法,然后方法就可以从方法内部改变它的属性;并且它做的任何改变在方法结束时还会保留在原始结构中。方法还可以给它隐含的`self`属性赋值一个全新的实例,这个新实例在方法结束后将替换原来的实例。
|
||||
|
||||
要使用`变异`方法, 将关键字`mutating` 放到方法的`func`关键字之前就可以了:
|
||||
|
||||
```swift
|
||||
struct Point {
|
||||
var x = 0.0, y = 0.0
|
||||
mutating func moveByX(deltaX: Double, y deltaY: Double) {
|
||||
x += deltaX
|
||||
y += deltaY
|
||||
}
|
||||
}
|
||||
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)"
|
||||
```
|
||||
|
||||
上面的`Point`结构体定义了一个变异方法(mutating method)`moveByX`,`moveByX`用来移动点。`moveByX`方法在被调用时修改了这个点,而不是返回一个新的点。方法定义时加上`mutating`关键字,这才让方法可以修改值类型的属性。
|
||||
|
||||
注意:不能在结构体类型常量上调用变异方法,因为常量的属性不能被改变,即使想改变的是常量的变量属性也不行,详情参见[存储属性和实例变量]("10_Properties.html")
|
||||
|
||||
```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>
|
||||
### 在变异方法中给self赋值(Assigning to self Within a Mutating Method)
|
||||
|
||||
变异方法能够赋给隐含属性`self`一个全新的实例。上面`Point`的例子可以用下面的方式改写:
|
||||
|
||||
```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)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
新版的变异方法`moveByX`创建了一个新的结构(它的 x 和 y 的值都被设定为目标值)。调用这个版本的方法和调用上个版本的最终结果是一样的。
|
||||
|
||||
枚举的变异方法可以把`self`设置为相同的枚举类型中不同的成员:
|
||||
|
||||
```swift
|
||||
enum TriStateSwitch {
|
||||
case Off, Low, High
|
||||
mutating func next() {
|
||||
switch self {
|
||||
case Off:
|
||||
self = Low
|
||||
case Low:
|
||||
self = High
|
||||
case High:
|
||||
self = Off
|
||||
}
|
||||
}
|
||||
}
|
||||
var ovenLight = TriStateSwitch.Low
|
||||
ovenLight.next()
|
||||
// ovenLight 现在等于 .High
|
||||
ovenLight.next()
|
||||
// ovenLight 现在等于 .Off
|
||||
```
|
||||
|
||||
上面的例子中定义了一个三态开关的枚举。每次调用`next`方法时,开关在不同的电源状态(`Off`,`Low`,`High`)之前循环切换。
|
||||
|
||||
<a name="type_methods"></a>
|
||||
## 类型方法(Type Methods)
|
||||
|
||||
实例方法是被类型的某个实例调用的方法。你也可以定义类型本身调用的方法,这种方法就叫做**类型方法**。声明类的类型方法,在方法的`func`关键字之前加上关键字`class`;声明结构体和枚举的类型方法,在方法的`func`关键字之前加上关键字`static`。
|
||||
|
||||
> 注意:
|
||||
> 在 Objective-C 里面,你只能为 Objective-C 的类定义类型方法(type-level methods)。在 Swift 中,你可以为所有的类、结构体和枚举定义类型方法:每一个类型方法都被它所支持的类型显式包含。
|
||||
|
||||
类型方法和实例方法一样用点语法调用。但是,你是在类型层面上调用这个方法,而不是在实例层面上调用。下面是如何在`SomeClass`类上调用类型方法的例子:
|
||||
|
||||
```swift
|
||||
class SomeClass {
|
||||
class func someTypeMethod() {
|
||||
// type method implementation goes here
|
||||
}
|
||||
}
|
||||
SomeClass.someTypeMethod()
|
||||
```
|
||||
|
||||
在类型方法的方法体(body)中,`self`指向这个类型本身,而不是类型的某个实例。对于结构体和枚举来说,这意味着你可以用`self`来消除静态属性和静态方法参数之间的歧义(类似于我们在前面处理实例属性和实例方法参数时做的那样)。
|
||||
|
||||
一般来说,任何未限定的方法和属性名称,将会来自于本类中另外的类型级别的方法和属性。一个类型方法可以调用本类中另一个类型方法的名称,而无需在方法名称前面加上类型名称的前缀。同样,结构体和枚举的类型方法也能够直接通过静态属性的名称访问静态属性,而不需要类型名称前缀。
|
||||
|
||||
下面的例子定义了一个名为`LevelTracker`结构体。它监测玩家的游戏发展情况(游戏的不同层次或阶段)。这是一个单人游戏,但也可以存储多个玩家在同一设备上的游戏信息。
|
||||
|
||||
游戏初始时,所有的游戏等级(除了等级 1)都被锁定。每次有玩家完成一个等级,这个等级就对这个设备上的所有玩家解锁。`LevelTracker`结构体用静态属性和方法监测游戏的哪个等级已经被解锁。它还监测每个玩家的当前等级。
|
||||
|
||||
```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
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`LevelTracker`监测玩家的已解锁的最高等级。这个值被存储在静态属性`highestUnlockedLevel`中。
|
||||
|
||||
`LevelTracker`还定义了两个类型方法与`highestUnlockedLevel`配合工作。第一个类型方法是`unlockLevel`:一旦新等级被解锁,它会更新`highestUnlockedLevel`的值。第二个类型方法是`levelIsUnlocked`:如果某个给定的等级已经被解锁,它将返回`true`。(注意:尽管我们没有使用类似`LevelTracker.highestUnlockedLevel`的写法,这个类型方法还是能够访问静态属性`highestUnlockedLevel`)
|
||||
|
||||
除了静态属性和类型方法,`LevelTracker`还监测每个玩家的进度。它用实例属性`currentLevel`来监测玩家当前的等级。
|
||||
|
||||
为了便于管理`currentLevel`属性,`LevelTracker`定义了实例方法`advanceToLevel`。这个方法会在更新`currentLevel`之前检查所请求的新等级是否已经解锁。`advanceToLevel`方法返回布尔值以指示是否能够设置`currentLevel`。
|
||||
|
||||
下面,`Player`类使用`LevelTracker`来监测和更新每个玩家的发展进度:
|
||||
|
||||
```swift
|
||||
class Player {
|
||||
var tracker = LevelTracker()
|
||||
let playerName: String
|
||||
func completedLevel(level: Int) {
|
||||
LevelTracker.unlockLevel(level + 1)
|
||||
tracker.advanceToLevel(level + 1)
|
||||
}
|
||||
init(name: String) {
|
||||
playerName = name
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`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)
|
||||
```
|
||||
|
||||
如果你创建了第二个玩家,并尝试让它开始一个没有被任何玩家解锁的等级,那么这次设置玩家当前等级的尝试将会失败:
|
||||
|
||||
```swift
|
||||
player = Player(name: "Beto")
|
||||
if player.tracker.advanceToLevel(6) {
|
||||
println("player is now on level 6")
|
||||
} else {
|
||||
println("level 6 has not yet been unlocked")
|
||||
}
|
||||
// 输出 "level 6 has not yet been unlocked"(等级6还没被解锁)
|
||||
```
|
||||
|
||||
@ -1,184 +1,167 @@
|
||||
> 翻译:siemenliu
|
||||
> 校对:zq54zquan
|
||||
|
||||
|
||||
# 下标脚本(Subscripts)
|
||||
-----------------
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [下标脚本语法](#subscript_syntax)
|
||||
- [下标脚本用法](#subscript_usage)
|
||||
- [下标脚本选项](#subscript_options)
|
||||
|
||||
*下标脚本* 可以定义在类(Class)、结构体(structure)和枚举(enumeration)这些目标中,可以认为是访问对象、集合或序列的快捷方式,不需要再调用实例的特定的赋值和访问方法。举例来说,用下标脚本访问一个数组(Array)实例中的元素可以这样写 `someArray[index]` ,访问字典(Dictionary)实例中的元素可以这样写 `someDictionary[key]`。
|
||||
|
||||
对于同一个目标可以定义多个下标脚本,通过索引值类型的不同来进行重载,而且索引值的个数可以是多个。
|
||||
|
||||
<<<<<<< HEAD
|
||||
> 译者:这里附属脚本重载在本小节中原文并没有任何演示
|
||||
=======
|
||||
> 译者:这里下标脚本重载在本小节中原文并没有任何演示
|
||||
>>>>>>> a516af6a531a104ec88da0d236ecf389a5ec72af
|
||||
|
||||
<a name="subscript_syntax"></a>
|
||||
## 下标脚本语法
|
||||
|
||||
下标脚本允许你通过在实例后面的方括号中传入一个或者多个的索引值来对实例进行访问和赋值。语法类似于实例方法和计算型属性的混合。与定义实例方法类似,定义下标脚本使用`subscript`关键字,显式声明入参(一个或多个)和返回类型。与实例方法不同的是下标脚本可以设定为读写或只读。这种方式又有点像计算型属性的getter和setter:
|
||||
|
||||
```swift
|
||||
subscript(index: Int) -> Int {
|
||||
get {
|
||||
// 返回与入参匹配的Int类型的值
|
||||
}
|
||||
|
||||
set(newValue) {
|
||||
// 执行赋值操作
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`newValue`的类型必须和下标脚本定义的返回类型相同。与计算型属性相同的是set的入参声明`newValue`就算不写,在set代码块中依然可以使用默认的`newValue`这个变量来访问新赋的值。
|
||||
|
||||
与只读计算型属性一样,可以直接将原本应该写在`get`代码块中的代码写在`subscript`中:
|
||||
|
||||
```swift
|
||||
subscript(index: Int) -> Int {
|
||||
// 返回与入参匹配的Int类型的值
|
||||
}
|
||||
```
|
||||
|
||||
下面代码演示了一个在`TimesTable`结构体中使用只读下标脚本的用法,该结构体用来展示传入整数的*n*倍。
|
||||
|
||||
```swift
|
||||
struct TimesTable {
|
||||
let multiplier: Int
|
||||
subscript(index: Int) -> Int {
|
||||
return multiplier * index
|
||||
}
|
||||
}
|
||||
let threeTimesTable = TimesTable(multiplier: 3)
|
||||
println("3的6倍是\(threeTimesTable[6])")
|
||||
// 输出 "3的6倍是18"
|
||||
```
|
||||
|
||||
在上例中,通过`TimesTable`结构体创建了一个用来表示索引值三倍的实例。数值`3`作为结构体`构造函数`入参初始化实例成员`multiplier`。
|
||||
|
||||
你可以通过下标脚本来得到结果,比如`threeTimesTable[6]`。这条语句访问了`threeTimesTable`的第六个元素,返回`6`的`3`倍即`18`。
|
||||
|
||||
<<<<<<< HEAD
|
||||
>注意:
|
||||
> `TimesTable`例子是基于一个固定的数学公式。它并不适合开放写权限来对`threeTimesTable[someIndex]`进行赋值操作,这也是为什么附属脚本只定义为只读的原因。
|
||||
=======
|
||||
>注意:
|
||||
>
|
||||
> `TimesTable`例子是基于一个固定的数学公式。它并不适合开放写权限来对`threeTimesTable[someIndex]`进行赋值操作,这也是为什么下标脚本只定义为只读的原因。
|
||||
>>>>>>> a516af6a531a104ec88da0d236ecf389a5ec72af
|
||||
|
||||
|
||||
<a name="subscript_usage"></a>
|
||||
## 下标脚本用法
|
||||
|
||||
根据使用场景不同下标脚本也具有不同的含义。通常下标脚本是用来访问集合(collection),列表(list)或序列(sequence)中元素的快捷方式。你可以在你自己特定的类或结构体中自由的实现下标脚本来提供合适的功能。
|
||||
|
||||
例如,Swift 的字典(Dictionary)实现了通过下标脚本来对其实例中存放的值进行存取操作。在下标脚本中使用和字典索引相同类型的值,并且把一个字典值类型的值赋值给这个下标脚本来为字典设值:
|
||||
|
||||
```swift
|
||||
var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
|
||||
numberOfLegs["bird"] = 2
|
||||
```
|
||||
|
||||
上例定义一个名为`numberOfLegs`的变量并用一个字典字面量初始化出了包含三对键值的字典实例。`numberOfLegs`的字典存放值类型推断为`Dictionary<String, Int>`。字典实例创建完成之后通过下标脚本的方式将整型值`2`赋值到字典实例的索引为`bird`的位置中。
|
||||
|
||||
更多关于字典(Dictionary)下标脚本的信息请参考[读取和修改字典](../chapter2/04_Collection_Types.html)
|
||||
|
||||
<<<<<<< HEAD
|
||||
> 注意:
|
||||
> Swift 中字典的附属脚本实现中,在`get`部分返回值是`Int?`,上例中的`numberOfLegs`字典通过附属脚本返回的是一个`Int?`或者说“可选的int”,不是每个字典的索引都能得到一个整型值,对于没有设过值的索引的访问返回的结果就是`nil`;同样想要从字典实例中删除某个索引下的值也只需要给这个索引赋值为`nil`即可。
|
||||
=======
|
||||
> 注意:
|
||||
>
|
||||
> Swift 中字典的下标脚本实现中,在`get`部分返回值是`Int?`,上例中的`numberOfLegs`字典通过下标脚本返回的是一个`Int?`或者说“可选的int”,不是每个字典的索引都能得到一个整型值,对于没有设过值的索引的访问返回的结果就是`nil`;同样想要从字典实例中删除某个索引下的值也只需要给这个索引赋值为`nil`即可。
|
||||
>>>>>>> a516af6a531a104ec88da0d236ecf389a5ec72af
|
||||
|
||||
<a name="subscript_options"></a>
|
||||
## 下标脚本选项
|
||||
|
||||
下标脚本允许任意数量的入参索引,并且每个入参类型也没有限制。下标脚本的返回值也可以是任何类型。下标脚本可以使用变量参数和可变参数,但使用写入读出(in-out)参数或给参数设置默认值都是不允许的。
|
||||
|
||||
一个类或结构体可以根据自身需要提供多个下标脚本实现,在定义下标脚本时通过入参个类型进行区分,使用下标脚本时会自动匹配合适的下标脚本实现运行,这就是*下标脚本的重载*。
|
||||
|
||||
一个下标脚本入参是最常见的情况,但只要有合适的场景也可以定义多个下标脚本入参。如下例定义了一个`Matrix`结构体,将呈现一个`Double`类型的二维矩阵。`Matrix`结构体的下标脚本需要两个整型参数:
|
||||
|
||||
```swift
|
||||
struct Matrix {
|
||||
let rows: Int, columns: Int
|
||||
var grid: Double[]
|
||||
init(rows: Int, columns: Int) {
|
||||
self.rows = rows
|
||||
self.columns = columns
|
||||
grid = Array(count: rows * columns, repeatedValue: 0.0)
|
||||
}
|
||||
func indexIsValidForRow(row: Int, column: Int) -> Bool {
|
||||
return row >= 0 && row < rows && column >= 0 && column < columns
|
||||
}
|
||||
subscript(row: Int, column: Int) -> Double {
|
||||
get {
|
||||
assert(indexIsValidForRow(row, column: column), "Index out of range")
|
||||
return grid[(row * columns) + column]
|
||||
}
|
||||
set {
|
||||
assert(indexIsValidForRow(row, column: column), "Index out of range")
|
||||
grid[(row * columns) + columns] = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`Matrix`提供了一个两个入参的构造方法,入参分别是`rows`和`columns`,创建了一个足够容纳`rows * columns`个数的`Double`类型数组。为了存储,将数组的大小和数组每个元素初始值0.0,都传入数组的构造方法中来创建一个正确大小的新数组。关于数组的构造方法和析构方法请参考[创建并且构造一个数组](../chapter2/04_Collection_Types.html)。
|
||||
|
||||
你可以通过传入合适的`row`和`column`的数量来构造一个新的`Matrix`实例:
|
||||
|
||||
```swift
|
||||
var matrix = Matrix(rows: 2, columns: 2)
|
||||
```
|
||||
|
||||
上例中创建了一个新的两行两列的`Matrix`实例。在阅读顺序从左上到右下的`Matrix`实例中的数组实例`grid`是矩阵二维数组的扁平化存储:
|
||||
|
||||
```swift
|
||||
// 示意图
|
||||
grid = [0.0, 0.0, 0.0, 0.0]
|
||||
|
||||
col0 col1
|
||||
row0 [0.0, 0.0,
|
||||
row1 0.0, 0.0]
|
||||
```
|
||||
|
||||
将值赋给带有`row`和`column`下标脚本的`matrix`实例表达式可以完成赋值操作,下标脚本入参使用逗号分割
|
||||
|
||||
```swift
|
||||
matrix[0, 1] = 1.5
|
||||
matrix[1, 0] = 3.2
|
||||
```
|
||||
|
||||
上面两条语句分别`让matrix`的右上值为 1.5,坐下值为 3.2:
|
||||
|
||||
```swift
|
||||
[0.0, 1.5,
|
||||
3.2, 0.0]
|
||||
```
|
||||
|
||||
`Matrix`下标脚本的`getter`和`setter`中同时调用了下标脚本入参的`row`和`column`是否有效的判断。为了方便进行断言,`Matrix`包含了一个名为`indexIsValid`的成员方法,用来确认入参的`row`或`column`值是否会造成数组越界:
|
||||
|
||||
```swift
|
||||
func indexIsValidForRow(row: Int, column: Int) -> Bool {
|
||||
return row >= 0 && row < rows && column >= 0 && column < columns
|
||||
}
|
||||
```
|
||||
|
||||
断言在下标脚本越界时触发:
|
||||
|
||||
```swift
|
||||
let someValue = matrix[2, 2]
|
||||
// 断言将会触发,因为 [2, 2] 已经超过了matrix的最大长度
|
||||
```
|
||||
> 翻译:[siemenliu](https://github.com/siemenliu)
|
||||
> 校对:[zq54zquan](https://github.com/zq54zquan)
|
||||
|
||||
|
||||
# 下标脚本(Subscripts)
|
||||
-----------------
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [下标脚本语法](#subscript_syntax)
|
||||
- [下标脚本用法](#subscript_usage)
|
||||
- [下标脚本选项](#subscript_options)
|
||||
|
||||
*下标脚本* 可以定义在类(Class)、结构体(structure)和枚举(enumeration)这些目标中,可以认为是访问对象、集合或序列的快捷方式,不需要再调用实例的特定的赋值和访问方法。举例来说,用下标脚本访问一个数组(Array)实例中的元素可以这样写 `someArray[index]` ,访问字典(Dictionary)实例中的元素可以这样写 `someDictionary[key]`。
|
||||
|
||||
对于同一个目标可以定义多个下标脚本,通过索引值类型的不同来进行重载,而且索引值的个数可以是多个。
|
||||
|
||||
> 译者:这里附属脚本重载在本小节中原文并没有任何演示
|
||||
|
||||
<a name="subscript_syntax"></a>
|
||||
## 下标脚本语法
|
||||
|
||||
下标脚本允许你通过在实例后面的方括号中传入一个或者多个的索引值来对实例进行访问和赋值。语法类似于实例方法和计算型属性的混合。与定义实例方法类似,定义下标脚本使用`subscript`关键字,显式声明入参(一个或多个)和返回类型。与实例方法不同的是下标脚本可以设定为读写或只读。这种方式又有点像计算型属性的getter和setter:
|
||||
|
||||
```swift
|
||||
subscript(index: Int) -> Int {
|
||||
get {
|
||||
// 返回与入参匹配的Int类型的值
|
||||
}
|
||||
|
||||
set(newValue) {
|
||||
// 执行赋值操作
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`newValue`的类型必须和下标脚本定义的返回类型相同。与计算型属性相同的是set的入参声明`newValue`就算不写,在set代码块中依然可以使用默认的`newValue`这个变量来访问新赋的值。
|
||||
|
||||
与只读计算型属性一样,可以直接将原本应该写在`get`代码块中的代码写在`subscript`中:
|
||||
|
||||
```swift
|
||||
subscript(index: Int) -> Int {
|
||||
// 返回与入参匹配的Int类型的值
|
||||
}
|
||||
```
|
||||
|
||||
下面代码演示了一个在`TimesTable`结构体中使用只读下标脚本的用法,该结构体用来展示传入整数的*n*倍。
|
||||
|
||||
```swift
|
||||
struct TimesTable {
|
||||
let multiplier: Int
|
||||
subscript(index: Int) -> Int {
|
||||
return multiplier * index
|
||||
}
|
||||
}
|
||||
let threeTimesTable = TimesTable(multiplier: 3)
|
||||
println("3的6倍是\(threeTimesTable[6])")
|
||||
// 输出 "3的6倍是18"
|
||||
```
|
||||
|
||||
在上例中,通过`TimesTable`结构体创建了一个用来表示索引值三倍的实例。数值`3`作为结构体`构造函数`入参初始化实例成员`multiplier`。
|
||||
|
||||
你可以通过下标脚本来得到结果,比如`threeTimesTable[6]`。这条语句访问了`threeTimesTable`的第六个元素,返回`6`的`3`倍即`18`。
|
||||
|
||||
>注意:
|
||||
> `TimesTable`例子是基于一个固定的数学公式。它并不适合开放写权限来对`threeTimesTable[someIndex]`进行赋值操作,这也是为什么附属脚本只定义为只读的原因。
|
||||
|
||||
<a name="subscript_usage"></a>
|
||||
## 下标脚本用法
|
||||
|
||||
根据使用场景不同下标脚本也具有不同的含义。通常下标脚本是用来访问集合(collection),列表(list)或序列(sequence)中元素的快捷方式。你可以在你自己特定的类或结构体中自由的实现下标脚本来提供合适的功能。
|
||||
|
||||
例如,Swift 的字典(Dictionary)实现了通过下标脚本来对其实例中存放的值进行存取操作。在下标脚本中使用和字典索引相同类型的值,并且把一个字典值类型的值赋值给这个下标脚本来为字典设值:
|
||||
|
||||
```swift
|
||||
var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
|
||||
numberOfLegs["bird"] = 2
|
||||
```
|
||||
|
||||
上例定义一个名为`numberOfLegs`的变量并用一个字典字面量初始化出了包含三对键值的字典实例。`numberOfLegs`的字典存放值类型推断为`Dictionary<String, Int>`。字典实例创建完成之后通过下标脚本的方式将整型值`2`赋值到字典实例的索引为`bird`的位置中。
|
||||
|
||||
更多关于字典(Dictionary)下标脚本的信息请参考[读取和修改字典](../chapter2/04_Collection_Types.html)
|
||||
|
||||
> 注意:
|
||||
> Swift 中字典的附属脚本实现中,在`get`部分返回值是`Int?`,上例中的`numberOfLegs`字典通过附属脚本返回的是一个`Int?`或者说“可选的int”,不是每个字典的索引都能得到一个整型值,对于没有设过值的索引的访问返回的结果就是`nil`;同样想要从字典实例中删除某个索引下的值也只需要给这个索引赋值为`nil`即可。
|
||||
|
||||
<a name="subscript_options"></a>
|
||||
## 下标脚本选项
|
||||
|
||||
下标脚本允许任意数量的入参索引,并且每个入参类型也没有限制。下标脚本的返回值也可以是任何类型。下标脚本可以使用变量参数和可变参数,但使用写入读出(in-out)参数或给参数设置默认值都是不允许的。
|
||||
|
||||
一个类或结构体可以根据自身需要提供多个下标脚本实现,在定义下标脚本时通过入参个类型进行区分,使用下标脚本时会自动匹配合适的下标脚本实现运行,这就是*下标脚本的重载*。
|
||||
|
||||
一个下标脚本入参是最常见的情况,但只要有合适的场景也可以定义多个下标脚本入参。如下例定义了一个`Matrix`结构体,将呈现一个`Double`类型的二维矩阵。`Matrix`结构体的下标脚本需要两个整型参数:
|
||||
|
||||
```swift
|
||||
struct Matrix {
|
||||
let rows: Int, columns: Int
|
||||
var grid: Double[]
|
||||
init(rows: Int, columns: Int) {
|
||||
self.rows = rows
|
||||
self.columns = columns
|
||||
grid = Array(count: rows * columns, repeatedValue: 0.0)
|
||||
}
|
||||
func indexIsValidForRow(row: Int, column: Int) -> Bool {
|
||||
return row >= 0 && row < rows && column >= 0 && column < columns
|
||||
}
|
||||
subscript(row: Int, column: Int) -> Double {
|
||||
get {
|
||||
assert(indexIsValidForRow(row, column: column), "Index out of range")
|
||||
return grid[(row * columns) + column]
|
||||
}
|
||||
set {
|
||||
assert(indexIsValidForRow(row, column: column), "Index out of range")
|
||||
grid[(row * columns) + columns] = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`Matrix`提供了一个两个入参的构造方法,入参分别是`rows`和`columns`,创建了一个足够容纳`rows * columns`个数的`Double`类型数组。为了存储,将数组的大小和数组每个元素初始值0.0,都传入数组的构造方法中来创建一个正确大小的新数组。关于数组的构造方法和析构方法请参考[创建并且构造一个数组](../chapter2/04_Collection_Types.html)。
|
||||
|
||||
你可以通过传入合适的`row`和`column`的数量来构造一个新的`Matrix`实例:
|
||||
|
||||
```swift
|
||||
var matrix = Matrix(rows: 2, columns: 2)
|
||||
```
|
||||
|
||||
上例中创建了一个新的两行两列的`Matrix`实例。在阅读顺序从左上到右下的`Matrix`实例中的数组实例`grid`是矩阵二维数组的扁平化存储:
|
||||
|
||||
```swift
|
||||
// 示意图
|
||||
grid = [0.0, 0.0, 0.0, 0.0]
|
||||
|
||||
col0 col1
|
||||
row0 [0.0, 0.0,
|
||||
row1 0.0, 0.0]
|
||||
```
|
||||
|
||||
将值赋给带有`row`和`column`下标脚本的`matrix`实例表达式可以完成赋值操作,下标脚本入参使用逗号分割
|
||||
|
||||
```swift
|
||||
matrix[0, 1] = 1.5
|
||||
matrix[1, 0] = 3.2
|
||||
```
|
||||
|
||||
上面两条语句分别`让matrix`的右上值为 1.5,坐下值为 3.2:
|
||||
|
||||
```swift
|
||||
[0.0, 1.5,
|
||||
3.2, 0.0]
|
||||
```
|
||||
|
||||
`Matrix`下标脚本的`getter`和`setter`中同时调用了下标脚本入参的`row`和`column`是否有效的判断。为了方便进行断言,`Matrix`包含了一个名为`indexIsValid`的成员方法,用来确认入参的`row`或`column`值是否会造成数组越界:
|
||||
|
||||
```swift
|
||||
func indexIsValidForRow(row: Int, column: Int) -> Bool {
|
||||
return row >= 0 && row < rows && column >= 0 && column < columns
|
||||
}
|
||||
```
|
||||
|
||||
断言在下标脚本越界时触发:
|
||||
|
||||
```swift
|
||||
let someValue = matrix[2, 2]
|
||||
// 断言将会触发,因为 [2, 2] 已经超过了matrix的最大长度
|
||||
```
|
||||
|
||||
@ -1,270 +1,270 @@
|
||||
> 翻译:Hawstein
|
||||
> 校对:menlongsheng
|
||||
|
||||
# 继承(Inheritance)
|
||||
-------------------
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [定义一个基类(Base class)](#defining_a_base_class)
|
||||
- [子类生成(Subclassing)](#subclassing)
|
||||
- [重写(Overriding)](#overriding)
|
||||
- [防止重写](#preventing_overrides)
|
||||
|
||||
一个类可以*继承(inherit)*另一个类的方法(methods),属性(property)和其它特性。当一个类继承其它类时,继承类叫*子类(subclass)*,被继承类叫*超类(或父类,superclass)*。在 Swift 中,继承是区分「类」与其它类型的一个基本特征。
|
||||
|
||||
在 Swift 中,类可以调用和访问超类的方法,属性和下标脚本(subscripts),并且可以重写(override)这些方法,属性和下标脚本来优化或修改它们的行为。Swift 会检查你的重写定义在超类中是否有匹配的定义,以此确保你的重写行为是正确的。
|
||||
|
||||
可以为类中继承来的属性添加属性观察器(property observer),这样一来,当属性值改变时,类就会被通知到。可以为任何属性添加属性观察器,无论它原本被定义为存储型属性(stored property)还是计算型属性(computed property)。
|
||||
|
||||
<a name="defining_a_base_class"></a>
|
||||
## 定义一个基类(Base class)
|
||||
|
||||
不继承于其它类的类,称之为*基类(base calss)*。
|
||||
|
||||
> 注意:
|
||||
Swift 中的类并不是从一个通用的基类继承而来。如果你不为你定义的类指定一个超类的话,这个类就自动成为基类。
|
||||
|
||||
下面的例子定义了一个叫`Vehicle`的基类。这个基类声明了两个对所有车辆都通用的属性(`numberOfWheels`和`maxPassengers`)。这些属性在`description`方法中使用,这个方法返回一个`String`类型的,对车辆特征的描述:
|
||||
|
||||
```swift
|
||||
class Vehicle {
|
||||
var numberOfWheels: Int
|
||||
var maxPassengers: Int
|
||||
func description() -> String {
|
||||
return "\(numberOfWheels) wheels; up to \(maxPassengers) passengers"
|
||||
}
|
||||
init() {
|
||||
numberOfWheels = 0
|
||||
maxPassengers = 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`Vehicle`类定义了*构造器(initializer)*来设置属性的值。构造器会在[构造过程](../chapter2/_14Initialization.html)一节中详细介绍,这里我们做一下简单介绍,以便于讲解子类中继承来的属性如何被修改。
|
||||
|
||||
构造器用于创建某个类型的一个新实例。尽管构造器并不是方法,但在语法上,两者很相似。构造器的工作是准备新实例以供使用,并确保实例中的所有属性都拥有有效的初始化值。
|
||||
|
||||
构造器的最简单形式就像一个没有参数的实例方法,使用`init`关键字:
|
||||
|
||||
```swift
|
||||
init() {
|
||||
// 执行构造过程
|
||||
}
|
||||
```
|
||||
|
||||
如果要创建一个`Vehicle`类的新实例,使用*构造器*语法调用上面的初始化器,即类名后面跟一个空的小括号:
|
||||
|
||||
```swift
|
||||
let someVehicle = Vehicle()
|
||||
```
|
||||
|
||||
这个`Vehicle`类的构造器为任意的一辆车设置一些初始化属性值(`numberOfWheels = 0 `和`maxPassengers = 1`)。
|
||||
|
||||
`Vehicle`类定义了车辆的共同特性,但这个类本身并没太大用处。为了使它更为实用,你需要进一步细化它来描述更具体的车辆。
|
||||
|
||||
<a name="subclassing"></a>
|
||||
## 子类生成(Subclassing)
|
||||
|
||||
*子类生成(Subclassing)*指的是在一个已有类的基础上创建一个新的类。子类继承超类的特性,并且可以优化或改变它。你还可以为子类添加新的特性。
|
||||
|
||||
为了指明某个类的超类,将超类名写在子类名的后面,用冒号分隔:
|
||||
|
||||
```swift
|
||||
class SomeClass: SomeSuperclass {
|
||||
// 类的定义
|
||||
}
|
||||
```
|
||||
|
||||
下一个例子,定义一个更具体的车辆类叫`Bicycle`。这个新类是在 `Vehicle`类的基础上创建起来。因此你需要将`Vehicle`类放在 `Bicycle`类后面,用冒号分隔。
|
||||
|
||||
我们可以将这读作:
|
||||
|
||||
“定义一个新的类叫`Bicycle `,它继承了`Vehicle`的特性”;
|
||||
|
||||
```swift
|
||||
class Bicycle: Vehicle {
|
||||
init() {
|
||||
super.init()
|
||||
numberOfWheels = 2
|
||||
}
|
||||
}
|
||||
```
|
||||
preview
|
||||
`Bicycle`是`Vehicle`的子类,`Vehicle`是`Bicycle`的超类。新的`Bicycle`类自动获得`Vehicle`类的特性,比如 `maxPassengers`和`numberOfWheels`属性。你可以在子类中定制这些特性,或添加新的特性来更好地描述`Bicycle`类。
|
||||
|
||||
`Bicycle`类定义了一个构造器来设置它定制的特性(自行车只有2个轮子)。`Bicycle`的构造器调用了它父类`Vehicle`的构造器 `super.init()`,以此确保在`Bicycle`类试图修改那些继承来的属性前`Vehicle`类已经初始化过它们了。
|
||||
|
||||
> 注意:
|
||||
不像 Objective-C,在 Swift 中,初始化器默认是不继承的,见[初始化器的继承与重写](../chapter2/_14Initialization.html#initializer_inheritance_and_ overriding)
|
||||
|
||||
`Vehicle`类中`maxPassengers`的默认值对自行车来说已经是正确的,因此在`Bicycle`的构造器中并没有改变它。而`numberOfWheels`原来的值对自行车来说是不正确的,因此在初始化器中将它更改为 2。
|
||||
|
||||
`Bicycle`不仅可以继承`Vehicle`的属性,还可以继承它的方法。如果你创建了一个`Bicycle`类的实例,你就可以调用它继承来的`description`方法,并且可以看到,它输出的属性值已经发生了变化:
|
||||
|
||||
```swift
|
||||
let bicycle = Bicycle()
|
||||
println("Bicycle: \(bicycle.description())")
|
||||
// Bicycle: 2 wheels; up to 1 passengers
|
||||
```
|
||||
|
||||
子类还可以继续被其它类继承:
|
||||
|
||||
```swift
|
||||
class Tandem: Bicycle {
|
||||
init() {
|
||||
super.init()
|
||||
maxPassengers = 2
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
上面的例子创建了`Bicycle`的一个子类:双人自行车(tandem)。`Tandem`从`Bicycle`继承了两个属性,而这两个属性是`Bicycle`从`Vehicle`继承而来的。`Tandem`并不修改轮子的数量,因为它仍是一辆自行车,有 2 个轮子。但它需要修改`maxPassengers`的值,因为双人自行车可以坐两个人。
|
||||
|
||||
> 注意:
|
||||
子类只允许修改从超类继承来的变量属性,而不能修改继承来的常量属性。
|
||||
|
||||
创建一个`Tandem`类的实例,打印它的描述,即可看到它的属性已被更新:
|
||||
|
||||
```swift
|
||||
let tandem = Tandem()
|
||||
println("Tandem: \(tandem.description())")
|
||||
// Tandem: 2 wheels; up to 2 passengers
|
||||
```
|
||||
|
||||
注意,`Tandem`类也继承了`description`方法。一个类的实例方法会被这个类的所有子类继承。
|
||||
|
||||
<a name="overriding"></a>
|
||||
## 重写(Overriding)
|
||||
|
||||
子类可以为继承来的实例方法(instance method),类方法(class method),实例属性(instance property),或下标脚本(subscript)提供自己定制的实现(implementation)。我们把这种行为叫*重写(overriding)*。
|
||||
|
||||
如果要重写某个特性,你需要在重写定义的前面加上`override`关键字。这么做,你就表明了你是想提供一个重写版本,而非错误地提供了一个相同的定义。意外的重写行为可能会导致不可预知的错误,任何缺少`override`关键字的重写都会在编译时被诊断为错误。
|
||||
|
||||
`override`关键字会提醒 Swift 编译器去检查该类的超类(或其中一个父类)是否有匹配重写版本的声明。这个检查可以确保你的重写定义是正确的。
|
||||
|
||||
### 访问超类的方法,属性及下标脚本
|
||||
|
||||
当你在子类中重写超类的方法,属性或下标脚本时,有时在你的重写版本中使用已经存在的超类实现会大有裨益。比如,你可以优化已有实现的行为,或在一个继承来的变量中存储一个修改过的值。
|
||||
|
||||
在合适的地方,你可以通过使用`super`前缀来访问超类版本的方法,属性或下标脚本:
|
||||
|
||||
* 在方法`someMethod`的重写实现中,可以通过`super.someMethod()`来调用超类版本的`someMethod`方法。
|
||||
* 在属性`someProperty`的 getter 或 setter 的重写实现中,可以通过`super.someProperty`来访问超类版本的`someProperty`属性。
|
||||
* 在下标脚本的重写实现中,可以通过`super[someIndex]`来访问超类版本中的相同下标脚本。
|
||||
|
||||
### 重写方法
|
||||
|
||||
在子类中,你可以重写继承来的实例方法或类方法,提供一个定制或替代的方法实现。
|
||||
|
||||
下面的例子定义了`Vehicle`的一个新的子类,叫`Car`,它重写了从`Vehicle`类继承来的`description`方法:
|
||||
|
||||
```swift
|
||||
class Car: Vehicle {
|
||||
var speed: Double = 0.0
|
||||
init() {
|
||||
super.init()
|
||||
maxPassengers = 5
|
||||
numberOfWheels = 4
|
||||
}
|
||||
override func description() -> String {
|
||||
return super.description() + "; "
|
||||
+ "traveling at \(speed) mph"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`Car`声明了一个新的存储型属性`speed`,它是`Double`类型的,默认值是`0.0`,表示“时速是0英里”。`Car`有自己的初始化器,它将乘客的最大数量设为5,轮子数量设为4。
|
||||
|
||||
`Car`重写了继承来的`description`方法,它的声明与`Vehicle`中的`description`方法一致,声明前面加上了`override`关键字。
|
||||
|
||||
`Car`中的`description`方法并非完全自定义,而是通过`super.description`使用了超类`Vehicle`中的`description`方法,然后再追加一些额外的信息,比如汽车的当前速度。
|
||||
|
||||
如果你创建一个`Car`的新实例,并打印`description`方法的输出,你就会发现描述信息已经发生了改变:
|
||||
|
||||
```swift
|
||||
let car = Car()
|
||||
println("Car: \(car.description())")
|
||||
// Car: 4 wheels; up to 5 passengers; traveling at 0.0 mph
|
||||
```
|
||||
|
||||
### 重写属性
|
||||
|
||||
你可以重写继承来的实例属性或类属性,提供自己定制的getter和setter,或添加属性观察器使重写的属性观察属性值什么时候发生改变。
|
||||
|
||||
#### 重写属性的Getters和Setters
|
||||
|
||||
你可以提供定制的 getter(或 setter)来重写任意继承来的属性,无论继承来的属性是存储型的还是计算型的属性。子类并不知道继承来的属性是存储型的还是计算型的,它只知道继承来的属性会有一个名字和类型。你在重写一个属性时,必需将它的名字和类型都写出来。这样才能使编译器去检查你重写的属性是与超类中同名同类型的属性相匹配的。
|
||||
|
||||
你可以将一个继承来的只读属性重写为一个读写属性,只需要你在重写版本的属性里提供 getter 和 setter 即可。但是,你不可以将一个继承来的读写属性重写为一个只读属性。
|
||||
|
||||
> 注意:
|
||||
如果你在重写属性中提供了 setter,那么你也一定要提供 getter。如果你不想在重写版本中的 getter 里修改继承来的属性值,你可以直接返回`super.someProperty`来返回继承来的值。正如下面的`SpeedLimitedCar`的例子所示。
|
||||
|
||||
以下的例子定义了一个新类,叫`SpeedLimitedCar`,它是`Car`的子类。类`SpeedLimitedCar`表示安装了限速装置的车,它的最高速度只能达到40mph。你可以通过重写继承来的`speed`属性来实现这个速度限制:
|
||||
|
||||
```swift
|
||||
class SpeedLimitedCar: Car {
|
||||
override var speed: Double {
|
||||
get {
|
||||
return super.speed
|
||||
}
|
||||
set {
|
||||
super.speed = min(newValue, 40.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
当你设置一个`SpeedLimitedCar`实例的`speed`属性时,属性setter的实现会去检查新值与限制值40mph的大小,它会将超类的`speed`设置为`newValue`和`40.0`中较小的那个。这两个值哪个较小由`min`函数决定,它是Swift标准库中的一个全局函数。`min`函数接收两个或更多的数,返回其中最小的那个。
|
||||
|
||||
如果你尝试将`SpeedLimitedCar`实例的`speed`属性设置为一个大于40mph的数,然后打印`description`函数的输出,你会发现速度被限制在40mph:
|
||||
|
||||
```swift
|
||||
let limitedCar = SpeedLimitedCar()
|
||||
limitedCar.speed = 60.0
|
||||
println("SpeedLimitedCar: \(limitedCar.description())")
|
||||
// SpeedLimitedCar: 4 wheels; up to 5 passengers; traveling at 40.0 mph
|
||||
```
|
||||
|
||||
#### 重写属性观察器(Property Observer)
|
||||
|
||||
你可以在属性重写中为一个继承来的属性添加属性观察器。这样一来,当继承来的属性值发生改变时,你就会被通知到,无论那个属性原本是如何实现的。关于属性观察器的更多内容,请看[属性观察器](../chapter2/_10Properties.html#property_observer)。
|
||||
|
||||
> 注意:
|
||||
你不可以为继承来的常量存储型属性或继承来的只读计算型属性添加属性观察器。这些属性的值是不可以被设置的,所以,为它们提供`willSet`或`didSet`实现是不恰当。此外还要注意,你不可以同时提供重写的 setter 和重写的属性观察器。如果你想观察属性值的变化,并且你已经为那个属性提供了定制的 setter,那么你在 setter 中就可以观察到任何值变化了。
|
||||
|
||||
下面的例子定义了一个新类叫`AutomaticCar`,它是`Car`的子类。`AutomaticCar`表示自动挡汽车,它可以根据当前的速度自动选择合适的挡位。`AutomaticCar`也提供了定制的`description`方法,可以输出当前挡位。
|
||||
|
||||
```swift
|
||||
class AutomaticCar: Car {
|
||||
var gear = 1
|
||||
override var speed: Double {
|
||||
didSet {
|
||||
gear = Int(speed / 10.0) + 1
|
||||
}
|
||||
}
|
||||
override func description() -> String {
|
||||
return super.description() + " in gear \(gear)"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
当你设置`AutomaticCar`的`speed`属性,属性的`didSet`观察器就会自动地设置`gear`属性,为新的速度选择一个合适的挡位。具体来说就是,属性观察器将新的速度值除以10,然后向下取得最接近的整数值,最后加1来得到档位`gear`的值。例如,速度为10.0时,挡位为1;速度为35.0时,挡位为4:
|
||||
|
||||
```swift
|
||||
let automatic = AutomaticCar()
|
||||
automatic.speed = 35.0
|
||||
println("AutomaticCar: \(automatic.description())")
|
||||
// AutomaticCar: 4 wheels; up to 5 passengers; traveling at 35.0 mph in gear 4
|
||||
```
|
||||
|
||||
<a name="preventing_overrides"></a>
|
||||
## 防止重写
|
||||
|
||||
你可以通过把方法,属性或下标脚本标记为*`final`*来防止它们被重写,只需要在声明关键字前加上`@final`特性即可。(例如:`@final var`, `@final func`, `@final class func`, 以及 `@final subscript`)
|
||||
|
||||
如果你重写了`final`方法,属性或下标脚本,在编译时会报错。在扩展中,你添加到类里的方法,属性或下标脚本也可以在扩展的定义里标记为 final。
|
||||
|
||||
你可以通过在关键字`class`前添加`@final`特性(`@final class`)来将整个类标记为 final 的,这样的类是不可被继承的,否则会报编译错误。
|
||||
|
||||
> 翻译:[Hawstein](https://github.com/Hawstein)
|
||||
> 校对:[menlongsheng](https://github.com/menlongsheng)
|
||||
|
||||
# 继承(Inheritance)
|
||||
-------------------
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [定义一个基类(Base class)](#defining_a_base_class)
|
||||
- [子类生成(Subclassing)](#subclassing)
|
||||
- [重写(Overriding)](#overriding)
|
||||
- [防止重写](#preventing_overrides)
|
||||
|
||||
一个类可以*继承(inherit)*另一个类的方法(methods),属性(property)和其它特性。当一个类继承其它类时,继承类叫*子类(subclass)*,被继承类叫*超类(或父类,superclass)*。在 Swift 中,继承是区分「类」与其它类型的一个基本特征。
|
||||
|
||||
在 Swift 中,类可以调用和访问超类的方法,属性和下标脚本(subscripts),并且可以重写(override)这些方法,属性和下标脚本来优化或修改它们的行为。Swift 会检查你的重写定义在超类中是否有匹配的定义,以此确保你的重写行为是正确的。
|
||||
|
||||
可以为类中继承来的属性添加属性观察器(property observer),这样一来,当属性值改变时,类就会被通知到。可以为任何属性添加属性观察器,无论它原本被定义为存储型属性(stored property)还是计算型属性(computed property)。
|
||||
|
||||
<a name="defining_a_base_class"></a>
|
||||
## 定义一个基类(Base class)
|
||||
|
||||
不继承于其它类的类,称之为*基类(base calss)*。
|
||||
|
||||
> 注意:
|
||||
Swift 中的类并不是从一个通用的基类继承而来。如果你不为你定义的类指定一个超类的话,这个类就自动成为基类。
|
||||
|
||||
下面的例子定义了一个叫`Vehicle`的基类。这个基类声明了两个对所有车辆都通用的属性(`numberOfWheels`和`maxPassengers`)。这些属性在`description`方法中使用,这个方法返回一个`String`类型的,对车辆特征的描述:
|
||||
|
||||
```swift
|
||||
class Vehicle {
|
||||
var numberOfWheels: Int
|
||||
var maxPassengers: Int
|
||||
func description() -> String {
|
||||
return "\(numberOfWheels) wheels; up to \(maxPassengers) passengers"
|
||||
}
|
||||
init() {
|
||||
numberOfWheels = 0
|
||||
maxPassengers = 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`Vehicle`类定义了*构造器(initializer)*来设置属性的值。构造器会在[构造过程](../chapter2/_14Initialization.html)一节中详细介绍,这里我们做一下简单介绍,以便于讲解子类中继承来的属性如何被修改。
|
||||
|
||||
构造器用于创建某个类型的一个新实例。尽管构造器并不是方法,但在语法上,两者很相似。构造器的工作是准备新实例以供使用,并确保实例中的所有属性都拥有有效的初始化值。
|
||||
|
||||
构造器的最简单形式就像一个没有参数的实例方法,使用`init`关键字:
|
||||
|
||||
```swift
|
||||
init() {
|
||||
// 执行构造过程
|
||||
}
|
||||
```
|
||||
|
||||
如果要创建一个`Vehicle`类的新实例,使用*构造器*语法调用上面的初始化器,即类名后面跟一个空的小括号:
|
||||
|
||||
```swift
|
||||
let someVehicle = Vehicle()
|
||||
```
|
||||
|
||||
这个`Vehicle`类的构造器为任意的一辆车设置一些初始化属性值(`numberOfWheels = 0 `和`maxPassengers = 1`)。
|
||||
|
||||
`Vehicle`类定义了车辆的共同特性,但这个类本身并没太大用处。为了使它更为实用,你需要进一步细化它来描述更具体的车辆。
|
||||
|
||||
<a name="subclassing"></a>
|
||||
## 子类生成(Subclassing)
|
||||
|
||||
*子类生成(Subclassing)*指的是在一个已有类的基础上创建一个新的类。子类继承超类的特性,并且可以优化或改变它。你还可以为子类添加新的特性。
|
||||
|
||||
为了指明某个类的超类,将超类名写在子类名的后面,用冒号分隔:
|
||||
|
||||
```swift
|
||||
class SomeClass: SomeSuperclass {
|
||||
// 类的定义
|
||||
}
|
||||
```
|
||||
|
||||
下一个例子,定义一个更具体的车辆类叫`Bicycle`。这个新类是在 `Vehicle`类的基础上创建起来。因此你需要将`Vehicle`类放在 `Bicycle`类后面,用冒号分隔。
|
||||
|
||||
我们可以将这读作:
|
||||
|
||||
“定义一个新的类叫`Bicycle `,它继承了`Vehicle`的特性”;
|
||||
|
||||
```swift
|
||||
class Bicycle: Vehicle {
|
||||
init() {
|
||||
super.init()
|
||||
numberOfWheels = 2
|
||||
}
|
||||
}
|
||||
```
|
||||
preview
|
||||
`Bicycle`是`Vehicle`的子类,`Vehicle`是`Bicycle`的超类。新的`Bicycle`类自动获得`Vehicle`类的特性,比如 `maxPassengers`和`numberOfWheels`属性。你可以在子类中定制这些特性,或添加新的特性来更好地描述`Bicycle`类。
|
||||
|
||||
`Bicycle`类定义了一个构造器来设置它定制的特性(自行车只有2个轮子)。`Bicycle`的构造器调用了它父类`Vehicle`的构造器 `super.init()`,以此确保在`Bicycle`类试图修改那些继承来的属性前`Vehicle`类已经初始化过它们了。
|
||||
|
||||
> 注意:
|
||||
不像 Objective-C,在 Swift 中,初始化器默认是不继承的,见[初始化器的继承与重写](../chapter2/_14Initialization.html#initializer_inheritance_and_ overriding)
|
||||
|
||||
`Vehicle`类中`maxPassengers`的默认值对自行车来说已经是正确的,因此在`Bicycle`的构造器中并没有改变它。而`numberOfWheels`原来的值对自行车来说是不正确的,因此在初始化器中将它更改为 2。
|
||||
|
||||
`Bicycle`不仅可以继承`Vehicle`的属性,还可以继承它的方法。如果你创建了一个`Bicycle`类的实例,你就可以调用它继承来的`description`方法,并且可以看到,它输出的属性值已经发生了变化:
|
||||
|
||||
```swift
|
||||
let bicycle = Bicycle()
|
||||
println("Bicycle: \(bicycle.description())")
|
||||
// Bicycle: 2 wheels; up to 1 passengers
|
||||
```
|
||||
|
||||
子类还可以继续被其它类继承:
|
||||
|
||||
```swift
|
||||
class Tandem: Bicycle {
|
||||
init() {
|
||||
super.init()
|
||||
maxPassengers = 2
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
上面的例子创建了`Bicycle`的一个子类:双人自行车(tandem)。`Tandem`从`Bicycle`继承了两个属性,而这两个属性是`Bicycle`从`Vehicle`继承而来的。`Tandem`并不修改轮子的数量,因为它仍是一辆自行车,有 2 个轮子。但它需要修改`maxPassengers`的值,因为双人自行车可以坐两个人。
|
||||
|
||||
> 注意:
|
||||
子类只允许修改从超类继承来的变量属性,而不能修改继承来的常量属性。
|
||||
|
||||
创建一个`Tandem`类的实例,打印它的描述,即可看到它的属性已被更新:
|
||||
|
||||
```swift
|
||||
let tandem = Tandem()
|
||||
println("Tandem: \(tandem.description())")
|
||||
// Tandem: 2 wheels; up to 2 passengers
|
||||
```
|
||||
|
||||
注意,`Tandem`类也继承了`description`方法。一个类的实例方法会被这个类的所有子类继承。
|
||||
|
||||
<a name="overriding"></a>
|
||||
## 重写(Overriding)
|
||||
|
||||
子类可以为继承来的实例方法(instance method),类方法(class method),实例属性(instance property),或下标脚本(subscript)提供自己定制的实现(implementation)。我们把这种行为叫*重写(overriding)*。
|
||||
|
||||
如果要重写某个特性,你需要在重写定义的前面加上`override`关键字。这么做,你就表明了你是想提供一个重写版本,而非错误地提供了一个相同的定义。意外的重写行为可能会导致不可预知的错误,任何缺少`override`关键字的重写都会在编译时被诊断为错误。
|
||||
|
||||
`override`关键字会提醒 Swift 编译器去检查该类的超类(或其中一个父类)是否有匹配重写版本的声明。这个检查可以确保你的重写定义是正确的。
|
||||
|
||||
### 访问超类的方法,属性及下标脚本
|
||||
|
||||
当你在子类中重写超类的方法,属性或下标脚本时,有时在你的重写版本中使用已经存在的超类实现会大有裨益。比如,你可以优化已有实现的行为,或在一个继承来的变量中存储一个修改过的值。
|
||||
|
||||
在合适的地方,你可以通过使用`super`前缀来访问超类版本的方法,属性或下标脚本:
|
||||
|
||||
* 在方法`someMethod`的重写实现中,可以通过`super.someMethod()`来调用超类版本的`someMethod`方法。
|
||||
* 在属性`someProperty`的 getter 或 setter 的重写实现中,可以通过`super.someProperty`来访问超类版本的`someProperty`属性。
|
||||
* 在下标脚本的重写实现中,可以通过`super[someIndex]`来访问超类版本中的相同下标脚本。
|
||||
|
||||
### 重写方法
|
||||
|
||||
在子类中,你可以重写继承来的实例方法或类方法,提供一个定制或替代的方法实现。
|
||||
|
||||
下面的例子定义了`Vehicle`的一个新的子类,叫`Car`,它重写了从`Vehicle`类继承来的`description`方法:
|
||||
|
||||
```swift
|
||||
class Car: Vehicle {
|
||||
var speed: Double = 0.0
|
||||
init() {
|
||||
super.init()
|
||||
maxPassengers = 5
|
||||
numberOfWheels = 4
|
||||
}
|
||||
override func description() -> String {
|
||||
return super.description() + "; "
|
||||
+ "traveling at \(speed) mph"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`Car`声明了一个新的存储型属性`speed`,它是`Double`类型的,默认值是`0.0`,表示“时速是0英里”。`Car`有自己的初始化器,它将乘客的最大数量设为5,轮子数量设为4。
|
||||
|
||||
`Car`重写了继承来的`description`方法,它的声明与`Vehicle`中的`description`方法一致,声明前面加上了`override`关键字。
|
||||
|
||||
`Car`中的`description`方法并非完全自定义,而是通过`super.description`使用了超类`Vehicle`中的`description`方法,然后再追加一些额外的信息,比如汽车的当前速度。
|
||||
|
||||
如果你创建一个`Car`的新实例,并打印`description`方法的输出,你就会发现描述信息已经发生了改变:
|
||||
|
||||
```swift
|
||||
let car = Car()
|
||||
println("Car: \(car.description())")
|
||||
// Car: 4 wheels; up to 5 passengers; traveling at 0.0 mph
|
||||
```
|
||||
|
||||
### 重写属性
|
||||
|
||||
你可以重写继承来的实例属性或类属性,提供自己定制的getter和setter,或添加属性观察器使重写的属性观察属性值什么时候发生改变。
|
||||
|
||||
#### 重写属性的Getters和Setters
|
||||
|
||||
你可以提供定制的 getter(或 setter)来重写任意继承来的属性,无论继承来的属性是存储型的还是计算型的属性。子类并不知道继承来的属性是存储型的还是计算型的,它只知道继承来的属性会有一个名字和类型。你在重写一个属性时,必需将它的名字和类型都写出来。这样才能使编译器去检查你重写的属性是与超类中同名同类型的属性相匹配的。
|
||||
|
||||
你可以将一个继承来的只读属性重写为一个读写属性,只需要你在重写版本的属性里提供 getter 和 setter 即可。但是,你不可以将一个继承来的读写属性重写为一个只读属性。
|
||||
|
||||
> 注意:
|
||||
如果你在重写属性中提供了 setter,那么你也一定要提供 getter。如果你不想在重写版本中的 getter 里修改继承来的属性值,你可以直接返回`super.someProperty`来返回继承来的值。正如下面的`SpeedLimitedCar`的例子所示。
|
||||
|
||||
以下的例子定义了一个新类,叫`SpeedLimitedCar`,它是`Car`的子类。类`SpeedLimitedCar`表示安装了限速装置的车,它的最高速度只能达到40mph。你可以通过重写继承来的`speed`属性来实现这个速度限制:
|
||||
|
||||
```swift
|
||||
class SpeedLimitedCar: Car {
|
||||
override var speed: Double {
|
||||
get {
|
||||
return super.speed
|
||||
}
|
||||
set {
|
||||
super.speed = min(newValue, 40.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
当你设置一个`SpeedLimitedCar`实例的`speed`属性时,属性setter的实现会去检查新值与限制值40mph的大小,它会将超类的`speed`设置为`newValue`和`40.0`中较小的那个。这两个值哪个较小由`min`函数决定,它是Swift标准库中的一个全局函数。`min`函数接收两个或更多的数,返回其中最小的那个。
|
||||
|
||||
如果你尝试将`SpeedLimitedCar`实例的`speed`属性设置为一个大于40mph的数,然后打印`description`函数的输出,你会发现速度被限制在40mph:
|
||||
|
||||
```swift
|
||||
let limitedCar = SpeedLimitedCar()
|
||||
limitedCar.speed = 60.0
|
||||
println("SpeedLimitedCar: \(limitedCar.description())")
|
||||
// SpeedLimitedCar: 4 wheels; up to 5 passengers; traveling at 40.0 mph
|
||||
```
|
||||
|
||||
#### 重写属性观察器(Property Observer)
|
||||
|
||||
你可以在属性重写中为一个继承来的属性添加属性观察器。这样一来,当继承来的属性值发生改变时,你就会被通知到,无论那个属性原本是如何实现的。关于属性观察器的更多内容,请看[属性观察器](../chapter2/_10Properties.html#property_observer)。
|
||||
|
||||
> 注意:
|
||||
你不可以为继承来的常量存储型属性或继承来的只读计算型属性添加属性观察器。这些属性的值是不可以被设置的,所以,为它们提供`willSet`或`didSet`实现是不恰当。此外还要注意,你不可以同时提供重写的 setter 和重写的属性观察器。如果你想观察属性值的变化,并且你已经为那个属性提供了定制的 setter,那么你在 setter 中就可以观察到任何值变化了。
|
||||
|
||||
下面的例子定义了一个新类叫`AutomaticCar`,它是`Car`的子类。`AutomaticCar`表示自动挡汽车,它可以根据当前的速度自动选择合适的挡位。`AutomaticCar`也提供了定制的`description`方法,可以输出当前挡位。
|
||||
|
||||
```swift
|
||||
class AutomaticCar: Car {
|
||||
var gear = 1
|
||||
override var speed: Double {
|
||||
didSet {
|
||||
gear = Int(speed / 10.0) + 1
|
||||
}
|
||||
}
|
||||
override func description() -> String {
|
||||
return super.description() + " in gear \(gear)"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
当你设置`AutomaticCar`的`speed`属性,属性的`didSet`观察器就会自动地设置`gear`属性,为新的速度选择一个合适的挡位。具体来说就是,属性观察器将新的速度值除以10,然后向下取得最接近的整数值,最后加1来得到档位`gear`的值。例如,速度为10.0时,挡位为1;速度为35.0时,挡位为4:
|
||||
|
||||
```swift
|
||||
let automatic = AutomaticCar()
|
||||
automatic.speed = 35.0
|
||||
println("AutomaticCar: \(automatic.description())")
|
||||
// AutomaticCar: 4 wheels; up to 5 passengers; traveling at 35.0 mph in gear 4
|
||||
```
|
||||
|
||||
<a name="preventing_overrides"></a>
|
||||
## 防止重写
|
||||
|
||||
你可以通过把方法,属性或下标脚本标记为*`final`*来防止它们被重写,只需要在声明关键字前加上`@final`特性即可。(例如:`@final var`, `@final func`, `@final class func`, 以及 `@final subscript`)
|
||||
|
||||
如果你重写了`final`方法,属性或下标脚本,在编译时会报错。在扩展中,你添加到类里的方法,属性或下标脚本也可以在扩展的定义里标记为 final。
|
||||
|
||||
你可以通过在关键字`class`前添加`@final`特性(`@final class`)来将整个类标记为 final 的,这样的类是不可被继承的,否则会报编译错误。
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,109 +1,108 @@
|
||||
> 翻译:bruce0505
|
||||
> 校对: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`实例,因此为了清空它占用的内存从而释放它。在这发生前一步,其析构函数被自动调用,其硬币被返回到银行。
|
||||
> 翻译:[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`实例,因此为了清空它占用的内存从而释放它。在这发生前一步,其析构函数被自动调用,其硬币被返回到银行。
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,291 +1,321 @@
|
||||
> 翻译:Jasonbroker
|
||||
|
||||
> 校对:numbbbbb
|
||||
|
||||
|
||||
# Optional Chaining
|
||||
-----------------
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [可选链可替代强制解析](#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)
|
||||
|
||||
可选链(Optional Chaining)是一种可以请求和调用属性、方法及子脚本的过程,它的可选性体现于请求或调用的目标当前可能为空(`nil`)。如果可选的目标有值,那么调用就会成功;相反,如果选择的目标为空(`nil`),则这种调用将返回空(`nil`)。多次请求或调用可以被链接在一起形成一个链,如果任何一个节点为空(`nil`)将导致整个链失效。
|
||||
|
||||
> 注意:
|
||||
Swift 的可选链和 Objective-C 中的消息为空有些相像,但是 Swift 可以使用在任意类型中,并且失败与否可以被检测到。
|
||||
|
||||
<a name="optional_chaining_as_an_alternative_to_forced_unwrapping"></a>
|
||||
## 可选链可替代强制解析
|
||||
|
||||
通过在想调用的属性、方法、或子脚本的可选值(`optional value`)(非空)后面放一个问号,可以定义一个可选链。这一点很像在可选值后面放一个叹号来强制拆得其封包内的值。它们的主要的区别在于当可选值为空时可选链即刻失败,然而一般的强制解析将会引发运行时错误。
|
||||
|
||||
为了反映可选链可以调用空(`nil`),不论你调用的属性、方法、子脚本等返回的值是不是可选值,它的返回结果都是一个可选值。你可以利用这个返回值来检测你的可选链是否调用成功,有返回值即成功,返回nil则失败。
|
||||
|
||||
调用可选链的返回结果与原本的返回结果具有相同的类型,但是原本的返回结果被包装成了一个可选值,当可选链调用成功时,一个应该返回`Int`的属性将会返回`Int?`。
|
||||
|
||||
下面几段代码将解释可选链和强制解析的不同。
|
||||
|
||||
首先定义两个类`Person`和`Residence`。
|
||||
|
||||
class Person {
|
||||
var residence: Residence?
|
||||
}
|
||||
|
||||
class Residence {
|
||||
var numberOfRooms = 1
|
||||
}
|
||||
|
||||
`Residence`具有一个`Int`类型的`numberOfRooms`,其值为 1。`Person`具有一个可选`residence`属性,它的类型是`Residence?`。
|
||||
|
||||
如果你创建一个新的`Person`实例,它的`residence`属性由于是被定义为可选型的,此属性将默认初始化为空:
|
||||
|
||||
let john = Person()
|
||||
|
||||
如果你想使用感叹号(`!`)强制解析获得这个人`residence`属性`numberOfRooms`属性值,将会引发运行时错误,因为这时没有可以供解析的`residence`值。
|
||||
|
||||
let roomCount = john.residence!.numberOfRooms
|
||||
//将导致运行时错误
|
||||
当`john.residence`不是`nil`时,会运行通过,且会将`roomCount` 设置为一个`int`类型的合理值。然而,如上所述,当`residence`为空时,这个代码将会导致运行时错误。
|
||||
|
||||
可选链提供了一种另一种获得`numberOfRooms`的方法。利用可选链,使用问号来代替原来`!`的位置:
|
||||
|
||||
if let roomCount = john.residence?.numberOfRooms {
|
||||
println("John's residence has \(roomCount) room(s).")
|
||||
} else {
|
||||
println("Unable to retrieve the number of rooms.")
|
||||
}
|
||||
// 打印 "Unable to retrieve the number of rooms.
|
||||
|
||||
|
||||
这告诉 Swift 来链接可选`residence?`属性,如果`residence`存在则取回`numberOfRooms`的值。
|
||||
|
||||
因为这种尝试获得`numberOfRooms`的操作有可能失败,可选链会返回`Int?`类型值,或者称作“可选`Int`”。当`residence`是空的时候(上例),选择`Int`将会为空,因此会出先无法访问`numberOfRooms`的情况。
|
||||
|
||||
要注意的是,即使numberOfRooms是非可选`Int`(`Int?`)时这一点也成立。只要是通过可选链的请求就意味着最后`numberOfRooms`总是返回一个`Int?`而不是`Int`。
|
||||
|
||||
你可以自己定义一个`Residence`实例给`john.residence`,这样它就不再为空了:
|
||||
|
||||
john.residence = Residence()
|
||||
|
||||
`john.residence` 现在有了实际存在的实例而不是nil了。如果你想使用和前面一样的可选链来获得`numberOfRoooms`,它将返回一个包含默认值 1 的`Int?`:
|
||||
|
||||
if let roomCount = john.residence?.numberOfRooms {
|
||||
println("John's residence has \(roomCount) room(s).")
|
||||
} else {
|
||||
println("Unable to retrieve the number of rooms.")
|
||||
}
|
||||
// 打印 "John's residence has 1 room(s)"。
|
||||
|
||||
<a name="defining_model_classes_for_optional_chaining"></a>
|
||||
##为可选链定义模型类
|
||||
|
||||
你可以使用可选链来多层调用属性,方法,和子脚本。这让你可以利用它们之间的复杂模型来获取更底层的属性,并检查是否可以成功获取此类底层属性。
|
||||
|
||||
后面的代码定义了四个将在后面使用的模型类,其中包括多层可选链。这些类是由上面的`Person`和`Residence`模型通过添加一个`Room`和一个`Address`类拓展来。
|
||||
|
||||
`Person`类定义与之前相同。
|
||||
|
||||
class Person {
|
||||
var residence: Residence?
|
||||
}
|
||||
|
||||
`Residence`类比之前复杂些。这次,它定义了一个变量 `rooms`,它被初始化为一个`Room[]`类型的空数组:
|
||||
|
||||
class Residence {
|
||||
var rooms = Room[]()
|
||||
var numberOfRooms: Int {
|
||||
return rooms.count
|
||||
}
|
||||
subscript(i: Int) -> Room {
|
||||
return rooms[i]
|
||||
}
|
||||
func printNumberOfRooms() {
|
||||
println("The number of rooms is \(numberOfRooms)")
|
||||
}
|
||||
var address: Address?
|
||||
}
|
||||
|
||||
因为`Residence`存储了一个`Room`实例的数组,它的`numberOfRooms`属性值不是一个固定的存储值,而是通过计算而来的。`numberOfRooms`属性值是由返回`rooms`数组的`count`属性值得到的。
|
||||
|
||||
为了能快速访问`rooms`数组,`Residence`定义了一个只读的子脚本,通过插入数组的元素角标就可以成功调用。如果该角标存在,子脚本则将该元素返回。
|
||||
|
||||
`Residence`中也提供了一个`printNumberOfRooms`的方法,即简单的打印房间个数。
|
||||
|
||||
最后,`Residence`定义了一个可选属性叫`address`(`address?`)。`Address`类的属性将在后面定义。
|
||||
用于`rooms`数组的`Room`类是一个很简单的类,它只有一个`name`属性和一个设定`room`名的初始化器。
|
||||
|
||||
class Room {
|
||||
let name: String
|
||||
init(name: String) { self.name = name }
|
||||
}
|
||||
|
||||
|
||||
这个模型中的最终类叫做`Address`。它有三个类型是`String?`的可选属性。前面两个可选属性`buildingName`和 `buildingNumber`作为地址的一部分,是定义某个建筑物的两种方式。第三个属性`street`,用于命名地址的街道名:
|
||||
|
||||
class Address {
|
||||
var buildingName: String?
|
||||
var buildingNumber: String?
|
||||
var street: String?
|
||||
func buildingIdentifier() -> String? {
|
||||
if buildingName {
|
||||
return buildingName
|
||||
} else if buildingNumber {
|
||||
return buildingNumber
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
`Address`类还提供了一个`buildingIdentifier`的方法,它的返回值类型为`String?`。这个方法检查`buildingName`和`buildingNumber`的属性,如果`buildingName`有值则将其返回,或者如果`buildingNumber`有值则将其返回,再或如果没有一个属性有值,返回空。
|
||||
|
||||
<a name="calling_properties_through_optional_chaining"></a>
|
||||
##通过可选链调用属性
|
||||
|
||||
正如上面“ [可选链可替代强制解析](#optional_chaining_as_an_alternative_to_forced_unwrapping)”中所述,你可以利用可选链的可选值获取属性,并且检查属性是否获取成功。然而,你不能使用可选链为属性赋值。
|
||||
|
||||
使用上述定义的类来创建一个人实例,并再次尝试后去它的`numberOfRooms`属性:
|
||||
|
||||
let john = Person()
|
||||
if let roomCount = john.residence?.numberOfRooms {
|
||||
println("John's residence has \(roomCount) room(s).")
|
||||
} else {
|
||||
println("Unable to retrieve the number of rooms.")
|
||||
}
|
||||
// 打印 "Unable to retrieve the number of rooms。
|
||||
|
||||
由于`john.residence`是空,所以这个可选链和之前一样失败了,但是没有运行时错误。
|
||||
|
||||
<a name="calling_methods_through_optional_chaining"></a>
|
||||
##通过可选链调用方法
|
||||
|
||||
你可以使用可选链的来调用可选值的方法并检查方法调用是否成功。即使这个方法没有返回值,你依然可以使用可选链来达成这一目的。
|
||||
|
||||
`Residence`的`printNumberOfRooms`方法会打印`numberOfRooms`的当前值。方法如下:
|
||||
|
||||
func printNumberOfRooms(){
|
||||
println(“The number of rooms is \(numberOfRooms)”)
|
||||
}
|
||||
|
||||
这个方法没有返回值。但是,没有返回值类型的函数和方法有一个隐式的返回值类型`Void`(参见Function Without Return Values)。
|
||||
|
||||
如果你利用可选链调用此方法,这个方法的返回值类型将是`Void?`,而不是`Void`,因为当通过可选链调用方法时返回值总是可选类型(optional type)。即使这个方法本身没有定义返回值,你也可以使用`if`语句来检查是否能成功调用`printNumberOfRooms`方法:如果方法通过可选链调用成功,`printNumberOfRooms`的隐式返回值将会是`Void`,如果没有成功,将返回`nil`:
|
||||
|
||||
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.")
|
||||
}
|
||||
// 打印 "It was not possible to print the number of rooms."。
|
||||
|
||||
<a name="calling_subscripts_through_optional_chaining"></a>
|
||||
##使用可选链调用子脚本
|
||||
|
||||
你可以使用可选链来尝试从子脚本获取值并检查子脚本的调用是否成功,然而,你不能通过可选链来设置子代码。
|
||||
|
||||
> 注意:
|
||||
当你使用可选链来获取子脚本的时候,你应该将问号放在子脚本括号的前面而不是后面。可选链的问号一般直接跟在表达语句的后面。
|
||||
|
||||
下面这个例子用在`Residence`类中定义的子脚本来获取`john.residence`数组中第一个房间的名字。因为`john.residence`现在是`nil`,子脚本的调用失败了。
|
||||
|
||||
if let firstRoomName = john.residence?[0].name {
|
||||
println("The first room name is \(firstRoomName).")
|
||||
} else {
|
||||
println("Unable to retrieve the first room name.")
|
||||
}
|
||||
// 打印 "Unable to retrieve the first room name."。
|
||||
|
||||
在子代码调用中可选链的问号直接跟在`john.residence`的后面,在子脚本括号的前面,因为`john.residence`是可选链试图获得的可选值。
|
||||
|
||||
如果你创建一个`Residence`实例给`john.residence`,且在他的`rooms`数组中有一个或多个`Room`实例,那么你可以使用可选链通过`Residence`子脚本来获取在`rooms`数组中的实例了:
|
||||
|
||||
let johnsHouse = Residence()
|
||||
johnsHouse.rooms += Room(name: "Living Room")
|
||||
johnsHouse.rooms += Room(name: "Kitchen")
|
||||
john.residence = johnsHouse
|
||||
|
||||
if let firstRoomName = john.residence?[0].name {
|
||||
println("The first room name is \(firstRoomName).")
|
||||
} else {
|
||||
println("Unable to retrieve the first room name.")
|
||||
}
|
||||
// 打印 "The first room name is Living Room."。
|
||||
|
||||
<a name="linking_multiple_levels_of_chaining"></a>
|
||||
##连接多层链接
|
||||
|
||||
你可以将多层可选链连接在一起,可以掘取模型内更下层的属性方法和子脚本。然而多层可选链不能再添加比已经返回的可选值更多的层。
|
||||
也就是说:
|
||||
|
||||
如果你试图获得的类型不是可选类型,由于使用了可选链它将变成可选类型。
|
||||
如果你试图获得的类型已经是可选类型,由于可选链它也不会提高可选性。
|
||||
|
||||
因此:
|
||||
|
||||
如果你试图通过可选链获得`Int`值,不论使用了多少层链接返回的总是`Int?`。
|
||||
相似的,如果你试图通过可选链获得`Int?`值,不论使用了多少层链接返回的总是`Int?`。
|
||||
|
||||
下面的例子试图获取`john`的`residence`属性里的`address`的`street`属性。这里使用了两层可选链来联系`residence`和`address`属性,它们两者都是可选类型:
|
||||
|
||||
if let johnsStreet = john.residence?.address?.street {
|
||||
println("John's street name is \(johnsStreet).")
|
||||
} else {
|
||||
println("Unable to retrieve the address.")
|
||||
}
|
||||
// 打印 "Unable to retrieve the address.”。
|
||||
|
||||
`john.residence`的值现在包含一个`Residence`实例,然而`john.residence.address`现在是`nil`,因此`john.residence?.address?.street`调用失败。
|
||||
|
||||
从上面的例子发现,你试图获得`street`属性值。这个属性的类型是`String?`。因此尽管在可选类型属性前使用了两层可选链,`john.residence?.address?.street`的返回值类型也是`String?`。
|
||||
|
||||
如果你为`Address`设定一个实例来作为`john.residence.address`的值,并为`address`的`street`属性设定一个实际值,你可以通过多层可选链来得到这个属性值。
|
||||
|
||||
let johnsAddress = Address()
|
||||
johnsAddress.buildingName = "The Larches"
|
||||
johnsAddress.street = "Laurel Street"
|
||||
john.residence!.address = johnsAddress
|
||||
|
||||
if let johnsStreet = john.residence?.address?.street {
|
||||
println("John's street name is \(johnsStreet).")
|
||||
} else {
|
||||
println("Unable to retrieve the address.")
|
||||
}
|
||||
// 打印 "John's street name is Laurel Street."。
|
||||
|
||||
值得注意的是,“`!`”符号在给`john.residence.address`分配`address`实例时的使用。`john.residence`属性是一个可选类型,因此你需要在它获取`address`属性之前使用`!`解析以获得它的实际值。
|
||||
|
||||
<a name="chaining_on_methods_with_optional_return_values"></a>
|
||||
##链接可选返回值的方法
|
||||
|
||||
前面的例子解释了如何通过可选链来获得可选类型属性值。你也可以通过可选链调用一个返回可选类型值的方法并按需链接该方法的返回值。
|
||||
|
||||
下面的例子通过可选链调用了`Address`类中的`buildingIdentifier` 方法。这个方法的返回值类型是`String?`。如上所述,这个方法在可选链调用后最终的返回值类型依然是`String?`:
|
||||
|
||||
if let buildingIdentifier = john.residence?.address?.buildingIdentifier() {
|
||||
println("John's building identifier is \(buildingIdentifier).")
|
||||
}
|
||||
// 打印 "John's building identifier is The Larches."。
|
||||
|
||||
如果你还想进一步对方法返回值执行可选链,将可选链问号符放在方法括号的后面:
|
||||
|
||||
if let upper = john.residence?.address?.buildingIdentifier()?.uppercaseString {
|
||||
println("John's uppercase building identifier is \(upper).")
|
||||
}
|
||||
// 打印 "John's uppercase building identifier is THE LARCHES."。
|
||||
|
||||
> 注意:
|
||||
在上面的例子中,你将可选链问号符放在括号后面是因为你想要链接的可选值是`buildingIdentifier`方法的返回值,不是`buildingIdentifier`方法本身。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
> 翻译:[Jasonbroker](https://github.com/Jasonbroker)
|
||||
> 校对:[numbbbbb](https://github.com/numbbbbb), [stanzhai](https://github.com/stanzhai)
|
||||
|
||||
# Optional Chaining
|
||||
-----------------
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [可选链可替代强制解析](#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)
|
||||
|
||||
可选链(Optional Chaining)是一种可以请求和调用属性、方法及子脚本的过程,它的可选性体现于请求或调用的目标当前可能为空(`nil`)。如果可选的目标有值,那么调用就会成功;相反,如果选择的目标为空(`nil`),则这种调用将返回空(`nil`)。多次请求或调用可以被链接在一起形成一个链,如果任何一个节点为空(`nil`)将导致整个链失效。
|
||||
|
||||
> 注意:
|
||||
Swift 的可选链和 Objective-C 中的消息为空有些相像,但是 Swift 可以使用在任意类型中,并且失败与否可以被检测到。
|
||||
|
||||
<a name="optional_chaining_as_an_alternative_to_forced_unwrapping"></a>
|
||||
## 可选链可替代强制解析
|
||||
|
||||
通过在想调用的属性、方法、或子脚本的可选值(`optional value`)(非空)后面放一个问号,可以定义一个可选链。这一点很像在可选值后面放一个叹号来强制拆得其封包内的值。它们的主要的区别在于当可选值为空时可选链即刻失败,然而一般的强制解析将会引发运行时错误。
|
||||
|
||||
为了反映可选链可以调用空(`nil`),不论你调用的属性、方法、子脚本等返回的值是不是可选值,它的返回结果都是一个可选值。你可以利用这个返回值来检测你的可选链是否调用成功,有返回值即成功,返回nil则失败。
|
||||
|
||||
调用可选链的返回结果与原本的返回结果具有相同的类型,但是原本的返回结果被包装成了一个可选值,当可选链调用成功时,一个应该返回`Int`的属性将会返回`Int?`。
|
||||
|
||||
下面几段代码将解释可选链和强制解析的不同。
|
||||
|
||||
首先定义两个类`Person`和`Residence`。
|
||||
|
||||
```swift
|
||||
class Person {
|
||||
var residence: Residence?
|
||||
}
|
||||
|
||||
class Residence {
|
||||
var numberOfRooms = 1
|
||||
}
|
||||
```
|
||||
|
||||
`Residence`具有一个`Int`类型的`numberOfRooms`,其值为 1。`Person`具有一个可选`residence`属性,它的类型是`Residence?`。
|
||||
|
||||
如果你创建一个新的`Person`实例,它的`residence`属性由于是被定义为可选型的,此属性将默认初始化为空:
|
||||
|
||||
```swift
|
||||
let john = Person()
|
||||
```
|
||||
|
||||
如果你想使用感叹号(`!`)强制解析获得这个人`residence`属性`numberOfRooms`属性值,将会引发运行时错误,因为这时没有可以供解析的`residence`值。
|
||||
|
||||
```swift
|
||||
let roomCount = john.residence!.numberOfRooms
|
||||
//将导致运行时错误
|
||||
```
|
||||
当`john.residence`不是`nil`时,会运行通过,且会将`roomCount` 设置为一个`int`类型的合理值。然而,如上所述,当`residence`为空时,这个代码将会导致运行时错误。
|
||||
|
||||
可选链提供了一种另一种获得`numberOfRooms`的方法。利用可选链,使用问号来代替原来`!`的位置:
|
||||
|
||||
```swift
|
||||
if let roomCount = john.residence?.numberOfRooms {
|
||||
println("John's residence has \(roomCount) room(s).")
|
||||
} else {
|
||||
println("Unable to retrieve the number of rooms.")
|
||||
}
|
||||
// 打印 "Unable to retrieve the number of rooms.
|
||||
```
|
||||
|
||||
这告诉 Swift 来链接可选`residence?`属性,如果`residence`存在则取回`numberOfRooms`的值。
|
||||
|
||||
因为这种尝试获得`numberOfRooms`的操作有可能失败,可选链会返回`Int?`类型值,或者称作“可选`Int`”。当`residence`是空的时候(上例),选择`Int`将会为空,因此会出先无法访问`numberOfRooms`的情况。
|
||||
|
||||
要注意的是,即使numberOfRooms是非可选`Int`(`Int?`)时这一点也成立。只要是通过可选链的请求就意味着最后`numberOfRooms`总是返回一个`Int?`而不是`Int`。
|
||||
|
||||
你可以自己定义一个`Residence`实例给`john.residence`,这样它就不再为空了:
|
||||
|
||||
```swift
|
||||
john.residence = Residence()
|
||||
```
|
||||
|
||||
`john.residence` 现在有了实际存在的实例而不是nil了。如果你想使用和前面一样的可选链来获得`numberOfRoooms`,它将返回一个包含默认值 1 的`Int?`:
|
||||
|
||||
```swift
|
||||
if let roomCount = john.residence?.numberOfRooms {
|
||||
println("John's residence has \(roomCount) room(s).")
|
||||
} else {
|
||||
println("Unable to retrieve the number of rooms.")
|
||||
}
|
||||
// 打印 "John's residence has 1 room(s)"。
|
||||
```
|
||||
|
||||
<a name="defining_model_classes_for_optional_chaining"></a>
|
||||
##为可选链定义模型类
|
||||
|
||||
你可以使用可选链来多层调用属性,方法,和子脚本。这让你可以利用它们之间的复杂模型来获取更底层的属性,并检查是否可以成功获取此类底层属性。
|
||||
|
||||
后面的代码定义了四个将在后面使用的模型类,其中包括多层可选链。这些类是由上面的`Person`和`Residence`模型通过添加一个`Room`和一个`Address`类拓展来。
|
||||
|
||||
`Person`类定义与之前相同。
|
||||
|
||||
```swift
|
||||
class Person {
|
||||
var residence: Residence?
|
||||
}
|
||||
```
|
||||
|
||||
`Residence`类比之前复杂些。这次,它定义了一个变量 `rooms`,它被初始化为一个`Room[]`类型的空数组:
|
||||
|
||||
```swift
|
||||
class Residence {
|
||||
var rooms = Room[]()
|
||||
var numberOfRooms: Int {
|
||||
return rooms.count
|
||||
}
|
||||
subscript(i: Int) -> Room {
|
||||
return rooms[i]
|
||||
}
|
||||
func printNumberOfRooms() {
|
||||
println("The number of rooms is \(numberOfRooms)")
|
||||
}
|
||||
var address: Address?
|
||||
}
|
||||
```
|
||||
|
||||
因为`Residence`存储了一个`Room`实例的数组,它的`numberOfRooms`属性值不是一个固定的存储值,而是通过计算而来的。`numberOfRooms`属性值是由返回`rooms`数组的`count`属性值得到的。
|
||||
|
||||
为了能快速访问`rooms`数组,`Residence`定义了一个只读的子脚本,通过插入数组的元素角标就可以成功调用。如果该角标存在,子脚本则将该元素返回。
|
||||
|
||||
`Residence`中也提供了一个`printNumberOfRooms`的方法,即简单的打印房间个数。
|
||||
|
||||
最后,`Residence`定义了一个可选属性叫`address`(`address?`)。`Address`类的属性将在后面定义。
|
||||
用于`rooms`数组的`Room`类是一个很简单的类,它只有一个`name`属性和一个设定`room`名的初始化器。
|
||||
|
||||
```swift
|
||||
class Room {
|
||||
let name: String
|
||||
init(name: String) { self.name = name }
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
这个模型中的最终类叫做`Address`。它有三个类型是`String?`的可选属性。前面两个可选属性`buildingName`和 `buildingNumber`作为地址的一部分,是定义某个建筑物的两种方式。第三个属性`street`,用于命名地址的街道名:
|
||||
|
||||
```swift
|
||||
class Address {
|
||||
var buildingName: String?
|
||||
var buildingNumber: String?
|
||||
var street: String?
|
||||
func buildingIdentifier() -> String? {
|
||||
if buildingName {
|
||||
return buildingName
|
||||
} else if buildingNumber {
|
||||
return buildingNumber
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`Address`类还提供了一个`buildingIdentifier`的方法,它的返回值类型为`String?`。这个方法检查`buildingName`和`buildingNumber`的属性,如果`buildingName`有值则将其返回,或者如果`buildingNumber`有值则将其返回,再或如果没有一个属性有值,返回空。
|
||||
|
||||
<a name="calling_properties_through_optional_chaining"></a>
|
||||
##通过可选链调用属性
|
||||
|
||||
正如上面“ [可选链可替代强制解析](#optional_chaining_as_an_alternative_to_forced_unwrapping)”中所述,你可以利用可选链的可选值获取属性,并且检查属性是否获取成功。然而,你不能使用可选链为属性赋值。
|
||||
|
||||
使用上述定义的类来创建一个人实例,并再次尝试后去它的`numberOfRooms`属性:
|
||||
|
||||
```swift
|
||||
let john = Person()
|
||||
if let roomCount = john.residence?.numberOfRooms {
|
||||
println("John's residence has \(roomCount) room(s).")
|
||||
} else {
|
||||
println("Unable to retrieve the number of rooms.")
|
||||
}
|
||||
// 打印 "Unable to retrieve the number of rooms。
|
||||
```
|
||||
|
||||
由于`john.residence`是空,所以这个可选链和之前一样失败了,但是没有运行时错误。
|
||||
|
||||
<a name="calling_methods_through_optional_chaining"></a>
|
||||
##通过可选链调用方法
|
||||
|
||||
你可以使用可选链的来调用可选值的方法并检查方法调用是否成功。即使这个方法没有返回值,你依然可以使用可选链来达成这一目的。
|
||||
|
||||
`Residence`的`printNumberOfRooms`方法会打印`numberOfRooms`的当前值。方法如下:
|
||||
|
||||
```swift
|
||||
func printNumberOfRooms(){
|
||||
println(“The number of rooms is \(numberOfRooms)”)
|
||||
}
|
||||
```
|
||||
|
||||
这个方法没有返回值。但是,没有返回值类型的函数和方法有一个隐式的返回值类型`Void`(参见Function Without Return Values)。
|
||||
|
||||
如果你利用可选链调用此方法,这个方法的返回值类型将是`Void?`,而不是`Void`,因为当通过可选链调用方法时返回值总是可选类型(optional type)。即使这个方法本身没有定义返回值,你也可以使用`if`语句来检查是否能成功调用`printNumberOfRooms`方法:如果方法通过可选链调用成功,`printNumberOfRooms`的隐式返回值将会是`Void`,如果没有成功,将返回`nil`:
|
||||
|
||||
```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.")
|
||||
}
|
||||
// 打印 "It was not possible to print the number of rooms."。
|
||||
```
|
||||
|
||||
<a name="calling_subscripts_through_optional_chaining"></a>
|
||||
##使用可选链调用子脚本
|
||||
|
||||
你可以使用可选链来尝试从子脚本获取值并检查子脚本的调用是否成功,然而,你不能通过可选链来设置子代码。
|
||||
|
||||
> 注意:
|
||||
当你使用可选链来获取子脚本的时候,你应该将问号放在子脚本括号的前面而不是后面。可选链的问号一般直接跟在表达语句的后面。
|
||||
|
||||
下面这个例子用在`Residence`类中定义的子脚本来获取`john.residence`数组中第一个房间的名字。因为`john.residence`现在是`nil`,子脚本的调用失败了。
|
||||
|
||||
```swift
|
||||
if let firstRoomName = john.residence?[0].name {
|
||||
println("The first room name is \(firstRoomName).")
|
||||
} else {
|
||||
println("Unable to retrieve the first room name.")
|
||||
}
|
||||
// 打印 "Unable to retrieve the first room name."。
|
||||
```
|
||||
|
||||
在子代码调用中可选链的问号直接跟在`john.residence`的后面,在子脚本括号的前面,因为`john.residence`是可选链试图获得的可选值。
|
||||
|
||||
如果你创建一个`Residence`实例给`john.residence`,且在他的`rooms`数组中有一个或多个`Room`实例,那么你可以使用可选链通过`Residence`子脚本来获取在`rooms`数组中的实例了:
|
||||
|
||||
```swift
|
||||
let johnsHouse = Residence()
|
||||
johnsHouse.rooms += Room(name: "Living Room")
|
||||
johnsHouse.rooms += Room(name: "Kitchen")
|
||||
john.residence = johnsHouse
|
||||
|
||||
if let firstRoomName = john.residence?[0].name {
|
||||
println("The first room name is \(firstRoomName).")
|
||||
} else {
|
||||
println("Unable to retrieve the first room name.")
|
||||
}
|
||||
// 打印 "The first room name is Living Room."。
|
||||
```
|
||||
|
||||
<a name="linking_multiple_levels_of_chaining"></a>
|
||||
##连接多层链接
|
||||
|
||||
你可以将多层可选链连接在一起,可以掘取模型内更下层的属性方法和子脚本。然而多层可选链不能再添加比已经返回的可选值更多的层。
|
||||
也就是说:
|
||||
|
||||
如果你试图获得的类型不是可选类型,由于使用了可选链它将变成可选类型。
|
||||
如果你试图获得的类型已经是可选类型,由于可选链它也不会提高可选性。
|
||||
|
||||
因此:
|
||||
|
||||
如果你试图通过可选链获得`Int`值,不论使用了多少层链接返回的总是`Int?`。
|
||||
相似的,如果你试图通过可选链获得`Int?`值,不论使用了多少层链接返回的总是`Int?`。
|
||||
|
||||
下面的例子试图获取`john`的`residence`属性里的`address`的`street`属性。这里使用了两层可选链来联系`residence`和`address`属性,它们两者都是可选类型:
|
||||
|
||||
```swift
|
||||
if let johnsStreet = john.residence?.address?.street {
|
||||
println("John's street name is \(johnsStreet).")
|
||||
} else {
|
||||
println("Unable to retrieve the address.")
|
||||
}
|
||||
// 打印 "Unable to retrieve the address.”。
|
||||
```
|
||||
|
||||
`john.residence`的值现在包含一个`Residence`实例,然而`john.residence.address`现在是`nil`,因此`john.residence?.address?.street`调用失败。
|
||||
|
||||
从上面的例子发现,你试图获得`street`属性值。这个属性的类型是`String?`。因此尽管在可选类型属性前使用了两层可选链,`john.residence?.address?.street`的返回值类型也是`String?`。
|
||||
|
||||
如果你为`Address`设定一个实例来作为`john.residence.address`的值,并为`address`的`street`属性设定一个实际值,你可以通过多层可选链来得到这个属性值。
|
||||
|
||||
```swift
|
||||
let johnsAddress = Address()
|
||||
johnsAddress.buildingName = "The Larches"
|
||||
johnsAddress.street = "Laurel Street"
|
||||
john.residence!.address = johnsAddress
|
||||
```
|
||||
|
||||
```swift
|
||||
if let johnsStreet = john.residence?.address?.street {
|
||||
println("John's street name is \(johnsStreet).")
|
||||
} else {
|
||||
println("Unable to retrieve the address.")
|
||||
}
|
||||
// 打印 "John's street name is Laurel Street."。
|
||||
```
|
||||
|
||||
值得注意的是,“`!`”符号在给`john.residence.address`分配`address`实例时的使用。`john.residence`属性是一个可选类型,因此你需要在它获取`address`属性之前使用`!`解析以获得它的实际值。
|
||||
|
||||
<a name="chaining_on_methods_with_optional_return_values"></a>
|
||||
##链接可选返回值的方法
|
||||
|
||||
前面的例子解释了如何通过可选链来获得可选类型属性值。你也可以通过可选链调用一个返回可选类型值的方法并按需链接该方法的返回值。
|
||||
|
||||
下面的例子通过可选链调用了`Address`类中的`buildingIdentifier` 方法。这个方法的返回值类型是`String?`。如上所述,这个方法在可选链调用后最终的返回值类型依然是`String?`:
|
||||
|
||||
```swift
|
||||
if let buildingIdentifier = john.residence?.address?.buildingIdentifier() {
|
||||
println("John's building identifier is \(buildingIdentifier).")
|
||||
}
|
||||
// 打印 "John's building identifier is The Larches."。
|
||||
```
|
||||
|
||||
如果你还想进一步对方法返回值执行可选链,将可选链问号符放在方法括号的后面:
|
||||
|
||||
```swift
|
||||
if let upper = john.residence?.address?.buildingIdentifier()?.uppercaseString {
|
||||
println("John's uppercase building identifier is \(upper).")
|
||||
}
|
||||
// 打印 "John's uppercase building identifier is THE LARCHES."。
|
||||
```
|
||||
|
||||
> 注意:
|
||||
在上面的例子中,你将可选链问号符放在括号后面是因为你想要链接的可选值是`buildingIdentifier`方法的返回值,不是`buildingIdentifier`方法本身。
|
||||
|
||||
@ -1,239 +1,253 @@
|
||||
> 翻译:xiehurricane
|
||||
|
||||
> 校对:happyming
|
||||
|
||||
# 类型转换(Type Casting)
|
||||
-----------------
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [定义一个类层次作为例子](#defining_a_class_hierarchy_for_type_casting)
|
||||
- [检查类型](#checking_type)
|
||||
- [向下转型(Downcasting)](#downcasting)
|
||||
- [`Any`和`AnyObject`的类型转换](#type_casting_for_any_and_anyobject)
|
||||
|
||||
|
||||
_类型检查_是一种检查类实例的方式,并且或者也是让实例作为它的父类或者子类的一种方式。
|
||||
|
||||
类型检查在 Swift 中使用`is` 和 `as`操作符实现。这两个操作符提供了一种简单达意的方式去检查值的类型或者转换它的类型。
|
||||
|
||||
你也可以用来检查一个类是否实现了某个协议,就像在 [Checking for Protocol Conformance](Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-XID_363)部分讲述的一样。
|
||||
|
||||
<a name="defining_a_class_hierarchy_for_type_casting"></a>
|
||||
## 定义一个类层次作为例子
|
||||
|
||||
你可以将它用在类和子类的层次结构上,检查特定类实例的类型并且转换这个类实例的类型成为这个层次结构中的其他类型。这下面的三个代码段定义了一个类层次和一个包含了几个这些类实例的数组,作为类型检查的例子。
|
||||
|
||||
第一个代码片段定义了一个新的基础类`MediaItem`。这个类为任何出现在数字媒体库的媒体项提供基础功能。特别的,它声明了一个 `String` 类型的 `name` 属性,和一个`init name`初始化器。(它假定所有的媒体项都有个名称。)
|
||||
|
||||
class MediaItem {
|
||||
var name: String
|
||||
init(name: String) {
|
||||
self.name = name
|
||||
}
|
||||
}
|
||||
|
||||
下一个代码段定义了 `MediaItem` 的两个子类。第一个子类`Movie`,在父类(或者说基类)的基础上增加了一个 `director`(导演) 属性,和相应的初始化器。第二个类在父类的基础上增加了一个 `artist`(艺术家) 属性,和相应的初始化器:
|
||||
|
||||
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` 的类型。
|
||||
|
||||
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>
|
||||
## 检查类型
|
||||
|
||||
用类型检查操作符(`is`)来检查一个实例是否属于特定子类型。若实例属于那个子类型,类型检查操作符返回 `true` ,否则返回 `false` 。
|
||||
|
||||
下面的例子定义了两个变量,`movieCount` 和 `songCount`,用来计算数组`library` 中 `Movie` 和 `Song` 类型的实例数量。
|
||||
|
||||
var movieCount = 0
|
||||
var songCount = 0
|
||||
|
||||
for item in library {
|
||||
if item is Movie {
|
||||
++movieCount
|
||||
} else if item is Song {
|
||||
++songCount
|
||||
}
|
||||
}
|
||||
|
||||
println("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`)
|
||||
|
||||
因为向下转型可能会失败,类型转型操作符带有两种不同形式。可选形式( optional form) `as?` 返回一个你试图下转成的类型的可选值(optional value)。强制形式 `as` 把试图向下转型和强制解包(force-unwraps)结果作为一个混合动作。
|
||||
|
||||
当你不确定向下转型可以成功时,用类型转换的可选形式(`as?`)。可选形式的类型转换总是返回一个可选值(optional value),并且若下转是不可能的,可选值将是 `nil` 。这使你能够检查向下转型是否成功。
|
||||
|
||||
只有你可以确定向下转型一定会成功时,才使用强制形式。当你试图向下转型为一个不正确的类型时,强制形式的类型转换会触发一个运行时错误。
|
||||
|
||||
下面的例子,迭代了`library`里的每一个 `MediaItem` ,并打印出适当的描述。要这样做,`item`需要真正作为`Movie` 或 `Song`的类型来使用。不仅仅是作为 `MediaItem`。为了能够使用`Movie` 或 `Song`的 `director` 或 `artist`属性,这是必要的。
|
||||
|
||||
在这个示例中,数组中的每一个`item`可能是 `Movie` 或 `Song`。 事前你不知道每个`item`的真实类型,所以这里使用可选形式的类型转换 (`as?`)去检查循环里的每次下转。
|
||||
|
||||
for item in library {
|
||||
if let movie = item as? Movie {
|
||||
println("Movie: '\(movie.name)', dir. \(movie.director)")
|
||||
} else if let song = item as? Song {
|
||||
println("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?`类型或 “optional `Movie`”。
|
||||
|
||||
当向下转型为 `Movie` 应用在两个 `Song`
|
||||
实例时将会失败。为了处理这种情况,上面的例子使用了可选绑定(optional binding)来检查可选 `Movie`真的包含一个值(这个是为了判断下转是否成功。)可选绑定是这样写的“`if let movie = item as? Movie`”,可以这样解读:
|
||||
|
||||
“尝试将 `item` 转为 `Movie`类型。若成功,设置一个新的临时常量 `movie` 来存储返回的可选`Movie`”
|
||||
|
||||
若向下转型成功,然后`movie`的属性将用于打印一个`Movie`实例的描述,包括它的导演的名字`director`。当`Song`被找到时,一个相近的原理被用来检测 `Song` 实例和打印它的描述。
|
||||
|
||||
> 注意:
|
||||
>
|
||||
转换没有真的改变实例或它的值。潜在的根本的实例保持不变;只是简单地把它作为它被转换成的类来使用。
|
||||
|
||||
<a name="type_casting_for_any_and_anyobject"></a>
|
||||
## `Any`和`AnyObject`的类型转换
|
||||
|
||||
Swift为不确定类型提供了两种特殊类型别名:
|
||||
|
||||
* `AnyObject`可以代表任何class类型的实例。
|
||||
|
||||
* `Any`可以表示任何类型,除了方法类型(function types)。
|
||||
|
||||
> 注意:
|
||||
>
|
||||
只有当你明确的需要它的行为和功能时才使用`Any`和`AnyObject`。在你的代码里使用你期望的明确的类型总是更好的。
|
||||
|
||||
### `AnyObject`类型
|
||||
|
||||
当需要在工作中使用 Cocoa
|
||||
APIs,它一般接收一个`AnyObject[]`类型的数组,或者说“一个任何对象类型的数组”。这是因为 Objective-C 没有明确的类型化数组。但是,你常常可以确定包含在仅从你知道的 API 信息提供的这样一个数组中的对象的类型。
|
||||
|
||||
在这些情况下,你可以使用强制形式的类型转换(`as`)来下转在数组中的每一项到比 `AnyObject` 更明确的类型,不需要可选解析(optional unwrapping)。
|
||||
|
||||
下面的示例定义了一个 `AnyObject[]` 类型的数组并填入三个`Movie`类型的实例:
|
||||
|
||||
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`类型(ps:其实就是我们常用的正常类型,这里是为了和可选类型相对比)。
|
||||
|
||||
for object in someObjects {
|
||||
let movie = object as Movie
|
||||
println("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[]`类型来代替下转每一项方式。
|
||||
|
||||
for movie in someObjects as Movie[] {
|
||||
println("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`。
|
||||
|
||||
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` 数组包含两个 `Int` 值,2个 `Double` 值,1个 `String` 值,一个元组 `(Double, Double)` ,Ivan Reitman 导演的电影“Ghostbusters”。
|
||||
|
||||
你可以在 `switch` `cases`里用`is` 和 `as` 操作符来发觉只知道是 `Any` 或 `AnyObject`的常量或变量的类型。 下面的示例迭代 `things`数组中的每一项的并用`switch`语句查找每一项的类型。这几种`switch`语句的情形绑定它们匹配的值到一个规定类型的常量,让它们可以打印它们的值:
|
||||
|
||||
for thing in things {
|
||||
switch thing {
|
||||
case 0 as Int:
|
||||
println("zero as an Int")
|
||||
case 0 as Double:
|
||||
println("zero as a Double")
|
||||
case let someInt as Int:
|
||||
println("an integer value of \(someInt)")
|
||||
case let someDouble as Double where someDouble > 0:
|
||||
println("a positive double value of \(someDouble)")
|
||||
case is Double:
|
||||
println("some other double value that I don't want to print")
|
||||
case let someString as String:
|
||||
println("a string value of \"\(someString)\"")
|
||||
case let (x, y) as (Double, Double):
|
||||
println("an (x, y) point at \(x), \(y)")
|
||||
case let movie as Movie:
|
||||
println("a movie called '\(movie.name)', dir. \(movie.director)")
|
||||
default:
|
||||
println("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
|
||||
。
|
||||
|
||||
> 注意:
|
||||
>
|
||||
在一个switch语句的case中使用强制形式的类型转换操作符(as, 而不是 as?)来检查和转换到一个明确的类型。在 switch case 语句的内容中这种检查总是安全的。
|
||||
> 翻译:[xiehurricane](https://github.com/xiehurricane)
|
||||
> 校对:[happyming](https://github.com/happyming)
|
||||
|
||||
# 类型检查(Type Casting)
|
||||
-----------------
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [定义一个类层次作为例子](#defining_a_class_hierarchy_for_type_casting)
|
||||
- [检查类型](#checking_type)
|
||||
- [向下转型(Downcasting)](#downcasting)
|
||||
- [`Any`和`AnyObject`的类型检查](#type_casting_for_any_and_anyobject)
|
||||
|
||||
|
||||
_类型检查_是一种检查类实例的方式,并且或者也是让实例作为它的父类或者子类的一种方式。
|
||||
|
||||
类型检查在 Swift 中使用`is` 和 `as`操作符实现。这两个操作符提供了一种简单达意的方式去检查值的类型或者转换它的类型。
|
||||
|
||||
你也可以用来检查一个类是否实现了某个协议,就像在 [Checking for Protocol Conformance](Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-XID_363)部分讲述的一样。
|
||||
|
||||
<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`(导演) 属性,和相应的初始化器。第二个类在父类的基础上增加了一个 `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
|
||||
}
|
||||
}
|
||||
|
||||
println("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`)
|
||||
|
||||
因为向下转型可能会失败,类型转型操作符带有两种不同形式。可选形式( optional form) `as?` 返回一个你试图下转成的类型的可选值(optional value)。强制形式 `as` 把试图向下转型和强制解包(force-unwraps)结果作为一个混合动作。
|
||||
|
||||
当你不确定向下转型可以成功时,用类型检查的可选形式(`as?`)。可选形式的类型检查总是返回一个可选值(optional value),并且若下转是不可能的,可选值将是 `nil` 。这使你能够检查向下转型是否成功。
|
||||
|
||||
只有你可以确定向下转型一定会成功时,才使用强制形式。当你试图向下转型为一个不正确的类型时,强制形式的类型检查会触发一个运行时错误。
|
||||
|
||||
下面的例子,迭代了`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 {
|
||||
println("Movie: '\(movie.name)', dir. \(movie.director)")
|
||||
} else if let song = item as? Song {
|
||||
println("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?`类型或 “optional `Movie`”。
|
||||
|
||||
当向下转型为 `Movie` 应用在两个 `Song`
|
||||
实例时将会失败。为了处理这种情况,上面的例子使用了可选绑定(optional binding)来检查可选 `Movie`真的包含一个值(这个是为了判断下转是否成功。)可选绑定是这样写的“`if let movie = item as? Movie`”,可以这样解读:
|
||||
|
||||
“尝试将 `item` 转为 `Movie`类型。若成功,设置一个新的临时常量 `movie` 来存储返回的可选`Movie`”
|
||||
|
||||
若向下转型成功,然后`movie`的属性将用于打印一个`Movie`实例的描述,包括它的导演的名字`director`。当`Song`被找到时,一个相近的原理被用来检测 `Song` 实例和打印它的描述。
|
||||
|
||||
> 注意:
|
||||
转换没有真的改变实例或它的值。潜在的根本的实例保持不变;只是简单地把它作为它被转换成的类来使用。
|
||||
|
||||
<a name="type_casting_for_any_and_anyobject"></a>
|
||||
## `Any`和`AnyObject`的类型检查
|
||||
|
||||
Swift为不确定类型提供了两种特殊类型别名:
|
||||
|
||||
* `AnyObject`可以代表任何class类型的实例。
|
||||
* `Any`可以表示任何类型,除了方法类型(function types)。
|
||||
|
||||
> 注意:
|
||||
只有当你明确的需要它的行为和功能时才使用`Any`和`AnyObject`。在你的代码里使用你期望的明确的类型总是更好的。
|
||||
|
||||
### `AnyObject`类型
|
||||
|
||||
当需要在工作中使用 Cocoa
|
||||
APIs,它一般接收一个`AnyObject[]`类型的数组,或者说“一个任何对象类型的数组”。这是因为 Objective-C 没有明确的类型化数组。但是,你常常可以确定包含在仅从你知道的 API 信息提供的这样一个数组中的对象的类型。
|
||||
|
||||
在这些情况下,你可以使用强制形式的类型检查(`as`)来下转在数组中的每一项到比 `AnyObject` 更明确的类型,不需要可选解析(optional unwrapping)。
|
||||
|
||||
下面的示例定义了一个 `AnyObject[]` 类型的数组并填入三个`Movie`类型的实例:
|
||||
|
||||
```swift
|
||||
let someObjects: AnyObject[] = [
|
||||
Movie(name: "2001: A Space Odyssey", director: "Stanley Kubrick"),
|
||||
Movie(name: "Moon", director: "Duncan Jones"),
|
||||
Movie(name: "Alien", director: "Ridley Scott")
|
||||
]
|
||||
```
|
||||
|
||||
因为知道这个数组只包含 `Movie` 实例,你可以直接用(`as`)下转并解包到不可选的`Movie`类型(ps:其实就是我们常用的正常类型,这里是为了和可选类型相对比)。
|
||||
|
||||
```swift
|
||||
for object in someObjects {
|
||||
let movie = object as Movie
|
||||
println("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[] {
|
||||
println("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` 数组包含两个 `Int` 值,2个 `Double` 值,1个 `String` 值,一个元组 `(Double, Double)` ,Ivan Reitman 导演的电影“Ghostbusters”。
|
||||
|
||||
你可以在 `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")
|
||||
case 0 as Double:
|
||||
println("zero as a Double")
|
||||
case let someInt as Int:
|
||||
println("an integer value of \(someInt)")
|
||||
case let someDouble as Double where someDouble > 0:
|
||||
println("a positive double value of \(someDouble)")
|
||||
case is Double:
|
||||
println("some other double value that I don't want to print")
|
||||
case let someString as String:
|
||||
println("a string value of \"\(someString)\"")
|
||||
case let (x, y) as (Double, Double):
|
||||
println("an (x, y) point at \(x), \(y)")
|
||||
case let movie as Movie:
|
||||
println("a movie called '\(movie.name)', dir. \(movie.director)")
|
||||
default:
|
||||
println("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
|
||||
```
|
||||
|
||||
|
||||
> 注意:
|
||||
在一个switch语句的case中使用强制形式的类型检查操作符(as, 而不是 as?)来检查和转换到一个明确的类型。在 switch case 语句的内容中这种检查总是安全的。
|
||||
@ -1,88 +1,97 @@
|
||||
> 翻译:Lin-H
|
||||
|
||||
> 校对:shinyzhu
|
||||
|
||||
# 类型嵌套
|
||||
-----------------
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [类型嵌套实例](#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`来表示。
|
||||
|
||||
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.toRaw(), second: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
// BlackjackCard 的属性和方法
|
||||
let rank: Rank, suit: Suit
|
||||
var description: String {
|
||||
var output = "suit is \(suit.toRaw()),"
|
||||
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`。对于`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`:
|
||||
|
||||
let theAceOfSpades = BlackjackCard(rank: .Ace, suit: .Spades)
|
||||
println("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>
|
||||
##类型嵌套的引用
|
||||
|
||||
在外部对嵌套类型的引用,以被嵌套类型的名字为前缀,加上所要引用的属性名:
|
||||
|
||||
let heartsSymbol = BlackjackCard.Suit.Hearts.toRaw()
|
||||
// 红心的符号 为 "♡"
|
||||
|
||||
对于上面这个例子,这样可以使`Suit`, `Rank`, 和 `Values`的名字尽可能的短,因为它们的名字会自然的由被定义的上下文来限定。
|
||||
> 翻译:[Lin-H](https://github.com/Lin-H)
|
||||
> 校对:[shinyzhu](https://github.com/shinyzhu)
|
||||
|
||||
# 类型嵌套
|
||||
-----------------
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [类型嵌套实例](#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.toRaw(), second: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BlackjackCard 的属性和方法
|
||||
let rank: Rank, suit: Suit
|
||||
var description: String {
|
||||
var output = "suit is \(suit.toRaw()),"
|
||||
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`。对于`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`:
|
||||
|
||||
```swift
|
||||
let theAceOfSpades = BlackjackCard(rank: .Ace, suit: .Spades)
|
||||
println("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.toRaw()
|
||||
// 红心的符号 为 "♡"
|
||||
```
|
||||
|
||||
对于上面这个例子,这样可以使`Suit`, `Rank`, 和 `Values`的名字尽可能的短,因为它们的名字会自然的由被定义的上下文来限定。
|
||||
|
||||
preview
|
||||
@ -1,303 +1,298 @@
|
||||
> 翻译:lyuka
|
||||
|
||||
> 校对:Hawstein
|
||||
|
||||
#扩展(Extensions)
|
||||
----
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [扩展语法](#extension_syntax)
|
||||
- [计算型属性](#computed_properties)
|
||||
- [构造器](#initializers)
|
||||
- [方法](#methods)
|
||||
- [下标](#subscripts)
|
||||
- [嵌套类型](#nested_types)
|
||||
|
||||
*扩展*就是向一个已有的类、结构体或枚举类型添加新功能(functionality)。这包括在没有权限获取原始源代码的情况下扩展类型的能力(即*逆向建模*)。扩展和 Objective-C 中的分类(categories)类似。(不过与Objective-C不同的是,Swift 的扩展没有名字。)
|
||||
|
||||
Swift 中的扩展可以:
|
||||
|
||||
- 添加计算型属性和计算静态属性
|
||||
- 定义实例方法和类型方法
|
||||
- 提供新的构造器
|
||||
- 定义下标
|
||||
- 定义和使用新的嵌套类型
|
||||
- 使一个已有类型符合某个协议
|
||||
|
||||
|
||||
>注意:
|
||||
>
|
||||
如果你定义了一个扩展向一个已有类型添加新功能,那么这个新功能对该类型的所有已有实例中都是可用的,即使它们是在你的这个扩展的前面定义的。
|
||||
|
||||
<a name="extension_syntax"></a>
|
||||
## 扩展语法(Extension Syntax)
|
||||
|
||||
声明一个扩展使用关键字`extension`:
|
||||
|
||||
```
|
||||
extension SomeType {
|
||||
// 加到SomeType的新功能写到这里
|
||||
}
|
||||
```
|
||||
|
||||
一个扩展可以扩展一个已有类型,使其能够适配一个或多个协议(protocol)。当这种情况发生时,协议的名字应该完全按照类或结构体的名字的方式进行书写:
|
||||
|
||||
```
|
||||
extension SomeType: SomeProtocol, AnotherProctocol {
|
||||
// 协议实现写到这里
|
||||
}
|
||||
```
|
||||
|
||||
按照这种方式添加的协议遵循者(protocol conformance)被称之为[在扩展中添加协议遵循者](21_Protocols.html#adding_protocol_conformance_with_an_extension)
|
||||
|
||||
<a name="computed_properties"></a>
|
||||
## 计算型属性(Computed Properties)
|
||||
|
||||
扩展可以向已有类型添加计算型实例属性和计算型类型属性。下面的例子向 Swift 的内建`Double`类型添加了5个计算型实例属性,从而提供与距离单位协作的基本支持。
|
||||
|
||||
```
|
||||
extension Double {
|
||||
var km: Double { return self * 1_000.0 }
|
||||
var m : Double { return self }
|
||||
var cm: Double { return self / 100.0 }
|
||||
var mm: Double { return self / 1_000.0 }
|
||||
var ft: Double { return self / 3.28084 }
|
||||
}
|
||||
let oneInch = 25.4.mm
|
||||
println("One inch is \(oneInch) meters")
|
||||
// 打印输出:"One inch is 0.0254 meters"
|
||||
let threeFeet = 3.ft
|
||||
println("Three feet is \(threeFeet) meters")
|
||||
// 打印输出:"Three feet is 0.914399970739201 meters"
|
||||
```
|
||||
|
||||
这些计算属性表达的含义是把一个`Double`型的值看作是某单位下的长度值。即使它们被实现为计算型属性,但这些属性仍可以接一个带有dot语法的浮点型字面值,而这恰恰是使用这些浮点型字面量实现距离转换的方式。
|
||||
|
||||
在上述例子中,一个`Double`型的值`1.0`被用来表示“1米”。这就是为什么`m`计算型属性返回`self`——表达式`1.m`被认为是计算`1.0`的`Double`值。
|
||||
|
||||
其它单位则需要一些转换来表示在米下测量的值。1千米等于1,000米,所以`km`计算型属性要把值乘以`1_000.00`来转化成单位米下的数值。类似地,1米有3.28024英尺,所以`ft`计算型属性要把对应的`Double`值除以`3.28024`来实现英尺到米的单位换算。
|
||||
|
||||
这些属性是只读的计算型属性,所有从简考虑它们不用`get`关键字表示。它们的返回值是`Double`型,而且可以用于所有接受`Double`的数学计算中:
|
||||
|
||||
```
|
||||
let aMarathon = 42.km + 195.m
|
||||
println("A marathon is \(aMarathon) meters long")
|
||||
// 打印输出:"A marathon is 42495.0 meters long"
|
||||
```
|
||||
|
||||
|
||||
>注意:
|
||||
>
|
||||
扩展可以添加新的计算属性,但是不可以添加存储属性,也不可以向已有属性添加属性观测器(property observers)。
|
||||
|
||||
<a name="initializers"></a>
|
||||
## 构造器(Initializers)
|
||||
|
||||
扩展可以向已有类型添加新的构造器。这可以让你扩展其它类型,将你自己的定制类型作为构造器参数,或者提供该类型的原始实现中没有包含的额外初始化选项。
|
||||
|
||||
扩展能向类中添加新的便利构造器,但是它们不能向类中添加新的指定构造器或析构函数。指定构造器和析构函数必须总是由原始的类实现来提供。
|
||||
|
||||
> 注意:
|
||||
>
|
||||
如果你使用扩展向一个值类型添加一个构造器,该构造器向所有的存储属性提供默认值,而且没有定义任何定制构造器(custom initializers),那么对于来自你的扩展构造器中的值类型,你可以调用默认构造器(default initializers)和逐一成员构造器(memberwise initializers)。
|
||||
正如在值类型的构造器授权中描述的,如果你已经把构造器写成值类型原始实现的一部分,上述规则不再适用。
|
||||
|
||||
下面的例子定义了一个用于描述几何矩形的定制结构体`Rect`。这个例子同时定义了两个辅助结构体`Size`和`Point`,它们都把`0.0`作为所有属性的默认值:
|
||||
|
||||
```
|
||||
struct Size {
|
||||
var width = 0.0, height = 0.0
|
||||
}
|
||||
struct Point {
|
||||
var x = 0.0, y = 0.0
|
||||
}
|
||||
struct Rect {
|
||||
var origin = Point()
|
||||
var size = Size()
|
||||
}
|
||||
```
|
||||
|
||||
因为结构体`Rect`提供了其所有属性的默认值,所以正如默认构造器中描述的,它可以自动接受一个默认的构造器和一个成员级构造器。这些构造器可以用于构造新的`Rect`实例:
|
||||
|
||||
```
|
||||
let defaultRect = Rect()
|
||||
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0),
|
||||
size: Size(width: 5.0, height: 5.0))
|
||||
```
|
||||
|
||||
你可以提供一个额外的使用特殊中心点和大小的构造器来扩展`Rect`结构体:
|
||||
|
||||
```
|
||||
extension Rect {
|
||||
init(center: Point, size: Size) {
|
||||
let originX = center.x - (size.width / 2)
|
||||
let originY = center.y - (size.height / 2)
|
||||
self.init(origin: Point(x: originX, y: originY), size: size)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
这个新的构造器首先根据提供的`center`和`size`值计算一个合适的原点。然后调用该结构体自动的成员构造器`init(origin:size:)`,该构造器将新的原点和大小存到了合适的属性中:
|
||||
|
||||
```
|
||||
let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
|
||||
size: Size(width: 3.0, height: 3.0))
|
||||
// centerRect的原点是 (2.5, 2.5),大小是 (3.0, 3.0)
|
||||
```
|
||||
|
||||
|
||||
>注意:
|
||||
>
|
||||
如果你使用扩展提供了一个新的构造器,你依旧有责任保证构造过程能够让所有实例完全初始化。
|
||||
|
||||
<a name="methods"></a>
|
||||
## 方法(Methods)
|
||||
|
||||
扩展可以向已有类型添加新的实例方法和类型方法。下面的例子向`Int`类型添加一个名为`repetitions`的新实例方法:
|
||||
|
||||
```
|
||||
extension Int {
|
||||
func repetitions(task: () -> ()) {
|
||||
for i in 0..self {
|
||||
task()
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
这个`repetitions`方法使用了一个`() -> ()`类型的单参数(single argument),表明函数没有参数而且没有返回值。
|
||||
|
||||
定义该扩展之后,你就可以对任意整数调用`repetitions`方法,实现的功能则是多次执行某任务:
|
||||
|
||||
```
|
||||
3.repetitions({
|
||||
println("Hello!")
|
||||
})
|
||||
// Hello!
|
||||
// Hello!
|
||||
// Hello!
|
||||
```
|
||||
|
||||
可以使用 trailing 闭包使调用更加简洁:
|
||||
|
||||
```
|
||||
3.repetitions{
|
||||
println("Goodbye!")
|
||||
}
|
||||
// Goodbye!
|
||||
// Goodbye!
|
||||
// Goodbye!
|
||||
```
|
||||
|
||||
<a name="mutating_instance_methods"></a>
|
||||
### 修改实例方法(Mutating Instance Methods)
|
||||
|
||||
通过扩展添加的实例方法也可以修改该实例本身。结构体和枚举类型中修改`self`或其属性的方法必须将该实例方法标注为`mutating`,正如来自原始实现的修改方法一样。
|
||||
|
||||
下面的例子向Swift的`Int`类型添加了一个新的名为`square`的修改方法,来实现一个原始值的平方计算:
|
||||
|
||||
```
|
||||
extension Int {
|
||||
mutating func square() {
|
||||
self = self * self
|
||||
}
|
||||
}
|
||||
var someInt = 3
|
||||
someInt.square()
|
||||
// someInt 现在值是 9
|
||||
```
|
||||
|
||||
<a name="subscripts"></a>
|
||||
## 下标(Subscripts)
|
||||
|
||||
扩展可以向一个已有类型添加新下标。这个例子向Swift内建类型`Int`添加了一个整型下标。该下标`[n]`返回十进制数字从右向左数的第n个数字
|
||||
|
||||
- 123456789[0]返回9
|
||||
- 123456789[1]返回8
|
||||
|
||||
...等等
|
||||
|
||||
```
|
||||
extension Int {
|
||||
subscript(digitIndex: Int) -> Int {
|
||||
var decimalBase = 1
|
||||
for _ in 1...digitIndex {
|
||||
decimalBase *= 10
|
||||
}
|
||||
return (self / decimalBase) % 10
|
||||
}
|
||||
}
|
||||
746381295[0]
|
||||
// returns 5
|
||||
746381295[1]
|
||||
// returns 9
|
||||
746381295[2]
|
||||
// returns 2
|
||||
746381295[8]
|
||||
// returns 7
|
||||
```
|
||||
|
||||
如果该`Int`值没有足够的位数,即下标越界,那么上述实现的下标会返回0,因为它会在数字左边自动补0:
|
||||
|
||||
```
|
||||
746381295[9]
|
||||
//returns 0, 即等同于:
|
||||
0746381295[9]
|
||||
```
|
||||
|
||||
<a name="nested_types"></a>
|
||||
## 嵌套类型(Nested Types)
|
||||
|
||||
扩展可以向已有的类、结构体和枚举添加新的嵌套类型:
|
||||
|
||||
```
|
||||
extension Character {
|
||||
enum Kind {
|
||||
case Vowel, Consonant, Other
|
||||
}
|
||||
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
|
||||
default:
|
||||
return .Other
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
该例子向`Character`添加了新的嵌套枚举。这个名为`Kind`的枚举表示特定字符的类型。具体来说,就是表示一个标准的拉丁脚本中的字符是元音还是辅音(不考虑口语和地方变种),或者是其它类型。
|
||||
|
||||
这个类子还向`Character`添加了一个新的计算实例属性,即`kind`,用来返回合适的`Kind`枚举成员。
|
||||
|
||||
现在,这个嵌套枚举可以和一个`Character`值联合使用了:
|
||||
|
||||
```
|
||||
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 ")
|
||||
}
|
||||
}
|
||||
print("\n")
|
||||
}
|
||||
printLetterKinds("Hello")
|
||||
// 'Hello' is made up of the following kinds of letters:
|
||||
// consonant vowel consonant consonant vowel
|
||||
```
|
||||
|
||||
函数`printLetterKinds`的输入是一个`String`值并对其字符进行迭代。在每次迭代过程中,考虑当前字符的`kind`计算属性,并打印出合适的类别描述。所以`printLetterKinds`就可以用来打印一个完整单词中所有字母的类型,正如上述单词`"hello"`所展示的。
|
||||
|
||||
>注意:
|
||||
>
|
||||
由于已知`character.kind`是`Character.Kind`型,所以`Character.Kind`中的所有成员值都可以使用`switch`语句里的形式简写,比如使用 `.Vowel`代替`Character.Kind.Vowel`
|
||||
> 翻译:[lyuka](https://github.com/lyuka)
|
||||
> 校对:[Hawstein](https://github.com/Hawstein)
|
||||
|
||||
#扩展(Extensions)
|
||||
----
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [扩展语法](#extension_syntax)
|
||||
- [计算型属性](#computed_properties)
|
||||
- [构造器](#initializers)
|
||||
- [方法](#methods)
|
||||
- [下标](#subscripts)
|
||||
- [嵌套类型](#nested_types)
|
||||
|
||||
*扩展*就是向一个已有的类、结构体或枚举类型添加新功能(functionality)。这包括在没有权限获取原始源代码的情况下扩展类型的能力(即*逆向建模*)。扩展和 Objective-C 中的分类(categories)类似。(不过与Objective-C不同的是,Swift 的扩展没有名字。)
|
||||
|
||||
Swift 中的扩展可以:
|
||||
|
||||
- 添加计算型属性和计算静态属性
|
||||
- 定义实例方法和类型方法
|
||||
- 提供新的构造器
|
||||
- 定义下标
|
||||
- 定义和使用新的嵌套类型
|
||||
- 使一个已有类型符合某个协议
|
||||
|
||||
|
||||
>注意:
|
||||
如果你定义了一个扩展向一个已有类型添加新功能,那么这个新功能对该类型的所有已有实例中都是可用的,即使它们是在你的这个扩展的前面定义的。
|
||||
|
||||
<a name="extension_syntax"></a>
|
||||
## 扩展语法(Extension Syntax)
|
||||
|
||||
声明一个扩展使用关键字`extension`:
|
||||
|
||||
```swift
|
||||
extension SomeType {
|
||||
// 加到SomeType的新功能写到这里
|
||||
}
|
||||
```
|
||||
|
||||
一个扩展可以扩展一个已有类型,使其能够适配一个或多个协议(protocol)。当这种情况发生时,协议的名字应该完全按照类或结构体的名字的方式进行书写:
|
||||
|
||||
```swift
|
||||
extension SomeType: SomeProtocol, AnotherProctocol {
|
||||
// 协议实现写到这里
|
||||
}
|
||||
```
|
||||
|
||||
按照这种方式添加的协议遵循者(protocol conformance)被称之为[在扩展中添加协议遵循者](21_Protocols.html#adding_protocol_conformance_with_an_extension)
|
||||
|
||||
<a name="computed_properties"></a>
|
||||
## 计算型属性(Computed Properties)
|
||||
|
||||
扩展可以向已有类型添加计算型实例属性和计算型类型属性。下面的例子向 Swift 的内建`Double`类型添加了5个计算型实例属性,从而提供与距离单位协作的基本支持。
|
||||
|
||||
```swift
|
||||
extension Double {
|
||||
var km: Double { return self * 1_000.0 }
|
||||
var m : Double { return self }
|
||||
var cm: Double { return self / 100.0 }
|
||||
var mm: Double { return self / 1_000.0 }
|
||||
var ft: Double { return self / 3.28084 }
|
||||
}
|
||||
let oneInch = 25.4.mm
|
||||
println("One inch is \(oneInch) meters")
|
||||
// 打印输出:"One inch is 0.0254 meters"
|
||||
let threeFeet = 3.ft
|
||||
println("Three feet is \(threeFeet) meters")
|
||||
// 打印输出:"Three feet is 0.914399970739201 meters"
|
||||
```
|
||||
|
||||
这些计算属性表达的含义是把一个`Double`型的值看作是某单位下的长度值。即使它们被实现为计算型属性,但这些属性仍可以接一个带有dot语法的浮点型字面值,而这恰恰是使用这些浮点型字面量实现距离转换的方式。
|
||||
|
||||
在上述例子中,一个`Double`型的值`1.0`被用来表示“1米”。这就是为什么`m`计算型属性返回`self`——表达式`1.m`被认为是计算`1.0`的`Double`值。
|
||||
|
||||
其它单位则需要一些转换来表示在米下测量的值。1千米等于1,000米,所以`km`计算型属性要把值乘以`1_000.00`来转化成单位米下的数值。类似地,1米有3.28024英尺,所以`ft`计算型属性要把对应的`Double`值除以`3.28024`来实现英尺到米的单位换算。
|
||||
|
||||
这些属性是只读的计算型属性,所有从简考虑它们不用`get`关键字表示。它们的返回值是`Double`型,而且可以用于所有接受`Double`的数学计算中:
|
||||
|
||||
```swift
|
||||
let aMarathon = 42.km + 195.m
|
||||
println("A marathon is \(aMarathon) meters long")
|
||||
// 打印输出:"A marathon is 42495.0 meters long"
|
||||
```
|
||||
|
||||
|
||||
>注意:
|
||||
扩展可以添加新的计算属性,但是不可以添加存储属性,也不可以向已有属性添加属性观测器(property observers)。
|
||||
|
||||
<a name="initializers"></a>
|
||||
## 构造器(Initializers)
|
||||
|
||||
扩展可以向已有类型添加新的构造器。这可以让你扩展其它类型,将你自己的定制类型作为构造器参数,或者提供该类型的原始实现中没有包含的额外初始化选项。
|
||||
|
||||
扩展能向类中添加新的便利构造器,但是它们不能向类中添加新的指定构造器或析构函数。指定构造器和析构函数必须总是由原始的类实现来提供。
|
||||
|
||||
> 注意:
|
||||
如果你使用扩展向一个值类型添加一个构造器,该构造器向所有的存储属性提供默认值,而且没有定义任何定制构造器(custom initializers),那么对于来自你的扩展构造器中的值类型,你可以调用默认构造器(default initializers)和逐一成员构造器(memberwise initializers)。
|
||||
正如在值类型的构造器授权中描述的,如果你已经把构造器写成值类型原始实现的一部分,上述规则不再适用。
|
||||
|
||||
下面的例子定义了一个用于描述几何矩形的定制结构体`Rect`。这个例子同时定义了两个辅助结构体`Size`和`Point`,它们都把`0.0`作为所有属性的默认值:
|
||||
|
||||
```swift
|
||||
struct Size {
|
||||
var width = 0.0, height = 0.0
|
||||
}
|
||||
struct Point {
|
||||
var x = 0.0, y = 0.0
|
||||
}
|
||||
struct Rect {
|
||||
var origin = Point()
|
||||
var size = Size()
|
||||
}
|
||||
```
|
||||
|
||||
因为结构体`Rect`提供了其所有属性的默认值,所以正如默认构造器中描述的,它可以自动接受一个默认的构造器和一个成员级构造器。这些构造器可以用于构造新的`Rect`实例:
|
||||
|
||||
```swift
|
||||
let defaultRect = Rect()
|
||||
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0),
|
||||
size: Size(width: 5.0, height: 5.0))
|
||||
```
|
||||
|
||||
你可以提供一个额外的使用特殊中心点和大小的构造器来扩展`Rect`结构体:
|
||||
|
||||
```swift
|
||||
extension Rect {
|
||||
init(center: Point, size: Size) {
|
||||
let originX = center.x - (size.width / 2)
|
||||
let originY = center.y - (size.height / 2)
|
||||
self.init(origin: Point(x: originX, y: originY), size: size)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
这个新的构造器首先根据提供的`center`和`size`值计算一个合适的原点。然后调用该结构体自动的成员构造器`init(origin:size:)`,该构造器将新的原点和大小存到了合适的属性中:
|
||||
|
||||
```swift
|
||||
let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
|
||||
size: Size(width: 3.0, height: 3.0))
|
||||
// centerRect的原点是 (2.5, 2.5),大小是 (3.0, 3.0)
|
||||
```
|
||||
|
||||
|
||||
>注意:
|
||||
如果你使用扩展提供了一个新的构造器,你依旧有责任保证构造过程能够让所有实例完全初始化。
|
||||
|
||||
<a name="methods"></a>
|
||||
## 方法(Methods)
|
||||
|
||||
扩展可以向已有类型添加新的实例方法和类型方法。下面的例子向`Int`类型添加一个名为`repetitions`的新实例方法:
|
||||
|
||||
```swift
|
||||
extension Int {
|
||||
func repetitions(task: () -> ()) {
|
||||
for i in 0..self {
|
||||
task()
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
这个`repetitions`方法使用了一个`() -> ()`类型的单参数(single argument),表明函数没有参数而且没有返回值。
|
||||
|
||||
定义该扩展之后,你就可以对任意整数调用`repetitions`方法,实现的功能则是多次执行某任务:
|
||||
|
||||
```swift
|
||||
3.repetitions({
|
||||
println("Hello!")
|
||||
})
|
||||
// Hello!
|
||||
// Hello!
|
||||
// Hello!
|
||||
```
|
||||
|
||||
可以使用 trailing 闭包使调用更加简洁:
|
||||
|
||||
```swift
|
||||
3.repetitions{
|
||||
println("Goodbye!")
|
||||
}
|
||||
// Goodbye!
|
||||
// Goodbye!
|
||||
// Goodbye!
|
||||
```
|
||||
|
||||
<a name="mutating_instance_methods"></a>
|
||||
### 修改实例方法(Mutating Instance Methods)
|
||||
|
||||
通过扩展添加的实例方法也可以修改该实例本身。结构体和枚举类型中修改`self`或其属性的方法必须将该实例方法标注为`mutating`,正如来自原始实现的修改方法一样。
|
||||
|
||||
下面的例子向Swift的`Int`类型添加了一个新的名为`square`的修改方法,来实现一个原始值的平方计算:
|
||||
|
||||
```swift
|
||||
extension Int {
|
||||
mutating func square() {
|
||||
self = self * self
|
||||
}
|
||||
}
|
||||
var someInt = 3
|
||||
someInt.square()
|
||||
// someInt 现在值是 9
|
||||
```
|
||||
|
||||
<a name="subscripts"></a>
|
||||
## 下标(Subscripts)
|
||||
|
||||
扩展可以向一个已有类型添加新下标。这个例子向Swift内建类型`Int`添加了一个整型下标。该下标`[n]`返回十进制数字从右向左数的第n个数字
|
||||
|
||||
- 123456789[0]返回9
|
||||
- 123456789[1]返回8
|
||||
|
||||
...等等
|
||||
|
||||
```swift
|
||||
extension Int {
|
||||
subscript(digitIndex: Int) -> Int {
|
||||
var decimalBase = 1
|
||||
for _ in 1...digitIndex {
|
||||
decimalBase *= 10
|
||||
}
|
||||
return (self / decimalBase) % 10
|
||||
}
|
||||
}
|
||||
746381295[0]
|
||||
// returns 5
|
||||
746381295[1]
|
||||
// returns 9
|
||||
746381295[2]
|
||||
// returns 2
|
||||
746381295[8]
|
||||
// returns 7
|
||||
```
|
||||
|
||||
如果该`Int`值没有足够的位数,即下标越界,那么上述实现的下标会返回0,因为它会在数字左边自动补0:
|
||||
|
||||
```swift
|
||||
746381295[9]
|
||||
//returns 0, 即等同于:
|
||||
0746381295[9]
|
||||
```
|
||||
|
||||
<a name="nested_types"></a>
|
||||
## 嵌套类型(Nested Types)
|
||||
|
||||
扩展可以向已有的类、结构体和枚举添加新的嵌套类型:
|
||||
|
||||
```swift
|
||||
extension Character {
|
||||
enum Kind {
|
||||
case Vowel, Consonant, Other
|
||||
}
|
||||
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
|
||||
default:
|
||||
return .Other
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
该例子向`Character`添加了新的嵌套枚举。这个名为`Kind`的枚举表示特定字符的类型。具体来说,就是表示一个标准的拉丁脚本中的字符是元音还是辅音(不考虑口语和地方变种),或者是其它类型。
|
||||
|
||||
这个类子还向`Character`添加了一个新的计算实例属性,即`kind`,用来返回合适的`Kind`枚举成员。
|
||||
|
||||
现在,这个嵌套枚举可以和一个`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 ")
|
||||
}
|
||||
}
|
||||
print("\n")
|
||||
}
|
||||
printLetterKinds("Hello")
|
||||
// 'Hello' is made up of the following kinds of letters:
|
||||
// consonant vowel consonant consonant vowel
|
||||
```
|
||||
|
||||
函数`printLetterKinds`的输入是一个`String`值并对其字符进行迭代。在每次迭代过程中,考虑当前字符的`kind`计算属性,并打印出合适的类别描述。所以`printLetterKinds`就可以用来打印一个完整单词中所有字母的类型,正如上述单词`"hello"`所展示的。
|
||||
|
||||
>注意:
|
||||
由于已知`character.kind`是`Character.Kind`型,所以`Character.Kind`中的所有成员值都可以使用`switch`语句里的形式简写,比如使用 `.Vowel`代替`Character.Kind.Vowel`
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,472 +1,518 @@
|
||||
> 翻译:takalard
|
||||
|
||||
> 校对:lifedim
|
||||
|
||||
# 泛型
|
||||
|
||||
------
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [泛型所解决的问题](#the_problem_that_generics_solve)
|
||||
- [泛型函数](#generic_functions)
|
||||
- [类型参数](#type_parameters)
|
||||
- [命名类型参数](#naming_type_parameters)
|
||||
- [泛型类型](#generic_types)
|
||||
- [类型约束](#type_constraints)
|
||||
- [关联类型](#associated_types)
|
||||
- [`Where`语句](#where_clauses)
|
||||
|
||||
*泛型代码*可以让你写出根据自我需求定义、适用于任何类型的,灵活且可重用的函数和类型。它的可以让你避免重复的代码,用一种清晰和抽象的方式来表达代码的意图。
|
||||
|
||||
泛型是 Swift 强大特征中的其中一个,许多 Swift 标准库是通过泛型代码构建出来的。事实上,泛型的使用贯穿了整本语言手册,只是你没有发现而已。例如,Swift 的数组和字典类型都是泛型集。你可以创建一个`Int`数组,也可创建一个`String`数组,或者甚至于可以是任何其他 Swift 的类型数据数组。同样的,你也可以创建存储任何指定类型的字典(dictionary),而且这些类型可以是没有限制的。
|
||||
|
||||
<a name="the_problem_that_generics_solve"></a>
|
||||
## 泛型所解决的问题
|
||||
|
||||
这里是一个标准的,非泛型函数`swapTwoInts`,用来交换两个Int值:
|
||||
|
||||
func swapTwoInts(inout a: Int, inout b: Int)
|
||||
let temporaryA = a
|
||||
a = b
|
||||
b = temporaryA
|
||||
}
|
||||
|
||||
这个函数使用写入读出(in-out)参数来交换`a`和`b`的值,请参考[写入读出参数][1]。
|
||||
|
||||
`swapTwoInts`函数可以交换`b`的原始值到`a`,也可以交换a的原始值到`b`,你可以调用这个函数交换两个`Int`变量值:
|
||||
|
||||
var someInt = 3
|
||||
var anotherInt = 107
|
||||
swapTwoInts(&someInt, &anotherInt)
|
||||
println("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
|
||||
// 输出 "someInt is now 107, and anotherInt is now 3"
|
||||
|
||||
|
||||
`swapTwoInts`函数是非常有用的,但是它只能交换`Int`值,如果你想要交换两个`String`或者`Double`,就不得不写更多的函数,如 `swapTwoStrings`和`swapTwoDoublesfunctions `,如同如下所示:
|
||||
|
||||
func swapTwoStrings(inout a: String, inout b: String) {
|
||||
let temporaryA = a
|
||||
a = b
|
||||
b = temporaryA
|
||||
}
|
||||
|
||||
func swapTwoDoubles(inout a: Double, inout b: Double) {
|
||||
let temporaryA = a
|
||||
a = b
|
||||
b = temporaryA
|
||||
}
|
||||
|
||||
你可能注意到 `swapTwoInts`、 `swapTwoStrings`和`swapTwoDoubles`函数功能都是相同的,唯一不同之处就在于传入的变量类型不同,分别是`Int`、`String`和`Double`。
|
||||
|
||||
但实际应用中通常需要一个用处更强大并且尽可能的考虑到更多的灵活性单个函数,可以用来交换两个任何类型值,很幸运的是,泛型代码帮你解决了这种问题。(一个这种泛型函数后面已经定义好了。)
|
||||
|
||||
>注意:
|
||||
在所有三个函数中,`a`和`b`的类型是一样的。如果`a`和`b`不是相同的类型,那它们俩就不能互换值。Swift 是类型安全的语言,所以它不允许一个`String`类型的变量和一个`Double`类型的变量互相交换值。如果一定要做,Swift 将报编译错误。
|
||||
|
||||
<a name="generic_functions"></a>
|
||||
## 泛型函数
|
||||
|
||||
`泛型函数`可以工作于任何类型,这里是一个上面`swapTwoInts`函数的泛型版本,用于交换两个值:
|
||||
|
||||
func swapTwoValues<T>(inout a: T, inout b: T) {
|
||||
let temporaryA = a
|
||||
a = b
|
||||
b = temporaryA
|
||||
}
|
||||
|
||||
`swapTwoValues`函数主体和`swapTwoInts`函数是一样的,它只在第一行稍微有那么一点点不同于`swapTwoInts`,如下所示:
|
||||
|
||||
func swapTwoInts(inout a: Int, inout b: Int)
|
||||
func swapTwoValues<T>(inout a: T, inout b: T)
|
||||
|
||||
|
||||
这个函数的泛型版本使用了占位类型名字(通常此情况下用字母`T`来表示)来代替实际类型名(如`In`、`String`或`Doubl`)。占位类型名没有提示`T`必须是什么类型,但是它提示了`a`和`b`必须是同一类型`T`,而不管`T`表示什么类型。只有`swapTwoValues`函数在每次调用时所传入的实际类型才能决定`T`所代表的类型。
|
||||
|
||||
另外一个不同之处在于这个泛型函数名后面跟着的展位类型名字(T)是用尖括号括起来的(`<T>`)。这个尖括号告诉 Swift 那个`T`是`swapTwoValues`函数所定义的一个类型。因为`T`是一个占位命名类型,Swift 不会去查找命名为T的实际类型。
|
||||
|
||||
`swapTwoValues`函数除了要求传入的两个任何类型值是同一类型外,也可以作为`swapTwoInts`函数被调用。每次`swapTwoValues`被调用,T所代表的类型值都会传给函数。
|
||||
|
||||
在下面的两个例子中,`T`分别代表`Int`和`String`:
|
||||
|
||||
var someInt = 3
|
||||
var anotherInt = 107
|
||||
swapTwoValues(&someInt, &anotherInt)
|
||||
// someInt is now 107, and anotherInt is now 3
|
||||
|
||||
var someString = "hello"
|
||||
var anotherString = "world"
|
||||
swapTwoValues(&someString, &anotherString)
|
||||
// someString is now "world", and anotherString is now "hello"
|
||||
|
||||
|
||||
>注意
|
||||
上面定义的函数`swapTwoValues`是受`swap`函数启发而实现的。`swap`函数存在于 Swift 标准库,并可以在其它类中任意使用。如果你在自己代码中需要类似`swapTwoValues`函数的功能,你可以使用已存在的交换函数`swap`函数。
|
||||
|
||||
<a name="type_parameters"></a>
|
||||
## 类型参数
|
||||
|
||||
在上面的`swapTwoValues`例子中,占位类型`T`是一种类型参数的示例。类型参数指定并命名为一个占位类型,并且紧随在函数名后面,使用一对尖括号括起来(如`<T>`)。
|
||||
|
||||
一旦一个类型参数被指定,那么其可以被使用来定义一个函数的参数类型(如`swapTwoValues`函数中的参数`a`和`b`),或作为一个函数返回类型,或用作函数主体中的注释类型。在这种情况下,被类型参数所代表的占位类型不管函数任何时候被调用,都会被实际类型所替换(在上面`swapTwoValues`例子中,当函数第一次被调用时,`T`被`Int`替换,第二次调用时,被`String`替换。)。
|
||||
|
||||
你可支持多个类型参数,命名在尖括号中,用逗号分开。
|
||||
|
||||
<a name="naming_type_parameters"></a>
|
||||
## 命名类型参数
|
||||
|
||||
在简单的情况下,泛型函数或泛型类型需要指定一个占位类型(如上面的`swapTwoValues`泛型函数,或一个存储单一类型的泛型集,如数组),通常用一单个字母`T`来命名类型参数。不过,你可以使用任何有效的标识符来作为类型参数名。
|
||||
|
||||
如果你使用多个参数定义更复杂的泛型函数或泛型类型,那么使用更多的描述类型参数是非常有用的。例如,Swift 字典(Dictionary)类型有两个类型参数,一个是键,另外一个是值。如果你自己写字典,你或许会定义这两个类型参数为`KeyType`和`ValueType`,用来记住它们在你的泛型代码中的作用。
|
||||
|
||||
>注意
|
||||
请始终使用大写字母开头的驼峰式命名法(例如`T`和`KeyType`)来给类型参数命名,以表明它们是类型的占位符,而非类型值。
|
||||
|
||||
<a name="generic_types"></a>
|
||||
## 泛型类型
|
||||
|
||||
|
||||
通常在泛型函数中,Swift 允许你定义你自己的泛型类型。这些自定义类、结构体和枚举作用于任何类型,如同`Array`和`Dictionary`的用法。
|
||||
|
||||
这部分向你展示如何写一个泛型集类型--`Stack`(栈)。一个栈是一系列值域的集合,和`Array`(数组)类似,但其是一个比 Swift 的`Array`类型更多限制的集合。一个数组可以允许其里面任何位置的插入/删除操作,而栈,只允许在集合的末端添加新的项(如同*push*一个新值进栈)。同样的一个栈也只能从末端移除项(如同*pop*一个值出栈)。
|
||||
|
||||
>注意
|
||||
栈的概念已被`UINavigationController`类使用来模拟试图控制器的导航结构。你通过调用`UINavigationController`的`pushViewController:animated:`方法来为导航栈添加(add)新的试图控制器;而通过`popViewControllerAnimated:`的方法来从导航栈中移除(pop)某个试图控制器。每当你需要一个严格的`后进先出`方式来管理集合,堆栈都是最实用的模型。
|
||||
|
||||
下图展示了一个栈的压栈(push)/出栈(pop)的行为:
|
||||
|
||||
![此处输入图片的描述][2]
|
||||
|
||||
1. 现在有三个值在栈中;
|
||||
2. 第四个值“pushed”到栈的顶部;
|
||||
3. 现在有四个值在栈中,最近的那个在顶部;
|
||||
4. 栈中最顶部的那个项被移除,或称之为“popped”;
|
||||
5. 移除掉一个值后,现在栈又重新只有三个值。
|
||||
|
||||
这里展示了如何写一个非泛型版本的栈,`Int`值型的栈:
|
||||
|
||||
struct IntStack {
|
||||
var items = Int[]()
|
||||
mutating func push(item: Int) {
|
||||
items.append(item)
|
||||
}
|
||||
mutating func pop() -> Int {
|
||||
return items.removeLast()
|
||||
}
|
||||
}
|
||||
|
||||
这个结构体在栈中使用一个`Array`性质的`items`存储值。`Stack`提供两个方法:`push`和`pop`,从栈中压进一个值和移除一个值。这些方法标记为可变的,因为它们需要修改(或*转换*)结构体的`items`数组。
|
||||
|
||||
上面所展现的`IntStack`类型只能用于`Int`值,不过,其对于定义一个泛型`Stack`类(可以处理*任何*类型值的栈)是非常有用的。
|
||||
|
||||
这里是一个相同代码的泛型版本:
|
||||
|
||||
|
||||
struct Stack<T> {
|
||||
var items = T[]()
|
||||
mutating func push(item: T) {
|
||||
items.append(item)
|
||||
}
|
||||
mutating func pop() -> T {
|
||||
return items.removeLast()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
注意到`Stack`的泛型版本基本上和非泛型版本相同,但是泛型版本的占位类型参数为T代替了实际`Int`类型。这种类型参数包含在一对尖括号里(`<T>`),紧随在结构体名字后面。
|
||||
|
||||
`T`定义了一个名为“某种类型T”的节点提供给后来用。这种将来类型可以在结构体的定义里任何地方表示为“T”。在这种情况下,`T`在如下三个地方被用作节点:
|
||||
|
||||
- 创建一个名为`items`的属性,使用空的T类型值数组对其进行初始化;
|
||||
- 指定一个包含一个参数名为`item`的`push`方法,该参数必须是T类型;
|
||||
- 指定一个`pop`方法的返回值,该返回值将是一个T类型值。
|
||||
|
||||
当创建一个新单例并初始化时, 通过用一对紧随在类型名后的尖括号里写出实际指定栈用到类型,创建一个`Stack`实例,同创建`Array`和`Dictionary`一样:
|
||||
|
||||
var stackOfStrings = Stack<String>()
|
||||
stackOfStrings.push("uno")
|
||||
stackOfStrings.push("dos")
|
||||
stackOfStrings.push("tres")
|
||||
stackOfStrings.push("cuatro")
|
||||
// 现在栈已经有4个string了
|
||||
|
||||
下图将展示`stackOfStrings`如何`push`这四个值进栈的过程:
|
||||
|
||||
![此处输入图片的描述][3]
|
||||
|
||||
从栈中`pop`并移除值"cuatro":
|
||||
|
||||
let fromTheTop = stackOfStrings.pop()
|
||||
// fromTheTop is equal to "cuatro", and the stack now contains 3 strings
|
||||
|
||||
下图展示了如何从栈中pop一个值的过程:
|
||||
![此处输入图片的描述][4]
|
||||
|
||||
由于`Stack`是泛型类型,所以在 Swift 中其可以用来创建任何有效类型的栈,这种方式如同`Array`和`Dictionary`。
|
||||
|
||||
<a name="type_constraints"></a>
|
||||
##类型约束
|
||||
|
||||
`swapTwoValues`函数和`Stack`类型可以作用于任何类型,不过,有的时候对使用在泛型函数和泛型类型上的类型强制约束为某种特定类型是非常有用的。类型约束指定了一个必须继承自指定类的类型参数,或者遵循一个特定的协议或协议构成。
|
||||
|
||||
例如,Swift 的`Dictionary`类型对作用于其键的类型做了些限制。在[字典][5]的描述中,字典的键类型必须是*可哈希*,也就是说,必须有一种方法可以使其是唯一的表示。`Dictionary`之所以需要其键是可哈希是为了以便于其检查其是否包含某个特定键的值。如无此需求,`Dictionary`即不会告诉是否插入或者替换了某个特定键的值,也不能查找到已经存储在字典里面的给定键值。
|
||||
|
||||
这个需求强制加上一个类型约束作用于`Dictionary`的键上,当然其键类型必须遵循`Hashable`协议(Swift 标准库中定义的一个特定协议)。所有的 Swift 基本类型(如`String`,`Int`, `Double`和 `Bool`)默认都是可哈希。
|
||||
|
||||
当你创建自定义泛型类型时,你可以定义你自己的类型约束,当然,这些约束要支持泛型编程的强力特征中的多数。抽象概念如`可哈希`具有的类型特征是根据它们概念特征来界定的,而不是它们的直接类型特征。
|
||||
|
||||
### 类型约束语法
|
||||
|
||||
你可以写一个在一个类型参数名后面的类型约束,通过冒号分割,来作为类型参数链的一部分。这种作用于泛型函数的类型约束的基础语法如下所示(和泛型类型的语法相同):
|
||||
|
||||
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
|
||||
// function body goes here
|
||||
}
|
||||
|
||||
上面这个假定函数有两个类型参数。第一个类型参数`T`,有一个需要`T`必须是`SomeClass`子类的类型约束;第二个类型参数`U`,有一个需要`U`必须遵循`SomeProtocol`协议的类型约束。
|
||||
|
||||
### 类型约束行为
|
||||
|
||||
这里有个名为`findStringIndex`的非泛型函数,该函数功能是去查找包含一给定`String`值的数组。若查找到匹配的字符串,`findStringIndex`函数返回该字符串在数组中的索引值(`Int`),反之则返回`nil`:
|
||||
|
||||
func findStringIndex(array: String[], valueToFind: String) -> Int? {
|
||||
for (index, value) in enumerate(array) {
|
||||
if value == valueToFind {
|
||||
return index
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
`findStringIndex`函数可以作用于查找一字符串数组中的某个字符串:
|
||||
|
||||
let strings = ["cat", "dog", "llama", "parakeet", "terrapin"]
|
||||
if let foundIndex = findStringIndex(strings, "llama") {
|
||||
println("The index of llama is \(foundIndex)")
|
||||
}
|
||||
// 输出 "The index of llama is 2"
|
||||
|
||||
如果只是针对字符串而言查找在数组中的某个值的索引,用处不是很大,不过,你可以写出相同功能的泛型函数`findIndex`,用某个类型`T`值替换掉提到的字符串。
|
||||
|
||||
这里展示如何写一个你或许期望的`findStringIndex`的泛型版本`findIndex`。请注意这个函数仍然返回`Int`,是不是有点迷惑呢,而不是泛型类型?那是因为函数返回的是一个可选的索引数,而不是从数组中得到的一个可选值。需要提醒的是,这个函数不会编译,原因在例子后面会说明:
|
||||
|
||||
func findIndex<T>(array: T[], valueToFind: T) -> Int? {
|
||||
for (index, value) in enumerate(array) {
|
||||
if value == valueToFind {
|
||||
return index
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
上面所写的函数不会编译。这个问题的位置在等式的检查上,`“if value == valueToFind”`。不是所有的 Swift 中的类型都可以用等式符(==)进行比较。例如,如果你创建一个你自己的类或结构体来表示一个复杂的数据模型,那么 Swift 没法猜到对于这个类或结构体而言“等于”的意思。正因如此,这部分代码不能可能保证工作于每个可能的类型`T`,当你试图编译这部分代码时估计会出现相应的错误。
|
||||
|
||||
不过,所有的这些并不会让我们无从下手。Swift 标准库中定义了一个`Equatable`协议,该协议要求任何遵循的类型实现等式符(==)和不等符(!=)对任何两个该类型进行比较。所有的 Swift 标准类型自动支持`Equatable`协议。
|
||||
|
||||
任何`Equatable`类型都可以安全的使用在`findIndex`函数中,因为其保证支持等式操作。为了说明这个事实,当你定义一个函数时,你可以写一个`Equatable`类型约束作为类型参数定义的一部分:
|
||||
|
||||
func findIndex<T: Equatable>(array: T[], valueToFind: T) -> Int? {
|
||||
for (index, value) in enumerate(array) {
|
||||
if value == valueToFind {
|
||||
return index
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
`findIndex`中这个单个类型参数写做:`T: Equatable`,也就意味着“任何T类型都遵循`Equatable`协议”。
|
||||
|
||||
`findIndex`函数现在则可以成功的编译过,并且作用于任何遵循`Equatable`的类型,如`Double`或`String`:
|
||||
|
||||
let doubleIndex = findIndex([3.14159, 0.1, 0.25], 9.3)
|
||||
// doubleIndex is an optional Int with no value, because 9.3 is not in the array
|
||||
let stringIndex = findIndex(["Mike", "Malcolm", "Andrea"], "Andrea")
|
||||
// stringIndex is an optional Int containing a value of 2
|
||||
|
||||
<a name="associated_types"></a>
|
||||
##关联类型
|
||||
|
||||
当定义一个协议时,有的时候声明一个或多个关联类型作为协议定义的一部分是非常有用的。一个关联类型给定作用于协议部分的类型一个节点名(或*别名*)。作用于关联类型上实际类型是不需要指定的,直到该协议接受。关联类型被指定为`typealias`关键字。
|
||||
|
||||
### 关联类型行为
|
||||
|
||||
这里是一个`Container`协议的例子,定义了一个ItemType关联类型:
|
||||
|
||||
protocol Container {
|
||||
typealias ItemType
|
||||
mutating func append(item: ItemType)
|
||||
var count: Int { get }
|
||||
subscript(i: Int) -> ItemType { get }
|
||||
}
|
||||
|
||||
`Container`协议定义了三个任何容器必须支持的兼容要求:
|
||||
|
||||
- 必须可能通过`append`方法添加一个新item到容器里;
|
||||
- 必须可能通过使用`count`属性获取容器里items的数量,并返回一个`Int`值;
|
||||
- 必须可能通过容器的`Int`索引值下标可以检索到每一个item。
|
||||
|
||||
这个协议没有指定容器里item是如何存储的或何种类型是允许的。这个协议只指定三个任何遵循`Container`类型所必须支持的功能点。一个遵循的类型也可以提供其他额外的功能,只要满足这三个条件。
|
||||
|
||||
任何遵循`Container`协议的类型必须指定存储在其里面的值类型,必须保证只有正确类型的items可以加进容器里,必须明确可以通过其下标返回item类型。
|
||||
|
||||
为了定义这三个条件,`Container`协议需要一个方法指定容器里的元素将会保留,而不需要知道特定容器的类型。`Container`协议需要指定任何通过`append`方法添加到容器里的值和容器里元素是相同类型,并且通过容器下标返回的容器元素类型的值的类型是相同类型。
|
||||
|
||||
为了达到此目的,`Container`协议声明了一个ItemType的关联类型,写作`typealias ItemType`。The protocol does not define what ItemType is an alias for—that information is left for any conforming type to provide(这个协议不会定义`ItemType`是遵循类型所提供的何种信息的别名)。尽管如此,`ItemType`别名支持一种方法识别在一个容器里的items类型,以及定义一种使用在`append`方法和下标中的类型,以便保证任何期望的`Container`的行为是强制性的。
|
||||
|
||||
这里是一个早前IntStack类型的非泛型版本,适用于遵循Container协议:
|
||||
|
||||
struct IntStack: Container {
|
||||
// original IntStack implementation
|
||||
var items = Int[]()
|
||||
mutating func push(item: Int) {
|
||||
items.append(item)
|
||||
}
|
||||
mutating func pop() -> Int {
|
||||
return items.removeLast()
|
||||
}
|
||||
// conformance to the Container protocol
|
||||
typealias ItemType = Int
|
||||
mutating func append(item: Int) {
|
||||
self.push(item)
|
||||
}
|
||||
var count: Int {
|
||||
return items.count
|
||||
}
|
||||
subscript(i: Int) -> Int {
|
||||
return items[i]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
`IntStack`类型实现了`Container`协议的所有三个要求,在`IntStack`类型的每个包含部分的功能都满足这些要求。
|
||||
|
||||
此外,`IntStack`指定了`Container`的实现,适用的ItemType被用作`Int`类型。对于这个`Container`协议实现而言,定义 `typealias ItemType = Int`,将抽象的`ItemType`类型转换为具体的`Int`类型。
|
||||
|
||||
感谢Swift类型参考,你不用在`IntStack`定义部分声明一个具体的`Int`的`ItemType`。由于`IntStack`遵循`Container`协议的所有要求,只要通过简单的查找`append`方法的item参数类型和下标返回的类型,Swift就可以推断出合适的`ItemType`来使用。确实,如果上面的代码中你删除了 `typealias ItemType = Int`这一行,一切仍旧可以工作,因为它清楚的知道ItemType使用的是何种类型。
|
||||
|
||||
你也可以生成遵循`Container`协议的泛型`Stack`类型:
|
||||
|
||||
struct Stack<T>: Container {
|
||||
// original Stack<T> implementation
|
||||
var items = T[]()
|
||||
mutating func push(item: T) {
|
||||
items.append(item)
|
||||
}
|
||||
mutating func pop() -> T {
|
||||
return items.removeLast()
|
||||
}
|
||||
// conformance to the Container protocol
|
||||
mutating func append(item: T) {
|
||||
self.push(item)
|
||||
}
|
||||
var count: Int {
|
||||
return items.count
|
||||
}
|
||||
subscript(i: Int) -> T {
|
||||
return items[i]
|
||||
}
|
||||
}
|
||||
|
||||
这个时候,占位类型参数`T`被用作`append`方法的item参数和下标的返回类型。Swift 因此可以推断出被用作这个特定容器的`ItemType`的`T`的合适类型。
|
||||
|
||||
|
||||
### 扩展一个存在的类型为一指定关联类型
|
||||
|
||||
在[使用扩展来添加协议兼容性][6]中有描述扩展一个存在的类型添加遵循一个协议。这个类型包含一个关联类型的协议。
|
||||
|
||||
Swift的`Array`已经提供`append`方法,一个`count`属性和通过下标来查找一个自己的元素。这三个功能都达到`Container`协议的要求。也就意味着你可以扩展`Array`去遵循`Container`协议,只要通过简单声明`Array`适用于该协议而已。如何实践这样一个空扩展,在[使用扩展来声明协议的采纳][7]中有描述这样一个实现一个空扩展的行为:
|
||||
|
||||
extension Array: Container {}
|
||||
|
||||
如同上面的泛型`Stack`类型一样,`Array的append`方法和下标保证`Swift`可以推断出`ItemType`所使用的适用的类型。定义了这个扩展后,你可以将任何`Array`当作`Container`来使用。
|
||||
|
||||
<a name="where_clauses"></a>
|
||||
## Where 语句
|
||||
|
||||
[类型约束][8]中描述的类型约束确保你定义关于类型参数的需求和一泛型函数或类型有关联。
|
||||
|
||||
对于关联类型的定义需求也是非常有用的。你可以通过这样去定义*where语句*作为一个类型参数队列的一部分。一个`where`语句使你能够要求一个关联类型遵循一个特定的协议,以及(或)那个特定的类型参数和关联类型可以是相同的。你可写一个`where`语句,通过紧随放置`where`关键字在类型参数队列后面,其后跟着一个或者多个针对关联类型的约束,以及(或)一个或多个类型和关联类型的等于关系。
|
||||
|
||||
下面的列子定义了一个名为`allItemsMatch`的泛型函数,用来检查是否两个`Container`单例包含具有相同顺序的相同元素。如果匹配到所有的元素,那么返回一个为`true`的`Boolean`值,反之,则相反。
|
||||
|
||||
这两个容器可以被检查出是否是相同类型的容器(虽然它们可以是),但它们确实拥有相同类型的元素。这个需求通过一个类型约束和`where`语句结合来表示:
|
||||
|
||||
func allItemsMatch<
|
||||
C1: Container, C2: Container
|
||||
where C1.ItemType == C2.ItemType, C1.ItemType: Equatable>
|
||||
(someContainer: C1, anotherContainer: C2) -> Bool {
|
||||
|
||||
// check that both containers contain the same number of items
|
||||
if someContainer.count != anotherContainer.count {
|
||||
return false
|
||||
}
|
||||
|
||||
// check each pair of items to see if they are equivalent
|
||||
for i in 0..someContainer.count {
|
||||
if someContainer[i] != anotherContainer[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// all items match, so return true
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
|
||||
这个函数用了两个参数:`someContainer`和`anotherContainer`。`someContainer`参数是类型`C1`,`anotherContainer`参数是类型`C2`。`C1`和`C2`是容器的两个占位类型参数,决定了这个函数何时被调用。
|
||||
|
||||
这个函数的类型参数列紧随在两个类型参数需求的后面:
|
||||
|
||||
- `C1`必须遵循`Container`协议 (写作 `C1: Container`)。
|
||||
- `C2`必须遵循`Container`协议 (写作 `C2: Container`)。
|
||||
- `C1`的`ItemType`同样是C2的`ItemType`(写作 `C1.ItemType == C2.ItemType`)。
|
||||
- `C1`的`ItemType`必须遵循`Equatable`协议 (写作 `C1.ItemType: Equatable`)。
|
||||
|
||||
第三个和第四个要求被定义为一个`where`语句的一部分,写在关键字`where`后面,作为函数类型参数链的一部分。
|
||||
|
||||
这些要求意思是:
|
||||
|
||||
`someContainer`是一个`C1`类型的容器。
|
||||
`anotherContainer`是一个`C2`类型的容器。
|
||||
`someContainer`和`anotherContainer`包含相同的元素类型。
|
||||
`someContainer`中的元素可以通过不等于操作(`!=`)来检查它们是否彼此不同。
|
||||
|
||||
第三个和第四个要求结合起来的意思是`anotherContainer`中的元素也可以通过 `!=` 操作来检查,因为它们在`someContainer`中元素确实是相同的类型。
|
||||
|
||||
这些要求能够使`allItemsMatch`函数比较两个容器,即便它们是不同的容器类型。
|
||||
|
||||
`allItemsMatch`首先检查两个容器是否拥有同样数目的items,如果它们的元素数目不同,没有办法进行匹配,函数就会`false`。
|
||||
|
||||
检查完之后,函数通过`for-in`循环和半闭区间操作(..)来迭代`someContainer`中的所有元素。对于每个元素,函数检查是否`someContainer`中的元素不等于对应的`anotherContainer`中的元素,如果这两个元素不等,则这两个容器不匹配,返回`false`。
|
||||
|
||||
如果循环体结束后未发现没有任何的不匹配,那表明两个容器匹配,函数返回`true`。
|
||||
|
||||
这里演示了allItemsMatch函数运算的过程:
|
||||
|
||||
var stackOfStrings = Stack<String>()
|
||||
stackOfStrings.push("uno")
|
||||
stackOfStrings.push("dos")
|
||||
stackOfStrings.push("tres")
|
||||
|
||||
var arrayOfStrings = ["uno", "dos", "tres"]
|
||||
|
||||
if allItemsMatch(stackOfStrings, arrayOfStrings) {
|
||||
println("All items match.")
|
||||
} else {
|
||||
println("Not all items match.")
|
||||
}
|
||||
// 输出 "All items match."
|
||||
|
||||
上面的例子创建一个`Stack`单例来存储`String`,然后压了三个字符串进栈。这个例子也创建了一个`Array`单例,并初始化包含三个同栈里一样的原始字符串。即便栈和数组否是不同的类型,但它们都遵循`Container`协议,而且它们都包含同样的类型值。你因此可以调用`allItemsMatch`函数,用这两个容器作为它的参数。在上面的例子中,`allItemsMatch`函数正确的显示了所有的这两个容器的`items`匹配。
|
||||
|
||||
[1]: ../chapter2/06_Functions.html
|
||||
[2]: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/stackPushPop_2x.png
|
||||
[3]: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/stackPushedFourStrings_2x.png
|
||||
[4]: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/stackPoppedOneString_2x.png
|
||||
[5]: ../chapter2/04_Collection_Types.html
|
||||
[6]: ../chapter2/21_Protocols.html
|
||||
[7]: ../chapter2/21_Protocols.html
|
||||
> 翻译:[takalard](https://github.com/takalard)
|
||||
> 校对:[lifedim](https://github.com/lifedim)
|
||||
|
||||
# 泛型
|
||||
|
||||
------
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [泛型所解决的问题](#the_problem_that_generics_solve)
|
||||
- [泛型函数](#generic_functions)
|
||||
- [类型参数](#type_parameters)
|
||||
- [命名类型参数](#naming_type_parameters)
|
||||
- [泛型类型](#generic_types)
|
||||
- [类型约束](#type_constraints)
|
||||
- [关联类型](#associated_types)
|
||||
- [`Where`语句](#where_clauses)
|
||||
|
||||
*泛型代码*可以让你写出根据自我需求定义、适用于任何类型的,灵活且可重用的函数和类型。它的可以让你避免重复的代码,用一种清晰和抽象的方式来表达代码的意图。
|
||||
|
||||
泛型是 Swift 强大特征中的其中一个,许多 Swift 标准库是通过泛型代码构建出来的。事实上,泛型的使用贯穿了整本语言手册,只是你没有发现而已。例如,Swift 的数组和字典类型都是泛型集。你可以创建一个`Int`数组,也可创建一个`String`数组,或者甚至于可以是任何其他 Swift 的类型数据数组。同样的,你也可以创建存储任何指定类型的字典(dictionary),而且这些类型可以是没有限制的。
|
||||
|
||||
<a name="the_problem_that_generics_solve"></a>
|
||||
## 泛型所解决的问题
|
||||
|
||||
这里是一个标准的,非泛型函数`swapTwoInts`,用来交换两个Int值:
|
||||
|
||||
```swift
|
||||
func swapTwoInts(inout a: Int, inout b: Int)
|
||||
let temporaryA = a
|
||||
a = b
|
||||
b = temporaryA
|
||||
}
|
||||
```
|
||||
|
||||
这个函数使用写入读出(in-out)参数来交换`a`和`b`的值,请参考[写入读出参数][1]。
|
||||
|
||||
`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)")
|
||||
// 输出 "someInt is now 107, and anotherInt is now 3"
|
||||
```
|
||||
|
||||
|
||||
`swapTwoInts`函数是非常有用的,但是它只能交换`Int`值,如果你想要交换两个`String`或者`Double`,就不得不写更多的函数,如 `swapTwoStrings`和`swapTwoDoublesfunctions `,如同如下所示:
|
||||
|
||||
```swift
|
||||
func swapTwoStrings(inout a: String, inout b: String) {
|
||||
let temporaryA = a
|
||||
a = b
|
||||
b = temporaryA
|
||||
}
|
||||
|
||||
func swapTwoDoubles(inout a: Double, inout b: Double) {
|
||||
let temporaryA = a
|
||||
a = b
|
||||
b = temporaryA
|
||||
}
|
||||
```
|
||||
|
||||
你可能注意到 `swapTwoInts`、 `swapTwoStrings`和`swapTwoDoubles`函数功能都是相同的,唯一不同之处就在于传入的变量类型不同,分别是`Int`、`String`和`Double`。
|
||||
|
||||
但实际应用中通常需要一个用处更强大并且尽可能的考虑到更多的灵活性单个函数,可以用来交换两个任何类型值,很幸运的是,泛型代码帮你解决了这种问题。(一个这种泛型函数后面已经定义好了。)
|
||||
|
||||
>注意:
|
||||
在所有三个函数中,`a`和`b`的类型是一样的。如果`a`和`b`不是相同的类型,那它们俩就不能互换值。Swift 是类型安全的语言,所以它不允许一个`String`类型的变量和一个`Double`类型的变量互相交换值。如果一定要做,Swift 将报编译错误。
|
||||
|
||||
<a name="generic_functions"></a>
|
||||
## 泛型函数
|
||||
|
||||
`泛型函数`可以工作于任何类型,这里是一个上面`swapTwoInts`函数的泛型版本,用于交换两个值:
|
||||
|
||||
```swift
|
||||
func swapTwoValues<T>(inout a: T, inout b: T) {
|
||||
let temporaryA = a
|
||||
a = b
|
||||
b = temporaryA
|
||||
}
|
||||
```
|
||||
|
||||
`swapTwoValues`函数主体和`swapTwoInts`函数是一样的,它只在第一行稍微有那么一点点不同于`swapTwoInts`,如下所示:
|
||||
|
||||
```swift
|
||||
func swapTwoInts(inout a: Int, inout b: Int)
|
||||
func swapTwoValues<T>(inout a: T, inout b: T)
|
||||
```
|
||||
|
||||
|
||||
这个函数的泛型版本使用了占位类型名字(通常此情况下用字母`T`来表示)来代替实际类型名(如`In`、`String`或`Doubl`)。占位类型名没有提示`T`必须是什么类型,但是它提示了`a`和`b`必须是同一类型`T`,而不管`T`表示什么类型。只有`swapTwoValues`函数在每次调用时所传入的实际类型才能决定`T`所代表的类型。
|
||||
|
||||
另外一个不同之处在于这个泛型函数名后面跟着的展位类型名字(T)是用尖括号括起来的(`<T>`)。这个尖括号告诉 Swift 那个`T`是`swapTwoValues`函数所定义的一个类型。因为`T`是一个占位命名类型,Swift 不会去查找命名为T的实际类型。
|
||||
|
||||
`swapTwoValues`函数除了要求传入的两个任何类型值是同一类型外,也可以作为`swapTwoInts`函数被调用。每次`swapTwoValues`被调用,T所代表的类型值都会传给函数。
|
||||
|
||||
在下面的两个例子中,`T`分别代表`Int`和`String`:
|
||||
|
||||
```swift
|
||||
var someInt = 3
|
||||
var anotherInt = 107
|
||||
swapTwoValues(&someInt, &anotherInt)
|
||||
// someInt is now 107, and anotherInt is now 3
|
||||
```
|
||||
|
||||
```swift
|
||||
var someString = "hello"
|
||||
var anotherString = "world"
|
||||
swapTwoValues(&someString, &anotherString)
|
||||
// someString is now "world", and anotherString is now "hello"
|
||||
```
|
||||
|
||||
|
||||
>注意
|
||||
上面定义的函数`swapTwoValues`是受`swap`函数启发而实现的。`swap`函数存在于 Swift 标准库,并可以在其它类中任意使用。如果你在自己代码中需要类似`swapTwoValues`函数的功能,你可以使用已存在的交换函数`swap`函数。
|
||||
|
||||
<a name="type_parameters"></a>
|
||||
## 类型参数
|
||||
|
||||
在上面的`swapTwoValues`例子中,占位类型`T`是一种类型参数的示例。类型参数指定并命名为一个占位类型,并且紧随在函数名后面,使用一对尖括号括起来(如`<T>`)。
|
||||
|
||||
一旦一个类型参数被指定,那么其可以被使用来定义一个函数的参数类型(如`swapTwoValues`函数中的参数`a`和`b`),或作为一个函数返回类型,或用作函数主体中的注释类型。在这种情况下,被类型参数所代表的占位类型不管函数任何时候被调用,都会被实际类型所替换(在上面`swapTwoValues`例子中,当函数第一次被调用时,`T`被`Int`替换,第二次调用时,被`String`替换。)。
|
||||
|
||||
你可支持多个类型参数,命名在尖括号中,用逗号分开。
|
||||
|
||||
<a name="naming_type_parameters"></a>
|
||||
## 命名类型参数
|
||||
|
||||
在简单的情况下,泛型函数或泛型类型需要指定一个占位类型(如上面的`swapTwoValues`泛型函数,或一个存储单一类型的泛型集,如数组),通常用一单个字母`T`来命名类型参数。不过,你可以使用任何有效的标识符来作为类型参数名。
|
||||
|
||||
如果你使用多个参数定义更复杂的泛型函数或泛型类型,那么使用更多的描述类型参数是非常有用的。例如,Swift 字典(Dictionary)类型有两个类型参数,一个是键,另外一个是值。如果你自己写字典,你或许会定义这两个类型参数为`KeyType`和`ValueType`,用来记住它们在你的泛型代码中的作用。
|
||||
|
||||
>注意
|
||||
请始终使用大写字母开头的驼峰式命名法(例如`T`和`KeyType`)来给类型参数命名,以表明它们是类型的占位符,而非类型值。
|
||||
|
||||
<a name="generic_types"></a>
|
||||
## 泛型类型
|
||||
|
||||
|
||||
通常在泛型函数中,Swift 允许你定义你自己的泛型类型。这些自定义类、结构体和枚举作用于任何类型,如同`Array`和`Dictionary`的用法。
|
||||
|
||||
这部分向你展示如何写一个泛型集类型--`Stack`(栈)。一个栈是一系列值域的集合,和`Array`(数组)类似,但其是一个比 Swift 的`Array`类型更多限制的集合。一个数组可以允许其里面任何位置的插入/删除操作,而栈,只允许在集合的末端添加新的项(如同*push*一个新值进栈)。同样的一个栈也只能从末端移除项(如同*pop*一个值出栈)。
|
||||
|
||||
>注意
|
||||
栈的概念已被`UINavigationController`类使用来模拟试图控制器的导航结构。你通过调用`UINavigationController`的`pushViewController:animated:`方法来为导航栈添加(add)新的试图控制器;而通过`popViewControllerAnimated:`的方法来从导航栈中移除(pop)某个试图控制器。每当你需要一个严格的`后进先出`方式来管理集合,堆栈都是最实用的模型。
|
||||
|
||||
下图展示了一个栈的压栈(push)/出栈(pop)的行为:
|
||||
|
||||
![此处输入图片的描述][2]
|
||||
|
||||
1. 现在有三个值在栈中;
|
||||
2. 第四个值“pushed”到栈的顶部;
|
||||
3. 现在有四个值在栈中,最近的那个在顶部;
|
||||
4. 栈中最顶部的那个项被移除,或称之为“popped”;
|
||||
5. 移除掉一个值后,现在栈又重新只有三个值。
|
||||
|
||||
这里展示了如何写一个非泛型版本的栈,`Int`值型的栈:
|
||||
|
||||
```swift
|
||||
struct IntStack {
|
||||
var items = Int[]()
|
||||
mutating func push(item: Int) {
|
||||
items.append(item)
|
||||
}
|
||||
mutating func pop() -> Int {
|
||||
return items.removeLast()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
这个结构体在栈中使用一个`Array`性质的`items`存储值。`Stack`提供两个方法:`push`和`pop`,从栈中压进一个值和移除一个值。这些方法标记为可变的,因为它们需要修改(或*转换*)结构体的`items`数组。
|
||||
|
||||
上面所展现的`IntStack`类型只能用于`Int`值,不过,其对于定义一个泛型`Stack`类(可以处理*任何*类型值的栈)是非常有用的。
|
||||
|
||||
这里是一个相同代码的泛型版本:
|
||||
|
||||
|
||||
```swift
|
||||
struct Stack<T> {
|
||||
var items = T[]()
|
||||
mutating func push(item: T) {
|
||||
items.append(item)
|
||||
}
|
||||
mutating func pop() -> T {
|
||||
return items.removeLast()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
注意到`Stack`的泛型版本基本上和非泛型版本相同,但是泛型版本的占位类型参数为T代替了实际`Int`类型。这种类型参数包含在一对尖括号里(`<T>`),紧随在结构体名字后面。
|
||||
|
||||
`T`定义了一个名为“某种类型T”的节点提供给后来用。这种将来类型可以在结构体的定义里任何地方表示为“T”。在这种情况下,`T`在如下三个地方被用作节点:
|
||||
|
||||
- 创建一个名为`items`的属性,使用空的T类型值数组对其进行初始化;
|
||||
- 指定一个包含一个参数名为`item`的`push`方法,该参数必须是T类型;
|
||||
- 指定一个`pop`方法的返回值,该返回值将是一个T类型值。
|
||||
|
||||
当创建一个新单例并初始化时, 通过用一对紧随在类型名后的尖括号里写出实际指定栈用到类型,创建一个`Stack`实例,同创建`Array`和`Dictionary`一样:
|
||||
|
||||
```swift
|
||||
var stackOfStrings = Stack<String>()
|
||||
stackOfStrings.push("uno")
|
||||
stackOfStrings.push("dos")
|
||||
stackOfStrings.push("tres")
|
||||
stackOfStrings.push("cuatro")
|
||||
// 现在栈已经有4个string了
|
||||
```
|
||||
|
||||
下图将展示`stackOfStrings`如何`push`这四个值进栈的过程:
|
||||
|
||||
![此处输入图片的描述][3]
|
||||
|
||||
从栈中`pop`并移除值"cuatro":
|
||||
|
||||
```swift
|
||||
let fromTheTop = stackOfStrings.pop()
|
||||
// fromTheTop is equal to "cuatro", and the stack now contains 3 strings
|
||||
```
|
||||
|
||||
下图展示了如何从栈中pop一个值的过程:
|
||||
![此处输入图片的描述][4]
|
||||
|
||||
由于`Stack`是泛型类型,所以在 Swift 中其可以用来创建任何有效类型的栈,这种方式如同`Array`和`Dictionary`。
|
||||
|
||||
<a name="type_constraints"></a>
|
||||
##类型约束
|
||||
|
||||
`swapTwoValues`函数和`Stack`类型可以作用于任何类型,不过,有的时候对使用在泛型函数和泛型类型上的类型强制约束为某种特定类型是非常有用的。类型约束指定了一个必须继承自指定类的类型参数,或者遵循一个特定的协议或协议构成。
|
||||
|
||||
例如,Swift 的`Dictionary`类型对作用于其键的类型做了些限制。在[字典][5]的描述中,字典的键类型必须是*可哈希*,也就是说,必须有一种方法可以使其是唯一的表示。`Dictionary`之所以需要其键是可哈希是为了以便于其检查其是否包含某个特定键的值。如无此需求,`Dictionary`即不会告诉是否插入或者替换了某个特定键的值,也不能查找到已经存储在字典里面的给定键值。
|
||||
|
||||
这个需求强制加上一个类型约束作用于`Dictionary`的键上,当然其键类型必须遵循`Hashable`协议(Swift 标准库中定义的一个特定协议)。所有的 Swift 基本类型(如`String`,`Int`, `Double`和 `Bool`)默认都是可哈希。
|
||||
|
||||
当你创建自定义泛型类型时,你可以定义你自己的类型约束,当然,这些约束要支持泛型编程的强力特征中的多数。抽象概念如`可哈希`具有的类型特征是根据它们概念特征来界定的,而不是它们的直接类型特征。
|
||||
|
||||
### 类型约束语法
|
||||
|
||||
你可以写一个在一个类型参数名后面的类型约束,通过冒号分割,来作为类型参数链的一部分。这种作用于泛型函数的类型约束的基础语法如下所示(和泛型类型的语法相同):
|
||||
|
||||
```swift
|
||||
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
|
||||
// function body goes here
|
||||
}
|
||||
```
|
||||
|
||||
上面这个假定函数有两个类型参数。第一个类型参数`T`,有一个需要`T`必须是`SomeClass`子类的类型约束;第二个类型参数`U`,有一个需要`U`必须遵循`SomeProtocol`协议的类型约束。
|
||||
|
||||
### 类型约束行为
|
||||
|
||||
这里有个名为`findStringIndex`的非泛型函数,该函数功能是去查找包含一给定`String`值的数组。若查找到匹配的字符串,`findStringIndex`函数返回该字符串在数组中的索引值(`Int`),反之则返回`nil`:
|
||||
|
||||
```swift
|
||||
func findStringIndex(array: String[], valueToFind: String) -> Int? {
|
||||
for (index, value) in enumerate(array) {
|
||||
if value == valueToFind {
|
||||
return index
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
`findStringIndex`函数可以作用于查找一字符串数组中的某个字符串:
|
||||
|
||||
```swift
|
||||
let strings = ["cat", "dog", "llama", "parakeet", "terrapin"]
|
||||
if let foundIndex = findStringIndex(strings, "llama") {
|
||||
println("The index of llama is \(foundIndex)")
|
||||
}
|
||||
// 输出 "The index of llama is 2"
|
||||
```
|
||||
|
||||
如果只是针对字符串而言查找在数组中的某个值的索引,用处不是很大,不过,你可以写出相同功能的泛型函数`findIndex`,用某个类型`T`值替换掉提到的字符串。
|
||||
|
||||
这里展示如何写一个你或许期望的`findStringIndex`的泛型版本`findIndex`。请注意这个函数仍然返回`Int`,是不是有点迷惑呢,而不是泛型类型?那是因为函数返回的是一个可选的索引数,而不是从数组中得到的一个可选值。需要提醒的是,这个函数不会编译,原因在例子后面会说明:
|
||||
|
||||
```swift
|
||||
func findIndex<T>(array: T[], valueToFind: T) -> Int? {
|
||||
for (index, value) in enumerate(array) {
|
||||
if value == valueToFind {
|
||||
return index
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
上面所写的函数不会编译。这个问题的位置在等式的检查上,`“if value == valueToFind”`。不是所有的 Swift 中的类型都可以用等式符(==)进行比较。例如,如果你创建一个你自己的类或结构体来表示一个复杂的数据模型,那么 Swift 没法猜到对于这个类或结构体而言“等于”的意思。正因如此,这部分代码不能可能保证工作于每个可能的类型`T`,当你试图编译这部分代码时估计会出现相应的错误。
|
||||
|
||||
不过,所有的这些并不会让我们无从下手。Swift 标准库中定义了一个`Equatable`协议,该协议要求任何遵循的类型实现等式符(==)和不等符(!=)对任何两个该类型进行比较。所有的 Swift 标准类型自动支持`Equatable`协议。
|
||||
|
||||
任何`Equatable`类型都可以安全的使用在`findIndex`函数中,因为其保证支持等式操作。为了说明这个事实,当你定义一个函数时,你可以写一个`Equatable`类型约束作为类型参数定义的一部分:
|
||||
|
||||
```swift
|
||||
func findIndex<T: Equatable>(array: T[], valueToFind: T) -> Int? {
|
||||
for (index, value) in enumerate(array) {
|
||||
if value == valueToFind {
|
||||
return index
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
`findIndex`中这个单个类型参数写做:`T: Equatable`,也就意味着“任何T类型都遵循`Equatable`协议”。
|
||||
|
||||
`findIndex`函数现在则可以成功的编译过,并且作用于任何遵循`Equatable`的类型,如`Double`或`String`:
|
||||
|
||||
```swift
|
||||
let doubleIndex = findIndex([3.14159, 0.1, 0.25], 9.3)
|
||||
// doubleIndex is an optional Int with no value, because 9.3 is not in the array
|
||||
let stringIndex = findIndex(["Mike", "Malcolm", "Andrea"], "Andrea")
|
||||
// stringIndex is an optional Int containing a value of 2
|
||||
```
|
||||
|
||||
<a name="associated_types"></a>
|
||||
##关联类型
|
||||
|
||||
当定义一个协议时,有的时候声明一个或多个关联类型作为协议定义的一部分是非常有用的。一个关联类型给定作用于协议部分的类型一个节点名(或*别名*)。作用于关联类型上实际类型是不需要指定的,直到该协议接受。关联类型被指定为`typealias`关键字。
|
||||
|
||||
### 关联类型行为
|
||||
|
||||
这里是一个`Container`协议的例子,定义了一个ItemType关联类型:
|
||||
|
||||
```swift
|
||||
protocol Container {
|
||||
typealias ItemType
|
||||
mutating func append(item: ItemType)
|
||||
var count: Int { get }
|
||||
subscript(i: Int) -> ItemType { get }
|
||||
}
|
||||
```
|
||||
|
||||
`Container`协议定义了三个任何容器必须支持的兼容要求:
|
||||
|
||||
- 必须可能通过`append`方法添加一个新item到容器里;
|
||||
- 必须可能通过使用`count`属性获取容器里items的数量,并返回一个`Int`值;
|
||||
- 必须可能通过容器的`Int`索引值下标可以检索到每一个item。
|
||||
|
||||
这个协议没有指定容器里item是如何存储的或何种类型是允许的。这个协议只指定三个任何遵循`Container`类型所必须支持的功能点。一个遵循的类型也可以提供其他额外的功能,只要满足这三个条件。
|
||||
|
||||
任何遵循`Container`协议的类型必须指定存储在其里面的值类型,必须保证只有正确类型的items可以加进容器里,必须明确可以通过其下标返回item类型。
|
||||
|
||||
为了定义这三个条件,`Container`协议需要一个方法指定容器里的元素将会保留,而不需要知道特定容器的类型。`Container`协议需要指定任何通过`append`方法添加到容器里的值和容器里元素是相同类型,并且通过容器下标返回的容器元素类型的值的类型是相同类型。
|
||||
|
||||
为了达到此目的,`Container`协议声明了一个ItemType的关联类型,写作`typealias ItemType`。The protocol does not define what ItemType is an alias for—that information is left for any conforming type to provide(这个协议不会定义`ItemType`是遵循类型所提供的何种信息的别名)。尽管如此,`ItemType`别名支持一种方法识别在一个容器里的items类型,以及定义一种使用在`append`方法和下标中的类型,以便保证任何期望的`Container`的行为是强制性的。
|
||||
|
||||
这里是一个早前IntStack类型的非泛型版本,适用于遵循Container协议:
|
||||
|
||||
```swift
|
||||
struct IntStack: Container {
|
||||
// original IntStack implementation
|
||||
var items = Int[]()
|
||||
mutating func push(item: Int) {
|
||||
items.append(item)
|
||||
}
|
||||
mutating func pop() -> Int {
|
||||
return items.removeLast()
|
||||
}
|
||||
// conformance to the Container protocol
|
||||
typealias ItemType = Int
|
||||
mutating func append(item: Int) {
|
||||
self.push(item)
|
||||
}
|
||||
var count: Int {
|
||||
return items.count
|
||||
}
|
||||
subscript(i: Int) -> Int {
|
||||
return items[i]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
`IntStack`类型实现了`Container`协议的所有三个要求,在`IntStack`类型的每个包含部分的功能都满足这些要求。
|
||||
|
||||
此外,`IntStack`指定了`Container`的实现,适用的ItemType被用作`Int`类型。对于这个`Container`协议实现而言,定义 `typealias ItemType = Int`,将抽象的`ItemType`类型转换为具体的`Int`类型。
|
||||
|
||||
感谢Swift类型参考,你不用在`IntStack`定义部分声明一个具体的`Int`的`ItemType`。由于`IntStack`遵循`Container`协议的所有要求,只要通过简单的查找`append`方法的item参数类型和下标返回的类型,Swift就可以推断出合适的`ItemType`来使用。确实,如果上面的代码中你删除了 `typealias ItemType = Int`这一行,一切仍旧可以工作,因为它清楚的知道ItemType使用的是何种类型。
|
||||
|
||||
你也可以生成遵循`Container`协议的泛型`Stack`类型:
|
||||
|
||||
```swift
|
||||
struct Stack<T>: Container {
|
||||
// original Stack<T> implementation
|
||||
var items = T[]()
|
||||
mutating func push(item: T) {
|
||||
items.append(item)
|
||||
}
|
||||
mutating func pop() -> T {
|
||||
return items.removeLast()
|
||||
}
|
||||
// conformance to the Container protocol
|
||||
mutating func append(item: T) {
|
||||
self.push(item)
|
||||
}
|
||||
var count: Int {
|
||||
return items.count
|
||||
}
|
||||
subscript(i: Int) -> T {
|
||||
return items[i]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
这个时候,占位类型参数`T`被用作`append`方法的item参数和下标的返回类型。Swift 因此可以推断出被用作这个特定容器的`ItemType`的`T`的合适类型。
|
||||
|
||||
|
||||
### 扩展一个存在的类型为一指定关联类型
|
||||
|
||||
在[使用扩展来添加协议兼容性][6]中有描述扩展一个存在的类型添加遵循一个协议。这个类型包含一个关联类型的协议。
|
||||
|
||||
Swift的`Array`已经提供`append`方法,一个`count`属性和通过下标来查找一个自己的元素。这三个功能都达到`Container`协议的要求。也就意味着你可以扩展`Array`去遵循`Container`协议,只要通过简单声明`Array`适用于该协议而已。如何实践这样一个空扩展,在[使用扩展来声明协议的采纳][7]中有描述这样一个实现一个空扩展的行为:
|
||||
|
||||
```swift
|
||||
extension Array: Container {}
|
||||
```
|
||||
|
||||
如同上面的泛型`Stack`类型一样,`Array的append`方法和下标保证`Swift`可以推断出`ItemType`所使用的适用的类型。定义了这个扩展后,你可以将任何`Array`当作`Container`来使用。
|
||||
|
||||
<a name="where_clauses"></a>
|
||||
## Where 语句
|
||||
|
||||
[类型约束][8]中描述的类型约束确保你定义关于类型参数的需求和一泛型函数或类型有关联。
|
||||
|
||||
对于关联类型的定义需求也是非常有用的。你可以通过这样去定义*where语句*作为一个类型参数队列的一部分。一个`where`语句使你能够要求一个关联类型遵循一个特定的协议,以及(或)那个特定的类型参数和关联类型可以是相同的。你可写一个`where`语句,通过紧随放置`where`关键字在类型参数队列后面,其后跟着一个或者多个针对关联类型的约束,以及(或)一个或多个类型和关联类型的等于关系。
|
||||
|
||||
下面的列子定义了一个名为`allItemsMatch`的泛型函数,用来检查是否两个`Container`单例包含具有相同顺序的相同元素。如果匹配到所有的元素,那么返回一个为`true`的`Boolean`值,反之,则相反。
|
||||
|
||||
这两个容器可以被检查出是否是相同类型的容器(虽然它们可以是),但它们确实拥有相同类型的元素。这个需求通过一个类型约束和`where`语句结合来表示:
|
||||
|
||||
```swift
|
||||
func allItemsMatch<
|
||||
C1: Container, C2: Container
|
||||
where C1.ItemType == C2.ItemType, C1.ItemType: Equatable>
|
||||
(someContainer: C1, anotherContainer: C2) -> Bool {
|
||||
|
||||
// check that both containers contain the same number of items
|
||||
if someContainer.count != anotherContainer.count {
|
||||
return false
|
||||
}
|
||||
|
||||
// check each pair of items to see if they are equivalent
|
||||
for i in 0..someContainer.count {
|
||||
if someContainer[i] != anotherContainer[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// all items match, so return true
|
||||
return true
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
这个函数用了两个参数:`someContainer`和`anotherContainer`。`someContainer`参数是类型`C1`,`anotherContainer`参数是类型`C2`。`C1`和`C2`是容器的两个占位类型参数,决定了这个函数何时被调用。
|
||||
|
||||
这个函数的类型参数列紧随在两个类型参数需求的后面:
|
||||
|
||||
- `C1`必须遵循`Container`协议 (写作 `C1: Container`)。
|
||||
- `C2`必须遵循`Container`协议 (写作 `C2: Container`)。
|
||||
- `C1`的`ItemType`同样是C2的`ItemType`(写作 `C1.ItemType == C2.ItemType`)。
|
||||
- `C1`的`ItemType`必须遵循`Equatable`协议 (写作 `C1.ItemType: Equatable`)。
|
||||
|
||||
第三个和第四个要求被定义为一个`where`语句的一部分,写在关键字`where`后面,作为函数类型参数链的一部分。
|
||||
|
||||
这些要求意思是:
|
||||
|
||||
`someContainer`是一个`C1`类型的容器。
|
||||
`anotherContainer`是一个`C2`类型的容器。
|
||||
`someContainer`和`anotherContainer`包含相同的元素类型。
|
||||
`someContainer`中的元素可以通过不等于操作(`!=`)来检查它们是否彼此不同。
|
||||
|
||||
第三个和第四个要求结合起来的意思是`anotherContainer`中的元素也可以通过 `!=` 操作来检查,因为它们在`someContainer`中元素确实是相同的类型。
|
||||
|
||||
这些要求能够使`allItemsMatch`函数比较两个容器,即便它们是不同的容器类型。
|
||||
|
||||
`allItemsMatch`首先检查两个容器是否拥有同样数目的items,如果它们的元素数目不同,没有办法进行匹配,函数就会`false`。
|
||||
|
||||
检查完之后,函数通过`for-in`循环和半闭区间操作(..)来迭代`someContainer`中的所有元素。对于每个元素,函数检查是否`someContainer`中的元素不等于对应的`anotherContainer`中的元素,如果这两个元素不等,则这两个容器不匹配,返回`false`。
|
||||
|
||||
如果循环体结束后未发现没有任何的不匹配,那表明两个容器匹配,函数返回`true`。
|
||||
|
||||
这里演示了allItemsMatch函数运算的过程:
|
||||
|
||||
```swift
|
||||
var stackOfStrings = Stack<String>()
|
||||
stackOfStrings.push("uno")
|
||||
stackOfStrings.push("dos")
|
||||
stackOfStrings.push("tres")
|
||||
|
||||
var arrayOfStrings = ["uno", "dos", "tres"]
|
||||
|
||||
if allItemsMatch(stackOfStrings, arrayOfStrings) {
|
||||
println("All items match.")
|
||||
} else {
|
||||
println("Not all items match.")
|
||||
}
|
||||
// 输出 "All items match."
|
||||
```
|
||||
|
||||
上面的例子创建一个`Stack`单例来存储`String`,然后压了三个字符串进栈。这个例子也创建了一个`Array`单例,并初始化包含三个同栈里一样的原始字符串。即便栈和数组否是不同的类型,但它们都遵循`Container`协议,而且它们都包含同样的类型值。你因此可以调用`allItemsMatch`函数,用这两个容器作为它的参数。在上面的例子中,`allItemsMatch`函数正确的显示了所有的这两个容器的`items`匹配。
|
||||
|
||||
[1]: ../chapter2/06_Functions.html
|
||||
[2]: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/stackPushPop_2x.png
|
||||
[3]: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/stackPushedFourStrings_2x.png
|
||||
[4]: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/stackPoppedOneString_2x.png
|
||||
[5]: ../chapter2/04_Collection_Types.html
|
||||
[6]: ../chapter2/21_Protocols.html
|
||||
[7]: ../chapter2/21_Protocols.html
|
||||
[8]: #type_constraints
|
||||
|
||||
@ -1,492 +1,488 @@
|
||||
> 翻译:xielingwang
|
||||
|
||||
> 校对:numbbbbb
|
||||
|
||||
# 高级运算符
|
||||
-----------------
|
||||
|
||||
本页内容包括:
|
||||
|
||||
- [位运算符](#bitwise_operators)
|
||||
- [溢出运算符](#overflow_operators)
|
||||
- [优先级和结合性(Precedence and Associativity)](#precedence_and_associativity)
|
||||
- [运算符函数(Operator Functions)](#operator_functions)
|
||||
- [自定义运算符](#custom_operators)
|
||||
|
||||
除了[基本操作符](02_Basic_Operators.html)中所讲的运算符,Swift还有许多复杂的高级运算符,包括了C语和Objective-C中的位运算符和移位运算。
|
||||
|
||||
不同于C语言中的数值计算,Swift的数值计算默认是不可溢出的。溢出行为会被捕获并报告为错误。你是故意的?好吧,你可以使用Swift为你准备的另一套默认允许溢出的数值运算符,如可溢出加`&+`。所有允许溢出的运算符都是以`&`开始的。
|
||||
|
||||
自定义的结构,类和枚举,是否可以使用标准的运算符来定义操作?当然可以!在Swift中,你可以为你创建的所有类型定制运算符的操作。
|
||||
|
||||
可定制的运算符并不限于那些预设的运算符,自定义有个性的中置,前置,后置及赋值运算符,当然还有优先级和结合性。这些运算符的实现可以运用预设的运算符,也可以运用之前定制的运算符。
|
||||
|
||||
<a name="bitwise_operators"></a>
|
||||
## 位运算符
|
||||
|
||||
位操作符通常在诸如图像处理和创建设备驱动等底层开发中使用,使用它可以单独操作数据结构中原始数据的比特位。在使用一个自定义的协议进行通信的时候,运用位运算符来对原始数据进行编码和解码也是非常有效的。
|
||||
|
||||
Swift支持如下所有C语言的位运算符:
|
||||
|
||||
### 按位取反运算符
|
||||
|
||||
按位取反运算符`~`对一个操作数的每一位都取反。
|
||||
|
||||

|
||||
|
||||
这个运算符是前置的,所以请不加任何空格地写着操作数之前。
|
||||
|
||||
```swift
|
||||
let initialBits: UInt8 = 0b00001111
|
||||
let invertedBits = ~initialBits // 等于 0b11110000
|
||||
```
|
||||
|
||||
`UInt8`是8位无符整型,可以存储0~255之间的任意数。这个例子初始化一个整型为二进制值`00001111`(前4位为`0`,后4位为`1`),它的十进制值为`15`。
|
||||
|
||||
使用按位取反运算`~`对`initialBits`操作,然后赋值给`invertedBits`这个新常量。这个新常量的值等于所有位都取反的`initialBits`,即`1`变成`0`,`0`变成`1`,变成了`11110000`,十进制值为`240`。
|
||||
|
||||
### 按位与运算符
|
||||
|
||||
按位与运算符对两个数进行操作,然后返回一个新的数,这个数的每个位都需要两个输入数的同一位都为1时才为1。
|
||||
|
||||

|
||||
|
||||
以下代码,`firstSixBits`和`lastSixBits`中间4个位都为1。对它俩进行按位与运算后,就得到了`00111100`,即十进制的`60`。
|
||||
|
||||
```swift
|
||||
let firstSixBits: UInt8 = 0b11111100
|
||||
let lastSixBits: UInt8 = 0b00111111
|
||||
let middleFourBits = firstSixBits & lastSixBits // 等于 00111100
|
||||
```
|
||||
|
||||
### 按位或运算
|
||||
|
||||
按位或运算符`|`比较两个数,然后返回一个新的数,这个数的每一位设置1的条件是两个输入数的同一位都不为0(即任意一个为1,或都为1)。
|
||||
|
||||

|
||||
|
||||
如下代码,`someBits`和`moreBits`在不同位上有`1`。按位或运行的结果是`11111110`,即十进制的`254`。
|
||||
|
||||
```swift
|
||||
let someBits: UInt8 = 0b10110010
|
||||
let moreBits: UInt8 = 0b01011110
|
||||
let combinedbits = someBits | moreBits // 等于 11111110
|
||||
```
|
||||
|
||||
### 按位异或运算符
|
||||
|
||||
按位异或运算符`^`比较两个数,然后返回一个数,这个数的每个位设为`1`的条件是两个输入数的同一位不同,如果相同就设为`0`。
|
||||
|
||||

|
||||
|
||||
以下代码,`firstBits`和`otherBits`都有一个`1`跟另一个数不同的。所以按位异或的结果是把它这些位置为`1`,其他都置为`0`。
|
||||
|
||||
```swift
|
||||
let firstBits: UInt8 = 0b00010100
|
||||
let otherBits: UInt8 = 0b00000101
|
||||
let outputBits = firstBits ^ otherBits // 等于 00010001
|
||||
```
|
||||
|
||||
### 按位左移/右移运算符
|
||||
|
||||
左移运算符`<<`和右移运算符`>>`会把一个数的所有比特位按以下定义的规则向左或向右移动指定位数。
|
||||
|
||||
按位左移和按位右移的效果相当把一个整数乘于或除于一个因子为`2`的整数。向左移动一个整型的比特位相当于把这个数乘于`2`,向右移一位就是除于`2`。
|
||||
|
||||
#### 无符整型的移位操作
|
||||
|
||||
对无符整型的移位的效果如下:
|
||||
|
||||
已经存在的比特位向左或向右移动指定的位数。被移出整型存储边界的的位数直接抛弃,移动留下的空白位用零`0`来填充。这种方法称为逻辑移位。
|
||||
|
||||
以下这张把展示了 `11111111 << 1`(`11111111`向左移1位),和 `11111111 >> 1`(`11111111`向右移1位)。蓝色的是被移位的,灰色是被抛弃的,橙色的`0`是被填充进来的。
|
||||
|
||||

|
||||
|
||||
```swift
|
||||
let shiftBits: UInt8 = 4 // 即二进制的00000100
|
||||
shiftBits << 1 // 00001000
|
||||
shiftBits << 2 // 00010000
|
||||
shiftBits << 5 // 10000000
|
||||
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)三个部分。
|
||||
|
||||
对`0xCC6699`和`0xFF0000`进行按位与`&`操作就可以得到红色部分。`0xFF0000`中的`0`了遮盖了`OxCC6699`的第二和第三个字节,这样`6699`被忽略了,只留下`0xCC0000`。
|
||||
|
||||
然后,按向右移动16位,即 `>> 16`。十六进制中每两个字符是8比特位,所以移动16位的结果是把`0xCC0000`变成`0x0000CC`。这和`0xCC`是相等的,都是十进制的`204`。
|
||||
|
||||
同样的,绿色部分来自于`0xCC6699`和`0x00FF00`的按位操作得到`0x006600`。然后向右移动8們,得到`0x66`,即十进制的`102`。
|
||||
|
||||
最后,蓝色部分对`0xCC6699`和`0x0000FF`进行按位与运算,得到`0x000099`,无需向右移位了,所以结果就是`0x99`,即十进制的`153`。
|
||||
|
||||
#### 有符整型的移位操作
|
||||
|
||||
有符整型的移位操作相对复杂得多,因为正负号也是用二进制位表示的。(这里举的例子虽然都是8位的,但它的原理是通用的。)
|
||||
|
||||
有符整型通过第1个比特位(称为符号位)来表达这个整数是正数还是负数。`0`代表正数,`1`代表负数。
|
||||
|
||||
其余的比特位(称为数值位)存储其实值。有符正整数和无符正整数在计算机里的存储结果是一样的,下来我们来看`+4`内部的二进制结构。
|
||||
|
||||

|
||||
|
||||
符号位为`0`,代表正数,另外7比特位二进制表示的实际值就刚好是`4`。
|
||||
|
||||
负数呢,跟正数不同。负数存储的是2的n次方减去它的绝对值,n为数值位的位数。一个8比特的数有7个数值位,所以是2的7次方,即128。
|
||||
|
||||
我们来看`-4`存储的二进制结构。
|
||||
|
||||

|
||||
|
||||
现在符号位为`1`,代表负数,7个数值位要表达的二进制值是124,即128 - 4。
|
||||
|
||||

|
||||
|
||||
负数的编码方式称为二进制补码表示。这种表示方式看起来很奇怪,但它有几个优点。
|
||||
|
||||
首先,只需要对全部8个比特位(包括符号)做标准的二进制加法就可以完成 `-1 + -4` 的操作,忽略加法过程产生的超过8个比特位表达的任何信息。
|
||||
|
||||

|
||||
|
||||
第二,由于使用二进制补码表示,我们可以和正数一样对负数进行按位左移右移的,同样也是左移1位时乘于`2`,右移1位时除于`2`。要达到此目的,对有符整型的右移有一个特别的要求:
|
||||
|
||||
对有符整型按位右移时,使用符号位(正数为`0`,负数为`1`)填充空白位。
|
||||
|
||||

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

|
||||
|
||||
### 值的下溢出
|
||||
|
||||
数值也有可能因为太小而越界。举个例子:
|
||||
|
||||
`UInt8`的最小值是`0`(二进制为`00000000`)。使用`&-`进行溢出减1,就会得到二进制的`11111111`即十进制的`255`。
|
||||
|
||||

|
||||
|
||||
Swift代码是这样的:
|
||||
|
||||
```swift
|
||||
var willUnderflow = UInt8.min
|
||||
// willUnderflow 等于UInt8的最小值0
|
||||
willUnderflow = willUnderflow &- 1
|
||||
// 此时 willUnderflow 等于 255
|
||||
```
|
||||
|
||||
有符整型也有类似的下溢出,有符整型所有的减法也都是对包括在符号位在内的二进制数进行二进制减法的,这在 "按位左移/右移运算符" 一节提到过。最小的有符整数是`-128`,即二进制的`10000000`。用溢出减法减去去1后,变成了`01111111`,即UInt8所能承载的最大整数`127`。
|
||||
|
||||

|
||||
|
||||
来看看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>
|
||||
## 优先级和结合性
|
||||
|
||||
运算符的优先级使得一些运算符优先于其他运算符,高优先级的运算符会先被计算。
|
||||
|
||||
结合性定义相同优先级的运算符在一起时是怎么组合或关联的,是和左边的一组呢,还是和右边的一组。意思就是,到底是和左边的表达式结合呢,还是和右边的表达式结合?
|
||||
|
||||
在混合表达式中,运算符的优先级和结合性是非常重要的。举个例子,为什么下列表达式的结果为`4`?
|
||||
|
||||
```swift
|
||||
2 + 3 * 4 % 5
|
||||
// 结果是 4
|
||||
```
|
||||
|
||||
如果严格地从左计算到右,计算过程会是这样:
|
||||
|
||||
- 2 plus 3 equals 5;
|
||||
- 2 + 3 = 5
|
||||
- 5 times 4 equals 20;
|
||||
- 5 * 4 = 20
|
||||
- 20 remainder 5 equals 0
|
||||
- 20 / 5 = 4 余 0
|
||||
|
||||
但是正确答案是`4`而不是`0`。优先级高的运算符要先计算,在Swift和C语言中,都是先乘除后加减的。所以,执行完乘法和求余运算才能执行加减运算。
|
||||
|
||||
乘法和求余拥有相同的优先级,在运算过程中,我们还需要结合性,乘法和求余运算都是左结合的。这相当于在表达式中有隐藏的括号让运算从左开始。
|
||||
|
||||
```swift
|
||||
2 + ((3 * 4) % 5)
|
||||
```
|
||||
|
||||
(3 * 4) is 12, so this is equivalent to:
|
||||
3 * 4 = 12,所以这相当于:
|
||||
|
||||
|
||||
```swift
|
||||
2 + (12 % 5)
|
||||
```
|
||||
|
||||
(12 % 5) is 2, so this is equivalent to:
|
||||
12 % 5 = 2,所这又相当于
|
||||
|
||||
```swift
|
||||
2 + 2
|
||||
```
|
||||
|
||||
计算结果为 4。
|
||||
|
||||
查阅Swift运算符的优先级和结合性的完整列表,请看[表达式](../chapter3/04_Expressions.html)。
|
||||
|
||||
> 注意:
|
||||
>
|
||||
Swift的运算符较C语言和Objective-C来得更简单和保守,这意味着跟基于C的语言可能不一样。所以,在移植已有代码到Swift时,注意去确保代码按你想的那样去执行。
|
||||
|
||||
<a name="operator_functions"></a>
|
||||
## 运算符函数
|
||||
|
||||
让已有的运算符也可以对自定义的类和结构进行运算,这称为运算符重载。
|
||||
|
||||
这个例子展示了如何用`+`让一个自定义的结构做加法。算术运算符`+`是一个两目运算符,因为它有两个操作数,而且它必须出现在两个操作数之间。
|
||||
|
||||
例子中定义了一个名为`Vector2D`的二维坐标向量 `(x,y)` 的结构,然后定义了让两个`Vector2D`的对象相加的运算符函数。
|
||||
|
||||
```swift
|
||||
struct Vector2D {
|
||||
var x = 0.0, y = 0.0
|
||||
}
|
||||
@infix func + (left: Vector2D, right: Vector2D) -> Vector2D {
|
||||
return Vector2D(x: left.x + right.x, y: left.y + right.y)
|
||||
}
|
||||
```
|
||||
|
||||
该运算符函数定义了一个全局的`+`函数,这个函数需要两个`Vector2D`类型的参数,返回值也是`Vector2D`类型。需要定义和实现一个中置运算的时候,在关键字`func`之前写上属性 `@infix` 就可以了。
|
||||
|
||||
|
||||
在这个代码实现中,参数被命名为了`left`和`right`,代表`+`左边和右边的两个`Vector2D`对象。函数返回了一个新的`Vector2D`的对象,这个对象的`x`和`y`分别等于两个参数对象的`x`和`y`的和。
|
||||
|
||||
这个函数是全局的,而不是`Vector2D`结构的成员方法,所以任意两个`Vector2D`对象都可以使用这个中置运算符。
|
||||
|
||||
```swift
|
||||
let vector = Vector2D(x: 3.0, y: 1.0)
|
||||
let anotherVector = Vector2D(x: 2.0, y: 4.0)
|
||||
let combinedVector = vector + anotherVector
|
||||
// combinedVector 是一个新的Vector2D, 值为 (5.0, 5.0)
|
||||
```
|
||||
|
||||
这个例子实现两个向量 `(3.0,1.0)` 和 `(2.0,4.0)` 相加,得到向量 `(5.0,5.0)` 的过程。如下图示:
|
||||
|
||||

|
||||
|
||||
### 前置和后置运算符
|
||||
|
||||
上个例子演示了一个双目中置运算符的自定义实现,同样我们也可以玩标准单目运算符的实现。单目运算符只有一个操作数,在操作数之前就是前置的,如`-a`; 在操作数之后就是后置的,如`i++`。
|
||||
|
||||
实现一个前置或后置运算符时,在定义该运算符的时候于关键字`func`之前标注 `@prefix` 或 `@postfix` 属性。
|
||||
|
||||
```swift
|
||||
@prefix func - (vector: Vector2D) -> Vector2D {
|
||||
return Vector2D(x: -vector.x, y: -vector.y)
|
||||
}
|
||||
```
|
||||
|
||||
这段代码为`Vector2D`类型提供了单目减运算`-a`,`@prefix`属性表明这是个前置运算符。
|
||||
|
||||
对于数值,单目减运算符可以把正数变负数,把负数变正数。对于`Vector2D`,单目减运算将其`x`和`y`都进进行单目减运算。
|
||||
|
||||
```swift
|
||||
let positive = Vector2D(x: 3.0, y: 4.0)
|
||||
let negative = -positive
|
||||
// negative 为 (-3.0, -4.0)
|
||||
let alsoPositive = -negative
|
||||
// alsoPositive 为 (3.0, 4.0)
|
||||
```
|
||||
|
||||
### 组合赋值运算符
|
||||
|
||||
组合赋值是其他运算符和赋值运算符一起执行的运算。如`+=`把加运算和赋值运算组合成一个操作。实现一个组合赋值符号需要使用`@assignment`属性,还需要把运算符的左参数设置成`inout`,因为这个参数会在运算符函数内直接修改它的值。
|
||||
|
||||
```swift
|
||||
@assignment 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)
|
||||
```
|
||||
|
||||
你可以将 `@assignment` 属性和 `@prefix` 或 `@postfix` 属性起来组合,实现一个`Vector2D`的前置运算符。
|
||||
|
||||
```swift
|
||||
@prefix @assignment func ++ (inout vector: Vector2D) -> Vector2D {
|
||||
vector += Vector2D(x: 1.0, y: 1.0)
|
||||
return vector
|
||||
}
|
||||
```
|
||||
|
||||
这个前置使用了已经定义好的高级加赋运算,将自己加上一个值为 `(1.0,1.0)` 的对象然后赋给自己,然后再将自己返回。
|
||||
|
||||
```swift
|
||||
var toIncrement = Vector2D(x: 3.0, y: 4.0)
|
||||
let afterIncrement = ++toIncrement
|
||||
// toIncrement 现在是 (4.0, 5.0)
|
||||
// afterIncrement 现在也是 (4.0, 5.0)
|
||||
```
|
||||
|
||||
>注意:
|
||||
>
|
||||
默认的赋值符是不可重载的。只有组合赋值符可以重载。三目条件运算符 `a?b:c` 也是不可重载。
|
||||
|
||||
### 比较运算符
|
||||
|
||||
Swift无所知道自定义类型是否相等或不等,因为等于或者不等于由你的代码说了算了。所以自定义的类和结构要使用比较符`==`或`!=`就需要重载。
|
||||
|
||||
定义相等运算符函数跟定义其他中置运算符雷同:
|
||||
|
||||
```swift
|
||||
@infix func == (left: Vector2D, right: Vector2D) -> Bool {
|
||||
return (left.x == right.x) && (left.y == right.y)
|
||||
}
|
||||
|
||||
@infix func != (left: Vector2D, right: Vector2D) -> Bool {
|
||||
return !(left == right)
|
||||
}
|
||||
```
|
||||
|
||||
上述代码实现了相等运算符`==`来判断两个`Vector2D`对象是否有相等的值,相等的概念就是它们有相同的`x`值和相同的`y`值,我们就用这个逻辑来实现。接着使用`==`的结果实现了不相等运算符`!=`。
|
||||
|
||||
现在我们可以使用这两个运算符来判断两个`Vector2D`对象是否相等。
|
||||
|
||||
```swift
|
||||
let twoThree = Vector2D(x: 2.0, y: 3.0)
|
||||
let anotherTwoThree = Vector2D(x: 2.0, y: 3.0)
|
||||
if twoThree == anotherTwoThree {
|
||||
println("这两个向量是相等的.")
|
||||
}
|
||||
// prints "这两个向量是相等的."
|
||||
```
|
||||
|
||||
### 自定义运算符
|
||||
|
||||
标准的运算符不够玩,那你可以声明一些个性的运算符,但个性的运算符只能使用这些字符 `/ = - + * % < >!& | ^。~`。
|
||||
|
||||
新的运算符声明需在全局域使用`operator`关键字声明,可以声明为前置,中置或后置的。
|
||||
|
||||
```swift
|
||||
operator prefix +++ {}
|
||||
```
|
||||
|
||||
|
||||
这段代码定义了一个新的前置运算符叫`+++`,此前Swift并不存在这个运算符。此处为了演示,我们让`+++`对`Vector2D`对象的操作定义为 `双自增` 这样一个独有的操作,这个操作使用了之前定义的加赋运算实现了自已加上自己然后返回的运算。
|
||||
|
||||
```swift
|
||||
@prefix @assignment func +++ (inout vector: Vector2D) -> Vector2D {
|
||||
vector += vector
|
||||
return vector
|
||||
}
|
||||
```
|
||||
|
||||
`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)
|
||||
```
|
||||
|
||||
### 自定义中置运算符的优先级和结合性
|
||||
|
||||
可以为自定义的中置运算符指定优先级和结合性。可以回头看看[优先级和结合性](#PrecedenceandAssociativity)解释这两个因素是如何影响多种中置运算符混合的表达式的计算的。
|
||||
|
||||
结合性(associativity)的值可取的值有`left`,`right`和`none`。左结合运算符跟其他优先级相同的左结合运算符写在一起时,会跟左边的操作数结合。同理,右结合运算符会跟右边的操作数结合。而非结合运算符不能跟其他相同优先级的运算符写在一起。
|
||||
|
||||
结合性(associativity)的值默认为`none`,优先级(precedence)默认为`100`。
|
||||
|
||||
以下例子定义了一个新的中置符`+-`,是左结合的`left`,优先级为`140`。
|
||||
|
||||
```swift
|
||||
operator infix +- { 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)
|
||||
```
|
||||
|
||||
这个运算符把两个向量的`x`相加,把向量的`y`相减。因为它实际是属于加减运算,所以让它保持了和加法一样的结合性和优先级(`left`和`140`)。查阅完整的Swift默认结合性和优先级的设置,请移步[表达式](../chapter3/04_Expressions.html);
|
||||
> 翻译:[xielingwang](https://github.com/xielingwang)
|
||||
> 校对:[numbbbbb](https://github.com/numbbbbb)
|
||||
|
||||
# 高级运算符
|
||||
-----------------
|
||||
|
||||
本页内容包括:
|
||||
|
||||
- [位运算符](#bitwise_operators)
|
||||
- [溢出运算符](#overflow_operators)
|
||||
- [优先级和结合性(Precedence and Associativity)](#precedence_and_associativity)
|
||||
- [运算符函数(Operator Functions)](#operator_functions)
|
||||
- [自定义运算符](#custom_operators)
|
||||
|
||||
除了[基本操作符](02_Basic_Operators.html)中所讲的运算符,Swift还有许多复杂的高级运算符,包括了C语和Objective-C中的位运算符和移位运算。
|
||||
|
||||
不同于C语言中的数值计算,Swift的数值计算默认是不可溢出的。溢出行为会被捕获并报告为错误。你是故意的?好吧,你可以使用Swift为你准备的另一套默认允许溢出的数值运算符,如可溢出加`&+`。所有允许溢出的运算符都是以`&`开始的。
|
||||
|
||||
自定义的结构,类和枚举,是否可以使用标准的运算符来定义操作?当然可以!在Swift中,你可以为你创建的所有类型定制运算符的操作。
|
||||
|
||||
可定制的运算符并不限于那些预设的运算符,自定义有个性的中置,前置,后置及赋值运算符,当然还有优先级和结合性。这些运算符的实现可以运用预设的运算符,也可以运用之前定制的运算符。
|
||||
|
||||
<a name="bitwise_operators"></a>
|
||||
## 位运算符
|
||||
|
||||
位操作符通常在诸如图像处理和创建设备驱动等底层开发中使用,使用它可以单独操作数据结构中原始数据的比特位。在使用一个自定义的协议进行通信的时候,运用位运算符来对原始数据进行编码和解码也是非常有效的。
|
||||
|
||||
Swift支持如下所有C语言的位运算符:
|
||||
|
||||
### 按位取反运算符
|
||||
|
||||
按位取反运算符`~`对一个操作数的每一位都取反。
|
||||
|
||||

|
||||
|
||||
这个运算符是前置的,所以请不加任何空格地写着操作数之前。
|
||||
|
||||
```swift
|
||||
let initialBits: UInt8 = 0b00001111
|
||||
let invertedBits = ~initialBits // 等于 0b11110000
|
||||
```
|
||||
|
||||
`UInt8`是8位无符整型,可以存储0~255之间的任意数。这个例子初始化一个整型为二进制值`00001111`(前4位为`0`,后4位为`1`),它的十进制值为`15`。
|
||||
|
||||
使用按位取反运算`~`对`initialBits`操作,然后赋值给`invertedBits`这个新常量。这个新常量的值等于所有位都取反的`initialBits`,即`1`变成`0`,`0`变成`1`,变成了`11110000`,十进制值为`240`。
|
||||
|
||||
### 按位与运算符
|
||||
|
||||
按位与运算符对两个数进行操作,然后返回一个新的数,这个数的每个位都需要两个输入数的同一位都为1时才为1。
|
||||
|
||||

|
||||
|
||||
以下代码,`firstSixBits`和`lastSixBits`中间4个位都为1。对它俩进行按位与运算后,就得到了`00111100`,即十进制的`60`。
|
||||
|
||||
```swift
|
||||
let firstSixBits: UInt8 = 0b11111100
|
||||
let lastSixBits: UInt8 = 0b00111111
|
||||
let middleFourBits = firstSixBits & lastSixBits // 等于 00111100
|
||||
```
|
||||
|
||||
### 按位或运算
|
||||
|
||||
按位或运算符`|`比较两个数,然后返回一个新的数,这个数的每一位设置1的条件是两个输入数的同一位都不为0(即任意一个为1,或都为1)。
|
||||
|
||||

|
||||
|
||||
如下代码,`someBits`和`moreBits`在不同位上有`1`。按位或运行的结果是`11111110`,即十进制的`254`。
|
||||
|
||||
```swift
|
||||
let someBits: UInt8 = 0b10110010
|
||||
let moreBits: UInt8 = 0b01011110
|
||||
let combinedbits = someBits | moreBits // 等于 11111110
|
||||
```
|
||||
|
||||
### 按位异或运算符
|
||||
|
||||
按位异或运算符`^`比较两个数,然后返回一个数,这个数的每个位设为`1`的条件是两个输入数的同一位不同,如果相同就设为`0`。
|
||||
|
||||

|
||||
|
||||
以下代码,`firstBits`和`otherBits`都有一个`1`跟另一个数不同的。所以按位异或的结果是把它这些位置为`1`,其他都置为`0`。
|
||||
|
||||
```swift
|
||||
let firstBits: UInt8 = 0b00010100
|
||||
let otherBits: UInt8 = 0b00000101
|
||||
let outputBits = firstBits ^ otherBits // 等于 00010001
|
||||
```
|
||||
|
||||
### 按位左移/右移运算符
|
||||
|
||||
左移运算符`<<`和右移运算符`>>`会把一个数的所有比特位按以下定义的规则向左或向右移动指定位数。
|
||||
|
||||
按位左移和按位右移的效果相当把一个整数乘于或除于一个因子为`2`的整数。向左移动一个整型的比特位相当于把这个数乘于`2`,向右移一位就是除于`2`。
|
||||
|
||||
#### 无符整型的移位操作
|
||||
|
||||
对无符整型的移位的效果如下:
|
||||
|
||||
已经存在的比特位向左或向右移动指定的位数。被移出整型存储边界的的位数直接抛弃,移动留下的空白位用零`0`来填充。这种方法称为逻辑移位。
|
||||
|
||||
以下这张把展示了 `11111111 << 1`(`11111111`向左移1位),和 `11111111 >> 1`(`11111111`向右移1位)。蓝色的是被移位的,灰色是被抛弃的,橙色的`0`是被填充进来的。
|
||||
|
||||

|
||||
|
||||
```swift
|
||||
let shiftBits: UInt8 = 4 // 即二进制的00000100
|
||||
shiftBits << 1 // 00001000
|
||||
shiftBits << 2 // 00010000
|
||||
shiftBits << 5 // 10000000
|
||||
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)三个部分。
|
||||
|
||||
对`0xCC6699`和`0xFF0000`进行按位与`&`操作就可以得到红色部分。`0xFF0000`中的`0`了遮盖了`OxCC6699`的第二和第三个字节,这样`6699`被忽略了,只留下`0xCC0000`。
|
||||
|
||||
然后,按向右移动16位,即 `>> 16`。十六进制中每两个字符是8比特位,所以移动16位的结果是把`0xCC0000`变成`0x0000CC`。这和`0xCC`是相等的,都是十进制的`204`。
|
||||
|
||||
同样的,绿色部分来自于`0xCC6699`和`0x00FF00`的按位操作得到`0x006600`。然后向右移动8們,得到`0x66`,即十进制的`102`。
|
||||
|
||||
最后,蓝色部分对`0xCC6699`和`0x0000FF`进行按位与运算,得到`0x000099`,无需向右移位了,所以结果就是`0x99`,即十进制的`153`。
|
||||
|
||||
#### 有符整型的移位操作
|
||||
|
||||
有符整型的移位操作相对复杂得多,因为正负号也是用二进制位表示的。(这里举的例子虽然都是8位的,但它的原理是通用的。)
|
||||
|
||||
有符整型通过第1个比特位(称为符号位)来表达这个整数是正数还是负数。`0`代表正数,`1`代表负数。
|
||||
|
||||
其余的比特位(称为数值位)存储其实值。有符正整数和无符正整数在计算机里的存储结果是一样的,下来我们来看`+4`内部的二进制结构。
|
||||
|
||||

|
||||
|
||||
符号位为`0`,代表正数,另外7比特位二进制表示的实际值就刚好是`4`。
|
||||
|
||||
负数呢,跟正数不同。负数存储的是2的n次方减去它的绝对值,n为数值位的位数。一个8比特的数有7个数值位,所以是2的7次方,即128。
|
||||
|
||||
我们来看`-4`存储的二进制结构。
|
||||
|
||||

|
||||
|
||||
现在符号位为`1`,代表负数,7个数值位要表达的二进制值是124,即128 - 4。
|
||||
|
||||

|
||||
|
||||
负数的编码方式称为二进制补码表示。这种表示方式看起来很奇怪,但它有几个优点。
|
||||
|
||||
首先,只需要对全部8个比特位(包括符号)做标准的二进制加法就可以完成 `-1 + -4` 的操作,忽略加法过程产生的超过8个比特位表达的任何信息。
|
||||
|
||||

|
||||
|
||||
第二,由于使用二进制补码表示,我们可以和正数一样对负数进行按位左移右移的,同样也是左移1位时乘于`2`,右移1位时除于`2`。要达到此目的,对有符整型的右移有一个特别的要求:
|
||||
|
||||
对有符整型按位右移时,使用符号位(正数为`0`,负数为`1`)填充空白位。
|
||||
|
||||

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

|
||||
|
||||
### 值的下溢出
|
||||
|
||||
数值也有可能因为太小而越界。举个例子:
|
||||
|
||||
`UInt8`的最小值是`0`(二进制为`00000000`)。使用`&-`进行溢出减1,就会得到二进制的`11111111`即十进制的`255`。
|
||||
|
||||

|
||||
|
||||
Swift代码是这样的:
|
||||
|
||||
```swift
|
||||
var willUnderflow = UInt8.min
|
||||
// willUnderflow 等于UInt8的最小值0
|
||||
willUnderflow = willUnderflow &- 1
|
||||
// 此时 willUnderflow 等于 255
|
||||
```
|
||||
|
||||
有符整型也有类似的下溢出,有符整型所有的减法也都是对包括在符号位在内的二进制数进行二进制减法的,这在 "按位左移/右移运算符" 一节提到过。最小的有符整数是`-128`,即二进制的`10000000`。用溢出减法减去去1后,变成了`01111111`,即UInt8所能承载的最大整数`127`。
|
||||
|
||||

|
||||
|
||||
来看看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>
|
||||
## 优先级和结合性
|
||||
|
||||
运算符的优先级使得一些运算符优先于其他运算符,高优先级的运算符会先被计算。
|
||||
|
||||
结合性定义相同优先级的运算符在一起时是怎么组合或关联的,是和左边的一组呢,还是和右边的一组。意思就是,到底是和左边的表达式结合呢,还是和右边的表达式结合?
|
||||
|
||||
在混合表达式中,运算符的优先级和结合性是非常重要的。举个例子,为什么下列表达式的结果为`4`?
|
||||
|
||||
```swift
|
||||
2 + 3 * 4 % 5
|
||||
// 结果是 4
|
||||
```
|
||||
|
||||
如果严格地从左计算到右,计算过程会是这样:
|
||||
|
||||
- 2 plus 3 equals 5;
|
||||
- 2 + 3 = 5
|
||||
- 5 times 4 equals 20;
|
||||
- 5 * 4 = 20
|
||||
- 20 remainder 5 equals 0
|
||||
- 20 / 5 = 4 余 0
|
||||
|
||||
但是正确答案是`4`而不是`0`。优先级高的运算符要先计算,在Swift和C语言中,都是先乘除后加减的。所以,执行完乘法和求余运算才能执行加减运算。
|
||||
|
||||
乘法和求余拥有相同的优先级,在运算过程中,我们还需要结合性,乘法和求余运算都是左结合的。这相当于在表达式中有隐藏的括号让运算从左开始。
|
||||
|
||||
```swift
|
||||
2 + ((3 * 4) % 5)
|
||||
```
|
||||
|
||||
(3 * 4) is 12, so this is equivalent to:
|
||||
3 * 4 = 12,所以这相当于:
|
||||
|
||||
|
||||
```swift
|
||||
2 + (12 % 5)
|
||||
```
|
||||
|
||||
(12 % 5) is 2, so this is equivalent to:
|
||||
12 % 5 = 2,所这又相当于
|
||||
|
||||
```swift
|
||||
2 + 2
|
||||
```
|
||||
|
||||
计算结果为 4。
|
||||
|
||||
查阅Swift运算符的优先级和结合性的完整列表,请看[表达式](../chapter3/04_Expressions.html)。
|
||||
|
||||
> 注意:
|
||||
Swift的运算符较C语言和Objective-C来得更简单和保守,这意味着跟基于C的语言可能不一样。所以,在移植已有代码到Swift时,注意去确保代码按你想的那样去执行。
|
||||
|
||||
<a name="operator_functions"></a>
|
||||
## 运算符函数
|
||||
|
||||
让已有的运算符也可以对自定义的类和结构进行运算,这称为运算符重载。
|
||||
|
||||
这个例子展示了如何用`+`让一个自定义的结构做加法。算术运算符`+`是一个两目运算符,因为它有两个操作数,而且它必须出现在两个操作数之间。
|
||||
|
||||
例子中定义了一个名为`Vector2D`的二维坐标向量 `(x,y)` 的结构,然后定义了让两个`Vector2D`的对象相加的运算符函数。
|
||||
|
||||
```swift
|
||||
struct Vector2D {
|
||||
var x = 0.0, y = 0.0
|
||||
}
|
||||
@infix func + (left: Vector2D, right: Vector2D) -> Vector2D {
|
||||
return Vector2D(x: left.x + right.x, y: left.y + right.y)
|
||||
}
|
||||
```
|
||||
|
||||
该运算符函数定义了一个全局的`+`函数,这个函数需要两个`Vector2D`类型的参数,返回值也是`Vector2D`类型。需要定义和实现一个中置运算的时候,在关键字`func`之前写上属性 `@infix` 就可以了。
|
||||
|
||||
在这个代码实现中,参数被命名为了`left`和`right`,代表`+`左边和右边的两个`Vector2D`对象。函数返回了一个新的`Vector2D`的对象,这个对象的`x`和`y`分别等于两个参数对象的`x`和`y`的和。
|
||||
|
||||
这个函数是全局的,而不是`Vector2D`结构的成员方法,所以任意两个`Vector2D`对象都可以使用这个中置运算符。
|
||||
|
||||
```swift
|
||||
let vector = Vector2D(x: 3.0, y: 1.0)
|
||||
let anotherVector = Vector2D(x: 2.0, y: 4.0)
|
||||
let combinedVector = vector + anotherVector
|
||||
// combinedVector 是一个新的Vector2D, 值为 (5.0, 5.0)
|
||||
```
|
||||
|
||||
这个例子实现两个向量 `(3.0,1.0)` 和 `(2.0,4.0)` 相加,得到向量 `(5.0,5.0)` 的过程。如下图示:
|
||||
|
||||

|
||||
|
||||
### 前置和后置运算符
|
||||
|
||||
上个例子演示了一个双目中置运算符的自定义实现,同样我们也可以玩标准单目运算符的实现。单目运算符只有一个操作数,在操作数之前就是前置的,如`-a`; 在操作数之后就是后置的,如`i++`。
|
||||
|
||||
实现一个前置或后置运算符时,在定义该运算符的时候于关键字`func`之前标注 `@prefix` 或 `@postfix` 属性。
|
||||
|
||||
```swift
|
||||
@prefix func - (vector: Vector2D) -> Vector2D {
|
||||
return Vector2D(x: -vector.x, y: -vector.y)
|
||||
}
|
||||
```
|
||||
|
||||
这段代码为`Vector2D`类型提供了单目减运算`-a`,`@prefix`属性表明这是个前置运算符。
|
||||
|
||||
对于数值,单目减运算符可以把正数变负数,把负数变正数。对于`Vector2D`,单目减运算将其`x`和`y`都进进行单目减运算。
|
||||
|
||||
```swift
|
||||
let positive = Vector2D(x: 3.0, y: 4.0)
|
||||
let negative = -positive
|
||||
// negative 为 (-3.0, -4.0)
|
||||
let alsoPositive = -negative
|
||||
// alsoPositive 为 (3.0, 4.0)
|
||||
```
|
||||
|
||||
### 组合赋值运算符
|
||||
|
||||
组合赋值是其他运算符和赋值运算符一起执行的运算。如`+=`把加运算和赋值运算组合成一个操作。实现一个组合赋值符号需要使用`@assignment`属性,还需要把运算符的左参数设置成`inout`,因为这个参数会在运算符函数内直接修改它的值。
|
||||
|
||||
```swift
|
||||
@assignment 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)
|
||||
```
|
||||
|
||||
你可以将 `@assignment` 属性和 `@prefix` 或 `@postfix` 属性起来组合,实现一个`Vector2D`的前置运算符。
|
||||
|
||||
```swift
|
||||
@prefix @assignment func ++ (inout vector: Vector2D) -> Vector2D {
|
||||
vector += Vector2D(x: 1.0, y: 1.0)
|
||||
return vector
|
||||
}
|
||||
```
|
||||
|
||||
这个前置使用了已经定义好的高级加赋运算,将自己加上一个值为 `(1.0,1.0)` 的对象然后赋给自己,然后再将自己返回。
|
||||
|
||||
```swift
|
||||
var toIncrement = Vector2D(x: 3.0, y: 4.0)
|
||||
let afterIncrement = ++toIncrement
|
||||
// toIncrement 现在是 (4.0, 5.0)
|
||||
// afterIncrement 现在也是 (4.0, 5.0)
|
||||
```
|
||||
|
||||
>注意:
|
||||
默认的赋值符是不可重载的。只有组合赋值符可以重载。三目条件运算符 `a?b:c` 也是不可重载。
|
||||
|
||||
### 比较运算符
|
||||
|
||||
Swift无所知道自定义类型是否相等或不等,因为等于或者不等于由你的代码说了算了。所以自定义的类和结构要使用比较符`==`或`!=`就需要重载。
|
||||
|
||||
定义相等运算符函数跟定义其他中置运算符雷同:
|
||||
|
||||
```swift
|
||||
@infix func == (left: Vector2D, right: Vector2D) -> Bool {
|
||||
return (left.x == right.x) && (left.y == right.y)
|
||||
}
|
||||
|
||||
@infix func != (left: Vector2D, right: Vector2D) -> Bool {
|
||||
return !(left == right)
|
||||
}
|
||||
```
|
||||
|
||||
上述代码实现了相等运算符`==`来判断两个`Vector2D`对象是否有相等的值,相等的概念就是它们有相同的`x`值和相同的`y`值,我们就用这个逻辑来实现。接着使用`==`的结果实现了不相等运算符`!=`。
|
||||
|
||||
现在我们可以使用这两个运算符来判断两个`Vector2D`对象是否相等。
|
||||
|
||||
```swift
|
||||
let twoThree = Vector2D(x: 2.0, y: 3.0)
|
||||
let anotherTwoThree = Vector2D(x: 2.0, y: 3.0)
|
||||
if twoThree == anotherTwoThree {
|
||||
println("这两个向量是相等的.")
|
||||
}
|
||||
// prints "这两个向量是相等的."
|
||||
```
|
||||
|
||||
### 自定义运算符
|
||||
|
||||
标准的运算符不够玩,那你可以声明一些个性的运算符,但个性的运算符只能使用这些字符 `/ = - + * % < >!& | ^。~`。
|
||||
|
||||
新的运算符声明需在全局域使用`operator`关键字声明,可以声明为前置,中置或后置的。
|
||||
|
||||
```swift
|
||||
operator prefix +++ {}
|
||||
```
|
||||
|
||||
|
||||
这段代码定义了一个新的前置运算符叫`+++`,此前Swift并不存在这个运算符。此处为了演示,我们让`+++`对`Vector2D`对象的操作定义为 `双自增` 这样一个独有的操作,这个操作使用了之前定义的加赋运算实现了自已加上自己然后返回的运算。
|
||||
|
||||
```swift
|
||||
@prefix @assignment func +++ (inout vector: Vector2D) -> Vector2D {
|
||||
vector += vector
|
||||
return vector
|
||||
}
|
||||
```
|
||||
|
||||
`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)
|
||||
```
|
||||
|
||||
### 自定义中置运算符的优先级和结合性
|
||||
|
||||
可以为自定义的中置运算符指定优先级和结合性。可以回头看看[优先级和结合性](#PrecedenceandAssociativity)解释这两个因素是如何影响多种中置运算符混合的表达式的计算的。
|
||||
|
||||
结合性(associativity)的值可取的值有`left`,`right`和`none`。左结合运算符跟其他优先级相同的左结合运算符写在一起时,会跟左边的操作数结合。同理,右结合运算符会跟右边的操作数结合。而非结合运算符不能跟其他相同优先级的运算符写在一起。
|
||||
|
||||
结合性(associativity)的值默认为`none`,优先级(precedence)默认为`100`。
|
||||
|
||||
以下例子定义了一个新的中置符`+-`,是左结合的`left`,优先级为`140`。
|
||||
|
||||
```swift
|
||||
operator infix +- { 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)
|
||||
```
|
||||
|
||||
这个运算符把两个向量的`x`相加,把向量的`y`相减。因为他实际是属于加减运算,所以让它保持了和加法一样的结合性和优先级(`left`和`140`)。查阅完整的Swift默认结合性和优先级的设置,请移步[表达式](../chapter3/04_Expressions.html);
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
# Swift 教程
|
||||
|
||||
本章介绍了 Swift 的各种特性及其使用方法,是全书的核心部分。
|
||||
# Swift 教程
|
||||
|
||||
本章介绍了 Swift 的各种特性及其使用方法,是全书的核心部分。
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
> 翻译:ChildhoodAndy
|
||||
|
||||
> 校对:numbbbbb
|
||||
> 翻译:[dabing1022](https://github.com/dabing1022)
|
||||
> 校对:[numbbbbb](https://github.com/numbbbbb)
|
||||
|
||||
|
||||
# 关于语言附注
|
||||
@ -20,24 +19,20 @@ Swift语言相对小点,这是由于在Swift代码中几乎无处不在的许
|
||||
|
||||
用来描述Swift编程语言形式语法的记法遵循下面几个约定:
|
||||
|
||||
- 箭头(→)用来标记语法产式,可以被理解为“可以包含”。
|
||||
-](https://github.com/numbbbbb)箭头(→)用来标记语法产式,可以被理](https://github.com/numbbbbb)解为“可以包含”。
|
||||
- 句法范畴由*斜体*文字表示,并出现在一个语法产式规则两侧。
|
||||
- 义词和标点符号由粗体固定宽度的文本显示和只出现在一个语法产式规则的右边。
|
||||
- 选择性的语法产式由竖线(|)分隔。当可选用的语法产式太多时,为了阅读方便,它们将被拆分为多行语法产式规则。
|
||||
- 在少数情况下,常规字体文字用来描述语法产式规则的右边。
|
||||
- 可选的句法范畴和文字用尾标`opt`来标记。
|
||||
|
||||
|
||||
举个例子,getter-setter的语法块的定义如下:
|
||||
|
||||
> GRAMMAR OF A GETTER-SETTER BLOCK
|
||||
|
||||
> 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子句构成,用大括号括起来。上述的文法产生等价于下面的两个产生,明确阐明如何二中择一:
|
||||
|
||||
> 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* }
|
||||
|
||||
> 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,313 +1,238 @@
|
||||
> 翻译:superkam
|
||||
|
||||
> 校对: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>... 这些命名在闭包作用域内是合法的标识符。
|
||||
|
||||
> 标识符语法
|
||||
>
|
||||
> *identifier* → [identifier-head](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/identifier-head) [identifier-characters](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/identifier-characters) *opt*
|
||||
>
|
||||
> *identifier* → \` [identifier-head](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/identifier-head) [identifier-characters](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/identifier-characters) *opt* \`
|
||||
>
|
||||
> *identifier* → [implicit-parameter-name](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/implicit-parameter-name)
|
||||
>
|
||||
> *identifier-list* → [identifier](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/identifier) | [identifier](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/identifier) , [identifier-list](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/identifier-list)
|
||||
>
|
||||
> *identifier-head* → A 到 Z 大写或小写字母
|
||||
>
|
||||
> *identifier-head* → U+00A8, U+00AA, U+00AD, U+00AF, U+00B2–U+00B5, 或 U+00B7–U+00BA
|
||||
>
|
||||
> *identifier-head* → U+00BC–U+00BE, U+00C0–U+00D6, U+00D8–U+00F6, 或 U+00F8–U+00FF
|
||||
>
|
||||
> *identifier-head* → U+0100–U+02FF, U+0370–U+167F, U+1681–U+180D, 或 U+180F–U+1DBF
|
||||
>
|
||||
> *identifier-head* → U+1E00–U+1FFF
|
||||
>
|
||||
> *identifier-head* → U+200B–U+200D, U+202A–U+202E, U+203F–U+2040, U+2054, 或 U+2060–U+206F
|
||||
>
|
||||
> *identifier-head* → U+2070–U+20CF, U+2100–U+218F, U+2460–U+24FF, 或 U+2776–U+2793
|
||||
>
|
||||
> *identifier-head* → U+2C00–U+2DFF 或 U+2E80–U+2FFF
|
||||
>
|
||||
> *identifier-head* → U+3004–U+3007, U+3021–U+302F, U+3031–U+303F, 或 U+3040–U+D7FF
|
||||
>
|
||||
> *identifier-head* → U+F900–U+FD3D, U+FD40–U+FDCF, U+FDF0–U+FE1F, 或 U+FE30–U+FE44
|
||||
>
|
||||
> *identifier-head* → U+FE47–U+FFFD
|
||||
>
|
||||
> *identifier-head* → U+10000–U+1FFFD, U+20000–U+2FFFD, U+30000–U+3FFFD, 或 U+40000–U+4FFFD
|
||||
>
|
||||
> *identifier-head* → U+50000–U+5FFFD, U+60000–U+6FFFD, U+70000–U+7FFFD, 或 U+80000–U+8FFFD
|
||||
>
|
||||
> *identifier-head* → U+90000–U+9FFFD, U+A0000–U+AFFFD, U+B0000–U+BFFFD, 或 U+C0000–U+CFFFD
|
||||
>
|
||||
> *identifier-head* → U+D0000–U+DFFFD 或 U+E0000–U+EFFFD
|
||||
>
|
||||
> *identifier-character* → 数字 0 到 9
|
||||
>
|
||||
> *identifier-character* → U+0300–U+036F, U+1DC0–U+1DFF, U+20D0–U+20FF, or U+FE20–U+FE2F
|
||||
>
|
||||
> *identifier-character* → [identifier-head](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/identifier-head)
|
||||
>
|
||||
> *identifier-characters* → [identifier-character](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/identifier-character) [identifier-characters](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/identifier-characters) *opt*
|
||||
>
|
||||
> *implicit-parameter-name* → **$** [decimal-digits](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/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!" // 文本型字面量
|
||||
```
|
||||
|
||||
> 字面量语法
|
||||
>
|
||||
> *literal* → [integer-literal](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/integer-literal) | [floating-point-literal](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/floating-point-literal) | [string-literal](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/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)。
|
||||
|
||||
> 整型字面量语法
|
||||
>
|
||||
> *integer-literal* → [binary-literal](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/binary-literal)
|
||||
>
|
||||
> *integer-literal* → [octal-literal](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/octal-literal)
|
||||
>
|
||||
> *integer-literal* → [decimal-literal](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/decimal-literal)
|
||||
>
|
||||
> *integer-literal* → [hexadecimal-literal](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-literal)
|
||||
>
|
||||
> *binary-literal* → **0b** [binary-digit](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/binary-digit) [binary-literal-characters](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/binary-literal-characters) *opt*
|
||||
>
|
||||
> *binary-digit* → 数字 0 或 1
|
||||
>
|
||||
> *binary-literal-character* → [binary-digit](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/binary-digit) | _
|
||||
>
|
||||
> *binary-literal-characters* → [binary-literal-character](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/binary-literal-character) [binary-literal-characters](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/binary-literal-characters) *opt*
|
||||
>
|
||||
> *octal-literal* → **0o** [octal-digit](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/octal-digit) [octal-literal-characters](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/octal-literal-characters) *opt*
|
||||
>
|
||||
> *octal-digit* → 数字 0 至 7
|
||||
>
|
||||
> *octal-literal-character* → [octal-digit](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/octal-digit) | _
|
||||
>
|
||||
> *octal-literal-characters* → [octal-literal-character](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/octal-literal-character) [octal-literal-characters](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/octal-literal-characters) *opt*
|
||||
>
|
||||
> *decimal-literal* → [decimal-digit](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/decimal-digit) [decimal-literal-characters](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/decimal-literal-characters) *opt*
|
||||
>
|
||||
> *decimal-digit* → 数字 0 至 9
|
||||
>
|
||||
> *decimal-digits* → [decimal-digit](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/decimal-digit) [decimal-digits](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/decimal-digits) *opt*
|
||||
>
|
||||
> *decimal-literal-character* → [decimal-digit](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/decimal-digit) | _
|
||||
>
|
||||
> *decimal-literal-characters* → [decimal-literal-character](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/decimal-literal-character) [decimal-literal-characters](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/decimal-literal-characters) *opt*
|
||||
>
|
||||
> *hexadecimal-literal* → **0x** [hexadecimal-digit](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-digit) [hexadecimal-literal-characters](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-literal-characters) *opt*
|
||||
>
|
||||
> *hexadecimal-digit* → 数字 0 到 9, a 到 f, 或 A 到 F
|
||||
>
|
||||
> *hexadecimal-literal-character* → [hexadecimal-digit](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-digit) | _
|
||||
>
|
||||
> *hexadecimal-literal-characters* → [hexadecimal-literal-character](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-literal-character) [hexadecimal-literal-characters](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-literal-characters) *opt*
|
||||
|
||||
### 浮点型字面量
|
||||
|
||||
浮点型字面量(*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位浮点数。
|
||||
|
||||
> 浮点型字面量语法
|
||||
>
|
||||
> *floating-point-literal* → [decimal-literal](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/decimal-literal) [decimal-fraction](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/decimal-fraction) *opt* [decimal-exponent](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/decimal-exponent) *opt*
|
||||
>
|
||||
> *floating-point-literal* → [hexadecimal-literal](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-literal) [hexadecimal-fraction](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-fraction) *opt* [hexadecimal-exponent](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-exponent)
|
||||
>
|
||||
> *decimal-fraction* → . [decimal-literal](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/decimal-literal)
|
||||
>
|
||||
> *decimal-exponent* → [floating-point-e](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/floating-point-e) [sign](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/sign) *opt* [decimal-literal](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/decimal-literal)
|
||||
>
|
||||
> *hexadecimal-fraction* → . [hexadecimal-literal](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-literal) *opt*
|
||||
>
|
||||
> *hexadecimal-exponent* → [floating-point-p](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/floating-point-p) [sign](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/sign) *opt* [hexadecimal-literal](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-literal)
|
||||
>
|
||||
> *floating-point-e* → **e** | **E**
|
||||
>
|
||||
> *floating-point-p* → **p** | **P**
|
||||
>
|
||||
> *sign* → **+** | **-**
|
||||
|
||||
|
||||
|
||||
### 文本型字面量
|
||||
|
||||
文本型字面量(*string literal*)由双引号中的字符串组成,形式如下:
|
||||
|
||||
"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)。
|
||||
|
||||
> 文本型字面量语法
|
||||
>
|
||||
> *string-literal* → **"** [quoted-text](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/quoted-text) **"**
|
||||
>
|
||||
> *quoted-text* → [quoted-text-item](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/quoted-text-item) [quoted-text](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/quoted-text) *opt*
|
||||
>
|
||||
> *quoted-text-item* → [escaped-character](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/escaped-character)
|
||||
>
|
||||
> *quoted-text-item* → **\(** [expression](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression) **)**
|
||||
>
|
||||
> *quoted-text-item* → 除 `"`、`\`、`U+000A` 或 `U+000D` 以外的任何 Unicode 扩展字符集
|
||||
>
|
||||
> *escaped-character* → **\0** | **\\** | **\t** | **\n** | **\r** | **\"** | **\'**
|
||||
>
|
||||
> *escaped-character* → **\x** [hexadecimal-digit](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-digit) [hexadecimal-digit](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-digit)
|
||||
>
|
||||
> *escaped-character* → **\u** [hexadecimal-digit](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-digit) [hexadecimal-digit](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-digit) [hexadecimal-digit](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-digit) [hexadecimal-digit](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-digit)
|
||||
>
|
||||
> *escaped-character* → **\U** [hexadecimal-digit](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-digit) [hexadecimal-digit](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-digit) [hexadecimal-digit](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-digit) [hexadecimal-digit](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-digit) [hexadecimal-digit](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-digit) [hexadecimal-digit](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-digit) [hexadecimal-digit](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/hexadecimal-digit) [hexadecimal-digit](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/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)。
|
||||
|
||||
> 运算符语法
|
||||
>
|
||||
> *operator* → [operator-character](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/operator-character) [operator](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/operator) *opt*
|
||||
>
|
||||
> *operator-character* → **/** | **=** | **-** | **+** | **!** | ***** | **%** | **<** | **>** | **&** | **|** | **^** | **~** | **.**
|
||||
>
|
||||
> *binary-operator* → [operator](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/operator)
|
||||
>
|
||||
> *prefix-operator* → [operator](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/operator)
|
||||
>
|
||||
> *postfix-operator* → [operator](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/operator)
|
||||
> 翻译:[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)
|
||||
|
||||
@ -1,286 +1,300 @@
|
||||
> 翻译:lyuka
|
||||
|
||||
> 校对:numbbbbb
|
||||
|
||||
# 类型(Types)
|
||||
-----------------
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [类型注解(Type Annotation)](#type_annotation)
|
||||
- [类型标识符(Type Identifier)](#type_identifier)
|
||||
- [元组类型(Tuple Type)](#tuple_type)
|
||||
- [函数类型(Function Type)](#function_type)
|
||||
- [数组类型(Array Type)](#array_type)
|
||||
- [可选类型(Optional Type)](#optional_type)
|
||||
- [隐式解析可选类型(Implicitly Unwrapped Optional Type)](#implicitly_unwrapped_optional_type)
|
||||
- [协议合成类型(Protocol Composition Type)](#protocol_composition_type)
|
||||
- [元类型(Metatype Type)](#metatype_type)
|
||||
- [类型继承子句(Type Inheritance Clause)](#type_inheritance_clause)
|
||||
- [类型推断(Type Inference)](#type_inference)
|
||||
|
||||
Swift 语言存在两种类型:命名型类型和复合型类型。*命名型类型*是指定义时可以给定名字的类型。命名型类型包括类、结构体、枚举和协议。比如,一个用户定义的类`MyClass`的实例拥有类型`MyClass`。除了用户定义的命名型类型,Swift 标准库也定义了很多常用的命名型类型,包括那些表示数组、字典和可选值的类型。
|
||||
|
||||
那些通常被其它语言认为是基本或初级的数据型类型(Data types)——比如表示数字、字符和字符串——实际上就是命名型类型,Swift 标准库是使用结构体定义和实现它们的。因为它们是命名型类型,因此你可以按照“扩展和扩展声明”章节里讨论的那样,声明一个扩展来增加它们的行为以适应你程序的需求。
|
||||
|
||||
*复合型类型*是没有名字的类型,它由 Swift 本身定义。Swift 存在两种复合型类型:函数类型和元组类型。一个复合型类型可以包含命名型类型和其它复合型类型。例如,元组类型`(Int, (Int, Int))`包含两个元素:第一个是命名型类型`Int`,第二个是另一个复合型类型`(Int, Int)`.
|
||||
|
||||
本节讨论 Swift 语言本身定义的类型,并描述 Swift 中的类型推断行为。
|
||||
|
||||
>类型的语法:
|
||||
*type* → *array-type* | *function-type* | *type-identifier* | *tuple-type* | *optional-type* | *implicitly-unwrapped-optional-type* | protocol-composition-type | metatype-type
|
||||
|
||||
<a name="type_annotation"></a>
|
||||
##类型注解
|
||||
类型注解显式地指定一个变量或表达式的值。类型注解始于冒号`:`终于类型,比如下面两个例子:
|
||||
|
||||
```javascript
|
||||
let someTuple:(Double, Double) = (3.14159, 2.71828)
|
||||
func someFunction(a: Int){ /* ... */ }
|
||||
```
|
||||
在第一个例子中,表达式`someTuple`的类型被指定为`(Double, Double)`。在第二个例子中,函数`someFunction`的参数`a`的类型被指定为`Int`。
|
||||
|
||||
类型注解可以在类型之前包含一个类型特性(type attributes)的可选列表。
|
||||
|
||||
>类型注解的语法:
|
||||
*type-annotation* → :*attributes*[opt] *type*
|
||||
|
||||
<a name="type_identifier"></a>
|
||||
##类型标识符
|
||||
类型标识符引用命名型类型或者是命名型/复合型类型的别名。
|
||||
|
||||
大多数情况下,类型标识符引用的是同名的命名型类型。例如类型标识符`Int`引用命名型类型`Int`,同样,类型标识符`Dictionary<String, Int>`引用命名型类型`Dictionary<String, Int>`。
|
||||
|
||||
在两种情况下类型标识符引用的不是同名的类型。情况一,类型标识符引用的是命名型/复合型类型的类型别名。比如,在下面的例子中,类型标识符使用`Point`来引用元组`(Int, Int)`:
|
||||
|
||||
```javascript
|
||||
typealias Point = (Int, Int)
|
||||
let origin: Point = (0, 0)
|
||||
```
|
||||
|
||||
情况二,类型标识符使用dot(`.`)语法来表示在其它模块(modules)或其它类型嵌套内声明的命名型类型。例如,下面例子中的类型标识符引用在`ExampleModule`模块中声明的命名型类型`MyType`:
|
||||
|
||||
```javascript
|
||||
var someValue: ExampleModule.MyType
|
||||
```
|
||||
|
||||
>类型标识符的语法:
|
||||
*type-identifier* → *type-name generic-argument-clause*[opt] | *type-name generic-argument-clause*[opt].*type-identifier*
|
||||
*type-name* → *identifier*
|
||||
|
||||
<a name="tuple_type"></a>
|
||||
##元组类型
|
||||
元组类型使用逗号隔开并使用括号括起来的0个或多个类型组成的列表。
|
||||
|
||||
你可以使用元组类型作为一个函数的返回类型,这样就可以使函数返回多个值。你也可以命名元组类型中的元素,然后用这些名字来引用每个元素的值。元素的名字由一个标识符和`:`组成。“函数和多返回值”章节里有一个展示上述特性的例子。
|
||||
|
||||
`void`是空元组类型`()`的别名。如果括号内只有一个元素,那么该类型就是括号内元素的类型。比如,`(Int)`的类型是`Int`而不是`(Int)`。所以,只有当元组类型包含两个元素以上时才可以标记元组元素。
|
||||
|
||||
>元组类型语法:
|
||||
*tuple* → (*tuple-type-body*[opt])
|
||||
*tuple-type-body* → *tuple-type-element-list* ...[opt]
|
||||
*tuple-type-element-list* → *tuple-type-element* | *tuple-type-element*, *tuple-type-element-list*
|
||||
*tuple-type-element* → *attributes*[opt] **inout** [opt] *type* | **inout** [opt] *element-name type-annotation*
|
||||
*element-name* → *identifier*
|
||||
|
||||
<a name="function_type"></a>
|
||||
##函数类型
|
||||
函数类型表示一个函数、方法或闭包的类型,它由一个参数类型和返回值类型组成,中间用箭头`->`隔开:
|
||||
|
||||
- `parameter type` -> `return type`
|
||||
|
||||
由于 *参数类型* 和 *返回值类型* 可以是元组类型,所以函数类型可以让函数与方法支持多参数与多返回值。
|
||||
|
||||
你可以对函数类型应用带有参数类型`()`并返回表达式类型的`auto_closure`属性(见类型属性章节)。一个自动闭包函数捕获特定表达式上的隐式闭包而非表达式本身。下面的例子使用`auto_closure`属性来定义一个很简单的assert函数:
|
||||
|
||||
```javascript
|
||||
func simpleAssert(condition: @auto_closure () -> Bool, message: String){
|
||||
if !condition(){
|
||||
println(message)
|
||||
}
|
||||
}
|
||||
let testNumber = 5
|
||||
simpleAssert(testNumber % 2 == 0, "testNumber isn't an even number.")
|
||||
// prints "testNumber isn't an even number."
|
||||
```
|
||||
函数类型可以拥有一个可变长参数作为参数类型中的最后一个参数。从语法角度上讲,可变长参数由一个基础类型名字和`...`组成,如`Int...`。可变长参数被认为是一个包含了基础类型元素的数组。即`Int...`就是`Int[]`。关于使用可变长参数的例子,见章节“可变长参数”。
|
||||
|
||||
为了指定一个`in-out`参数,可以在参数类型前加`inout`前缀。但是你不可以对可变长参数或返回值类型使用`inout`。关于In-Out参数的讨论见章节In-Out参数部分。
|
||||
|
||||
柯里化函数(curried function)的类型相当于一个嵌套函数类型。例如,下面的柯里化函数`addTwoNumber()()`的类型是`Int -> Int -> Int`:
|
||||
|
||||
```javascript
|
||||
func addTwoNumbers(a: Int)(b: Int) -> Int{
|
||||
return a + b
|
||||
}
|
||||
addTwoNumbers(4)(5) // returns 9
|
||||
```
|
||||
柯里化函数的函数类型从右向左组成一组。例如,函数类型`Int -> Int -> Int`可以被理解为`Int -> (Int -> Int)`——也就是说,一个函数传入一个`Int`然后输出作为另一个函数的输入,然后又返回一个`Int`。例如,你可以使用如下嵌套函数来重写柯里化函数`addTwoNumbers()()`:
|
||||
|
||||
```javascript
|
||||
func addTwoNumbers(a: Int) -> (Int -> Int){
|
||||
func addTheSecondNumber(b: Int) -> Int{
|
||||
return a + b
|
||||
}
|
||||
return addTheSecondNumber
|
||||
}
|
||||
addTwoNumbers(4)(5) // Returns 9
|
||||
```
|
||||
>函数类型的语法:
|
||||
*function-type* → *type* **->** *type*
|
||||
|
||||
<a name="array_type"></a>
|
||||
##数组类型
|
||||
Swift语言使用类型名紧接中括号`[]`来简化标准库中定义的命名型类型`Array<T>`。换句话说,下面两个声明是等价的:
|
||||
|
||||
```javascript
|
||||
let someArray: String[] = ["Alex", "Brian", "Dave"]
|
||||
let someArray: Array<String> = ["Alex", "Brian", "Dave"]
|
||||
```
|
||||
上面两种情况下,常量`someArray`都被声明为字符串数组。数组的元素也可以通过`[]`获取访问:`someArray[0]`是指第0个元素`“Alex”`。
|
||||
|
||||
上面的例子同时显示,你可以使用`[]`作为初始值构造数组,空的`[]`则用来来构造指定类型的空数组。
|
||||
|
||||
```javascript
|
||||
var emptyArray: Double[] = []
|
||||
```
|
||||
你也可以使用链接起来的多个`[]`集合来构造多维数组。例如,下例使用三个`[]`集合来构造三维整型数组:
|
||||
|
||||
```javascript
|
||||
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。
|
||||
|
||||
>数组类型的语法:
|
||||
*array-type* → *type*`[ ]` | *array-type*`[ ]`
|
||||
|
||||
<a name="optional_type"></a>
|
||||
##可选类型
|
||||
Swift定义后缀`?`来作为标准库中的定义的命名型类型`Optional<T>`的简写。换句话说,下面两个声明是等价的:
|
||||
|
||||
```javascript
|
||||
var optionalInteger: Int?
|
||||
var optionalInteger: Optional<Int>
|
||||
```
|
||||
在上述两种情况下,变量`optionalInteger`都被声明为可选整型类型。注意在类型和`?`之间没有空格。
|
||||
|
||||
类型`Optional<T>`是一个枚举,有两种形式,`None`和`Some(T)`,又来代表可能出现或可能不出现的值。任意类型都可以被显式的声明(或隐式的转换)为可选类型。当声明一个可选类型时,确保使用括号给`?`提供合适的作用范围。比如说,声明一个整型的可选数组,应写作`(Int[])?`,写成`Int[]?`的话则会出错。
|
||||
|
||||
如果你在声明或定义可选变量或特性的时候没有提供初始值,它的值则会自动赋成缺省值`nil`。
|
||||
|
||||
可选符合`LogicValue`协议,因此可以出现在布尔值环境下。此时,如果一个可选类型`T?`实例包含有类型为`T`的值(也就是说值为`Optional.Some(T)`),那么此可选类型就为`true`,否则为`false`。
|
||||
|
||||
如果一个可选类型的实例包含一个值,那么你就可以使用后缀操作符`!`来获取该值,正如下面描述的:
|
||||
|
||||
```javascript
|
||||
optionalInteger = 42
|
||||
optionalInteger! // 42
|
||||
```
|
||||
使用`!`操作符获取值为`nil`的可选项会导致运行错误(runtime error)。
|
||||
|
||||
你也可以使用可选链和可选绑定来选择性的执行可选表达式上的操作。如果值为`nil`,不会执行任何操作因此也就没有运行错误产生。
|
||||
|
||||
更多细节以及更多如何使用可选类型的例子,见章节“可选”。
|
||||
|
||||
>可选类型语法:
|
||||
*optional-type* → *type*?
|
||||
|
||||
<a name="implicitly_unwrapped_optional_type"></a>
|
||||
##隐式解析可选类型
|
||||
Swift语言定义后缀`!`作为标准库中命名类型`ImplicitlyUnwrappedOptional<T>`的简写。换句话说,下面两个声明等价:
|
||||
|
||||
```javascript
|
||||
var implicitlyUnwrappedString: String!
|
||||
var implicitlyUnwrappedString: ImplicitlyUnwrappedOptional<String>
|
||||
```
|
||||
上述两种情况下,变量`implicitlyUnwrappedString`被声明为一个隐式解析可选类型的字符串。注意类型与`!`之间没有空格。
|
||||
|
||||
你可以在使用可选的地方同样使用隐式解析可选。比如,你可以将隐式解析可选的值赋给变量、常量和可选特性,反之亦然。
|
||||
|
||||
有了可选,你在声明隐式解析可选变量或特性的时候就不用指定初始值,因为它有缺省值`nil`。
|
||||
|
||||
由于隐式解析可选的值会在使用时自动解析,所以没必要使用操作符`!`来解析它。也就是说,如果你使用值为`nil`的隐式解析可选,就会导致运行错误。
|
||||
|
||||
使用可选链会选择性的执行隐式解析可选表达式上的某一个操作。如果值为`nil`,就不会执行任何操作,因此也不会产生运行错误。
|
||||
|
||||
关于隐式解析可选的更多细节,见章节“隐式解析可选”。
|
||||
|
||||
>隐式解析可选的语法:
|
||||
implicitly-unwrapped-optional-type → type!
|
||||
|
||||
<a name="protocol_composition_type"></a>
|
||||
##协议合成类型
|
||||
协议合成类型是一种符合每个协议的指定协议列表类型。协议合成类型可能会用在类型注解和泛型参数中。
|
||||
|
||||
协议合成类型的形式如下:
|
||||
```javascript
|
||||
protocol<Protocol 1, Procotol 2>
|
||||
```
|
||||
协议合成类型允许你指定一个值,其类型可以适配多个协议的条件,而且不需要定义一个新的命名型协议来继承其它想要适配的各个协议。比如,协议合成类型`protocol<Protocol A, Protocol B, Protocol C>`等效于一个从`Protocol A`,`Protocol B`, `Protocol C`继承而来的新协议`Protocol D`,很显然这样做有效率的多,甚至不需引入一个新名字。
|
||||
|
||||
协议合成列表中的每项必须是协议名或协议合成类型的类型别名。如果列表为空,它就会指定一个空协议合成列表,这样每个类型都能适配。
|
||||
|
||||
>协议合成类型的语法:
|
||||
*protocol-composition-type* → **protocol** <*protocol-identifier-list[opt]*>
|
||||
*protocol-identifier-list* → *protocol-identifier* | *protocol-identifier, protocol-identifier-list*
|
||||
*protocol-identifier* → *type-identifier*
|
||||
|
||||
<a name="metatype_type"></a>
|
||||
##元类型
|
||||
元类型是指所有类型的类型,包括类、结构体、枚举和协议。
|
||||
|
||||
类、结构体或枚举类型的元类型是相应的类型名紧跟`.Type`。协议类型的元类型——并不是运行时适配该协议的具体类型——是该协议名字紧跟`.Protocol`。比如,类`SomeClass`的元类型就是`SomeClass.Type`,协议`SomeProtocol`的元类型就是`SomeProtocal.Protocol`。
|
||||
|
||||
你可以使用后缀`self`表达式来获取类型。比如,`SomeClass.self`返回`SomeClass`本身,而不是`SomeClass`的一个实例。同样,`SomeProtocol.self`返回`SomeProtocol`本身,而不是运行时适配`SomeProtocol`的某个类型的实例。还可以对类型的实例使用`dynamicType`表达式来获取该实例在运行阶段的类型,如下所示:
|
||||
|
||||
```javascript
|
||||
class SomeBaseClass {
|
||||
class func printClassName() {
|
||||
println("SomeBaseClass")
|
||||
}
|
||||
}
|
||||
class SomeSubClass: SomeBaseClass {
|
||||
override class func printClassName() {
|
||||
println("SomeSubClass")
|
||||
}
|
||||
}
|
||||
let someInstance: SomeBaseClass = SomeSubClass()
|
||||
// someInstance is of type SomeBaseClass at compile time, but
|
||||
// someInstance is of type SomeSubClass at runtime
|
||||
someInstance.dynamicType.printClassName()
|
||||
// prints "SomeSubClass
|
||||
```
|
||||
|
||||
>元类型的语法:
|
||||
*metatype-type* → *type*.**Type** | *type*.**Protocol**
|
||||
|
||||
<a name="type_inheritance_clause"></a>
|
||||
##类型继承子句
|
||||
类型继承子句被用来指定一个命名型类型继承哪个类且适配哪些协议。类型继承子句开始于冒号`:`,紧跟由`,`隔开的类型标识符列表。
|
||||
|
||||
类可以继承单个超类,适配任意数量的协议。当定义一个类时,超类的名字必须出现在类型标识符列表首位,然后跟上该类需要适配的任意数量的协议。如果一个类不是从其它类继承而来,那么列表可以以协议开头。关于类继承更多的讨论和例子,见章节“继承”。
|
||||
|
||||
其它命名型类型可能只继承或适配一个协议列表。协议类型可能继承于其它任意数量的协议。当一个协议类型继承于其它协议时,其它协议的条件集合会被集成在一起,然后其它从当前协议继承的任意类型必须适配所有这些条件。
|
||||
|
||||
枚举定义中的类型继承子句可以是一个协议列表,或是指定原始值的枚举,一个单独的指定原始值类型的命名型类型。使用类型继承子句来指定原始值类型的枚举定义的例子,见章节“原始值”。
|
||||
|
||||
>类型继承子句的语法:
|
||||
*type-inheritance-clause* → :*type-inheritance-list*
|
||||
*type-inheritance-list* → *type-identifier* | *type-identifier*, *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>`。
|
||||
|
||||
在上面的两个例子中,类型信息从表达式树(expression tree)的叶子节点传向根节点。也就是说,`var x: Int = 0`中`x`的类型首先根据`0`的类型进行推断,然后将该类型信息传递到根节点(变量`x`)。
|
||||
|
||||
在Swift中,类型信息也可以反方向流动——从根节点传向叶子节点。在下面的例子中,常量`eFloat`上的显式类型注解(`:Float`)导致数字字面量`2.71828`的类型是`Float`而非`Double`。
|
||||
|
||||
```javascript
|
||||
let e = 2.71828 // The type of e is inferred to be Double.
|
||||
let eFloat: Float = 2.71828 // The type of eFloat is Float.
|
||||
```
|
||||
|
||||
Swift中的类型推断在单独的表达式或语句水平上进行。这意味着所有用于推断类型的信息必须可以从表达式或其某个子表达式的类型检查中获取。
|
||||
> 翻译:[lyuka](https://github.com/lyuka)
|
||||
> 校对:[numbbbbb](https://github.com/numbbbbb), [stanzhai](https://github.com/stanzhai)
|
||||
|
||||
# 类型(Types)
|
||||
-----------------
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [类型注解(Type Annotation)](#type_annotation)
|
||||
- [类型标识符(Type Identifier)](#type_identifier)
|
||||
- [元组类型(Tuple Type)](#tuple_type)
|
||||
- [函数类型(Function Type)](#function_type)
|
||||
- [数组类型(Array Type)](#array_type)
|
||||
- [可选类型(Optional Type)](#optional_type)
|
||||
- [隐式解析可选类型(Implicitly Unwrapped Optional Type)](#implicitly_unwrapped_optional_type)
|
||||
- [协议合成类型(Protocol Composition Type)](#protocol_composition_type)
|
||||
- [元类型(Metatype Type)](#metatype_type)
|
||||
- [类型继承子句(Type Inheritance Clause)](#type_inheritance_clause)
|
||||
- [类型推断(Type Inference)](#type_inference)
|
||||
|
||||
Swift 语言存在两种类型:命名型类型和复合型类型。*命名型类型*是指定义时可以给定名字的类型。命名型类型包括类、结构体、枚举和协议。比如,一个用户定义的类`MyClass`的实例拥有类型`MyClass`。除了用户定义的命名型类型,Swift 标准库也定义了很多常用的命名型类型,包括那些表示数组、字典和可选值的类型。
|
||||
|
||||
那些通常被其它语言认为是基本或初级的数据型类型(Data types)——比如表示数字、字符和字符串——实际上就是命名型类型,Swift 标准库是使用结构体定义和实现它们的。因为它们是命名型类型,因此你可以按照“扩展和扩展声明”章节里讨论的那样,声明一个扩展来增加它们的行为以适应你程序的需求。
|
||||
|
||||
*复合型类型*是没有名字的类型,它由 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)
|
||||
|
||||
<a name="type_annotation"></a>
|
||||
##类型注解
|
||||
|
||||
类型注解显式地指定一个变量或表达式的值。类型注解始于冒号`:`终于类型,比如下面两个例子:
|
||||
|
||||
```swift
|
||||
let someTuple: (Double, Double) = (3.14159, 2.71828)
|
||||
func someFunction(a: Int){ /* ... */ }
|
||||
```
|
||||
在第一个例子中,表达式`someTuple`的类型被指定为`(Double, Double)`。在第二个例子中,函数`someFunction`的参数`a`的类型被指定为`Int`。
|
||||
|
||||
类型注解可以在类型之前包含一个类型特性(type attributes)的可选列表。
|
||||
|
||||
> 类型注解语法
|
||||
> *类型注解* → **:** [*特性(Attributes)列表*](..\chapter3\06_Attributes.html#attributes) _可选_ [*类型*](..\chapter3\03_Types.html#type)
|
||||
|
||||
<a name="type_identifier"></a>
|
||||
##类型标识符
|
||||
|
||||
类型标识符引用命名型类型或者是命名型/复合型类型的别名。
|
||||
|
||||
大多数情况下,类型标识符引用的是同名的命名型类型。例如类型标识符`Int`引用命名型类型`Int`,同样,类型标识符`Dictionary<String, Int>`引用命名型类型`Dictionary<String, Int>`。
|
||||
|
||||
在两种情况下类型标识符引用的不是同名的类型。情况一,类型标识符引用的是命名型/复合型类型的类型别名。比如,在下面的例子中,类型标识符使用`Point`来引用元组`(Int, Int)`:
|
||||
|
||||
```swift
|
||||
typealias Point = (Int, Int)
|
||||
let origin: Point = (0, 0)
|
||||
```
|
||||
|
||||
情况二,类型标识符使用dot(`.`)语法来表示在其它模块(modules)或其它类型嵌套内声明的命名型类型。例如,下面例子中的类型标识符引用在`ExampleModule`模块中声明的命名型类型`MyType`:
|
||||
|
||||
```swift
|
||||
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)
|
||||
> *类名* → [*标识符*](LexicalStructure.html#identifier)
|
||||
|
||||
<a name="tuple_type"></a>
|
||||
##元组类型
|
||||
|
||||
元组类型使用逗号隔开并使用括号括起来的0个或多个类型组成的列表。
|
||||
|
||||
你可以使用元组类型作为一个函数的返回类型,这样就可以使函数返回多个值。你也可以命名元组类型中的元素,然后用这些名字来引用每个元素的值。元素的名字由一个标识符和`:`组成。“函数和多返回值”章节里有一个展示上述特性的例子。
|
||||
|
||||
`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)
|
||||
> *元素名* → [*标识符*](LexicalStructure.html#identifier)
|
||||
|
||||
<a name="function_type"></a>
|
||||
##函数类型
|
||||
|
||||
函数类型表示一个函数、方法或闭包的类型,它由一个参数类型和返回值类型组成,中间用箭头`->`隔开:
|
||||
|
||||
- `parameter type` -> `return type`
|
||||
|
||||
由于 *参数类型* 和 *返回值类型* 可以是元组类型,所以函数类型可以让函数与方法支持多参数与多返回值。
|
||||
|
||||
你可以对函数类型应用带有参数类型`()`并返回表达式类型的`auto_closure`属性(见类型属性章节)。一个自动闭包函数捕获特定表达式上的隐式闭包而非表达式本身。下面的例子使用`auto_closure`属性来定义一个很简单的assert函数:
|
||||
|
||||
```swift
|
||||
func simpleAssert(condition: @auto_closure () -> Bool, message: String){
|
||||
if !condition(){
|
||||
println(message)
|
||||
}
|
||||
}
|
||||
let testNumber = 5
|
||||
simpleAssert(testNumber % 2 == 0, "testNumber isn't an even number.")
|
||||
// prints "testNumber isn't an even number."
|
||||
```
|
||||
函数类型可以拥有一个可变长参数作为参数类型中的最后一个参数。从语法角度上讲,可变长参数由一个基础类型名字和`...`组成,如`Int...`。可变长参数被认为是一个包含了基础类型元素的数组。即`Int...`就是`Int[]`。关于使用可变长参数的例子,见章节“可变长参数”。
|
||||
|
||||
为了指定一个`in-out`参数,可以在参数类型前加`inout`前缀。但是你不可以对可变长参数或返回值类型使用`inout`。关于In-Out参数的讨论见章节In-Out参数部分。
|
||||
|
||||
柯里化函数(curried function)的类型相当于一个嵌套函数类型。例如,下面的柯里化函数`addTwoNumber()()`的类型是`Int -> Int -> Int`:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
> 函数类型语法
|
||||
> *函数类型* → [*类型*](..\chapter3\03_Types.html#type) **->** [*类型*](..\chapter3\03_Types.html#type)
|
||||
|
||||
<a name="array_type"></a>
|
||||
##数组类型
|
||||
|
||||
Swift语言使用类型名紧接中括号`[]`来简化标准库中定义的命名型类型`Array<T>`。换句话说,下面两个声明是等价的:
|
||||
|
||||
```swift
|
||||
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]]]
|
||||
```
|
||||
访问一个多维数组的元素时,最左边的下标指向最外层数组的相应位置元素。接下来往右的下标指向第一层嵌入的相应位置元素,依次类推。这就意味着,在上面的例子中,`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) **[** **]**
|
||||
|
||||
<a name="optional_type"></a>
|
||||
##可选类型
|
||||
|
||||
Swift定义后缀`?`来作为标准库中的定义的命名型类型`Optional<T>`的简写。换句话说,下面两个声明是等价的:
|
||||
|
||||
```swift
|
||||
var optionalInteger: Int?
|
||||
var optionalInteger: Optional<Int>
|
||||
```
|
||||
在上述两种情况下,变量`optionalInteger`都被声明为可选整型类型。注意在类型和`?`之间没有空格。
|
||||
|
||||
类型`Optional<T>`是一个枚举,有两种形式,`None`和`Some(T)`,又来代表可能出现或可能不出现的值。任意类型都可以被显式的声明(或隐式的转换)为可选类型。当声明一个可选类型时,确保使用括号给`?`提供合适的作用范围。比如说,声明一个整型的可选数组,应写作`(Int[])?`,写成`Int[]?`的话则会出错。
|
||||
|
||||
如果你在声明或定义可选变量或特性的时候没有提供初始值,它的值则会自动赋成缺省值`nil`。
|
||||
|
||||
可选符合`LogicValue`协议,因此可以出现在布尔值环境下。此时,如果一个可选类型`T?`实例包含有类型为`T`的值(也就是说值为`Optional.Some(T)`),那么此可选类型就为`true`,否则为`false`。
|
||||
|
||||
如果一个可选类型的实例包含一个值,那么你就可以使用后缀操作符`!`来获取该值,正如下面描述的:
|
||||
|
||||
```swift
|
||||
optionalInteger = 42
|
||||
optionalInteger! // 42
|
||||
```
|
||||
使用`!`操作符获取值为`nil`的可选项会导致运行错误(runtime error)。
|
||||
|
||||
你也可以使用可选链和可选绑定来选择性的执行可选表达式上的操作。如果值为`nil`,不会执行任何操作因此也就没有运行错误产生。
|
||||
|
||||
更多细节以及更多如何使用可选类型的例子,见章节“可选”。
|
||||
|
||||
> 可选类型语法
|
||||
> *可选类型* → [*类型*](..\chapter3\03_Types.html#type) **?**
|
||||
|
||||
<a name="implicitly_unwrapped_optional_type"></a>
|
||||
##隐式解析可选类型
|
||||
|
||||
Swift语言定义后缀`!`作为标准库中命名类型`ImplicitlyUnwrappedOptional<T>`的简写。换句话说,下面两个声明等价:
|
||||
|
||||
```swift
|
||||
var implicitlyUnwrappedString: String!
|
||||
var implicitlyUnwrappedString: ImplicitlyUnwrappedOptional<String>
|
||||
```
|
||||
上述两种情况下,变量`implicitlyUnwrappedString`被声明为一个隐式解析可选类型的字符串。注意类型与`!`之间没有空格。
|
||||
|
||||
你可以在使用可选的地方同样使用隐式解析可选。比如,你可以将隐式解析可选的值赋给变量、常量和可选特性,反之亦然。
|
||||
|
||||
有了可选,你在声明隐式解析可选变量或特性的时候就不用指定初始值,因为它有缺省值`nil`。
|
||||
|
||||
由于隐式解析可选的值会在使用时自动解析,所以没必要使用操作符`!`来解析它。也就是说,如果你使用值为`nil`的隐式解析可选,就会导致运行错误。
|
||||
|
||||
使用可选链会选择性的执行隐式解析可选表达式上的某一个操作。如果值为`nil`,就不会执行任何操作,因此也不会产生运行错误。
|
||||
|
||||
关于隐式解析可选的更多细节,见章节“隐式解析可选”。
|
||||
|
||||
> 隐式解析可选类型(Implicitly Unwrapped Optional Type)语法
|
||||
> *隐式解析可选类型* → [*类型*](..\chapter3\03_Types.html#type) **!**
|
||||
|
||||
<a name="protocol_composition_type"></a>
|
||||
##协议合成类型
|
||||
|
||||
协议合成类型是一种符合每个协议的指定协议列表类型。协议合成类型可能会用在类型注解和泛型参数中。
|
||||
|
||||
协议合成类型的形式如下:
|
||||
|
||||
```swift
|
||||
protocol<Protocol 1, Procotol 2>
|
||||
```
|
||||
|
||||
协议合成类型允许你指定一个值,其类型可以适配多个协议的条件,而且不需要定义一个新的命名型协议来继承其它想要适配的各个协议。比如,协议合成类型`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)
|
||||
|
||||
<a name="metatype_type"></a>
|
||||
##元类型
|
||||
|
||||
元类型是指所有类型的类型,包括类、结构体、枚举和协议。
|
||||
|
||||
类、结构体或枚举类型的元类型是相应的类型名紧跟`.Type`。协议类型的元类型——并不是运行时适配该协议的具体类型——是该协议名字紧跟`.Protocol`。比如,类`SomeClass`的元类型就是`SomeClass.Type`,协议`SomeProtocol`的元类型就是`SomeProtocal.Protocol`。
|
||||
|
||||
你可以使用后缀`self`表达式来获取类型。比如,`SomeClass.self`返回`SomeClass`本身,而不是`SomeClass`的一个实例。同样,`SomeProtocol.self`返回`SomeProtocol`本身,而不是运行时适配`SomeProtocol`的某个类型的实例。还可以对类型的实例使用`dynamicType`表达式来获取该实例在运行阶段的类型,如下所示:
|
||||
|
||||
```swift
|
||||
class SomeBaseClass {
|
||||
class func printClassName() {
|
||||
println("SomeBaseClass")
|
||||
}
|
||||
}
|
||||
class SomeSubClass: SomeBaseClass {
|
||||
override class func printClassName() {
|
||||
println("SomeSubClass")
|
||||
}
|
||||
}
|
||||
let someInstance: SomeBaseClass = SomeSubClass()
|
||||
// someInstance is of type SomeBaseClass at compile time, but
|
||||
// someInstance is of type SomeSubClass at runtime
|
||||
someInstance.dynamicType.printClassName()
|
||||
// prints "SomeSubClass
|
||||
```
|
||||
|
||||
> 元(Metatype)类型语法
|
||||
> *元类型* → [*类型*](..\chapter3\03_Types.html#type) **.** **Type** | [*类型*](..\chapter3\03_Types.html#type) **.** **Protocol** x
|
||||
|
||||
<a name="type_inheritance_clause"></a>
|
||||
##类型继承子句
|
||||
|
||||
类型继承子句被用来指定一个命名型类型继承哪个类且适配哪些协议。类型继承子句开始于冒号`:`,紧跟由`,`隔开的类型标识符列表。
|
||||
|
||||
类可以继承单个超类,适配任意数量的协议。当定义一个类时,超类的名字必须出现在类型标识符列表首位,然后跟上该类需要适配的任意数量的协议。如果一个类不是从其它类继承而来,那么列表可以以协议开头。关于类继承更多的讨论和例子,见章节“继承”。
|
||||
|
||||
其它命名型类型可能只继承或适配一个协议列表。协议类型可能继承于其它任意数量的协议。当一个协议类型继承于其它协议时,其它协议的条件集合会被集成在一起,然后其它从当前协议继承的任意类型必须适配所有这些条件。
|
||||
|
||||
枚举定义中的类型继承子句可以是一个协议列表,或是指定原始值的枚举,一个单独的指定原始值类型的命名型类型。使用类型继承子句来指定原始值类型的枚举定义的例子,见章节“原始值”。
|
||||
|
||||
> 类型继承子句语法
|
||||
> *类型继承子句* → **:** [*类型继承列表*](..\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>`。
|
||||
|
||||
在上面的两个例子中,类型信息从表达式树(expression tree)的叶子节点传向根节点。也就是说,`var x: Int = 0`中`x`的类型首先根据`0`的类型进行推断,然后将该类型信息传递到根节点(变量`x`)。
|
||||
|
||||
在Swift中,类型信息也可以反方向流动——从根节点传向叶子节点。在下面的例子中,常量`eFloat`上的显式类型注解(`:Float`)导致数字字面量`2.71828`的类型是`Float`而非`Double`。
|
||||
|
||||
```swift
|
||||
let e = 2.71828 // The type of e is inferred to be Double.
|
||||
let eFloat: Float = 2.71828 // The type of eFloat is Float.
|
||||
```
|
||||
|
||||
Swift中的类型推断在单独的表达式或语句水平上进行。这意味着所有用于推断类型的信息必须可以从表达式或其某个子表达式的类型检查中获取。
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,127 +1,124 @@
|
||||
> 翻译:Hawstein
|
||||
|
||||
> 校对:numbbbbb
|
||||
|
||||
# 特性
|
||||
-----------------
|
||||
|
||||
本页内容包括:
|
||||
|
||||
- [声明特性](#declaration_attributes)
|
||||
- [类型特性](#type_attributes)
|
||||
|
||||
特性提供了关于声明和类型的更多信息。在Swift中有两类特性,用于修饰声明的以及用于修饰类型的。例如,`required`特性,当应用于一个类的指定或便利初始化器声明时,表明它的每个子类都必须实现那个初始化器。再比如`noreturn`特性,当应用于函数或方法类型时,表明该函数或方法不会返回到它的调用者。
|
||||
|
||||
通过以下方式指定一个特性:符号`@`后面跟特性名,如果包含参数,则把参数带上:
|
||||
|
||||
```
|
||||
@attribute name
|
||||
@attribute name(attribute arguments)
|
||||
```
|
||||
|
||||
有些声明特性通过接收参数来指定特性的更多信息以及它是如何修饰一个特定的声明的。这些特性的参数写在小括号内,它们的格式由它们所属的特性来定义。
|
||||
|
||||
<a name="declaration_attributes"></a>
|
||||
## 声明特性
|
||||
|
||||
声明特性只能应用于声明。然而,你也可以将`noreturn`特性应用于函数或方法类型。
|
||||
|
||||
`assignment`
|
||||
|
||||
该特性用于修饰重载了复合赋值运算符的函数。重载了复合赋值运算符的函数必需将它们的初始输入参数标记为`inout`。如何使用`assignment`特性的一个例子,请见:[复合赋值运算符]()。
|
||||
|
||||
`class_protocol`
|
||||
|
||||
该特性用于修饰一个协议表明该协议只能被类类型采用[待改:adopted]。
|
||||
|
||||
如果你用`objc`特性修饰一个协议,`class_protocol`特性就会隐式地应用到该协议,因此无需显式地用`class_protocol`特性标记该协议。
|
||||
|
||||
`exported`
|
||||
|
||||
该特性用于修饰导入声明,以此来导出已导入的模块,子模块,或当前模块的声明。如果另一个模块导入了当前模块,那么那个模块可以访问当前模块的导出项。
|
||||
|
||||
`final`
|
||||
|
||||
该特性用于修饰一个类或类中的属性,方法,以及下标成员。如果用它修饰一个类,那么这个类则不能被继承。如果用它修饰类中的属性,方法或下标,则表示在子类中,它们不能被重写。
|
||||
|
||||
`lazy`
|
||||
|
||||
该特性用于修饰类或结构体中的存储型变量属性,表示该属性的初始值最多只被计算和存储一次,且发生在第一次访问它时。如何使用`lazy`特性的一个例子,请见:[惰性存储型属性]()。
|
||||
|
||||
`noreturn`
|
||||
|
||||
该特性用于修饰函数或方法声明,表明该函数或方法的对应类型,`T`,是`@noreturn T`。你可以用这个特性修饰函数或方法的类型,这样一来,函数或方法就不会返回到它的调用者中去。
|
||||
|
||||
对于一个没有用`noreturn`特性标记的函数或方法,你可以将它重写(override)为用该特性标记的。相反,对于一个已经用`noreturn`特性标记的函数或方法,你则不可以将它重写为没使用该特性标记的。相同的规则试用于当你在一个comforming类型中实现一个协议方法时。
|
||||
|
||||
`NSCopying`
|
||||
|
||||
该特性用于修饰一个类的存储型变量属性。该特性将使属性的setter与属性值的一个副本合成,由`copyWithZone`方法返回,而不是属性本身的值。该属性的类型必需遵循`NSCopying`协议。
|
||||
|
||||
`NSCopying`特性的行为与Objective-C中的`copy`特性相似。
|
||||
|
||||
`NSManaged`
|
||||
|
||||
该特性用于修饰`NSManagedObject`子类中的存储型变量属性,表明属性的存储和实现由Core Data在运行时基于相关实体描述动态提供。
|
||||
|
||||
`objc`
|
||||
|
||||
该特性用于修饰任意可以在Objective-C中表示的声明,比如,非嵌套类,协议,类和协议中的属性和方法(包含getter和setter),初始化器,析构器,以下下标。`objc`特性告诉编译器该声明可以在Objective-C代码中使用。
|
||||
|
||||
如果你将`objc`特性应用于一个类或协议,它也会隐式地应用于那个类或协议的成员。对于标记了`objc`特性的类,编译器会隐式地为它的子类添加`objc`特性。标记了`objc`特性的协议不能继承自没有标记`objc`的协议。
|
||||
|
||||
`objc`特性有一个可选的参数,由标记符组成。当你想把`objc`所修饰的实体以一个不同的名字暴露给Objective-C,你就可以使用这个特性参数。你可以使用这个参数来命名类,协议,方法,getters,setters,以及初始化器。下面的例子把`ExampleClass`中`enabled`属性的getter暴露给Objective-C,名字是`isEnabled`,而不是它原来的属性名。
|
||||
|
||||
```
|
||||
@objc
|
||||
class ExampleClass {
|
||||
var enabled: Bool {
|
||||
@objc(isEnabled) get {
|
||||
// Return the appropriate value
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`optional`
|
||||
|
||||
用该特性修饰协议的属性,方法或下标成员,表示实现这些成员并不需要一致性类型(conforming type)。
|
||||
|
||||
你只能用`optional`特性修饰那些标记了`objc`特性的协议。因此,只有类类型可以adopt和comform to那些包含可选成员需求的协议。更多关于如何使用`optional`特性以及如何访问可选协议成员的指导,例如,当你不确定一个conforming类型是否实现了它们,请见:[可选协议需求]()。
|
||||
|
||||
`required`
|
||||
|
||||
用该特性修饰一个类的指定或便利初始化器,表示该类的所有子类都必需实现该初始化器。
|
||||
|
||||
加了该特性的指定初始化器必需显式地实现,而便利初始化器既可显式地实现,也可以在子类实现了超类所有指定初始化器后继承而来(或者当子类使用便利初始化器重写了指定初始化器)。
|
||||
|
||||
### Interface Builder使用的声明特性
|
||||
|
||||
Interface Builder特性是Interface Builder用来与Xcode同步的声明特性。Swift提供了以下的Interface Builder特性:`IBAction`,`IBDesignable`,`IBInspectable`,以及`IBOutlet`。这些特性与Objective-C中对应的特性在概念上是相同的。
|
||||
|
||||
`IBOutlet`和`IBInspectable`用于修饰一个类的属性声明;`IBAction`特性用于修饰一个类的方法声明;`IBDesignable`用于修饰类的声明。
|
||||
|
||||
<a name="type_attributes"></a>
|
||||
## 类型特性
|
||||
|
||||
类型特性只能用于修饰类型。然而,你也可以用`noreturn`特性去修饰函数或方法声明。
|
||||
|
||||
`auto_closure`
|
||||
|
||||
这个特性通过自动地将表达式封闭到一个无参数闭包中来延迟表达式的求值。使用该特性修饰无参的函数或方法类型,返回表达式的类型。一个如何使用`auto_closure`特性的例子,见[函数类型]()
|
||||
|
||||
`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) _可选_ **}**
|
||||
> *平衡令牌* → **任意标识符, 关键字, 字面量或运算符**
|
||||
> *平衡令牌* → **任意标点除了(, ), [, ], {, 或 }**
|
||||
> 翻译:[Hawstein](https://github.com/Hawstein)
|
||||
> 校对:[numbbbbb](https://github.com/numbbbbb), [stanzhai](https://github.com/stanzhai)
|
||||
|
||||
# 特性
|
||||
-----------------
|
||||
|
||||
本页内容包括:
|
||||
|
||||
- [声明特性](#declaration_attributes)
|
||||
- [类型特性](#type_attributes)
|
||||
|
||||
特性提供了关于声明和类型的更多信息。在Swift中有两类特性,用于修饰声明的以及用于修饰类型的。例如,`required`特性,当应用于一个类的指定或便利初始化器声明时,表明它的每个子类都必须实现那个初始化器。再比如`noreturn`特性,当应用于函数或方法类型时,表明该函数或方法不会返回到它的调用者。
|
||||
|
||||
通过以下方式指定一个特性:符号`@`后面跟特性名,如果包含参数,则把参数带上:
|
||||
|
||||
> @`attribute name`
|
||||
> @`attribute name`(`attribute arguments`)
|
||||
|
||||
有些声明特性通过接收参数来指定特性的更多信息以及它是如何修饰一个特定的声明的。这些特性的参数写在小括号内,它们的格式由它们所属的特性来定义。
|
||||
|
||||
<a name="declaration_attributes"></a>
|
||||
## 声明特性
|
||||
|
||||
声明特性只能应用于声明。然而,你也可以将`noreturn`特性应用于函数或方法类型。
|
||||
|
||||
`assignment`
|
||||
|
||||
该特性用于修饰重载了复合赋值运算符的函数。重载了复合赋值运算符的函数必需将它们的初始输入参数标记为`inout`。如何使用`assignment`特性的一个例子,请见:[复合赋值运算符]()。
|
||||
|
||||
`class_protocol`
|
||||
|
||||
该特性用于修饰一个协议表明该协议只能被类类型采用[待改:adopted]。
|
||||
|
||||
如果你用`objc`特性修饰一个协议,`class_protocol`特性就会隐式地应用到该协议,因此无需显式地用`class_protocol`特性标记该协议。
|
||||
|
||||
`exported`
|
||||
|
||||
该特性用于修饰导入声明,以此来导出已导入的模块,子模块,或当前模块的声明。如果另一个模块导入了当前模块,那么那个模块可以访问当前模块的导出项。
|
||||
|
||||
`final`
|
||||
|
||||
该特性用于修饰一个类或类中的属性,方法,以及下标成员。如果用它修饰一个类,那么这个类则不能被继承。如果用它修饰类中的属性,方法或下标,则表示在子类中,它们不能被重写。
|
||||
|
||||
`lazy`
|
||||
|
||||
该特性用于修饰类或结构体中的存储型变量属性,表示该属性的初始值最多只被计算和存储一次,且发生在第一次访问它时。如何使用`lazy`特性的一个例子,请见:[惰性存储型属性]()。
|
||||
|
||||
`noreturn`
|
||||
|
||||
该特性用于修饰函数或方法声明,表明该函数或方法的对应类型,`T`,是`@noreturn T`。你可以用这个特性修饰函数或方法的类型,这样一来,函数或方法就不会返回到它的调用者中去。
|
||||
|
||||
对于一个没有用`noreturn`特性标记的函数或方法,你可以将它重写(override)为用该特性标记的。相反,对于一个已经用`noreturn`特性标记的函数或方法,你则不可以将它重写为没使用该特性标记的。相同的规则试用于当你在一个comforming类型中实现一个协议方法时。
|
||||
|
||||
`NSCopying`
|
||||
|
||||
该特性用于修饰一个类的存储型变量属性。该特性将使属性的setter与属性值的一个副本合成,由`copyWithZone`方法返回,而不是属性本身的值。该属性的类型必需遵循`NSCopying`协议。
|
||||
|
||||
`NSCopying`特性的行为与Objective-C中的`copy`特性相似。
|
||||
|
||||
`NSManaged`
|
||||
|
||||
该特性用于修饰`NSManagedObject`子类中的存储型变量属性,表明属性的存储和实现由Core Data在运行时基于相关实体描述动态提供。
|
||||
|
||||
`objc`
|
||||
|
||||
该特性用于修饰任意可以在Objective-C中表示的声明,比如,非嵌套类,协议,类和协议中的属性和方法(包含getter和setter),初始化器,析构器,以下下标。`objc`特性告诉编译器该声明可以在Objective-C代码中使用。
|
||||
|
||||
如果你将`objc`特性应用于一个类或协议,它也会隐式地应用于那个类或协议的成员。对于标记了`objc`特性的类,编译器会隐式地为它的子类添加`objc`特性。标记了`objc`特性的协议不能继承自没有标记`objc`的协议。
|
||||
|
||||
`objc`特性有一个可选的参数,由标记符组成。当你想把`objc`所修饰的实体以一个不同的名字暴露给Objective-C,你就可以使用这个特性参数。你可以使用这个参数来命名类,协议,方法,getters,setters,以及初始化器。下面的例子把`ExampleClass`中`enabled`属性的getter暴露给Objective-C,名字是`isEnabled`,而不是它原来的属性名。
|
||||
|
||||
```swift
|
||||
@objc
|
||||
class ExampleClass {
|
||||
var enabled: Bool {
|
||||
@objc(isEnabled) get {
|
||||
// Return the appropriate value
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`optional`
|
||||
|
||||
用该特性修饰协议的属性,方法或下标成员,表示实现这些成员并不需要一致性类型(conforming type)。
|
||||
|
||||
你只能用`optional`特性修饰那些标记了`objc`特性的协议。因此,只有类类型可以adopt和comform to那些包含可选成员需求的协议。更多关于如何使用`optional`特性以及如何访问可选协议成员的指导,例如,当你不确定一个conforming类型是否实现了它们,请见:[可选协议需求]()。
|
||||
|
||||
`required`
|
||||
|
||||
用该特性修饰一个类的指定或便利初始化器,表示该类的所有子类都必需实现该初始化器。
|
||||
|
||||
加了该特性的指定初始化器必需显式地实现,而便利初始化器既可显式地实现,也可以在子类实现了超类所有指定初始化器后继承而来(或者当子类使用便利初始化器重写了指定初始化器)。
|
||||
|
||||
### Interface Builder使用的声明特性
|
||||
|
||||
Interface Builder特性是Interface Builder用来与Xcode同步的声明特性。Swift提供了以下的Interface Builder特性:`IBAction`,`IBDesignable`,`IBInspectable`,以及`IBOutlet`。这些特性与Objective-C中对应的特性在概念上是相同的。
|
||||
|
||||
`IBOutlet`和`IBInspectable`用于修饰一个类的属性声明;`IBAction`特性用于修饰一个类的方法声明;`IBDesignable`用于修饰类的声明。
|
||||
|
||||
<a name="type_attributes"></a>
|
||||
## 类型特性
|
||||
|
||||
类型特性只能用于修饰类型。然而,你也可以用`noreturn`特性去修饰函数或方法声明。
|
||||
|
||||
`auto_closure`
|
||||
|
||||
这个特性通过自动地将表达式封闭到一个无参数闭包中来延迟表达式的求值。使用该特性修饰无参的函数或方法类型,返回表达式的类型。一个如何使用`auto_closure`特性的例子,见[函数类型]()
|
||||
|
||||
`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) _可选_ **}**
|
||||
> *平衡令牌* → **任意标识符, 关键字, 字面量或运算符**
|
||||
> *平衡令牌* → **任意标点除了(, ), [, ], {, 或 }**
|
||||
|
||||
@ -1,170 +1,182 @@
|
||||
> 翻译:honghaoz
|
||||
|
||||
> 校对:numbbbbb, stanzhai
|
||||
|
||||
# 模式(Patterns)
|
||||
-----------------
|
||||
|
||||
本页内容包括:
|
||||
|
||||
- [通配符模式(Wildcard Pattern)](#wildcard_pattern)
|
||||
- [标识符模式(Identifier Pattern)](#identifier_pattern)
|
||||
- [值绑定模式(Value-Binding Pattern)](#value-binding_pattern)
|
||||
- [元组模式(Tuple Pattern)](#tuple_pattern)
|
||||
- [枚举用例模式(Enumeration Case Pattern)](#enumeration_case_pattern)
|
||||
- [类型转换模式(Type-Casting Patterns)](#type-casting_patterns)
|
||||
- [表达式模式(Expression Pattern)](#expression_pattern)
|
||||
|
||||
模式(pattern)代表了单个值或者复合值的结构。例如,元组`(1, 2)`的结构是逗号分隔的,包含两个元素的列表。因为模式代表一种值的结构,而不是特定的某个值,你可以把模式和各种同类型的值匹配起来。比如,`(x, y)`可以匹配元组`(1, 2)`,以及任何含两个元素的元组。除了将模式与一个值匹配外,你可以从合成值中提取出部分或全部,然后分别把各个部分和一个常量或变量绑定起来。
|
||||
|
||||
在Swift中,模式出现在变量和常量的声明(在它们的左侧),`for-in`语句和`switch`语句(在它们的case标签)中。尽管任何模式都可以出现在`switch`语句的case标签中,但在其他情况下,只有通配符模式(wildcard pattern),标识符模式(identifier pattern)和包含这两种模式的模式才能出现。
|
||||
|
||||
你可以为通配符模式(wildcard pattern),标识符模式(identifier pattern)和元组模式(tuple pattern)指定类型注释,用来限制这种模式只匹配某种类型的值。
|
||||
|
||||
> 模式(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)
|
||||
|
||||
<a name="wildcard_pattern"></a>
|
||||
## 通配符模式(Wildcard Pattern)
|
||||
|
||||
通配符模式匹配并忽略任何值,包含一个下划线(_)。当你不关心被匹配的值时,可以使用此模式。例如,下面这段代码进行了`1...3`的循环,并忽略了每次循环的值:
|
||||
|
||||
for _ in 1...3 {
|
||||
// Do something three times.
|
||||
}
|
||||
|
||||
> 通配符模式语法
|
||||
> *通配符模式* → **_**
|
||||
|
||||
<a name="identifier_pattern"></a>
|
||||
## 标识符模式(Identifier Pattern)
|
||||
|
||||
标识符模式匹配任何值,并将匹配的值和一个变量或常量绑定起来。例如,在下面的常量申明中,`someValue`是一个标识符模式,匹配了类型是`Int`的`42`。
|
||||
|
||||
let someValue = 42
|
||||
|
||||
当匹配成功时,`42`被绑定(赋值)给常量`someValue`。
|
||||
|
||||
当一个变量或常量申明的左边是标识符模式时,此时,标识符模式是隐式的值绑定模式(value-binding pattern)。
|
||||
|
||||
> 标识符模式语法
|
||||
> *标识符模式* → [*标识符*](LexicalStructure.html#identifier)
|
||||
|
||||
<a name="value-binding_pattern"></a>
|
||||
## 值绑定模式(Value-Binding Pattern)
|
||||
|
||||
值绑定模式绑定匹配的值到一个变量或常量。当绑定匹配值给常量时,用关键字`let`,绑定给变量时,用关键之`var`。
|
||||
|
||||
标识符模式包含在值绑定模式中,绑定新的变量或常量到匹配的值。例如,你可以分解一个元组的元素,并把每个元素绑定到相应的标识符模式中。
|
||||
|
||||
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)).")
|
||||
}
|
||||
// prints "The point is at (3, 2).”
|
||||
|
||||
在上面这个例子中,`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)
|
||||
|
||||
<a name="tuple_pattern"></a>
|
||||
## 元组模式(Tuple Pattern)
|
||||
|
||||
元组模式是逗号分隔的列表,包含一个或多个模式,并包含在一对圆括号中。元组模式匹配相应元组类型的值。
|
||||
|
||||
你可以使用类型注释来限制一个元组模式来匹配某种元组类型。例如,在常量申明`let (x, y): (Int, Int) = (1, 2)`中的元组模式`(x, y): (Int, Int)`,只匹配两个元素都是`Int`这种类型的元组。如果仅需要限制一个元组模式中的某几个元素,只需要直接对这几个元素提供类型注释即可。例如,在`let (x: String, y)`中的元组模式,只要某个元组类型是包含两个元素,且第一个元素类型是`String`,则被匹配。
|
||||
|
||||
当元组模式被用在`for-in`语句或者变量或常量申明时,它可以包含通配符模式,标识符模式或者其他包含这两种模式的模式。例如,下面这段代码是不正确的,因为`(x, 0)`中的元素`0`是一个表达式模式:
|
||||
|
||||
let points = [(0, 0), (1, 0), (1, 1), (2, 0), (2, 1)]
|
||||
// This code isn't valid.
|
||||
for (x, 0) in points {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
对于只包含一个元素的元组,括号是不起作用的。模式匹配那个单个元素的类型。例如,下面是等效的:
|
||||
|
||||
let a = 2 // a: Int = 2
|
||||
let (a) = 2 // a: Int = 2
|
||||
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)
|
||||
|
||||
<a name="enumeration_case_pattern"></a>
|
||||
## 枚举用例模式(Enumeration Case Pattern)
|
||||
|
||||
枚举用例模式匹配现有的枚举类型的某种用例。枚举用例模式仅在`switch`语句中的`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) _可选_
|
||||
|
||||
<a name="type-casting_patterns"></a>
|
||||
## 类型转换模式(Type-Casting Patterns)
|
||||
|
||||
有两种类型转换模式,`is`模式和`as`模式。这两种模式均只出现在`switch`语句中的`case`标签中。`is`模式和`as`模式有以下形式:
|
||||
|
||||
is type
|
||||
pattern as type
|
||||
|
||||
`is`模式匹配一个值,如果这个值的类型在运行时(runtime)和`is`模式右边的指定类型(或者那个类型的子类)是一致的。`is`模式和`is`操作符一样,它们都进行类型转换,但是抛弃了返回的类型。
|
||||
|
||||
`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)
|
||||
|
||||
<a name="expression_pattern"></a>
|
||||
## 表达式模式(Expression Pattern)
|
||||
|
||||
表达式模式代表了一个表达式的值。这个模式只出现在`switch`语句中的`case`标签中。
|
||||
|
||||
由表达式模式所代表的表达式用Swift标准库中的`~=`操作符与输入表达式的值进行比较。如果`~=`操作符返回`true`,则匹配成功。默认情况下,`~=`操作符使用`==`操作符来比较两个相同类型的值。它也可以匹配一个整数值与一个`Range`对象中的整数范围,正如下面这个例子所示:
|
||||
|
||||
let point = (1, 2)
|
||||
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.")
|
||||
default:
|
||||
println("The point is at (\(point.0), \(point.1)).")
|
||||
}
|
||||
// prints "(1, 2) is near the origin.”
|
||||
|
||||
你可以重载`~=`操作符来提供自定义的表达式行为。例如,你可以重写上面的例子,以实现用字符串表达的点来比较`point`表达式。
|
||||
|
||||
// Overload the ~= operator to match a string with an integer
|
||||
func ~=(pattern: String, value: Int) -> Bool {
|
||||
return pattern == "\(value)"
|
||||
}
|
||||
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.")
|
||||
default:
|
||||
println("The point is at (\(point.0), \(point.1)).")
|
||||
}
|
||||
// prints "(1, 2) is near the origin.”
|
||||
|
||||
> 表达式模式语法
|
||||
> *表达式模式* → [*表达式*](..\chapter3\04_Expressions.html#expression)
|
||||
|
||||
> 翻译:[honghaoz](https://github.com/honghaoz)
|
||||
> 校对:[numbbbbb](https://github.com/numbbbbb), [stanzhai](https://github.com/stanzhai)
|
||||
|
||||
# 模式(Patterns)
|
||||
-----------------
|
||||
|
||||
本页内容包括:
|
||||
|
||||
- [通配符模式(Wildcard Pattern)](#wildcard_pattern)
|
||||
- [标识符模式(Identifier Pattern)](#identifier_pattern)
|
||||
- [值绑定模式(Value-Binding Pattern)](#value-binding_pattern)
|
||||
- [元组模式(Tuple Pattern)](#tuple_pattern)
|
||||
- [枚举用例模式(Enumeration Case Pattern)](#enumeration_case_pattern)
|
||||
- [类型转换模式(Type-Casting Patterns)](#type-casting_patterns)
|
||||
- [表达式模式(Expression Pattern)](#expression_pattern)
|
||||
|
||||
模式(pattern)代表了单个值或者复合值的结构。例如,元组`(1, 2)`的结构是逗号分隔的,包含两个元素的列表。因为模式代表一种值的结构,而不是特定的某个值,你可以把模式和各种同类型的值匹配起来。比如,`(x, y)`可以匹配元组`(1, 2)`,以及任何含两个元素的元组。除了将模式与一个值匹配外,你可以从合成值中提取出部分或全部,然后分别把各个部分和一个常量或变量绑定起来。
|
||||
|
||||
在Swift中,模式出现在变量和常量的声明(在它们的左侧),`for-in`语句和`switch`语句(在它们的case标签)中。尽管任何模式都可以出现在`switch`语句的case标签中,但在其他情况下,只有通配符模式(wildcard pattern),标识符模式(identifier pattern)和包含这两种模式的模式才能出现。
|
||||
|
||||
你可以为通配符模式(wildcard pattern),标识符模式(identifier pattern)和元组模式(tuple pattern)指定类型注释,用来限制这种模式只匹配某种类型的值。
|
||||
|
||||
> 模式(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)
|
||||
|
||||
<a name="wildcard_pattern"></a>
|
||||
## 通配符模式(Wildcard Pattern)
|
||||
|
||||
通配符模式匹配并忽略任何值,包含一个下划线(_)。当你不关心被匹配的值时,可以使用此模式。例如,下面这段代码进行了`1...3`的循环,并忽略了每次循环的值:
|
||||
|
||||
```swift
|
||||
for _ in 1...3 {
|
||||
// Do something three times.
|
||||
}
|
||||
```
|
||||
|
||||
> 通配符模式语法
|
||||
> *通配符模式* → **_**
|
||||
|
||||
<a name="identifier_pattern"></a>
|
||||
## 标识符模式(Identifier Pattern)
|
||||
|
||||
标识符模式匹配任何值,并将匹配的值和一个变量或常量绑定起来。例如,在下面的常量申明中,`someValue`是一个标识符模式,匹配了类型是`Int`的`42`。
|
||||
|
||||
```swift
|
||||
let someValue = 42
|
||||
```
|
||||
|
||||
当匹配成功时,`42`被绑定(赋值)给常量`someValue`。
|
||||
|
||||
当一个变量或常量申明的左边是标识符模式时,此时,标识符模式是隐式的值绑定模式(value-binding pattern)。
|
||||
|
||||
> 标识符模式语法
|
||||
> *标识符模式* → [*标识符*](LexicalStructure.html#identifier)
|
||||
|
||||
<a name="value-binding_pattern"></a>
|
||||
## 值绑定模式(Value-Binding Pattern)
|
||||
|
||||
值绑定模式绑定匹配的值到一个变量或常量。当绑定匹配值给常量时,用关键字`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)).")
|
||||
}
|
||||
// prints "The point is at (3, 2).”
|
||||
```
|
||||
|
||||
在上面这个例子中,`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)
|
||||
|
||||
<a name="tuple_pattern"></a>
|
||||
## 元组模式(Tuple Pattern)
|
||||
|
||||
元组模式是逗号分隔的列表,包含一个或多个模式,并包含在一对圆括号中。元组模式匹配相应元组类型的值。
|
||||
|
||||
你可以使用类型注释来限制一个元组模式来匹配某种元组类型。例如,在常量申明`let (x, y): (Int, Int) = (1, 2)`中的元组模式`(x, y): (Int, Int)`,只匹配两个元素都是`Int`这种类型的元组。如果仅需要限制一个元组模式中的某几个元素,只需要直接对这几个元素提供类型注释即可。例如,在`let (x: String, y)`中的元组模式,只要某个元组类型是包含两个元素,且第一个元素类型是`String`,则被匹配。
|
||||
|
||||
当元组模式被用在`for-in`语句或者变量或常量申明时,它可以包含通配符模式,标识符模式或者其他包含这两种模式的模式。例如,下面这段代码是不正确的,因为`(x, 0)`中的元素`0`是一个表达式模式:
|
||||
|
||||
```swift
|
||||
let points = [(0, 0), (1, 0), (1, 1), (2, 0), (2, 1)]
|
||||
// This code isn't valid.
|
||||
for (x, 0) in points {
|
||||
/* ... */
|
||||
}
|
||||
```
|
||||
|
||||
对于只包含一个元素的元组,括号是不起作用的。模式匹配那个单个元素的类型。例如,下面是等效的:
|
||||
|
||||
```swift
|
||||
let a = 2 // a: Int = 2
|
||||
let (a) = 2 // a: Int = 2
|
||||
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)
|
||||
|
||||
<a name="enumeration_case_pattern"></a>
|
||||
## 枚举用例模式(Enumeration Case Pattern)
|
||||
|
||||
枚举用例模式匹配现有的枚举类型的某种用例。枚举用例模式仅在`switch`语句中的`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) _可选_
|
||||
|
||||
<a name="type-casting_patterns"></a>
|
||||
## 类型转换模式(Type-Casting Patterns)
|
||||
|
||||
有两种类型转换模式,`is`模式和`as`模式。这两种模式均只出现在`switch`语句中的`case`标签中。`is`模式和`as`模式有以下形式:
|
||||
|
||||
> is `type`
|
||||
> `pattern` as `type`
|
||||
|
||||
`is`模式匹配一个值,如果这个值的类型在运行时(runtime)和`is`模式右边的指定类型(或者那个类型的子类)是一致的。`is`模式和`is`操作符一样,它们都进行类型转换,但是抛弃了返回的类型。
|
||||
|
||||
`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)
|
||||
|
||||
<a name="expression_pattern"></a>
|
||||
## 表达式模式(Expression Pattern)
|
||||
|
||||
表达式模式代表了一个表达式的值。这个模式只出现在`switch`语句中的`case`标签中。
|
||||
|
||||
由表达式模式所代表的表达式用Swift标准库中的`~=`操作符与输入表达式的值进行比较。如果`~=`操作符返回`true`,则匹配成功。默认情况下,`~=`操作符使用`==`操作符来比较两个相同类型的值。它也可以匹配一个整数值与一个`Range`对象中的整数范围,正如下面这个例子所示:
|
||||
|
||||
```swift
|
||||
let point = (1, 2)
|
||||
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.")
|
||||
default:
|
||||
println("The point is at (\(point.0), \(point.1)).")
|
||||
}
|
||||
// prints "(1, 2) is near the origin.”
|
||||
```
|
||||
|
||||
你可以重载`~=`操作符来提供自定义的表达式行为。例如,你可以重写上面的例子,以实现用字符串表达的点来比较`point`表达式。
|
||||
|
||||
```swift
|
||||
// Overload the ~= operator to match a string with an integer
|
||||
func ~=(pattern: String, value: Int) -> Bool {
|
||||
return pattern == "\(value)"
|
||||
}
|
||||
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.")
|
||||
default:
|
||||
println("The point is at (\(point.0), \(point.1)).")
|
||||
}
|
||||
// prints "(1, 2) is near the origin.”
|
||||
```
|
||||
|
||||
> 表达式模式语法
|
||||
> *表达式模式* → [*表达式*](..\chapter3\04_Expressions.html#expression)
|
||||
@ -1,99 +1,106 @@
|
||||
> 翻译:fd5788
|
||||
|
||||
> 校对:yankuangshi
|
||||
|
||||
# 泛型参数
|
||||
---------
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [泛型形参子句](#generic_parameter)
|
||||
- [泛型实参子句](#generic_argument)
|
||||
|
||||
本节涉及泛型类型、泛型函数以及泛型构造器的参数,包括形参和实参。声明泛型类型、函数或构造器时,须指定相应的类型参数。类型参数相当于一个占位符,当实例化泛型类型、调用泛型函数或泛型构造器时,就用具体的类型实参替代之。
|
||||
|
||||
关于 Swift 语言的泛型概述,见[泛型](../charpter2/22_Generics.md)(第二部分第22章)。
|
||||
|
||||
<a name="generic_parameter"></a>
|
||||
## 泛型形参子句
|
||||
|
||||
泛型形参子句指定泛型类型或函数的类型形参,以及这些参数的关联约束和要求。泛型形参子句用尖括号(<>)包住,并且有以下两种形式:
|
||||
|
||||
<generic parameter list>
|
||||
<generic parameter list where requirements >
|
||||
|
||||
泛型形参列表中泛型形参用逗号分开,每一个采用以下形式:
|
||||
|
||||
type parameter : constrain
|
||||
|
||||
泛型形参由两部分组成:类型形参及其后的可选约束。类型形参只是占位符类型(如T,U,V,KeyType,ValueType等)的名字而已。你可以在泛型类型、函数的其余部分或者构造器声明,以及函数或构造器的签名中使用它。
|
||||
|
||||
约束用于指明该类型形参继承自某个类或者遵守某个协议或协议的一部分。例如,在下面的泛型中,泛型形参`T: Comparable`表示任何用于替代类型形参`T`的类型实参必须满足`Comparable`协议。
|
||||
|
||||
func simpleMin<T: COmparable>(x: T, y: T) -> T {
|
||||
if x < y {
|
||||
return y
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
如,`Int`和`Double`均满足`Comparable`协议,该函数接受任何一种类型。与泛型类型相反,调用泛型函数或构造器时不需要指定泛型实参子句。类型实参由传递给函数或构造器的实参推断而出。
|
||||
|
||||
simpleMin(17, 42) // T is inferred to be Int
|
||||
simpleMin(3.14159, 2.71828) // T is inferred to be Double
|
||||
|
||||
## Where 子句
|
||||
|
||||
要想对类型形参及其关联类型指定额外要求,可以在泛型形参列表之后添加`where`子句。`where`子句由关键字`where`及其后的用逗号分割的多个要求组成。
|
||||
|
||||
`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`和`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)
|
||||
> *约束子句* → **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)
|
||||
|
||||
|
||||
<a name="generic_argument"></a>
|
||||
## 泛型实参子句
|
||||
|
||||
泛型实参子句指定_泛型类型_的类型实参。泛型实参子句用尖括号(<>)包住,形式如下:
|
||||
|
||||
< generic argument list >
|
||||
|
||||
泛型实参列表中类型实参有逗号分开。类型实参是实际具体类型的名字,用来替代泛型类型的泛型形参子句中的相应的类型形参。从而得到泛型类型的一个特化版本。如,Swift标准库的泛型字典类型定义如下:
|
||||
|
||||
struct Dictionary<KeyTypel: Hashable, ValueType>: Collection, DictionaryLiteralConvertible {
|
||||
/* .. */
|
||||
}
|
||||
|
||||
泛型`Dictionary`类型的特化版本,`Dictionary<String, Int>`就是用具体的`String`和`Int`类型替代泛型类型`KeyType: Hashable`和`ValueType`产生的。每一个类型实参必须满足它所替代的泛型形参的所有约束,包括任何`where`子句所指定的额外的要求。上面的例子中,类型形参`KeyType`要求满足`Hashable`协议,因此`String`也必须满足`Hashable`协议。
|
||||
|
||||
可以用本身就是泛型类型的特化版本的类型实参替代类型形参(假设已满足合适的约束和要求)。例如,为了生成一个元素类型是整型数组的数组,可以用数组的特化版本`Array<Int>`替代泛型类型`Array<T>`的类型形参`T`来实现。
|
||||
|
||||
let arrayOfArrays: Array<Array<Int>> = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
|
||||
|
||||
如[泛型形参子句](#generic_parameter)所述,不能用泛型实参子句来指定泛型函数或构造器的类型实参。
|
||||
|
||||
> 泛型实参子句语法
|
||||
> *(泛型参数子句Generic Argument Clause)* → **<** [*泛型参数列表*](GenericParametersAndArguments.html#generic_argument_list) **>**
|
||||
> *泛型参数列表* → [*泛型参数*](GenericParametersAndArguments.html#generic_argument) | [*泛型参数*](GenericParametersAndArguments.html#generic_argument) **,** [*泛型参数列表*](GenericParametersAndArguments.html#generic_argument_list)
|
||||
> 翻译:[fd5788](https://github.com/fd5788)
|
||||
> 校对:[yankuangshi](https://github.com/yankuangshi), [stanzhai](https://github.com/stanzhai)
|
||||
|
||||
# 泛型参数
|
||||
---------
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [泛型形参子句](#generic_parameter)
|
||||
- [泛型实参子句](#generic_argument)
|
||||
|
||||
本节涉及泛型类型、泛型函数以及泛型构造器的参数,包括形参和实参。声明泛型类型、函数或构造器时,须指定相应的类型参数。类型参数相当于一个占位符,当实例化泛型类型、调用泛型函数或泛型构造器时,就用具体的类型实参替代之。
|
||||
|
||||
关于 Swift 语言的泛型概述,见[泛型](../charpter2/22_Generics.md)(第二部分第22章)。
|
||||
|
||||
<a name="generic_parameter"></a>
|
||||
## 泛型形参子句
|
||||
|
||||
泛型形参子句指定泛型类型或函数的类型形参,以及这些参数的关联约束和要求。泛型形参子句用尖括号(<>)包住,并且有以下两种形式:
|
||||
|
||||
> <`generic parameter list`>
|
||||
> <`generic parameter list` where `requirements`>
|
||||
|
||||
泛型形参列表中泛型形参用逗号分开,每一个采用以下形式:
|
||||
|
||||
> `type parameter` : `constrain`
|
||||
|
||||
泛型形参由两部分组成:类型形参及其后的可选约束。类型形参只是占位符类型(如T,U,V,KeyType,ValueType等)的名字而已。你可以在泛型类型、函数的其余部分或者构造器声明,以及函数或构造器的签名中使用它。
|
||||
|
||||
约束用于指明该类型形参继承自某个类或者遵守某个协议或协议的一部分。例如,在下面的泛型中,泛型形参`T: Comparable`表示任何用于替代类型形参`T`的类型实参必须满足`Comparable`协议。
|
||||
|
||||
```swift
|
||||
func simpleMin<T: COmparable>(x: T, y: T) -> T {
|
||||
if x < y {
|
||||
return y
|
||||
}
|
||||
return x
|
||||
}
|
||||
```
|
||||
|
||||
如,`Int`和`Double`均满足`Comparable`协议,该函数接受任何一种类型。与泛型类型相反,调用泛型函数或构造器时不需要指定泛型实参子句。类型实参由传递给函数或构造器的实参推断而出。
|
||||
|
||||
```swift
|
||||
simpleMin(17, 42) // T is inferred to be Int
|
||||
simpleMin(3.14159, 2.71828) // T is inferred to be Double
|
||||
```
|
||||
|
||||
## Where 子句
|
||||
|
||||
要想对类型形参及其关联类型指定额外要求,可以在泛型形参列表之后添加`where`子句。`where`子句由关键字`where`及其后的用逗号分割的多个要求组成。
|
||||
|
||||
`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`和`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)
|
||||
> *约束子句* → **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)
|
||||
|
||||
|
||||
<a name="generic_argument"></a>
|
||||
## 泛型实参子句
|
||||
|
||||
泛型实参子句指定_泛型类型_的类型实参。泛型实参子句用尖括号(<>)包住,形式如下:
|
||||
|
||||
> <`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`协议。
|
||||
|
||||
可以用本身就是泛型类型的特化版本的类型实参替代类型形参(假设已满足合适的约束和要求)。例如,为了生成一个元素类型是整型数组的数组,可以用数组的特化版本`Array<Int>`替代泛型类型`Array<T>`的类型形参`T`来实现。
|
||||
|
||||
```swift
|
||||
let arrayOfArrays: Array<Array<Int>> = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
|
||||
```
|
||||
|
||||
如[泛型形参子句](#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)
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,393 +1,322 @@
|
||||
> 翻译:coverxit
|
||||
|
||||
> 校对:numbbbbb, coverxit
|
||||
|
||||
# 语句
|
||||
-----------------
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [循环语句](#loop_statements)
|
||||
- [分支语句](#branch_statements)
|
||||
- [带标签的语句](#labeled_statement)
|
||||
- [控制传递语句](#control_transfer_statements)
|
||||
|
||||
在 Swift 中,有两种类型的语句:简单语句和控制流语句。简单语句是最常见的,用于构造表达式和声明。控制流语句则用于控制程序执行的流程,Swift 中有三种类型的控制流语句:循环语句、分支语句和控制传递语句。
|
||||
|
||||
循环语句用于重复执行代码块;分支语句用于执行满足特定条件的代码块;控制传递语句则用于修改代码的执行顺序。在稍后的叙述中,将会详细地介绍每一种类型的控制流语句。
|
||||
|
||||
是否将分号(`;`)添加到语句的结尾处是可选的。但若要在同一行内写多条独立语句,请务必使用分号。
|
||||
|
||||
> GRAMMAR OF A STATEMENT
|
||||
|
||||
> *statement* → [*expression*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression)**;** *opt*
|
||||
|
||||
> *statement* → [*declaration*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/declaration)**;** *opt*
|
||||
|
||||
> *statement* → [*loop-statement*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/loop-statement)**;** *opt*
|
||||
|
||||
> *statement* → [*branch-statement*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/branch-statement)**;** *opt*
|
||||
|
||||
> *statement* → [*labeled-statement*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/labeled-statement)
|
||||
|
||||
> *statement* → [*control-transfer-statement*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/control-transfer-statement)**;** *opt*
|
||||
|
||||
> *statement* → [*statment*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/statement) [*statements*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/statements)**;** *opt*
|
||||
|
||||
<a name="loop_statements"></a>
|
||||
## 循环语句
|
||||
|
||||
取决于特定的循环条件,循环语句允许重复执行代码块。Swift 提供四种类型的循环语句:`for`语句、`for-in`语句、`while`语句和`do-while`语句。
|
||||
|
||||
通过`break`语句和`continue`语句可以改变循环语句的控制流。有关这两条语句,详情参见 [Break 语句](#break_statement)和 [Continue 语句](#continue_statement)。
|
||||
|
||||
> GRAMMAR OF A LOOP STATEMENT
|
||||
|
||||
> *loop-statement* → [*for-statement*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/for-statement)
|
||||
|
||||
> *loop-statement* → [*for-in-statement*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/for-in-statement)
|
||||
|
||||
> *loop-statement* → [*while-statement*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/while-statement)
|
||||
|
||||
> *loop-statement* → [*do-while-statement*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/do-while-statement)
|
||||
|
||||
### For 语句
|
||||
|
||||
`for`语句允许在重复执行代码块的同时,递增一个计数器。
|
||||
|
||||
`for`语句的形式如下:
|
||||
|
||||
```swift
|
||||
for `initialzation`; `condition`; `increment` {
|
||||
`statements`
|
||||
}
|
||||
```
|
||||
|
||||
*initialzation*、*condition* 和 *increment* 之间的分号,以及包围循环体 *statements* 的大括号都是不可省略的。
|
||||
|
||||
`for`语句的执行流程如下:
|
||||
|
||||
1. *initialzation* 只会被执行一次,通常用于声明和初始化在接下来的循环中需要使用的变量。
|
||||
|
||||
2. 计算 *condition* 表达式:
|
||||
如果为`true`,*statements* 将会被执行,然后转到第3步。如果为`false`,*statements* 和 *increment* 都不会被执行,`for`至此执行完毕。
|
||||
|
||||
3. 计算 *increment* 表达式,然后转到第2步。
|
||||
|
||||
定义在 *initialzation* 中的变量仅在`for`语句的作用域以内有效。*condition* 表达式的值的类型必须遵循`LogicValue`协议。
|
||||
|
||||
> GRAMMAR OF A FOR STATEMENT
|
||||
|
||||
> *for-statement* → **for** [*for-init*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/for-init) *opt* **;** [*expression*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression) *opt* **;** [*expression*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression) *opt* [*code-block*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/code-block)
|
||||
|
||||
> *for-statement* → **for (** [*for-init*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/for-init) *opt* **;** [*expression*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression) *opt* **;** [*expression*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression) *opt* **)** [*code-block*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/code-block)
|
||||
|
||||
> *for-statement* → [*variable-declaration*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/variable-declaration) | [*expression-list*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression-list)
|
||||
|
||||
### For-In 语句
|
||||
|
||||
`for-in`语句允许在重复执行代码块的同时,迭代集合(或遵循`Sequence`协议的任意类型)中的每一项。
|
||||
|
||||
`for-in`语句的形式如下:
|
||||
|
||||
```swift
|
||||
for `item` in `collection` {
|
||||
`statements`
|
||||
}
|
||||
```
|
||||
|
||||
`for-in`语句在循环开始前会调用 *collection* 表达式的`generate`方法来获取一个生成器类型(这是一个遵循`Generator`协议的类型)的值。接下来循环开始,调用 *collection* 表达式的`next`方法。如果其返回值不是`None`,它将会被赋给 *item*,然后执行 *statements*,执行完毕后回到循环开始处;否则,将不会赋值给 *item* 也不会执行 *statements*,`for-in`至此执行完毕。
|
||||
|
||||
> GRAMMAR OF A FOR-IN STATEMENT
|
||||
|
||||
> *for-in-statement* → **for** [*pattern*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Patterns.html#//apple_ref/swift/grammar/pattern) **in** [*expression*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression) [*code-block*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/code-block)
|
||||
|
||||
### While 语句
|
||||
|
||||
`while`语句允许重复执行代码块。
|
||||
|
||||
`while`语句的形式如下:
|
||||
|
||||
```swift
|
||||
while `condition` {
|
||||
`statements`
|
||||
}
|
||||
```
|
||||
|
||||
`while`语句的执行流程如下:
|
||||
|
||||
1. 计算 *condition* 表达式:
|
||||
如果为真`true`,转到第2步。如果为`false`,`while`至此执行完毕。
|
||||
|
||||
2. 执行 *statements* ,然后转到第1步。
|
||||
|
||||
由于 *condition* 的值在 *statements* 执行前就已计算出,因此`while`语句中的 *statements* 可能会被执行若干次,也可能不会被执行。
|
||||
|
||||
*condition* 表达式的值的类型必须遵循`LogicValue`协议。同时,*condition* 表达式也可以使用可选绑定,详情参见[可选绑定](../chapter2/01_The_Basics.html#optional_binding)。
|
||||
|
||||
> GRAMMAR OF A WHILE STATEMENT
|
||||
|
||||
> *while-statement* → **while** [*while-condition*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/while-condition) [*code-block*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/code-block)
|
||||
|
||||
> *while-condition* → [*expression*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression) | [*declaration*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/declaration)
|
||||
|
||||
### Do-While 语句
|
||||
|
||||
`do-while`语句允许代码块被执行一次或多次。
|
||||
|
||||
`do-while`语句的形式如下:
|
||||
|
||||
```swift
|
||||
do {
|
||||
`statements`
|
||||
} while `condition`
|
||||
```
|
||||
|
||||
`do-while`语句的执行流程如下:
|
||||
|
||||
1. 执行 *statements*,然后转到第2步。
|
||||
|
||||
2. 计算 *condition* 表达式:
|
||||
如果为`true`,转到第1步。如果为`false`,`do-while`至此执行完毕。
|
||||
|
||||
由于 *condition* 表达式的值是在 *statements* 执行后才计算出,因此`do-while`语句中的 *statements* 至少会被执行一次。
|
||||
|
||||
*condition* 表达式的值的类型必须遵循`LogicValue`协议。同时,*condition* 表达式也可以使用可选绑定,详情参见[可选绑定](../chapter2/01_The_Basics.html#optional_binding)。
|
||||
|
||||
> GRAMMAR OF A DO-WHILE STATEMENT
|
||||
|
||||
> *do-while-statement* → **do** [*code-block*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/code-block) **while** [*while-condition*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/while-condition)
|
||||
|
||||
<a name="branch_statements"></a>
|
||||
## 分支语句
|
||||
|
||||
取决于一个或者多个条件的值,分支语句允许程序执行指定部分的代码。显然,分支语句中条件的值将会决定如何分支以及执行哪一块代码。Swift 提供两种类型的分支语句:`if`语句和`switch`语句。
|
||||
|
||||
`switch`语句中的控制流可以用`break`语句修改,详情请见[Break 语句](#break_statement)。
|
||||
|
||||
> GRAMMAR OF A BRANCH STATEMENT
|
||||
|
||||
> *branch-statement* → [*if-statement*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/if-statement)
|
||||
|
||||
> *branch-statement* → [*switch-statement*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/switch-statement)
|
||||
|
||||
### If 语句
|
||||
|
||||
取决于一个或多个条件的值,`if`语句将决定执行哪一块代码。
|
||||
|
||||
`if`语句有两种标准形式,在这两种形式里都必须有大括号。
|
||||
|
||||
第一种形式是当且仅当条件为真时执行代码,像下面这样:
|
||||
|
||||
```swift
|
||||
if `condition` {
|
||||
`statements`
|
||||
}
|
||||
```
|
||||
|
||||
第二种形式是在第一种形式的基础上添加 *else 语句*,当只有一个 else 语句时,像下面这样:
|
||||
|
||||
```swift
|
||||
if `condition` {
|
||||
`statements to execute if condition is true`
|
||||
} else {
|
||||
`statements to execute if condition is false`
|
||||
}
|
||||
```
|
||||
|
||||
同时,else 语句也可包含`if`语句,从而形成一条链来测试更多的条件,像下面这样:
|
||||
|
||||
```swift
|
||||
if `condition 1` {
|
||||
`statements to execute if condition 1 is true`
|
||||
} else if `condition 2` {
|
||||
`statements to execute if condition 2 is true`
|
||||
}
|
||||
else {
|
||||
`statements to execute if both conditions are false`
|
||||
}
|
||||
```
|
||||
|
||||
`if`语句中条件的值的类型必须遵循`LogicValue`协议。同时,条件也可以使用可选绑定,详情参见[可选绑定](../chapter2/01_The_Basics.html#optional_binding)。
|
||||
|
||||
> GRAMMAR OF AN IF STATEMENT
|
||||
|
||||
> *if-statement* → **if** [*if-condition*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/if-condition) [*code-block*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/code-block) [*else-clause*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/else-clause) *opt*
|
||||
|
||||
> *if-condition* → [*expression*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression) | [*declaration*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/declaration)
|
||||
|
||||
> *else-clause* → **else** [*code-block*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/code-block) | **else** [*if-statement*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/if-statement) *opt*
|
||||
|
||||
### Switch 语句
|
||||
|
||||
取决于`switch`语句的*控制表达式(control expression)*,`switch`语句将决定执行哪一块代码。
|
||||
|
||||
`switch`语句的形式如下:
|
||||
|
||||
```swift
|
||||
switch `control expression` {
|
||||
case `pattern 1`:
|
||||
`statements`
|
||||
case `pattern 2` where `condition`:
|
||||
`statements`
|
||||
case `pattern 3` where `condition`,
|
||||
`pattern 4` where `condition`:
|
||||
`statements`
|
||||
default:
|
||||
`statements`
|
||||
}
|
||||
```
|
||||
|
||||
`switch`语句的*控制表达式(control expression)*会首先被计算,然后与每一个 case 的模式(pattern)进行匹配。如果匹配成功,程序将会执行对应的 case 分支里的 *statements*。另外,每一个 case 分支都不能为空,也就是说在每一个 case 分支中至少有一条语句。如果你不想在匹配到的 case 分支中执行代码,只需在该分支里写一条`break`语句即可。
|
||||
|
||||
可以用作控制表达式的值是十分灵活的,除了标量类型(scalar types,如`Int`、`Character`)外,你可以使用任何类型的值,包括浮点数、字符串、元组、自定义类的实例和可选(optional)类型,甚至是枚举类型中的成员值和指定的范围(range)等。关于在`switch`语句中使用这些类型,详情参见[控制流](../chapter2/05_Control_Flow.html)一章的 [Switch](../chapter2/05_Control_Flow.html#switch)。
|
||||
|
||||
你可以在模式后面添加一个起保护作用的表达式(guard expression)。*起保护作用的表达式*是这样构成的:关键字`where`后面跟着一个作为额外测试条件的表达式。因此,当且仅当*控制表达式*匹配一个*case*的某个模式且起保护作用的表达式为真时,对应 case 分支中的 *statements* 才会被执行。在下面的例子中,*控制表达式*只会匹配含两个相等元素的元组,如`(1, 1)`:
|
||||
|
||||
```swift
|
||||
case let (x, y) where x == y:
|
||||
```
|
||||
|
||||
正如上面这个例子,也可以在模式中使用`let`(或`var`)语句来绑定常量(或变量)。这些常量(或变量)可以在其对应的起保护作用的表达式和其对应的*case*块里的代码中引用。但是,如果 case 中有多个模式匹配控制表达式,那么这些模式都不能绑定常量(或变量)。
|
||||
|
||||
`switch`语句也可以包含默认(`default`)分支,只有其它 case 分支都无法匹配控制表达式时,默认分支中的代码才会被执行。一个`switch`语句只能有一个默认分支,而且必须在`switch`语句的最后面。
|
||||
|
||||
尽管模式匹配操作实际的执行顺序,特别是模式的计算顺序是不可知的,但是 Swift 规定`switch`语句中的模式匹配的顺序和书写源代码的顺序保持一致。因此,当多个模式含有相同的值且能够匹配控制表达式时,程序只会执行源代码中第一个匹配的 case 分支中的代码。
|
||||
|
||||
#### Switch 语句必须是完备的
|
||||
|
||||
在 Swift 中,`switch`语句中控制表达式的每一个可能的值都必须至少有一个 case 分支与之对应。在某些情况下(例如,表达式的类型是`Int`),你可以使用默认块满足该要求。
|
||||
|
||||
#### 不存在隐式的贯穿(fall through)
|
||||
|
||||
当匹配的 case 分支中的代码执行完毕后,程序会终止`switch`语句,而不会继续执行下一个 case 分支。这就意味着,如果你想执行下一个 case 分支,需要显式地在你需要的 case 分支里使用`fallthrough`语句。关于`fallthrough`语句的更多信息,详情参见 [Fallthrough 语句](#fallthrough_statement)。
|
||||
|
||||
> GRAMMAR OF A SWITCH STATEMENT
|
||||
|
||||
> *switch-statement* → **switch** [*expression*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression) **{** [*switch-cases*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/switch-cases) *opt* **}**
|
||||
|
||||
> *switch-cases* → [*switch-case*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/switch-case) [*switch-cases*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/switch-cases) *opt*
|
||||
|
||||
> *switch-case* → [*case-label*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/case-label) [*statement*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/statements) | [*default-label*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/default-label) [*statements*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/statements)
|
||||
|
||||
> *switch-case* → [*case-label*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/case-label) **;** | [*default-label*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/default-label) **;**
|
||||
|
||||
> *case-label* → **case** [*case-item-list*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/case-item-list) **:**
|
||||
|
||||
> *case-item-list* → [*pattern*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Patterns.html#//apple_ref/swift/grammar/pattern) [*guard-clause*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/guard-clause) *opt* | [*pattern*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Patterns.html#//apple_ref/swift/grammar/pattern) [*guard-clause*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/guard-clause) *opt*, [*case-item-list*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/case-item-list)
|
||||
|
||||
> *default-label* → **default :**
|
||||
|
||||
> *guard-clause* → **where** [*guard-expression*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/guard-expression)
|
||||
|
||||
> *guard-expression* → [*expression*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression)
|
||||
|
||||
<a name="labeled_statement"></a>
|
||||
<a name="control_transfer_statements"></a> 带标签的语句
|
||||
|
||||
你可以在循环语句或`switch`语句前面加上*标签*,它由标签名和紧随其后的冒号(:)组成。在`break`和`continue`后面跟上标签名可以显式地在循环语句或`switch`语句中更改控制流,把控制权传递给指定标签标记的语句。关于这两条语句用法,详情参见 [Break 语句](#break_statement)和 [Continue 语句](#continue_statement)。
|
||||
|
||||
标签的作用域是该标签所标记的语句之后的所有语句。你可以不使用带标签的语句,但只要使用它,标签名就必唯一。
|
||||
|
||||
关于使用带标签的语句的例子,详情参见[控制流](../chapter2/05_Control_Flow.html)一章的[带标签的语句](../chapter2/05_Control_Flow.html#labeled_statements)。
|
||||
|
||||
> GRAMMAR OF A LABELED STATEMENT
|
||||
|
||||
> *labeled-statement* → [*statement-label*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/statement-label) [*loop-statement*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/loop-statement) | [*statement-label*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/statement-label) [*switch-statement*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/switch-statement)
|
||||
|
||||
> *statement-label* → [*label-name*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/label-name) **:**
|
||||
|
||||
> *label-name* → [*identifier*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/swift/grammar/identifier)
|
||||
|
||||
|
||||
## 控制传递语句
|
||||
|
||||
通过无条件地把控制权从一片代码传递到另一片代码,控制传递语句能够改变代码执行的顺序。Swift 提供四种类型的控制传递语句:`break`语句、`continue`语句、`fallthrough`语句和`return`语句。
|
||||
|
||||
> GRAMMAR OF A CONTROL TRANSER STATEMENT
|
||||
|
||||
> *control-transfer-statement* → [*break-statement*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/break-statement)
|
||||
|
||||
> *control-transfer-statement* → [*continue-statement*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/continue-statement)
|
||||
|
||||
> *control-transfer-statement* → [*fallthrough-statement*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/fallthrough-statement)
|
||||
|
||||
> *control-transfer-statement* → [*return-statement*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/return-statement)
|
||||
|
||||
<a name="break_statement"></a>
|
||||
### Break 语句
|
||||
|
||||
`break`语句用于终止循环或`switch`语句的执行。使用`break`语句时,可以只写`break`这个关键词,也可以在`break`后面跟上标签名(label name),像下面这样:
|
||||
|
||||
```swift
|
||||
break
|
||||
break `label name`
|
||||
```
|
||||
|
||||
当`break`语句后面带标签名时,可用于终止由这个标签标记的循环或`switch`语句的执行。
|
||||
|
||||
而当只写`break`时,则会终止`switch`语句或上下文中包含`break`语句的最内层循环的执行。
|
||||
|
||||
在这两种情况下,控制权都会被传递给循环或`switch`语句外面的第一行语句。
|
||||
|
||||
关于使用`break`语句的例子,详情参见[控制流](../chapter2/05_Control_Flow.html)一章的 [Break](../chapter2/05_Control_Flow.html#break) 和[带标签的语句](../chapter2/05_Control_Flow.html#labeled_statements)。
|
||||
|
||||
> GRAMMAR OF A BREAK STATEMENT
|
||||
|
||||
> *break-statement* → **break** [*label-name*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/label-name) *opt*
|
||||
|
||||
<a name="continue_statement"></a>
|
||||
### Continue 语句
|
||||
|
||||
`continue`语句用于终止循环中当前迭代的执行,但不会终止该循环的执行。使用`continue`语句时,可以只写`continue`这个关键词,也可以在`continue`后面跟上标签名(label name),像下面这样:
|
||||
|
||||
```swift
|
||||
continue
|
||||
continue `label name`
|
||||
```
|
||||
|
||||
当`continue`语句后面带标签名时,可用于终止由这个标签标记的循环中当前迭代的执行。
|
||||
|
||||
而当只写`break`时,可用于终止上下文中包含`continue`语句的最内层循环中当前迭代的执行。
|
||||
|
||||
在这两种情况下,控制权都会被传递给循环外面的第一行语句。
|
||||
|
||||
在`for`语句中,`continue`语句执行后,*increment* 表达式还是会被计算,这是因为每次循环体执行完毕后 *increment* 表达式都会被计算。
|
||||
|
||||
关于使用`continue`语句的例子,详情参见[控制流](../chapter2/05_Control_Flow.html)一章的 [Continue](../chapter2/05_Control_Flow.html#continue) 和[带标签的语句](../chapter2/05_Control_Flow.html#labeled_statements)。
|
||||
|
||||
> GRAMMAR OF A CONTINUE STATEMENT
|
||||
|
||||
> *continue-statement* → **continue** [*label-name*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/swift/grammar/label-name) *opt*
|
||||
|
||||
<a name="fallthrough_statement"></a>
|
||||
### Fallthrough 语句
|
||||
|
||||
`fallthrough`语句用于在`switch`语句中传递控制权。`fallthrough`语句会把控制权从`switch`语句中的一个 case 传递给下一个 case 。这种传递是无条件的,即使下一个 case 的模式与`switch`语句的控制表达式的值不匹配。
|
||||
|
||||
`fallthrough`语句可出现在`switch`语句中的任意 case 里,但不能出现在最后一个 case 分支中。同时,`fallthrough`语句也不能把控制权传递给使用了可选绑定的 case 分支。
|
||||
|
||||
关于在`switch`语句中使用`fallthrough`语句的例子,详情参见[控制流](../chapter2/05_Control_Flow.html)一章的[控制传递语句](../chapter2/05_Control_Flow.html#control_transfer_statements)。
|
||||
|
||||
> GRAMMAR OF A FALLTHROUGH STATEMENT
|
||||
|
||||
> *continue-statement* → **fallthrough**
|
||||
|
||||
### Return 语句
|
||||
|
||||
`return`语句用于在函数或方法的实现中将控制权传递给调用者,接着程序将会从调用者的位置继续向下执行。
|
||||
|
||||
使用`return`语句时,可以只写`return`这个关键词,也可以在`return`后面跟上表达式,像下面这样:
|
||||
|
||||
```swift
|
||||
return
|
||||
return `expression`
|
||||
```
|
||||
|
||||
当`return`语句后面带表达式时,表达式的值将会返回给调用者。如果表达式值的类型与调用者期望的类型不匹配,Swift 则会在返回表达式的值之前将表达式值的类型转换为调用者期望的类型。
|
||||
|
||||
而当只写`return`时,仅仅是将控制权从该函数或方法传递给调用者,而不返回一个值。(这就是说,该函数或方法的返回类型为`Void`或`()`)
|
||||
|
||||
> GRAMMAR OF A RETURN STATEMENT
|
||||
|
||||
> *return-statement* → **return** [*expression*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/swift/grammar/expression) *opt*
|
||||
> 翻译:[coverxit](https://github.com/coverxit)
|
||||
> 校对:[numbbbbb](https://github.com/numbbbbb), [coverxit](https://github.com/coverxit), [stanzhai](https://github.com/stanzhai)
|
||||
|
||||
# 语句
|
||||
-----------------
|
||||
|
||||
本页包含内容:
|
||||
|
||||
- [循环语句](#loop_statements)
|
||||
- [分支语句](#branch_statements)
|
||||
- [带标签的语句](#labeled_statement)
|
||||
- [控制传递语句](#control_transfer_statements)
|
||||
|
||||
在 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) _可选_
|
||||
|
||||
<a name="loop_statements"></a>
|
||||
## 循环语句
|
||||
|
||||
取决于特定的循环条件,循环语句允许重复执行代码块。Swift 提供四种类型的循环语句:`for`语句、`for-in`语句、`while`语句和`do-while`语句。
|
||||
|
||||
通过`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 语句
|
||||
|
||||
`for`语句允许在重复执行代码块的同时,递增一个计数器。
|
||||
|
||||
`for`语句的形式如下:
|
||||
|
||||
> for `initialzation`; `condition`; `increment` {
|
||||
> `statements`
|
||||
> }
|
||||
|
||||
*initialzation*、*condition* 和 *increment* 之间的分号,以及包围循环体 *statements* 的大括号都是不可省略的。
|
||||
|
||||
`for`语句的执行流程如下:
|
||||
|
||||
1. *initialzation* 只会被执行一次,通常用于声明和初始化在接下来的循环中需要使用的变量。
|
||||
2. 计算 *condition* 表达式:
|
||||
如果为`true`,*statements* 将会被执行,然后转到第3步。如果为`false`,*statements* 和 *increment* 都不会被执行,`for`至此执行完毕。
|
||||
3. 计算 *increment* 表达式,然后转到第2步。
|
||||
|
||||
定义在 *initialzation* 中的变量仅在`for`语句的作用域以内有效。*condition* 表达式的值的类型必须遵循`LogicValue`协议。
|
||||
|
||||
> 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-In 语句
|
||||
|
||||
`for-in`语句允许在重复执行代码块的同时,迭代集合(或遵循`Sequence`协议的任意类型)中的每一项。
|
||||
|
||||
`for-in`语句的形式如下:
|
||||
|
||||
> for `item` in `collection` {
|
||||
> `statements`
|
||||
> }
|
||||
|
||||
`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)
|
||||
|
||||
### While 语句
|
||||
|
||||
`while`语句允许重复执行代码块。
|
||||
|
||||
`while`语句的形式如下:
|
||||
|
||||
> while `condition` {
|
||||
> `statements`
|
||||
> }
|
||||
|
||||
`while`语句的执行流程如下:
|
||||
|
||||
1. 计算 *condition* 表达式:
|
||||
如果为真`true`,转到第2步。如果为`false`,`while`至此执行完毕。
|
||||
2. 执行 *statements* ,然后转到第1步。
|
||||
|
||||
由于 *condition* 的值在 *statements* 执行前就已计算出,因此`while`语句中的 *statements* 可能会被执行若干次,也可能不会被执行。
|
||||
|
||||
*condition* 表达式的值的类型必须遵循`LogicValue`协议。同时,*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)
|
||||
|
||||
### Do-While 语句
|
||||
|
||||
`do-while`语句允许代码块被执行一次或多次。
|
||||
|
||||
`do-while`语句的形式如下:
|
||||
|
||||
> do {
|
||||
> `statements`
|
||||
> } while `condition`
|
||||
|
||||
`do-while`语句的执行流程如下:
|
||||
|
||||
1. 执行 *statements*,然后转到第2步。
|
||||
2. 计算 *condition* 表达式:
|
||||
如果为`true`,转到第1步。如果为`false`,`do-while`至此执行完毕。
|
||||
|
||||
由于 *condition* 表达式的值是在 *statements* 执行后才计算出,因此`do-while`语句中的 *statements* 至少会被执行一次。
|
||||
|
||||
*condition* 表达式的值的类型必须遵循`LogicValue`协议。同时,*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)
|
||||
|
||||
<a name="branch_statements"></a>
|
||||
## 分支语句
|
||||
|
||||
取决于一个或者多个条件的值,分支语句允许程序执行指定部分的代码。显然,分支语句中条件的值将会决定如何分支以及执行哪一块代码。Swift 提供两种类型的分支语句:`if`语句和`switch`语句。
|
||||
|
||||
`switch`语句中的控制流可以用`break`语句修改,详情请见[Break 语句](#break_statement)。
|
||||
|
||||
> 分支语句语法
|
||||
> *分支语句* → [*if语句*](..\chapter3\10_Statements.html#if_statement)
|
||||
> *分支语句* → [*switch语句*](..\chapter3\10_Statements.html#switch_statement)
|
||||
|
||||
### If 语句
|
||||
|
||||
取决于一个或多个条件的值,`if`语句将决定执行哪一块代码。
|
||||
|
||||
`if`语句有两种标准形式,在这两种形式里都必须有大括号。
|
||||
|
||||
第一种形式是当且仅当条件为真时执行代码,像下面这样:
|
||||
|
||||
> if `condition` {
|
||||
> `statements`
|
||||
> }
|
||||
|
||||
第二种形式是在第一种形式的基础上添加 *else 语句*,当只有一个 else 语句时,像下面这样:
|
||||
|
||||
> if `condition` {
|
||||
> `statements to execute if condition is true`
|
||||
> } else {
|
||||
> `statements to execute if condition is false`
|
||||
> }
|
||||
|
||||
同时,else 语句也可包含`if`语句,从而形成一条链来测试更多的条件,像下面这样:
|
||||
|
||||
> if `condition 1` {
|
||||
> `statements to execute if condition 1 is true`
|
||||
> } else if `condition 2` {
|
||||
> `statements to execute if condition 2 is true`
|
||||
> }
|
||||
> else {
|
||||
> `statements to execute if both conditions are false`
|
||||
> }
|
||||
|
||||
`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)
|
||||
|
||||
### Switch 语句
|
||||
|
||||
取决于`switch`语句的*控制表达式(control expression)*,`switch`语句将决定执行哪一块代码。
|
||||
|
||||
`switch`语句的形式如下:
|
||||
|
||||
> switch `control expression` {
|
||||
> case `pattern 1`:
|
||||
> `statements`
|
||||
> case `pattern 2` where `condition`:
|
||||
> `statements`
|
||||
> case `pattern 3` where `condition`,
|
||||
> `pattern 4` where `condition`:
|
||||
> `statements`
|
||||
> default:
|
||||
> `statements`
|
||||
> }
|
||||
|
||||
`switch`语句的*控制表达式(control expression)*会首先被计算,然后与每一个 case 的模式(pattern)进行匹配。如果匹配成功,程序将会执行对应的 case 分支里的 *statements*。另外,每一个 case 分支都不能为空,也就是说在每一个 case 分支中至少有一条语句。如果你不想在匹配到的 case 分支中执行代码,只需在该分支里写一条`break`语句即可。
|
||||
|
||||
可以用作控制表达式的值是十分灵活的,除了标量类型(scalar types,如`Int`、`Character`)外,你可以使用任何类型的值,包括浮点数、字符串、元组、自定义类的实例和可选(optional)类型,甚至是枚举类型中的成员值和指定的范围(range)等。关于在`switch`语句中使用这些类型,详情参见[控制流](../chapter2/05_Control_Flow.html)一章的 [Switch](../chapter2/05_Control_Flow.html#switch)。
|
||||
|
||||
你可以在模式后面添加一个起保护作用的表达式(guard expression)。*起保护作用的表达式*是这样构成的:关键字`where`后面跟着一个作为额外测试条件的表达式。因此,当且仅当*控制表达式*匹配一个*case*的某个模式且起保护作用的表达式为真时,对应 case 分支中的 *statements* 才会被执行。在下面的例子中,*控制表达式*只会匹配含两个相等元素的元组,如`(1, 1)`:
|
||||
|
||||
```swift
|
||||
case let (x, y) where x == y:
|
||||
```
|
||||
|
||||
正如上面这个例子,也可以在模式中使用`let`(或`var`)语句来绑定常量(或变量)。这些常量(或变量)可以在其对应的起保护作用的表达式和其对应的*case*块里的代码中引用。但是,如果 case 中有多个模式匹配控制表达式,那么这些模式都不能绑定常量(或变量)。
|
||||
|
||||
`switch`语句也可以包含默认(`default`)分支,只有其它 case 分支都无法匹配控制表达式时,默认分支中的代码才会被执行。一个`switch`语句只能有一个默认分支,而且必须在`switch`语句的最后面。
|
||||
|
||||
尽管模式匹配操作实际的执行顺序,特别是模式的计算顺序是不可知的,但是 Swift 规定`switch`语句中的模式匹配的顺序和书写源代码的顺序保持一致。因此,当多个模式含有相同的值且能够匹配控制表达式时,程序只会执行源代码中第一个匹配的 case 分支中的代码。
|
||||
|
||||
#### Switch 语句必须是完备的
|
||||
|
||||
在 Swift 中,`switch`语句中控制表达式的每一个可能的值都必须至少有一个 case 分支与之对应。在某些情况下(例如,表达式的类型是`Int`),你可以使用默认块满足该要求。
|
||||
|
||||
#### 不存在隐式的贯穿(fall through)
|
||||
|
||||
当匹配的 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)
|
||||
> *default标签* → **default** **:**
|
||||
> *guard-clause* → **where** [*guard-expression*](..\chapter3\10_Statements.html#guard_expression)
|
||||
> *guard-expression* → [*表达式*](..\chapter3\04_Expressions.html#expression)
|
||||
|
||||
<a name="labeled_statement"></a>
|
||||
<a name="control_transfer_statements"></a> 带标签的语句
|
||||
|
||||
你可以在循环语句或`switch`语句前面加上*标签*,它由标签名和紧随其后的冒号(:)组成。在`break`和`continue`后面跟上标签名可以显式地在循环语句或`switch`语句中更改控制流,把控制权传递给指定标签标记的语句。关于这两条语句用法,详情参见 [Break 语句](#break_statement)和 [Continue 语句](#continue_statement)。
|
||||
|
||||
标签的作用域是该标签所标记的语句之后的所有语句。你可以不使用带标签的语句,但只要使用它,标签名就必唯一。
|
||||
|
||||
关于使用带标签的语句的例子,详情参见[控制流](../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)
|
||||
|
||||
## 控制传递语句
|
||||
|
||||
通过无条件地把控制权从一片代码传递到另一片代码,控制传递语句能够改变代码执行的顺序。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)
|
||||
|
||||
<a name="break_statement"></a>
|
||||
### Break 语句
|
||||
|
||||
`break`语句用于终止循环或`switch`语句的执行。使用`break`语句时,可以只写`break`这个关键词,也可以在`break`后面跟上标签名(label name),像下面这样:
|
||||
|
||||
> break
|
||||
> break `label name`
|
||||
|
||||
当`break`语句后面带标签名时,可用于终止由这个标签标记的循环或`switch`语句的执行。
|
||||
|
||||
而当只写`break`时,则会终止`switch`语句或上下文中包含`break`语句的最内层循环的执行。
|
||||
|
||||
在这两种情况下,控制权都会被传递给循环或`switch`语句外面的第一行语句。
|
||||
|
||||
关于使用`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) _可选_
|
||||
|
||||
<a name="continue_statement"></a>
|
||||
### Continue 语句
|
||||
|
||||
`continue`语句用于终止循环中当前迭代的执行,但不会终止该循环的执行。使用`continue`语句时,可以只写`continue`这个关键词,也可以在`continue`后面跟上标签名(label name),像下面这样:
|
||||
|
||||
> continue
|
||||
> continue `label name`
|
||||
|
||||
当`continue`语句后面带标签名时,可用于终止由这个标签标记的循环中当前迭代的执行。
|
||||
|
||||
而当只写`break`时,可用于终止上下文中包含`continue`语句的最内层循环中当前迭代的执行。
|
||||
|
||||
在这两种情况下,控制权都会被传递给循环外面的第一行语句。
|
||||
|
||||
在`for`语句中,`continue`语句执行后,*increment* 表达式还是会被计算,这是因为每次循环体执行完毕后 *increment* 表达式都会被计算。
|
||||
|
||||
关于使用`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) _可选_
|
||||
|
||||
<a name="fallthrough_statement"></a>
|
||||
### Fallthrough 语句
|
||||
|
||||
`fallthrough`语句用于在`switch`语句中传递控制权。`fallthrough`语句会把控制权从`switch`语句中的一个 case 传递给下一个 case 。这种传递是无条件的,即使下一个 case 的模式与`switch`语句的控制表达式的值不匹配。
|
||||
|
||||
`fallthrough`语句可出现在`switch`语句中的任意 case 里,但不能出现在最后一个 case 分支中。同时,`fallthrough`语句也不能把控制权传递给使用了可选绑定的 case 分支。
|
||||
|
||||
关于在`switch`语句中使用`fallthrough`语句的例子,详情参见[控制流](../chapter2/05_Control_Flow.html)一章的[控制传递语句](../chapter2/05_Control_Flow.html#control_transfer_statements)。
|
||||
|
||||
> Fallthrough 语句语法
|
||||
> *fallthrough语句* → **fallthrough**
|
||||
|
||||
### Return 语句
|
||||
|
||||
`return`语句用于在函数或方法的实现中将控制权传递给调用者,接着程序将会从调用者的位置继续向下执行。
|
||||
|
||||
使用`return`语句时,可以只写`return`这个关键词,也可以在`return`后面跟上表达式,像下面这样:
|
||||
|
||||
> return
|
||||
> return `expression`
|
||||
|
||||
当`return`语句后面带表达式时,表达式的值将会返回给调用者。如果表达式值的类型与调用者期望的类型不匹配,Swift 则会在返回表达式的值之前将表达式值的类型转换为调用者期望的类型。
|
||||
|
||||
而当只写`return`时,仅仅是将控制权从该函数或方法传递给调用者,而不返回一个值。(这就是说,该函数或方法的返回类型为`Void`或`()`)
|
||||
|
||||
> Return 语句语法
|
||||
> *return语句* → **return** [*表达式*](..\chapter3\04_Expressions.html#expression) _可选_
|
||||
|
||||
Reference in New Issue
Block a user