diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..e43b0f98 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/README.md b/README.md index 9b6e18c7..352c17a8 100755 --- a/README.md +++ b/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 \ No newline at end of file diff --git a/chapter1/01_swift.html b/chapter1/01_swift.html index 6225bbd5..8cd2f0cc 100644 --- a/chapter1/01_swift.html +++ b/chapter1/01_swift.html @@ -10,7 +10,7 @@ - + @@ -46,7 +46,7 @@ -
-翻译:numbbbbb
+
校对:yeahdongcn翻译:numbbbbb
校对:yeahdongcn
-翻译:numbbbbb
+
校对:shinyzhu, stanzhai
在本章中您将了解 Swift 的特性和开发历史,并对 Swift 有一个初步的了解。
diff --git a/chapter2/01_The_Basics.html b/chapter2/01_The_Basics.html index 8b04d051..3a88c6eb 100644 --- a/chapter2/01_The_Basics.html +++ b/chapter2/01_The_Basics.html @@ -10,7 +10,7 @@ - + @@ -46,7 +46,7 @@ --翻译:numbbbbb, lyuka, JaySurplus
+
校对:lslxdx翻译:numbbbbb, lyuka, JaySurplus
校对:lslxdx
-翻译:xielingwang
+
校对:Evilcome翻译:xielingwang
校对:Evilcome
-翻译:wh1100717
+
校对:Hawstein
-翻译:zqp -校对:shinyzhu
+
Swift 语言提供经典的数组和字典两种集合类型来存储集合数据。数组用来按顺序存储相同类型的数据。字典虽然无序存储相同类型数据值但是需要由独有的标识符引用和寻址(就是键值对)。
Swift 语言里的数组和字典中存储的数据值类型必须明确。 这意味着我们不能把不正确的数据类型插入其中。 同时这也说明我们完全可以对获取出的值类型非常自信。 Swift 对显式类型集合的使用确保了我们的代码对工作所需要的类型非常清楚,也让我们在开发中可以早早地找到任何的类型不匹配错误。
-注意: -Swift 的数组结构在被声明成常量和变量或者被传入函数与方法中时会相对于其他类型展现出不同的特性。 获取更多信息请参见集合的可变性与集合在赋值和复制中的行为章节。
+注意:
Swift 的数组结构在被声明成常量和变量或者被传入函数与方法中时会相对于其他类型展现出不同的特性。 获取更多信息请参见集合的可变性与集合在赋值和复制中的行为章节。
shoppingList变量被声明为“字符串值类型的数组“,记作String[]。 因为这个数组被规定只有String一种数据结构,所以只有String类型可以在其中被存取。 在这里,shoppinglist数组由两个String值("Eggs" 和"Milk")构造,并且由字面量定义。
-注意: -
+Shoppinglist数组被声明为变量(var关键字创建)而不是常量(let创建)是因为以后可能会有更多的数据项被插入其中。注意:
Shoppinglist数组被声明为变量(var关键字创建)而不是常量(let创建)是因为以后可能会有更多的数据项被插入其中。
在这个例子中,字面量仅仅包含两个String值。匹配了该数组的变量声明(只能包含String的数组),所以这个字面量的分配过程就是允许用两个初始项来构造shoppinglist。
由于 Swift 的类型推断机制,当我们用字面量构造只拥有相同类型值数组的时候,我们不必把数组的类型定义清楚。 shoppinglist的构造也可以这样写:
shoppingList[4...6] = ["Bananas", "Apples"]
// shoppingList 现在有六项
-<<<<<<< HEAD
-注意:
-我们不能使用下标语法在数组尾部添加新项。如果我们试着用这种方法对索引越界的数据进行检索或者设置新值的操作,我们会引发一个运行期错误。我们可以使用索引值和数组的
-count属性进行比较来在使用某个索引之前先检验是否有效。除了当count等于 0 时(说明这是个空数组),最大索引值一直是count - 1,因为数组都是零起索引。注意:
-我们不能使用下标语法在数组尾部添加新项。如果我们试着用这种方法对索引越界的数据进行检索或者设置新值的操作,我们会引发一个运行时错误。我们可以使用索引值和数组的
-count属性进行比较来在使用某个索引之前先检验是否有效。除了当count等于 0 时(说明这是个空数组),最大索引值一直是count - 1,因为数组都是零起索引。-+----------a516af6a531a104ec88da0d236ecf389a5ec72af
-注意:
我们不能使用下标语法在数组尾部添加新项。如果我们试着用这种方法对索引越界的数据进行检索或者设置新值的操作,我们会引发一个运行期错误。我们可以使用索引值和数组的count属性进行比较来在使用某个索引之前先检验是否有效。除了当count等于 0 时(说明这是个空数组),最大索引值一直是count - 1,因为数组都是零起索引。
调用数组的insert(atIndex:)方法来在某个具体索引值之前添加数据项:
shoppingList.insert("Maple Syrup", atIndex: 0)
@@ -780,8 +760,7 @@ someInts = []
airports字典被定义为一种Dictionary<String, String>,它意味着这个字典的键和值都是String类型。
-注意: -
+airports字典被声明为变量(用var关键字)而不是常量(let关键字)因为后来更多的机场信息会被添加到这个示例字典中。注意:
airports字典被声明为变量(用var关键字)而不是常量(let关键字)因为后来更多的机场信息会被添加到这个示例字典中。
airports字典使用字典字面量初始化,包含两个键值对。第一对的键是TYO,值是Tokyo。第二对的键是DUB,值是Dublin。
这个字典语句包含了两个String: String类型的键值对。它们对应airports变量声明的类型(一个只有String键和String值的字典)所以这个字典字面量是构造两个初始数据项的airport字典。
for airportName in airports.values {
+
+for airportName in airports.values {
println("Airport name: \(airportName)")
}
// Airport name: Tokyo
@@ -858,13 +837,12 @@ airports["APL"] = nil
如果我们只是需要使用某个字典的键集合或者值集合来作为某个接受Array实例 API 的参数,可以直接使用keys或者values属性直接构造一个新数组:
let airportCodes = Array(airports.keys)
// airportCodes is ["TYO", "LHR"]
-
-let airportNames = Array(airports.values)
+
+let airportNames = Array(airports.values)
// airportNames is ["Tokyo", "London Heathrow"]
-注意:
-Swift 的字典类型是无序集合类型。其中字典键,值,键值对在遍历的时候会重新排列,而且其中顺序是不固定的。
+注意:
Swift 的字典类型是无序集合类型。其中字典键,值,键值对在遍历的时候会重新排列,而且其中顺序是不固定的。
创建一个空字典
@@ -880,8 +858,7 @@ namesOfIntegers = [:]
// namesOfIntegers 又成为了一个 Int, String类型的空字典
-注意: -在后台,Swift 的数组和字典都是由泛型集合来实现的,想了解更多泛型和集合信息请参见泛型。
+注意:
在后台,Swift 的数组和字典都是由泛型集合来实现的,想了解更多泛型和集合信息请参见泛型。
Swift 数组的可变性行为同时影响了数组实例如何被分配和修改,想获取更多信息,请参见集合在赋值和复制中的行为。
-diff --git a/chapter2/05_Control_Flow.html b/chapter2/05_Control_Flow.html index b4647dc3..19448b10 100644 --- a/chapter2/05_Control_Flow.html +++ b/chapter2/05_Control_Flow.html @@ -10,7 +10,7 @@ - + @@ -46,7 +46,7 @@ -注意: -在我们不需要改变数组大小的时候创建不可变数组是很好的习惯。如此 Swift 编译器可以优化我们创建的集合。
+注意:
在我们不需要改变数组大小的时候创建不可变数组是很好的习惯。如此 Swift 编译器可以优化我们创建的集合。
-翻译:vclwei, coverxit, NicePiao
+
校对:coverxit, stanzhai
-翻译:honghaoz
+
校对:LunaticM
函数是用来完成特定任务的独立的代码块。你给一个函数起一个合适的名字,用来标示函数做什么,并且当函数需要执行的时候,这个名字会被“调用”。
-Swift 统一的函数语法足够灵活,可以用来表示任何函数,包括从最简单的没有参数名字的 C 风格函数,到复杂的带局部和外部参数名的 Objective-C 风格函数。参数可以提供默认值,以简化函数调用。参数也可以即当做传入参数,也当做传出参数,也就是说,一旦函数执行结束,传入的参数值可以被修改。
+Swift 统一的函数语法足够灵活,可以用来表示任何函数,包括从最简单的没有参数名字的 C 风格函数,到复杂的带局部和外部参数名的 Objective-C 风格函数。参数可以提供默认值,以简化函数调用。参数也可以既当做传入参数,也当做传出参数,也就是说,一旦函数执行结束,传入的参数值可以被修改。
在 Swift 中,每个函数都有一种类型,包括函数的参数值类型和返回值类型。你可以把函数类型当做任何其他普通变量类型一样处理,这样就可以更简单地把函数当做别的函数的参数,也可以从其他函数中返回函数。函数的定义可以写在在其他函数定义中,这样可以在嵌套函数范围内实现功能封装。
-翻译:wh1100717
+
校对:lyuka
-翻译:yankuangshi
+
校对:shinyzhu翻译:yankuangshi
校对:shinyzhu
使用enum关键词并且把它们的整个定义放在一对大括号内:
enum SomeEumeration {
+enum SomeEnumeration {
// enumeration definition goes here
}
@@ -721,7 +721,7 @@ case let .QRCode(productCode):
case CarriageReturn = "\r"
}
-在这里,称为ASCIIControlCharacter的枚举的原始值类型被定义为字符型Character,并被设置了一些比较常见的 ASCII 控制字符。字符值的描述请详见字符串和字符Strings and Characters部分。
在这里,称为ASCIIControlCharacter的枚举的原始值类型被定义为字符型Character,并被设置了一些比较常见的 ASCII 控制字符。字符值的描述请详见字符串和字符Strings and Characters部分。
注意,原始值和实例值是不相同的。当你开始在你的代码中定义枚举的时候原始值是被预先填充的值,像上述三个 ASCII 码。对于一个特定的枚举成员,它的原始值始终是相同的。实例值是当你在创建一个基于枚举成员的新常量或变量时才会被设置,并且每次当你这么做得时候,它的值可以是不同的。
原始值可以是字符串,字符,或者任何整型值或浮点型值。每个原始值在它的枚举声明中必须是唯一的。当整型值被用于原始值,如果其他枚举成员没有值时,它们会自动递增。
下面的枚举是对之前Planet这个枚举的一个细化,利用原始整型值来表示每个 planet 在太阳系中的顺序:
-翻译:JaySurplus
+
校对:sg552翻译:JaySurplus
校对:sg552
本页包含内容:
@@ -609,36 +609,13 @@Swift 中类和结构体有很多共同点。共同处在于:
-<<<<<<< HEAD
--------------a516af6a531a104ec88da0d236ecf389a5ec72af
-
更多信息请参见 属性,方法,下标脚本,初始过程,扩展,和协议。
与结构体相比,类还有如下的附加功能:
diff --git a/chapter2/10_Properties.html b/chapter2/10_Properties.html index 03e90964..bea227b2 100644 --- a/chapter2/10_Properties.html +++ b/chapter2/10_Properties.html @@ -10,7 +10,7 @@ - + @@ -46,7 +46,7 @@ --翻译:shinyzhu
+
校对:pp-prog
只有 getter 没有 setter 的计算属性就是只读计算属性。只读计算属性总是返回一个值,可以通过点运算符访问,但不能设置新的值。
-<<<<<<< HEAD
--注意:
-必须使用
var关键字定义计算属性,包括只读计算属性,因为他们的值不是固定的。let关键字只用来声明常量属性,表示初始化后再也无法修改的值。注意:
必须使用
var关键字定义计算属性,包括只读计算属性,因为它们的值不是固定的。let关键字只用来声明常量属性,表示初始化后再也无法修改的值。@@ -782,10 +778,6 @@ println("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
willSet监视器会将新的属性值作为固定参数传入,在willSet的实现代码中可以为这个参数指定一个名称,如果不指定则参数仍然可用,这时使用默认名称newValue表示。类似地,
-didSet监视器会将旧的属性值作为参数传入,可以为该参数命名或者使用默认参数名oldValue。<<<<<<< HEAD
--注意:
-
willSet和didSet监视器在属性初始化过程中不会被调用,他们只会当属性的值在初始化之外的地方被设置时被调用。注意:
willSet和didSet监视器在属性初始化过程中不会被调用,它们只会当属性的值在初始化之外的地方被设置时被调用。diff --git a/chapter2/11_Methods.html b/chapter2/11_Methods.html index fd8c6d32..e6450517 100644 --- a/chapter2/11_Methods.html +++ b/chapter2/11_Methods.html @@ -10,7 +10,7 @@ - + @@ -46,7 +46,7 @@ -+@@ -587,10 +587,10 @@-+ -翻译:pp-prog
+
校对:zqp方法(Methods)
diff --git a/chapter2/12_Subscripts.html b/chapter2/12_Subscripts.html index 59da1110..c54e8553 100644 --- a/chapter2/12_Subscripts.html +++ b/chapter2/12_Subscripts.html @@ -10,7 +10,7 @@ - + @@ -46,7 +46,7 @@ -+@@ -587,10 +587,10 @@-+ -翻译:siemenliu
+
校对:zq54zquan下标脚本(Subscripts)
@@ -602,23 +602,8 @@下标脚本 可以定义在类(Class)、结构体(structure)和枚举(enumeration)这些目标中,可以认为是访问对象、集合或序列的快捷方式,不需要再调用实例的特定的赋值和访问方法。举例来说,用下标脚本访问一个数组(Array)实例中的元素可以这样写
someArray[index],访问字典(Dictionary)实例中的元素可以这样写someDictionary[key]。对于同一个目标可以定义多个下标脚本,通过索引值类型的不同来进行重载,而且索引值的个数可以是多个。
-<<<<<<< HEAD
-> 译者:这里附属脚本重载在本小节中原文并没有任何演示
-译者:这里下标脚本重载在本小节中原文并没有任何演示
--+----------a516af6a531a104ec88da0d236ecf389a5ec72af
-译者:这里附属脚本重载在本小节中原文并没有任何演示
下标脚本语法
@@ -652,25 +637,8 @@ println("3的6倍是\(threeTimesTable[6])")在上例中,通过
TimesTable结构体创建了一个用来表示索引值三倍的实例。数值3作为结构体构造函数入参初始化实例成员multiplier。你可以通过下标脚本来得到结果,比如
-threeTimesTable[6]。这条语句访问了threeTimesTable的第六个元素,返回6的3倍即18。<<<<<<< HEAD
-注意:
--
TimesTable例子是基于一个固定的数学公式。它并不适合开放写权限来对threeTimesTable[someIndex]进行赋值操作,这也是为什么附属脚本只定义为只读的原因。注意:
--
TimesTable例子是基于一个固定的数学公式。它并不适合开放写权限来对threeTimesTable[someIndex]进行赋值操作,这也是为什么下标脚本只定义为只读的原因。-+----------a516af6a531a104ec88da0d236ecf389a5ec72af
-注意:
TimesTable例子是基于一个固定的数学公式。它并不适合开放写权限来对threeTimesTable[someIndex]进行赋值操作,这也是为什么附属脚本只定义为只读的原因。下标脚本用法
@@ -681,25 +649,8 @@ numberOfLegs["bird"] = 2上例定义一个名为
numberOfLegs的变量并用一个字典字面量初始化出了包含三对键值的字典实例。numberOfLegs的字典存放值类型推断为Dictionary<String, Int>。字典实例创建完成之后通过下标脚本的方式将整型值2赋值到字典实例的索引为bird的位置中。更多关于字典(Dictionary)下标脚本的信息请参考读取和修改字典
-<<<<<<< HEAD
-注意:
-Swift 中字典的附属脚本实现中,在
-get部分返回值是Int?,上例中的numberOfLegs字典通过附属脚本返回的是一个Int?或者说“可选的int”,不是每个字典的索引都能得到一个整型值,对于没有设过值的索引的访问返回的结果就是nil;同样想要从字典实例中删除某个索引下的值也只需要给这个索引赋值为nil即可。注意:
-Swift 中字典的下标脚本实现中,在
-get部分返回值是Int?,上例中的numberOfLegs字典通过下标脚本返回的是一个Int?或者说“可选的int”,不是每个字典的索引都能得到一个整型值,对于没有设过值的索引的访问返回的结果就是nil;同样想要从字典实例中删除某个索引下的值也只需要给这个索引赋值为nil即可。-+----------a516af6a531a104ec88da0d236ecf389a5ec72af
-注意:
Swift 中字典的附属脚本实现中,在get部分返回值是Int?,上例中的numberOfLegs字典通过附属脚本返回的是一个Int?或者说“可选的int”,不是每个字典的索引都能得到一个整型值,对于没有设过值的索引的访问返回的结果就是nil;同样想要从字典实例中删除某个索引下的值也只需要给这个索引赋值为nil即可。下标脚本选项
diff --git a/chapter2/13_Inheritance.html b/chapter2/13_Inheritance.html index 84997154..de0485c0 100644 --- a/chapter2/13_Inheritance.html +++ b/chapter2/13_Inheritance.html @@ -10,7 +10,7 @@ - + @@ -46,7 +46,7 @@ -+@@ -587,10 +587,10 @@-+ -翻译:Hawstein
+
校对:menlongsheng翻译:Hawstein
校对:menlongsheng继承(Inheritance)
diff --git a/chapter2/14_Initialization.html b/chapter2/14_Initialization.html index 26a7fd41..957224cc 100644 --- a/chapter2/14_Initialization.html +++ b/chapter2/14_Initialization.html @@ -10,7 +10,7 @@ - + @@ -46,7 +46,7 @@ -+@@ -587,10 +587,10 @@-+ -翻译:lifedim
+
校对:lifedim构造过程(Initialization)
diff --git a/chapter2/15_Deinitialization.html b/chapter2/15_Deinitialization.html index db3f201b..7bcdc3ba 100644 --- a/chapter2/15_Deinitialization.html +++ b/chapter2/15_Deinitialization.html @@ -10,7 +10,7 @@ - + @@ -46,7 +46,7 @@ -+@@ -587,10 +587,10 @@-+ -翻译:bruce0505
+
校对:fd5788析构过程(Deinitialization)
diff --git a/chapter2/16_Automatic_Reference_Counting.html b/chapter2/16_Automatic_Reference_Counting.html index d264db13..23818d6a 100644 --- a/chapter2/16_Automatic_Reference_Counting.html +++ b/chapter2/16_Automatic_Reference_Counting.html @@ -10,7 +10,7 @@ - + @@ -46,7 +46,7 @@ -+@@ -587,11 +587,10 @@-+ diff --git a/chapter2/17_Optional_Chaining.html b/chapter2/17_Optional_Chaining.html index c07da2a9..c98610b6 100644 --- a/chapter2/17_Optional_Chaining.html +++ b/chapter2/17_Optional_Chaining.html @@ -10,7 +10,7 @@ - + @@ -46,7 +46,7 @@ - -翻译:TimothyYe
-校对:Hawstein
+自动引用计数
@@ -607,8 +606,7 @@Swift 使用自动引用计数(ARC)这一机制来跟踪和管理你的应用程序的内存。通常情况下,Swift 的内存管理机制会一直起着作用,你无须自己来考虑内存的管理。ARC 会在类的实例不再被使用时,自动释放其占用的内存。
然而,在少数情况下,ARC 为了能帮助你管理内存,需要更多的关于你的代码之间关系的信息。本章描述了这些情况,并且为你示范怎样启用 ARC 来管理你的应用程序的内存。
-注意:
-引用计数仅仅应用于类的实例。结构体和枚举类型是值类型,不是引用类型,也不是通过引用的方式存储和传递。
+注意:
引用计数仅仅应用于类的实例。结构体和枚举类型是值类型,不是引用类型,也不是通过引用的方式存储和传递。自动引用计数的工作机制
@@ -619,77 +617,86 @@自动引用计数实践
下面的例子展示了自动引用计数的工作机制。例子以一个简单的
-Person类开始,并定义了一个叫name的常量属性:+class Person { - let name: String - - init(name: String) { - self.name = name - println("\(name) is being initialized") - } - - deinit { - println("\(name) is being deinitialized") - } +class Person { + let name: String + init(name: String) { + self.name = name + println("\(name) is being initialized") } -+ deinit { + println("\(name) is being deinitialized") + } +} +
Person类有一个构造函数,此构造函数为实例的name属性赋值并打印出信息,以表明初始化过程生效。Person类同时也拥有析构函数,同样会在实例被销毁的时候打印出信息。
Person类有一个构造函数,此构造函数为实例的name属性赋值并打印出信息,以表明初始化过程生效。Person类同时也拥有析构函数,同样会在实例被销毁的时候打印出信息。接下来的代码片段定义了三个类型为
-Person?的变量,用来按照代码片段中的顺序,为新的Person实例建立多个引用。由于这些变量是被定义为可选类型(Person?,而不是Person),它们的值会被自动初始化为nil,目前还不会引用到Person类的实例。var reference1: Person? - var reference2: Person? - var reference3: Person? -现在你可以创建
-Person类的新实例,并且将它赋值给三个变量其中的一个:reference1 = Person(name: "John Appleseed") - // prints "John Appleseed is being initialized” -应当注意到当你调用
+Person类的构造函数的时候,"John Appleseed is being initialized”会被打印出来。由此可以确定构造函数被执行。+var reference1: Person? +var reference2: Person? +var reference3: Person? +现在你可以创建
+Person类的新实例,并且将它赋值给三个变量其中的一个:+reference1 = Person(name: "John Appleseed") +// prints "John Appleseed is being initialized” +应当注意到当你调用
Person类的构造函数的时候,"John Appleseed is being initialized”会被打印出来。由此可以确定构造函数被执行。由于
Person类的新实例被赋值给了reference1变量,所以reference1到Person类的新实例之间建立了一个强引用。正是因为这个强引用,ARC 会保证Person实例被保持在内存中不被销毁。如果你将同样的
-Person实例也赋值给其他两个变量,该实例又会多出两个强引用:reference2 = reference1 - reference3 = reference1 -现在这个
+Person实例已经有三个强引用了。+reference2 = reference1 +reference3 = reference1 +现在这个
Person实例已经有三个强引用了。如果你通过给两个变量赋值
-nil的方式断开两个强引用()包括最先的那个强引用),只留下一个强引用,Person实例不会被销毁:+reference2 = nil +reference2 = nil reference3 = nil -ARC 会在第三个,也即最后一个强引用被断开的时候,销毁
-Person实例,这也意味着你不再使用这个Person实例:+reference3 = nil +ARC 会在第三个,也即最后一个强引用被断开的时候,销毁
+Person实例,这也意味着你不再使用这个Person实例:+reference3 = nil // prints "John Appleseed is being deinitialized" -类实例之间的循环强引用
在上面的例子中,ARC 会跟踪你所新创建的
Person实例的引用数量,并且会在Person实例不再被需要时销毁它。然而,我们可能会写出这样的代码,一个类永远不会有0个强引用。这种情况发生在两个类实例互相保持对方的强引用,并让对方不被销毁。这就是所谓的循环强引用。
你可以通过定义类之间的关系为弱引用或者无主引用,以此替代强引用,从而解决循环强引用的问题。具体的过程在解决类实例之间的循环强引用中有描述。不管怎样,在你学习怎样解决循环强引用之前,很有必要了解一下它是怎样产生的。
下面展示了一个不经意产生循环强引用的例子。例子定义了两个类:
-Person和Apartment,用来建模公寓和它其中的居民:+class Person { ++class Person { let name: String init(name: String) { self.name = name } var apartment: Apartment? deinit { println("\(name) is being deinitialized") } } - -class Apartment { +class Apartment { let number: Int init(number: Int) { self.number = number } var tenant: Person? deinit { println("Apartment #\(number) is being deinitialized") } } -每一个
+Person实例有一个类型为String,名字为name的属性,并有一个可选的初始化为nil的apartment属性。apartment属性是可选的,因为一个人并不总是拥有公寓。每一个
Person实例有一个类型为String,名字为name的属性,并有一个可选的初始化为nil的apartment属性。apartment属性是可选的,因为一个人并不总是拥有公寓。类似的,每个
Apartment实例有一个叫number,类型为Int的属性,并有一个可选的初始化为nil的tenant属性。tenant属性是可选的,因为一栋公寓并不总是有居民。这两个类都定义了析构函数,用以在类实例被析构的时候输出信息。这让你能够知晓
Person和Apartment的实例是否像预期的那样被销毁。接下来的代码片段定义了两个可选类型的变量
-john和number73,并分别被设定为下面的Apartment和Person的实例。这两个变量都被初始化为nil,并为可选的:+var john: Person? +var john: Person? var number73: Apartment? -现在你可以创建特定的
-Person和Apartment实例并将类实例赋值给john和number73变量:+john = Person(name: "John Appleseed") +现在你可以创建特定的
+Person和Apartment实例并将类实例赋值给john和number73变量:john = Person(name: "John Appleseed") number73 = Apartment(number: 73) -在两个实例被创建和赋值后,下图表现了强引用的关系。变量
+john现在有一个指向Person实例的强引用,而变量number73有一个指向Apartment实例的强引用:在两个实例被创建和赋值后,下图表现了强引用的关系。变量
john现在有一个指向Person实例的强引用,而变量number73有一个指向Apartment实例的强引用:
现在你能够将这两个实例关联在一起,这样人就能有公寓住了,而公寓也有了房客。注意感叹号是用来展开和访问可选变量
-john和number73中的实例,这样实例的属性才能被赋值:+john!.apartment = number73 +john!.apartment = number73 number73!.tenant = john -在将两个实例联系在一起之后,强引用的关系如图所示:
+在将两个实例联系在一起之后,强引用的关系如图所示:
不幸的是,将这两个实例关联在一起之后,一个循环强引用被创建了。
-Person实例现在有了一个指向Apartment实例的强引用,而Apartment实例也有了一个指向Person实例的强引用。因此,当你断开john和number73变量所持有的强引用时,引用计数并不会降为 0,实例也不会被 ARC 销毁:+john = nil +john = nil number73 = nil -注意,当你把这两个变量设为
+nil时,没有任何一个析构函数被调用。强引用循环阻止了Person和Apartment类实例的销毁,并在你的应用程序中造成了内存泄漏。注意,当你把这两个变量设为
nil时,没有任何一个析构函数被调用。强引用循环阻止了Person和Apartment类实例的销毁,并在你的应用程序中造成了内存泄漏。在你将
john和number73赋值为nil后,强引用关系如下图:
@@ -702,27 +709,27 @@ number73 = nil
Person和Apartment实例之间的强引用关系保留了下来并且不会被断开。弱引用不会牢牢保持住引用的实例,并且不会阻止 ARC 销毁被引用的实例。这种行为阻止了引用变为循环强引用。声明属性或者变量时,在前面加上
weak关键字表明这是一个弱引用。在实例的生命周期中,如果某些时候引用没有值,那么弱引用可以阻止循环强引用。如果引用总是有值,则可以使用无主引用,在无主引用中有描述。在上面
Apartment的例子中,一个公寓的生命周期中,有时是没有“居民”的,因此适合使用弱引用来解决循环强引用。-注意: -弱引用必须被声明为变量,表明其值能在运行时被修改。弱引用不能被声明为常量。
+注意:
弱引用必须被声明为变量,表明其值能在运行时被修改。弱引用不能被声明为常量。因为弱引用可以没有值,你必须将每一个弱引用声明为可选类型。可选类型是在 Swift 语言中推荐的用来表示可能没有值的类型。
因为弱引用不会保持所引用的实例,即使引用存在,实例也有可能被销毁。因此,ARC 会在引用的实例被销毁后自动将其赋值为
nil。你可以像其他可选值一样,检查弱引用的值是否存在,你永远也不会遇到被销毁了而不存在的实例。下面的例子跟上面
-Person和Apartment的例子一致,但是有一个重要的区别。这一次,Apartment的tenant属性被声明为弱引用:+class Person { ++class Person { let name: String init(name: String) { self.name = name } var apartment: Apartment? deinit { println("\(name) is being deinitialized") } } - -class Apartment { +class Apartment { let number: Int init(number: Int) { self.number = number } weak var tenant: Person? deinit { println("Apartment #\(number) is being deinitialized") } } -然后跟之前一样,建立两个变量(john和number73)之间的强引用,并关联两个实例:
-+var john: Person? +然后跟之前一样,建立两个变量(john和number73)之间的强引用,并关联两个实例:
+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 -现在,两个关联在一起的实例的引用关系如下图所示:
+现在,两个关联在一起的实例的引用关系如下图所示:
Person实例依然保持对Apartment实例的强引用,但是Apartment实例只是对Person实例的弱引用。这意味着当你断开john变量所保持的强引用时,再也没有指向Person实例的强引用了:
由于再也没有指向
-Person实例的强引用,该实例会被销毁:+john = nil +john = nil // prints "John Appleseed is being deinitialized" -唯一剩下的指向
+Apartment实例的强引用来自于变量number73。如果你断开这个强引用,再也没有指向Apartment实例的强引用了:唯一剩下的指向
Apartment实例的强引用来自于变量number73。如果你断开这个强引用,再也没有指向Apartment实例的强引用了:
由于再也没有指向
-Apartment实例的强引用,该实例也会被销毁:+number73 = nil +number73 = nil // prints "Apartment #73 is being deinitialized" -上面的两段代码展示了变量
+john和number73在被赋值为nil后,Person实例和Apartment实例的析构函数都打印出“销毁”的信息。这证明了引用循环被打破了。上面的两段代码展示了变量
john和number73在被赋值为nil后,Person实例和Apartment实例的析构函数都打印出“销毁”的信息。这证明了引用循环被打破了。无主引用
和弱引用类似,无主引用不会牢牢保持住引用的实例。和弱引用不同的是,无主引用是永远有值的。因此,无主引用总是被定义为非可选类型(non-optional type)。你可以在声明属性或者变量时,在前面加上关键字
unowned表示这是一个无主引用。由于无主引用是非可选类型,你不需要在使用它的时候将它展开。无主引用总是可以被直接访问。不过 ARC 无法在实例被销毁后将无主引用设为
nil,因为非可选类型的变量不允许被赋值为nil。-注意: -如果你试图在实例被销毁后,访问该实例的无主引用,会触发运行时错误。使用无主引用,你必须确保引用始终指向一个未销毁的实例。
-还需要注意的是如果你试图访问实例已经被销毁的无主引用,程序会直接崩溃,而不会发生无法预期的行为。所以你应当避免这样的事情发生。
+注意:
如果你试图在实例被销毁后,访问该实例的无主引用,会触发运行时错误。使用无主引用,你必须确保引用始终指向一个未销毁的实例。
还需要注意的是如果你试图访问实例已经被销毁的无主引用,程序会直接崩溃,而不会发生无法预期的行为。所以你应当避免这样的事情发生。下面的例子定义了两个类,
Customer和CreditCard,模拟了银行客户和客户的信用卡。这两个类中,每一个都将另外一个类的实例作为自身的属性。这种关系会潜在的创造循环强引用。
Customer和CreditCard之间的关系与前面弱引用例子中Apartment和Person的关系截然不同。在这个数据模型中,一个客户可能有或者没有信用卡,但是一张信用卡总是关联着一个客户。为了表示这种关系,Customer类有一个可选类型的card属性,但是CreditCard类有一个非可选类型的customer属性。此外,只能通过将一个
number值和customer实例传递给CreditCard构造函数的方式来创建CreditCard实例。这样可以确保当创建CreditCard实例时总是有一个customer实例与之关联。由于信用卡总是关联着一个客户,因此将
-customer属性定义为无主引用,用以避免循环强引用:+class Customer { ++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 { +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") } } -下面的代码片段定义了一个叫
-john的可选类型Customer变量,用来保存某个特定客户的引用。由于是可选类型,所以变量被初始化为nil。var john: Customer? -现在你可以创建
-Customer类的实例,用它初始化CreditCard实例,并将新创建的CreditCard实例赋值为客户的card属性。+john = Customer(name: "John Appleseed") +下面的代码片段定义了一个叫
+john的可选类型Customer变量,用来保存某个特定客户的引用。由于是可选类型,所以变量被初始化为nil。+var john: Customer? +现在你可以创建
+Customer类的实例,用它初始化CreditCard实例,并将新创建的CreditCard实例赋值为客户的card属性。john = Customer(name: "John Appleseed") john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!) -在你关联两个实例后,它们的引用关系如下图所示:
+在你关联两个实例后,它们的引用关系如下图所示:
Customer实例持有对CreditCard实例的强引用,而CreditCard实例持有对Customer实例的无主引用。由于
customer的无主引用,当你断开john变量持有的强引用时,再也没有指向Customer实例的强引用了:
由于再也没有指向
-Customer实例的强引用,该实例被销毁了。其后,再也没有指向CreditCard实例的强引用,该实例也随之被销毁了:+john = nil +john = nil // prints "John Appleseed is being deinitialized" // prints "Card #1234567890123456 is being deinitialized" -最后的代码展示了在
+john变量被设为nil后Customer实例和CreditCard实例的构造函数都打印出了“销毁”的信息。最后的代码展示了在
john变量被设为nil后Customer实例和CreditCard实例的构造函数都打印出了“销毁”的信息。无主引用以及隐式解析可选属性
上面弱引用和无主引用的例子涵盖了两种常用的需要打破循环强引用的场景。
@@ -796,7 +808,7 @@ john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
Person和Apartment的例子展示了两个属性的值都允许为nil,并会潜在的产生循环强引用。这种场景最适合用弱引用来解决。然而,存在着第三种场景,在这种场景中,两个属性都必须有值,并且初始化完成后不能为
nil。在这种场景中,需要一个类使用无主属性,而另外一个类使用隐式解析可选属性。这使两个属性在初始化完成后能被直接访问(不需要可选展开),同时避免了循环引用。这一节将为你展示如何建立这种关系。
下面的例子定义了两个类,
-Country和City,每个类将另外一个类的实例保存为属性。在这个模型中,每个国家必须有首都,而每一个城市必须属于一个国家。为了实现这种关系,Country类拥有一个capitalCity属性,而City类有一个country属性:+class Country { ++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 { +class City { let name: String unowned let country: Country init(name: String, country: Country) { @@ -813,15 +825,17 @@ class City { self.country = country } } -为了建立两个类的依赖关系,
+City的构造函数有一个Country实例的参数,并且将实例保存为country属性。为了建立两个类的依赖关系,
City的构造函数有一个Country实例的参数,并且将实例保存为country属性。
Country的构造函数调用了City的构造函数。然而,只有Country的实例完全初始化完后,Country的构造函数才能把self传给City的构造函数。(在两段式构造过程中有具体描述)为了满足这种需求,通过在类型结尾处加上感叹号(City!)的方式,将
Country的capitalCity属性声明为隐式解析可选类型的属性。这表示像其他可选类型一样,capitalCity属性的默认值为nil,但是不需要展开它的值就能访问它。(在隐式解析可选类型中有描述)由于
capitalCity默认值为nil,一旦Country的实例在构造函数中给name属性赋值后,整个初始化过程就完成了。这代表一旦name属性被赋值后,Country的构造函数就能引用并传递隐式的self。Country的构造函数在赋值capitalCity时,就能将self作为参数传递给City的构造函数。以上的意义在于你可以通过一条语句同时创建
-Country和City的实例,而不产生循环强引用,并且capitalCity的属性能被直接访问,而不需要通过感叹号来展开它的可选值:+var country = Country(name: "Canada", capitalName: "Ottawa") +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" -在上面的例子中,使用隐式解析可选值的意义在于满足了两个类构造函数的需求。
+capitalCity属性在初始化完成后,能像非可选值一样使用和存取同时还避免了循环强引用。在上面的例子中,使用隐式解析可选值的意义在于满足了两个类构造函数的需求。
capitalCity属性在初始化完成后,能像非可选值一样使用和存取同时还避免了循环强引用。闭包引起的循环强引用
前面我们看到了循环强引用环是在两个类实例属性互相保持对方的强引用时产生的,还知道了如何用弱引用和无主引用来打破循环强引用。
@@ -829,7 +843,7 @@ println("\(country.name)'s capital city is called \(country.capitalCity循环强引用的产生,是因为闭包和类相似,都是引用类型。当你把一个闭包赋值给某个属性时,你也把一个引用赋值给了这个闭包。实质上,这跟之前的问题是一样的-两个强引用让彼此一直有效。但是,和两个类实例不同,这次一个是类实例,另一个是闭包。
Swift 提供了一种优雅的方法来解决这个问题,称之为闭包占用列表(closuer capture list)。同样的,在学习如何用闭包占用列表破坏循环强引用之前,先来了解一下循环强引用是如何产生的,这对我们是很有帮助的。
下面的例子为你展示了当一个闭包引用了
-self后是如何产生一个循环强引用的。例子中定义了一个叫HTMLElement的类,用一种简单的模型表示 HTML 中的一个单独的元素:+class HTMLElement { +class HTMLElement { let name: String let text: String? @@ -852,61 +866,61 @@ println("\(country.name)'s capital city is called \(country.capitalCity } } -+
HTMLElement类定义了一个name属性来表示这个元素的名称,例如代表段落的"p",或者代表换行的"br"。HTMLElement还定义了一个可选属性text,用来设置和展现 HTML 元素的文本。
HTMLElement类定义了一个name属性来表示这个元素的名称,例如代表段落的"p",或者代表换行的"br"。HTMLElement还定义了一个可选属性text,用来设置和展现 HTML 元素的文本。除了上面的两个属性,
HTMLElement还定义了一个lazy属性asHTML。这个属性引用了一个闭包,将name和text组合成 HTML 字符串片段。该属性是() -> String类型,或者可以理解为“一个没有参数,返回String的函数”。默认情况下,闭包赋值给了
asHTML属性,这个闭包返回一个代表 HTML 标签的字符串。如果text值存在,该标签就包含可选值text;如果text不存在,该标签就不包含文本。对于段落元素,根据text是"some text"还是nil,闭包会返回"<p>some text</p>"或者"<p />"。可以像实例方法那样去命名、使用
asHTML属性。然而,由于asHTML是闭包而不是实例方法,如果你想改变特定元素的 HTML 处理的话,可以用自定义的闭包来取代默认值。-注意:
-+
asHTML声明为lazy属性,因为只有当元素确实需要处理为HTML输出的字符串时,才需要使用asHTML。也就是说,在默认的闭包中可以使用self,因为只有当初始化完成以及self确实存在后,才能访问lazy属性。注意:
asHTML声明为lazy属性,因为只有当元素确实需要处理为HTML输出的字符串时,才需要使用asHTML。也就是说,在默认的闭包中可以使用self,因为只有当初始化完成以及self确实存在后,才能访问lazy属性。
HTMLElement类只提供一个构造函数,通过name和text(如果有的话)参数来初始化一个元素。该类也定义了一个析构函数,当HTMLElement实例被销毁时,打印一条消息。下面的代码展示了如何用
-HTMLElement类创建实例并打印消息。+var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world") +var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world") println(paragraph!.asHTML()) // prints"hello, world" --注意:
-上面的
+paragraph变量定义为可选HTMLElement,因此我们可以赋值nil给它来演示循环强引用。+注意:
上面的paragraph变量定义为可选HTMLElement,因此我们可以赋值nil给它来演示循环强引用。不幸的是,上面写的
HTMLElement类产生了类实例和asHTML默认值的闭包之间的循环强引用。循环强引用如下图所示:
实例的
asHTML属性持有闭包的强引用。但是,闭包在其闭包体内使用了self(引用了self.name和self.text),因此闭包捕获了self,这意味着闭包又反过来持有了HTMLElement实例的强引用。这样两个对象就产生了循环强引用。(更多关于闭包捕获值的信息,请参考值捕获)。-注意:
-虽然闭包多次使用了
+self,它只捕获HTMLElement实例的一个强引用。注意:
虽然闭包多次使用了self,它只捕获HTMLElement实例的一个强引用。如果设置
-paragraph变量为nil,打破它持有的HTMLElement实例的强引用,HTMLElement实例和它的闭包都不会被销毁,也是因为循环强引用:paragraph = nil -注意
+HTMLElementdeinitializer中的消息并没有别打印,证明了HTMLElement实例并没有被销毁。+paragraph = nil +注意
HTMLElementdeinitializer中的消息并没有别打印,证明了HTMLElement实例并没有被销毁。解决闭包引起的循环强引用
在定义闭包时同时定义捕获列表作为闭包的一部分,通过这种方式可以解决闭包和类实例之间的循环强引用。捕获列表定义了闭包体内捕获一个或者多个引用类型的规则。跟解决两个类实例间的循环强引用一样,声明每个捕获的引用为弱引用或无主引用,而不是强引用。应当根据代码关系来决定使用弱引用还是无主引用。
-注意:
-Swift 有如下要求:只要在闭包内使用
+self的成员,就要用self.someProperty或者self.someMethod(而不只是someProperty或someMethod)。这提醒你可能会不小心就捕获了self。注意:
Swift 有如下要求:只要在闭包内使用self的成员,就要用self.someProperty或者self.someMethod(而不只是someProperty或someMethod)。这提醒你可能会不小心就捕获了self。定义捕获列表
捕获列表中的每个元素都是由
weak或者unowned关键字和实例的引用(如self或someInstance)成对组成。每一对都在方括号中,通过逗号分开。捕获列表放置在闭包参数列表和返回类型之前:
-+@lazy var someClosure: (Int, String) -> String = { +@lazy var someClosure: (Int, String) -> String = { [unowned self] (index: Int, stringToProcess: String) -> String in // closure body goes here } -如果闭包没有指定参数列表或者返回类型,则可以通过上下文推断,那么可以捕获列表放在闭包开始的地方,跟着是关键字
-in:+@lazy var someClosure: () -> String = { +如果闭包没有指定参数列表或者返回类型,则可以通过上下文推断,那么可以捕获列表放在闭包开始的地方,跟着是关键字
+in:@lazy var someClosure: () -> String = { [unowned self] in // closure body goes here } -弱引用和无主引用
+弱引用和无主引用
当闭包和捕获的实例总是互相引用时并且总是同时销毁时,将闭包内的捕获定义为无主引用。
相反的,当捕获引用有时可能会是
nil时,将闭包内的捕获定义为弱引用。弱引用总是可选类型,并且当引用的实例被销毁后,弱引用的值会自动置为nil。这使我们可以在闭包内检查它们是否存在。-注意:
-如果捕获的引用绝对不会置为
+nil,应该用无主引用,而不是弱引用。注意:
如果捕获的引用绝对不会置为nil,应该用无主引用,而不是弱引用。前面的
-HTMLElement例子中,无主引用是正确的解决循环强引用的方法。这样编写HTMLElement类来避免循环强引用:+class HTMLElement { +class HTMLElement { let name: String let text: String? @@ -930,17 +944,20 @@ println(paragraph!.asHTML()) } } -上面的
+HTMLElement实现和之前的实现一致,只是在asHTML闭包中多了一个捕获列表。这里,捕获列表是[unowned self],表示“用无主引用而不是强引用来捕获self”。上面的
HTMLElement实现和之前的实现一致,只是在asHTML闭包中多了一个捕获列表。这里,捕获列表是[unowned self],表示“用无主引用而不是强引用来捕获self”。和之前一样,我们可以创建并打印
-HTMLElement实例:+var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world") +var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world") println(paragraph!.asHTML()) // prints "<p>hello, world</p>" -使用捕获列表后引用关系如下图所示:
+使用捕获列表后引用关系如下图所示:
这一次,闭包以无主引用的形式捕获
-self,并不会持有HTMLElement实例的强引用。如果将paragraph赋值为nil,HTMLElement实例将会被销毁,并能看到它的析构函数打印出的消息。paragraph = nil ++paragraph = nil // prints "p is being deinitialized"+@@ -587,11 +587,10 @@-+ -翻译:Jasonbroker
-校对:numbbbbb
+翻译:Jasonbroker
校对:numbbbbb, stanzhaiOptional Chaining
@@ -607,8 +606,7 @@可选链(Optional Chaining)是一种可以请求和调用属性、方法及子脚本的过程,它的可选性体现于请求或调用的目标当前可能为空(
nil)。如果可选的目标有值,那么调用就会成功;相反,如果选择的目标为空(nil),则这种调用将返回空(nil)。多次请求或调用可以被链接在一起形成一个链,如果任何一个节点为空(nil)将导致整个链失效。-注意: -Swift 的可选链和 Objective-C 中的消息为空有些相像,但是 Swift 可以使用在任意类型中,并且失败与否可以被检测到。
+注意:
Swift 的可选链和 Objective-C 中的消息为空有些相像,但是 Swift 可以使用在任意类型中,并且失败与否可以被检测到。可选链可替代强制解析
@@ -617,49 +615,56 @@ Swift 的可选链和 Objective-C 中的消息为空有些相像,但是 Swift调用可选链的返回结果与原本的返回结果具有相同的类型,但是原本的返回结果被包装成了一个可选值,当可选链调用成功时,一个应该返回
Int的属性将会返回Int?。下面几段代码将解释可选链和强制解析的不同。
首先定义两个类
-Person和Residence。+class Person { +class Person { var residence: Residence? } class Residence { var numberOfRooms = 1 } -+
Residence具有一个Int类型的numberOfRooms,其值为 1。Person具有一个可选residence属性,它的类型是Residence?。
Residence具有一个Int类型的numberOfRooms,其值为 1。Person具有一个可选residence属性,它的类型是Residence?。如果你创建一个新的
-Person实例,它的residence属性由于是被定义为可选型的,此属性将默认初始化为空:let john = Person() -如果你想使用感叹号(
-!)强制解析获得这个人residence属性numberOfRooms属性值,将会引发运行时错误,因为这时没有可以供解析的residence值。+let roomCount = john.residence!.numberOfRooms ++let john = Person() +如果你想使用感叹号(
+!)强制解析获得这个人residence属性numberOfRooms属性值,将会引发运行时错误,因为这时没有可以供解析的residence值。let roomCount = john.residence!.numberOfRooms //将导致运行时错误 -当
+john.residence不是nil时,会运行通过,且会将roomCount设置为一个int类型的合理值。然而,如上所述,当residence为空时,这个代码将会导致运行时错误。当
john.residence不是nil时,会运行通过,且会将roomCount设置为一个int类型的合理值。然而,如上所述,当residence为空时,这个代码将会导致运行时错误。可选链提供了一种另一种获得
-numberOfRooms的方法。利用可选链,使用问号来代替原来!的位置:+if let roomCount = john.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的值。这告诉 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 { ++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)"。 -为可选链定义模型类
你可以使用可选链来多层调用属性,方法,和子脚本。这让你可以利用它们之间的复杂模型来获取更底层的属性,并检查是否可以成功获取此类底层属性。
后面的代码定义了四个将在后面使用的模型类,其中包括多层可选链。这些类是由上面的
Person和Residence模型通过添加一个Room和一个Address类拓展来。-
Person类定义与之前相同。+class Person { +class Person { var residence: Residence? } --
Residence类比之前复杂些。这次,它定义了一个变量rooms,它被初始化为一个Room[]类型的空数组:+class Residence { ++
Residence类比之前复杂些。这次,它定义了一个变量rooms,它被初始化为一个Room[]类型的空数组:class Residence { var rooms = Room[]() var numberOfRooms: Int { return rooms.count @@ -672,17 +677,19 @@ class Residence { } var address: Address? } -因为
+Residence存储了一个Room实例的数组,它的numberOfRooms属性值不是一个固定的存储值,而是通过计算而来的。numberOfRooms属性值是由返回rooms数组的count属性值得到的。因为
Residence存储了一个Room实例的数组,它的numberOfRooms属性值不是一个固定的存储值,而是通过计算而来的。numberOfRooms属性值是由返回rooms数组的count属性值得到的。为了能快速访问
rooms数组,Residence定义了一个只读的子脚本,通过插入数组的元素角标就可以成功调用。如果该角标存在,子脚本则将该元素返回。
Residence中也提供了一个printNumberOfRooms的方法,即简单的打印房间个数。最后,
-Residence定义了一个可选属性叫address(address?)。Address类的属性将在后面定义。 用于rooms数组的Room类是一个很简单的类,它只有一个name属性和一个设定room名的初始化器。+class Room { +class Room { let name: String init(name: String) { self.name = name } } -这个模型中的最终类叫做
-Address。它有三个类型是String?的可选属性。前面两个可选属性buildingName和buildingNumber作为地址的一部分,是定义某个建筑物的两种方式。第三个属性street,用于命名地址的街道名:+class Address { +这个模型中的最终类叫做
+Address。它有三个类型是String?的可选属性。前面两个可选属性buildingName和buildingNumber作为地址的一部分,是定义某个建筑物的两种方式。第三个属性street,用于命名地址的街道名:class Address { var buildingName: String? var buildingNumber: String? var street: String? @@ -696,51 +703,55 @@ class Residence { } } } -+
Address类还提供了一个buildingIdentifier的方法,它的返回值类型为String?。这个方法检查buildingName和buildingNumber的属性,如果buildingName有值则将其返回,或者如果buildingNumber有值则将其返回,再或如果没有一个属性有值,返回空。
Address类还提供了一个buildingIdentifier的方法,它的返回值类型为String?。这个方法检查buildingName和buildingNumber的属性,如果buildingName有值则将其返回,或者如果buildingNumber有值则将其返回,再或如果没有一个属性有值,返回空。通过可选链调用属性
正如上面“ 可选链可替代强制解析”中所述,你可以利用可选链的可选值获取属性,并且检查属性是否获取成功。然而,你不能使用可选链为属性赋值。
使用上述定义的类来创建一个人实例,并再次尝试后去它的
-numberOfRooms属性:+let john = Person() +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是空,所以这个可选链和之前一样失败了,但是没有运行时错误。由于
john.residence是空,所以这个可选链和之前一样失败了,但是没有运行时错误。通过可选链调用方法
你可以使用可选链的来调用可选值的方法并检查方法调用是否成功。即使这个方法没有返回值,你依然可以使用可选链来达成这一目的。
-
Residence的printNumberOfRooms方法会打印numberOfRooms的当前值。方法如下:+func printNumberOfRooms(){ +func printNumberOfRooms(){ println(“The number of rooms is \(numberOfRooms)”) } -这个方法没有返回值。但是,没有返回值类型的函数和方法有一个隐式的返回值类型
+Void(参见Function Without Return Values)。这个方法没有返回值。但是,没有返回值类型的函数和方法有一个隐式的返回值类型
Void(参见Function Without Return Values)。如果你利用可选链调用此方法,这个方法的返回值类型将是
-Void?,而不是Void,因为当通过可选链调用方法时返回值总是可选类型(optional type)。即使这个方法本身没有定义返回值,你也可以使用if语句来检查是否能成功调用printNumberOfRooms方法:如果方法通过可选链调用成功,printNumberOfRooms的隐式返回值将会是Void,如果没有成功,将返回nil:+if john.residence?.printNumberOfRooms() { ++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."。 -使用可选链调用子脚本
你可以使用可选链来尝试从子脚本获取值并检查子脚本的调用是否成功,然而,你不能通过可选链来设置子代码。
-注意: -当你使用可选链来获取子脚本的时候,你应该将问号放在子脚本括号的前面而不是后面。可选链的问号一般直接跟在表达语句的后面。
+注意:
当你使用可选链来获取子脚本的时候,你应该将问号放在子脚本括号的前面而不是后面。可选链的问号一般直接跟在表达语句的后面。下面这个例子用在
-Residence类中定义的子脚本来获取john.residence数组中第一个房间的名字。因为john.residence现在是nil,子脚本的调用失败了。+if let firstRoomName = john.residence?[0].name { +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是可选链试图获得的可选值。在子代码调用中可选链的问号直接跟在
john.residence的后面,在子脚本括号的前面,因为john.residence是可选链试图获得的可选值。如果你创建一个
-Residence实例给john.residence,且在他的rooms数组中有一个或多个Room实例,那么你可以使用可选链通过Residence子脚本来获取在rooms数组中的实例了:+let johnsHouse = Residence() ++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."。 -连接多层链接
你可以将多层可选链连接在一起,可以掘取模型内更下层的属性方法和子脚本。然而多层可选链不能再添加比已经返回的可选值更多的层。 也就是说:
@@ -761,43 +773,46 @@ if let firstRoomName = john.residence?[0].name {如果你试图通过可选链获得
Int值,不论使用了多少层链接返回的总是Int?。 相似的,如果你试图通过可选链获得Int?值,不论使用了多少层链接返回的总是Int?。下面的例子试图获取
-john的residence属性里的address的street属性。这里使用了两层可选链来联系residence和address属性,它们两者都是可选类型:+if let johnsStreet = john.residence?.address?.street { +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调用失败。
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() ++let johnsAddress = Address() johnsAddress.buildingName = "The Larches" johnsAddress.street = "Laurel Street" john.residence!.address = johnsAddress - -if let johnsStreet = john.residence?.address?.street { +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属性之前使用!解析以获得它的实际值。值得注意的是,“
!”符号在给john.residence.address分配address实例时的使用。john.residence属性是一个可选类型,因此你需要在它获取address属性之前使用!解析以获得它的实际值。链接可选返回值的方法
前面的例子解释了如何通过可选链来获得可选类型属性值。你也可以通过可选链调用一个返回可选类型值的方法并按需链接该方法的返回值。
下面的例子通过可选链调用了
-Address类中的buildingIdentifier方法。这个方法的返回值类型是String?。如上所述,这个方法在可选链调用后最终的返回值类型依然是String?:+if let buildingIdentifier = john.residence?.address?.buildingIdentifier() { +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 { +如果你还想进一步对方法返回值执行可选链,将可选链问号符放在方法括号的后面:
+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方法本身。+diff --git a/chapter2/18_Type_Casting.html b/chapter2/18_Type_Casting.html index 71d8ef5e..c485f97c 100644 --- a/chapter2/18_Type_Casting.html +++ b/chapter2/18_Type_Casting.html @@ -10,7 +10,7 @@ - + @@ -46,7 +46,7 @@ -注意:
在上面的例子中,你将可选链问号符放在括号后面是因为你想要链接的可选值是buildingIdentifier方法的返回值,不是buildingIdentifier方法本身。+@@ -587,36 +587,36 @@-+ --翻译:xiehurricane
-校对:happyming
+翻译:xiehurricane
校对:happyming类型转换(Type Casting)
+类型检查(Type Casting)
本页包含内容:
-类型检查是一种检查类实例的方式,并且或者也是让实例作为它的父类或者子类的一种方式。
-类型检查在 Swift 中使用
-is和as操作符实现。这两个操作符提供了一种简单达意的方式去检查值的类型或者转换它的类型。你也可以用来检查一个类是否实现了某个协议,就像在 Checking for Protocol Conformance部分讲述的一样。
+类型检查是一种检查类实例的方式,并且或者也是让实例作为它的父类或者子类的一种方式。
+类型检查在 Swift 中使用
+is和as操作符实现。这两个操作符提供了一种简单达意的方式去检查值的类型或者转换它的类型。你也可以用来检查一个类是否实现了某个协议,就像在 Checking for Protocol Conformance部分讲述的一样。
定义一个类层次作为例子
-你可以将它用在类和子类的层次结构上,检查特定类实例的类型并且转换这个类实例的类型成为这个层次结构中的其他类型。这下面的三个代码段定义了一个类层次和一个包含了几个这些类实例的数组,作为类型检查的例子。
-第一个代码片段定义了一个新的基础类
-MediaItem。这个类为任何出现在数字媒体库的媒体项提供基础功能。特别的,它声明了一个String类型的name属性,和一个init name初始化器。(它假定所有的媒体项都有个名称。)+class MediaItem { +你可以将它用在类和子类的层次结构上,检查特定类实例的类型并且转换这个类实例的类型成为这个层次结构中的其他类型。这下面的三个代码段定义了一个类层次和一个包含了几个这些类实例的数组,作为类型检查的例子。
+第一个代码片段定义了一个新的基础类
+MediaItem。这个类为任何出现在数字媒体库的媒体项提供基础功能。特别的,它声明了一个String类型的name属性,和一个init name初始化器。(它假定所有的媒体项都有个名称。)class MediaItem { var name: String init(name: String) { self.name = name } } -下一个代码段定义了
-MediaItem的两个子类。第一个子类Movie,在父类(或者说基类)的基础上增加了一个director(导演) 属性,和相应的初始化器。第二个类在父类的基础上增加了一个artist(艺术家) 属性,和相应的初始化器:+class Movie: MediaItem { +下一个代码段定义了
+MediaItem的两个子类。第一个子类Movie,在父类(或者说基类)的基础上增加了一个director(导演) 属性,和相应的初始化器。第二个类在父类的基础上增加了一个artist(艺术家) 属性,和相应的初始化器: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) } } -最后一个代码段创建了一个数组常量
-library- ,包含两个Movie实例和三个Song实例。library的类型是在它被初始化时根据它数组中所包含的内容推断来的。Swift 的类型检测器能够演绎出Movie和Song有共同的父类MediaItem,所以它推断出MediaItem[]类作为library的类型。+let library = [ +最后一个代码段创建了一个数组常量
+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"), @@ -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[] -在幕后
+library里存储的媒体项依然是Movie和Song类型的,但是,若你迭代它,取出的实例会是MediaItem类型的,而不是Movie和Song类型的。为了让它们作为它们本来的类型工作,你需要检查它们的类型或者向下转换它们的类型到其它类型,就像下面描述的一样。在幕后
-library里存储的媒体项依然是Movie和Song类型的,但是,若你迭代它,取出的实例会是MediaItem类型的,而不是Movie和Song类型的。为了让它们作为它们本来的类型工作,你需要检查它们的类型或者向下转换它们的类型到其它类型,就像下面描述的一样。检查类型
-用类型检查操作符(
-is)来检查一个实例是否属于特定子类型。若实例属于那个子类型,类型检查操作符返回true,否则返回false。下面的例子定义了两个变量,
-movieCount和songCount,用来计算数组library中Movie和Song类型的实例数量。+var movieCount = 0 +检查类型(Checking Type)
+用类型检查操作符(
+is)来检查一个实例是否属于特定子类型。若实例属于那个子类型,类型检查操作符返回true,否则返回false。下面的例子定义了两个变量,
+movieCount和songCount,用来计算数组library中Movie和Song类型的实例数量。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" -示例迭代了数组
-library中的所有项。每一次,for-in循环设置 -item为数组中的下一个MediaItem。若当前
+MediaItem是一个Movie类型的实例,item is Movie返回 -true,相反返回false。同样的,item is - Song检查item是否为Song类型的实例。在循环结束后,movieCount和songCount的值就是被找到属于各自的类型的实例数量。示例迭代了数组
+library中的所有项。每一次,for-in循环设置 +item为数组中的下一个MediaItem。若当前
MediaItem是一个Movie类型的实例,item is Movie返回 +true,相反返回false。同样的,item is +Song检查item是否为Song类型的实例。在循环结束后,movieCount和songCount的值就是被找到属于各自的类型的实例数量。向下转型(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 { +某类型的一个常量或变量可能在幕后实际上属于一个子类。你可以相信,上面就是这种情况。你可以尝试向下转到它的子类型,用类型检查操作符(
+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 { @@ -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 -示例首先试图将
-item下转为Movie。因为item是一个MediaItem- 类型的实例,它可能是一个Movie;同样,它可能是一个Song,或者仅仅是基类 -MediaItem。因为不确定,as?形式在试图下转时将返还一个可选值。item as Movie的返回值是Movie?类型或 “optionalMovie”。当向下转型为
-Movie应用在两个Song- 实例时将会失败。为了处理这种情况,上面的例子使用了可选绑定(optional binding)来检查可选Movie真的包含一个值(这个是为了判断下转是否成功。)可选绑定是这样写的“if let movie = item as? Movie”,可以这样解读:“尝试将
-item转为Movie类型。若成功,设置一个新的临时常量movie来存储返回的可选Movie”若向下转型成功,然后
+movie的属性将用于打印一个Movie实例的描述,包括它的导演的名字director。当Song被找到时,一个相近的原理被用来检测Song实例和打印它的描述。示例首先试图将
+item下转为Movie。因为item是一个MediaItem+类型的实例,它可能是一个Movie;同样,它可能是一个Song,或者仅仅是基类 +MediaItem。因为不确定,as?形式在试图下转时将返还一个可选值。item as Movie的返回值是Movie?类型或 “optionalMovie”。当向下转型为
+Movie应用在两个Song+实例时将会失败。为了处理这种情况,上面的例子使用了可选绑定(optional binding)来检查可选Movie真的包含一个值(这个是为了判断下转是否成功。)可选绑定是这样写的“if let movie = item as? Movie”,可以这样解读:“尝试将
+item转为Movie类型。若成功,设置一个新的临时常量movie来存储返回的可选Movie”若向下转型成功,然后
movie的属性将用于打印一个Movie实例的描述,包括它的导演的名字director。当Song被找到时,一个相近的原理被用来检测Song实例和打印它的描述。--注意:
-转换没有真的改变实例或它的值。潜在的根本的实例保持不变;只是简单地把它作为它被转换成的类来使用。
+注意:
转换没有真的改变实例或它的值。潜在的根本的实例保持不变;只是简单地把它作为它被转换成的类来使用。-
Any和AnyObject的类型转换Swift为不确定类型提供了两种特殊类型别名:
++
Any和AnyObject的类型检查Swift为不确定类型提供了两种特殊类型别名:
-
- -
-
AnyObject可以代表任何class类型的实例。- +
-
Any可以表示任何类型,除了方法类型(function types)。- +
AnyObject可以代表任何class类型的实例。Any可以表示任何类型,除了方法类型(function types)。-注意:
-只有当你明确的需要它的行为和功能时才使用
+Any和AnyObject。在你的代码里使用你期望的明确的类型总是更好的。注意:
只有当你明确的需要它的行为和功能时才使用Any和AnyObject。在你的代码里使用你期望的明确的类型总是更好的。-
AnyObject类型当需要在工作中使用 Cocoa - APIs,它一般接收一个
-AnyObject[]类型的数组,或者说“一个任何对象类型的数组”。这是因为 Objective-C 没有明确的类型化数组。但是,你常常可以确定包含在仅从你知道的 API 信息提供的这样一个数组中的对象的类型。在这些情况下,你可以使用强制形式的类型转换(
-as)来下转在数组中的每一项到比AnyObject更明确的类型,不需要可选解析(optional unwrapping)。下面的示例定义了一个
-AnyObject[]类型的数组并填入三个Movie类型的实例:let someObjects: 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 { +因为知道这个数组只包含
+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[] { +为了变为一个更短的形式,下转
+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[]() ++
Any类型这里有个示例,使用
+Any类型来和混合的不同类型一起工作,包括非class类型。它创建了一个可以存储Any类型的数组things。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")) --
things数组包含两个Int值,2个Double值,1个String值,一个元组(Double, Double),Ivan Reitman 导演的电影“Ghostbusters”。你可以在
-switchcases里用is和as操作符来发觉只知道是Any或AnyObject的常量或变量的类型。 下面的示例迭代things数组中的每一项的并用switch语句查找每一项的类型。这几种switch语句的情形绑定它们匹配的值到一个规定类型的常量,让它们可以打印它们的值:+for thing in things { ++
things数组包含两个Int值,2个Double值,1个String值,一个元组(Double, Double),Ivan Reitman 导演的电影“Ghostbusters”。你可以在
+switchcases里用is和as操作符来发觉只知道是Any或AnyObject的常量或变量的类型。 下面的示例迭代things数组中的每一项的并用switch语句查找每一项的类型。这几种switch语句的情形绑定它们匹配的值到一个规定类型的常量,让它们可以打印它们的值: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 -。
+-diff --git a/chapter2/19_Nested_Types.html b/chapter2/19_Nested_Types.html index f4856881..49284a1a 100644 --- a/chapter2/19_Nested_Types.html +++ b/chapter2/19_Nested_Types.html @@ -10,7 +10,7 @@ - + @@ -46,7 +46,7 @@ -注意:
-在一个switch语句的case中使用强制形式的类型转换操作符(as, 而不是 as?)来检查和转换到一个明确的类型。在 switch case 语句的内容中这种检查总是安全的。
+注意:
在一个switch语句的case中使用强制形式的类型检查操作符(as, 而不是 as?)来检查和转换到一个明确的类型。在 switch case 语句的内容中这种检查总是安全的。+@@ -587,11 +587,10 @@-+ diff --git a/chapter2/20_Extensions.html b/chapter2/20_Extensions.html index 2e653209..86fc54ae 100644 --- a/chapter2/20_Extensions.html +++ b/chapter2/20_Extensions.html @@ -10,7 +10,7 @@ - + @@ -46,7 +46,7 @@ - -翻译:Lin-H
-校对:shinyzhu
+类型嵌套
@@ -606,11 +605,12 @@类型嵌套实例
下面这个例子定义了一个结构体
BlackjackCard(二十一点),用来模拟BlackjackCard中的扑克牌点数。BlackjackCard结构体包含2个嵌套定义的枚举类型Suit和Rank。在
-BlackjackCard规则中,Ace牌可以表示1或者11,Ace牌的这一特征用一个嵌套在枚举型Rank的结构体Values来表示。+struct BlackjackCard { +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 } } -枚举型的
+Suit用来描述扑克牌的四种花色,并分别用一个Character类型的值代表花色符号。枚举型的
Suit用来描述扑克牌的四种花色,并分别用一个Character类型的值代表花色符号。枚举型的
Rank用来描述扑克牌从Ace~10,J,Q,K,13张牌,并分别用一个Int类型的值表示牌的面值。(这个Int类型的值不适用于Ace,J,Q,K的牌)。如上文所提到的,枚举型
Rank在自己内部定义了一个嵌套结构体Values。这个结构体包含两个变量,只有Ace有两个数值,其余牌都只有一个数值。结构体Values中定义的两个属性:
first, 为Int@@ -648,16 +650,19 @@
Rank定义了一个计算属性values,这个计算属性会根据牌的面值,用适当的数值去初始化Values实例,并赋值给values。对于J,Q,K,Ace会使用特殊数值,对于数字面值的牌使用Int类型的值。
BlackjackCard结构体自身有两个属性—rank与suit,也同样定义了一个计算属性description,description属性用rank和suit的中内容来构建对这张扑克牌名字和数值的描述,并用可选类型second来检查是否存在第二个值,若存在,则在原有的描述中增加对第二数值的描述。因为
-BlackjackCard是一个没有自定义构造函数的结构体,在Memberwise Initializers for Structure Types中知道结构体有默认的成员构造函数,所以你可以用默认的initializer去初始化新的常量theAceOfSpades:+let theAceOfSpades = BlackjackCard(rank: .Ace, suit: .Spades) +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两个值。尽管
Rank和Suit嵌套在BlackjackCard中,但仍可被引用,所以在初始化实例时能够通过枚举类型中的成员名称单独引用。在上面的例子中description属性能正确得输出对Ace牌有1和11两个值。类型嵌套的引用
在外部对嵌套类型的引用,以被嵌套类型的名字为前缀,加上所要引用的属性名:
-+let heartsSymbol = BlackjackCard.Suit.Hearts.toRaw() +let heartsSymbol = BlackjackCard.Suit.Hearts.toRaw() // 红心的符号 为 "♡" -对于上面这个例子,这样可以使
+Suit,Rank, 和Values的名字尽可能的短,因为它们的名字会自然的由被定义的上下文来限定。对于上面这个例子,这样可以使
+Suit,Rank, 和Values的名字尽可能的短,因为它们的名字会自然的由被定义的上下文来限定。preview
+@@ -587,11 +587,10 @@-+ -翻译:lyuka
-校对:Hawstein
+扩展(Extensions)
@@ -615,24 +614,25 @@使一个已有类型符合某个协议 -注意:
-如果你定义了一个扩展向一个已有类型添加新功能,那么这个新功能对该类型的所有已有实例中都是可用的,即使它们是在你的这个扩展的前面定义的。
+注意:
如果你定义了一个扩展向一个已有类型添加新功能,那么这个新功能对该类型的所有已有实例中都是可用的,即使它们是在你的这个扩展的前面定义的。扩展语法(Extension Syntax)
声明一个扩展使用关键字
-extension:+extension SomeType { +extension SomeType { // 加到SomeType的新功能写到这里 } -一个扩展可以扩展一个已有类型,使其能够适配一个或多个协议(protocol)。当这种情况发生时,协议的名字应该完全按照类或结构体的名字的方式进行书写:
-+extension SomeType: SomeProtocol, AnotherProctocol { +一个扩展可以扩展一个已有类型,使其能够适配一个或多个协议(protocol)。当这种情况发生时,协议的名字应该完全按照类或结构体的名字的方式进行书写:
+extension SomeType: SomeProtocol, AnotherProctocol { // 协议实现写到这里 } -按照这种方式添加的协议遵循者(protocol conformance)被称之为在扩展中添加协议遵循者
+按照这种方式添加的协议遵循者(protocol conformance)被称之为在扩展中添加协议遵循者
计算型属性(Computed Properties)
扩展可以向已有类型添加计算型实例属性和计算型类型属性。下面的例子向 Swift 的内建
-Double类型添加了5个计算型实例属性,从而提供与距离单位协作的基本支持。+extension Double { +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" -这些计算属性表达的含义是把一个
+Double型的值看作是某单位下的长度值。即使它们被实现为计算型属性,但这些属性仍可以接一个带有dot语法的浮点型字面值,而这恰恰是使用这些浮点型字面量实现距离转换的方式。这些计算属性表达的含义是把一个
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 +let aMarathon = 42.km + 195.m println("A marathon is \(aMarathon) meters long") // 打印输出:"A marathon is 42495.0 meters long" --注意:
-扩展可以添加新的计算属性,但是不可以添加存储属性,也不可以向已有属性添加属性观测器(property observers)。
++注意:
扩展可以添加新的计算属性,但是不可以添加存储属性,也不可以向已有属性添加属性观测器(property observers)。构造器(Initializers)
扩展可以向已有类型添加新的构造器。这可以让你扩展其它类型,将你自己的定制类型作为构造器参数,或者提供该类型的原始实现中没有包含的额外初始化选项。
扩展能向类中添加新的便利构造器,但是它们不能向类中添加新的指定构造器或析构函数。指定构造器和析构函数必须总是由原始的类实现来提供。
-注意:
-如果你使用扩展向一个值类型添加一个构造器,该构造器向所有的存储属性提供默认值,而且没有定义任何定制构造器(custom initializers),那么对于来自你的扩展构造器中的值类型,你可以调用默认构造器(default initializers)和逐一成员构造器(memberwise initializers)。
+
正如在值类型的构造器授权中描述的,如果你已经把构造器写成值类型原始实现的一部分,上述规则不再适用。注意:
如果你使用扩展向一个值类型添加一个构造器,该构造器向所有的存储属性提供默认值,而且没有定义任何定制构造器(custom initializers),那么对于来自你的扩展构造器中的值类型,你可以调用默认构造器(default initializers)和逐一成员构造器(memberwise initializers)。
正如在值类型的构造器授权中描述的,如果你已经把构造器写成值类型原始实现的一部分,上述规则不再适用。下面的例子定义了一个用于描述几何矩形的定制结构体
-Rect。这个例子同时定义了两个辅助结构体Size和Point,它们都把0.0作为所有属性的默认值:+struct Size { +struct Size { var width = 0.0, height = 0.0 } struct Point { @@ -675,56 +675,62 @@ struct Rect { var origin = Point() var size = Size() } -因为结构体
-Rect提供了其所有属性的默认值,所以正如默认构造器中描述的,它可以自动接受一个默认的构造器和一个成员级构造器。这些构造器可以用于构造新的Rect实例:+let defaultRect = Rect() +因为结构体
+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 { +你可以提供一个额外的使用特殊中心点和大小的构造器来扩展
+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), +这个新的构造器首先根据提供的
+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) --注意:
-如果你使用扩展提供了一个新的构造器,你依旧有责任保证构造过程能够让所有实例完全初始化。
++注意:
如果你使用扩展提供了一个新的构造器,你依旧有责任保证构造过程能够让所有实例完全初始化。方法(Methods)
扩展可以向已有类型添加新的实例方法和类型方法。下面的例子向
-Int类型添加一个名为repetitions的新实例方法:+extension Int { +extension Int { func repetitions(task: () -> ()) { for i in 0..self { task() } } } -这个
+repetitions方法使用了一个() -> ()类型的单参数(single argument),表明函数没有参数而且没有返回值。这个
repetitions方法使用了一个() -> ()类型的单参数(single argument),表明函数没有参数而且没有返回值。定义该扩展之后,你就可以对任意整数调用
-repetitions方法,实现的功能则是多次执行某任务:+3.repetitions({ +3.repetitions({ println("Hello!") }) // Hello! // Hello! // Hello! -可以使用 trailing 闭包使调用更加简洁:
-+3.repetitions{ +可以使用 trailing 闭包使调用更加简洁:
++3.repetitions{ println("Goodbye!") } // Goodbye! // Goodbye! // Goodbye! -修改实例方法(Mutating Instance Methods)
通过扩展添加的实例方法也可以修改该实例本身。结构体和枚举类型中修改
self或其属性的方法必须将该实例方法标注为mutating,正如来自原始实现的修改方法一样。下面的例子向Swift的
-Int类型添加了一个新的名为square的修改方法,来实现一个原始值的平方计算:+extension Int { ++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 -下标(Subscripts)
扩展可以向一个已有类型添加新下标。这个例子向Swift内建类型
Int添加了一个整型下标。该下标[n]返回十进制数字从右向左数的第n个数字@@ -740,7 +747,7 @@ someInt.square()
- 123456789[1]返回8
...等等
-+extension Int { +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 -如果该
-Int值没有足够的位数,即下标越界,那么上述实现的下标会返回0,因为它会在数字左边自动补0:+746381295[9] +如果该
+Int值没有足够的位数,即下标越界,那么上述实现的下标会返回0,因为它会在数字左边自动补0:+746381295[9] //returns 0, 即等同于: 0746381295[9] -嵌套类型(Nested Types)
扩展可以向已有的类、结构体和枚举添加新的嵌套类型:
-+extension Character { +extension Character { enum Kind { case Vowel, Consonant, Other } @@ -780,10 +789,11 @@ someInt.square() } } } -该例子向
+Character添加了新的嵌套枚举。这个名为Kind的枚举表示特定字符的类型。具体来说,就是表示一个标准的拉丁脚本中的字符是元音还是辅音(不考虑口语和地方变种),或者是其它类型。该例子向
Character添加了新的嵌套枚举。这个名为Kind的枚举表示特定字符的类型。具体来说,就是表示一个标准的拉丁脚本中的字符是元音还是辅音(不考虑口语和地方变种),或者是其它类型。这个类子还向
Character添加了一个新的计算实例属性,即kind,用来返回合适的Kind枚举成员。现在,这个嵌套枚举可以和一个
-Character值联合使用了:+func printLetterKinds(word: String) { +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 -函数
+printLetterKinds的输入是一个String值并对其字符进行迭代。在每次迭代过程中,考虑当前字符的kind计算属性,并打印出合适的类别描述。所以printLetterKinds就可以用来打印一个完整单词中所有字母的类型,正如上述单词"hello"所展示的。函数
printLetterKinds的输入是一个String值并对其字符进行迭代。在每次迭代过程中,考虑当前字符的kind计算属性,并打印出合适的类别描述。所以printLetterKinds就可以用来打印一个完整单词中所有字母的类型,正如上述单词"hello"所展示的。-diff --git a/chapter2/21_Protocols.html b/chapter2/21_Protocols.html index 1455e659..a04c1d81 100644 --- a/chapter2/21_Protocols.html +++ b/chapter2/21_Protocols.html @@ -10,7 +10,7 @@ - + @@ -46,7 +46,7 @@ -注意:
-由于已知
+character.kind是Character.Kind型,所以Character.Kind中的所有成员值都可以使用switch语句里的形式简写,比如使用.Vowel代替Character.Kind.Vowel注意:
由于已知character.kind是Character.Kind型,所以Character.Kind中的所有成员值都可以使用switch语句里的形式简写,比如使用.Vowel代替Character.Kind.Vowel+@@ -587,11 +587,10 @@-+ diff --git a/chapter2/22_Generics.html b/chapter2/22_Generics.html index df3f243a..aaabbef8 100644 --- a/chapter2/22_Generics.html +++ b/chapter2/22_Generics.html @@ -10,7 +10,7 @@ - + @@ -46,7 +46,7 @@ - -翻译:geek5nan
-校对:dabing1022
+翻译:geek5nan
校对:dabing1022协议
@@ -604,7 +603,7 @@协议类型(Protocols as Types) 委托(代理)模式(Delegation) 在扩展中添加协议成员(Adding Protocol Conformance with an Extension) -通过延展补充协议声明(Declaring Protocol Adoption with an Extension) +通过扩展补充协议声明(Declaring Protocol Adoption with an Extension) 集合中的协议类型(Collections of Protocol Types) 协议的继承(Protocol Inheritance) 协议合成(Protocol Composition) @@ -616,42 +615,49 @@协议的语法
-
协议的定义与类,结构体,枚举的定义非常相似,如下所示:+protocol SomeProtocol { +protocol SomeProtocol { // 协议内容 } -在类,结构体,枚举的名称后加上
-协议名称,中间以冒号:分隔即可实现协议;实现多个协议时,各协议之间用逗号,分隔,如下所示:+struct SomeStructure: FirstProtocol, AnotherProtocol { +在类,结构体,枚举的名称后加上
+协议名称,中间以冒号:分隔即可实现协议;实现多个协议时,各协议之间用逗号,分隔,如下所示:struct SomeStructure: FirstProtocol, AnotherProtocol { // 结构体内容 } -当某个类含有父类的同时并实现了协议,应当把父类放在所有的协议之前,如下所示:
-+class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol { +当某个类含有父类的同时并实现了协议,应当把父类放在所有的协议之前,如下所示:
++class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol { // 类的内容 } -属性要求
-+
协议能够要求其遵循者必须含有一些特定名称和类型的实例属性(instance property)或类属性 (type property),也能够要求属性的(设置权限)settable和(访问权限)gettable,但它不要求属性是存储型属性(stored property)还是计算型属性(calculate property)。+
协议能够要求其遵循者必须含有一些特定名称和类型的实例属性(instance property)或类属性 (type property),也能够要求属性具有(设置权限)settable和(访问权限)gettable,但它不要求属性是存储型属性(stored property)还是计算型属性(calculate property)。如果协议要求属性具有设置权限和访问权限,那常量存储型属性或者只读计算型属性都无法满足此要求。如果协议只要求属性具有访问权限,那任何类型的属性都可以满足此要求,无论这些属性是否具有设置权限。
通常前置
-var关键字将属性声明为变量。在属性声明后写上{ get set }表示属性为可读写的。{ get }用来表示属性为可读的。即使你为可读的属性实现了setter方法,它也不会出错。+protocol SomeProtocol { +protocol SomeProtocol { var musBeSettable : Int { get set } var doesNotNeedToBeSettable: Int { get } } -用类来实现协议时,使用
-class关键字来表示该属性为类成员;用结构体或枚举实现协议时,则使用static关键字来表示:+protocol AnotherProtocol { +用类来实现协议时,使用
+class关键字来表示该属性为类成员;用结构体或枚举实现协议时,则使用static关键字来表示:protocol AnotherProtocol { class var someTypeProperty: Int { get set } } protocol FullyNamed { var fullName: String { get } } --
FullyNamed协议含有fullName属性。因此其遵循者必须含有一个名为fullName,类型为String的可读属性。+struct Person: FullyNamed{ ++
FullyNamed协议含有fullName属性。因此其遵循者必须含有一个名为fullName,类型为String的可读属性。struct Person: FullyNamed{ var fullName: String } let john = Person(fullName: "John Appleseed") //john.fullName 为 "John Appleseed" -+
Person结构体含有一个名为fullName的存储型属性,完整的遵循了协议。(若协议未被完整遵循,编译时则会报错)。
Person结构体含有一个名为fullName的存储型属性,完整的遵循了协议。(若协议未被完整遵循,编译时则会报错)。如下所示,
-Startship类遵循了FullyNamed协议:+class Starship: FullyNamed { +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" -+
Starship类将fullName实现为可读的计算型属性。它的每一个实例都有一个名为name的必备属性和一个名为prefix的可选属性。 当prefix存在时,将prefix插入到name之前来为Starship构建fullName。
Starship类将fullName实现为可读的计算型属性。它的每一个实例都有一个名为name的必备属性和一个名为prefix的可选属性。 当prefix存在时,将prefix插入到name之前来为Starship构建fullName。方法要求
协议能够要求其遵循者必备某些特定的实例方法和类方法。协议方法的声明与普通方法声明相似,但它不需要方法内容。-注意:
-协议方法支持
+变长参数(variadic parameter),不支持默认参数(default parameter)。注意: +协议方法支持
变长参数(variadic parameter),不支持默认参数(default parameter)。前置
-class关键字表示协议中的成员为类成员;当协议用于被枚举或结构体遵循时,则使用static关键字。如下所示:+protocol SomeProtocol { +protocol SomeProtocol { class func someTypeMethod() } protocol RandomNumberGenerator { func random() -> Double } -+
RandomNumberGenerator协议要求其遵循者必须拥有一个名为random, 返回值类型为Double的实例方法。(我们假设随机数在[0,1]区间内)。
RandomNumberGenerator协议要求其遵循者必须拥有一个名为random, 返回值类型为Double的实例方法。(我们假设随机数在[0,1]区间内)。-
LinearCongruentialGenerator类遵循了RandomNumberGenerator协议,并提供了一个叫做线性同余生成器(linear congruential generator)的伪随机数算法。+class LinearCongruentialGenerator: RandomNumberGenerator { ++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" -突变方法要求
能在
方法或函数内部改变实例类型的方法称为突变方法。在值类型(Value Type)(译者注:特指结构体和枚举)中的的函数前缀加上mutating关键字来表示该函数允许改变该实例和其属性的类型。 这一变换过程在实例方法(Instance Methods)章节中有详细描述。(译者注:类中的成员为
引用类型(Reference Type),可以方便的修改实例及其属性的值而无需改变类型;而结构体和枚举中的成员均为值类型(Value Type),修改变量的值就相当于修改变量的类型,而Swift默认不允许修改类型,因此需要前置mutating关键字用来表示该函数中能够修改类型)-注意:
-用
+class实现协议中的mutating方法时,不用写mutating关键字;用结构体,枚举实现协议中的mutating方法时,必须写mutating关键字。注意: +用
class实现协议中的mutating方法时,不用写mutating关键字;用结构体,枚举实现协议中的mutating方法时,必须写mutating关键字。如下所示,
-Togglable协议含有toggle函数。根据函数名称推测,toggle可能用于切换或恢复某个属性的状态。mutating关键字表示它为突变方法:+protocol Togglable { +protocol Togglable { mutating func toggle() } -当使用
+枚举或结构体来实现Togglabl协议时,必须在toggle方法前加上mutating关键字。当使用
枚举或结构体来实现Togglabl协议时,必须在toggle方法前加上mutating关键字。如下所示,
-OnOffSwitch枚举遵循了Togglable协议,On,Off两个成员用于表示当前状态+enum OnOffSwitch: Togglable { ++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 -协议类型
协议本身不实现任何功能,但你可以将它当做类型来使用。使用场景:
@@ -735,10 +746,10 @@ lightSwitch.toggle()作为数组,字典或其他容器中的元素类型 --注意:
-协议类型应与其他类型(Int,Double,String)的写法相同,使用驼峰式
+注意: +协议类型应与其他类型(Int,Double,String)的写法相同,使用驼峰式
+class Dice { +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 } } -这里定义了一个名为
-Dice的类,用来代表桌游中的N个面的骰子。+
Dice含有sides和generator两个属性,前者用来表示骰子有几个面,后者为骰子提供一个随机数生成器。由于后者为RandomNumberGenerator的协议类型。所以它能够被赋值为任意遵循该协议的类型。这里定义了一个名为
+Dice的类,用来代表桌游中的N个面的骰子。
Dice含有sides和generator两个属性,前者用来表示骰子有几个面,后者为骰子提供一个随机数生成器。由于后者为RandomNumberGenerator的协议类型。所以它能够被赋值为任意遵循该协议的类型。此外,使用
构造器(init)来代替之前版本中的setup操作。构造器中含有一个名为generator,类型为RandomNumberGenerator的形参,使得它可以接收任意遵循RandomNumberGenerator协议的类型。
roll方法用来模拟骰子的面值。它先使用generator的random方法来创建一个[0-1]区间内的随机数种子,然后加工这个随机数种子生成骰子的面值。如下所示,
-LinearCongruentialGenerator的实例作为随机数生成器传入Dice的构造器+var d6 = Dice(sides: 6,generator: LinearCongruentialGenerator()) ++var d6 = Dice(sides: 6,generator: LinearCongruentialGenerator()) for _ in 1...5 { println("Random dice roll is \(d6.roll())") } @@ -764,24 +776,27 @@ for _ in 1...5 { //Random dice roll is 4 //Random dice roll is 5 //Random dice roll is 4 -委托(代理)模式
委托是一种设计模式,它允许类或结构体将一些需要它们负责的功能
交由(委托)给其他的类型。委托模式的实现很简单: 定义
协议来封装那些需要被委托的函数和方法, 使其遵循者拥有这些被委托的函数和方法。委托模式可以用来响应特定的动作或接收外部数据源提供的数据,而无需要知道外部数据源的类型。
下文是两个基于骰子游戏的协议:
-+protocol DiceGame { +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) } -+
DiceGame协议可以在任意含有骰子的游戏中实现,DiceGameDelegate协议可以用来追踪DiceGame的游戏过程。
DiceGame协议可以在任意含有骰子的游戏中实现,DiceGameDelegate协议可以用来追踪DiceGame的游戏过程。如下所示,
-SnakesAndLadders是Snakes and Ladders(译者注:控制流章节有该游戏的详细介绍)游戏的新版本。新版本使用Dice作为骰子,并且实现了DiceGame和DiceGameDelegate协议+class SnakesAndLadders: DiceGame { +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) } } -游戏的
+初始化设置(setup)被SnakesAndLadders类的构造器(initializer)实现。所有的游戏逻辑被转移到了play方法中。游戏的
初始化设置(setup)被SnakesAndLadders类的构造器(initializer)实现。所有的游戏逻辑被转移到了play方法中。-注意:
-因为
+delegate并不是该游戏的必备条件,delegate被定义为遵循DiceGameDelegate协议的可选属性注意: +因为
delegate并不是该游戏的必备条件,delegate被定义为遵循DiceGameDelegate协议的可选属性
DicegameDelegate协议提供了三个方法用来追踪游戏过程。被放置于游戏的逻辑中,即play()方法内。分别在游戏开始时,新一轮开始时,游戏结束时被调用。因为
delegate是一个遵循DiceGameDelegate的可选属性,因此在play()方法中使用了可选链来调用委托方法。 若delegate属性为nil, 则委托调用优雅地失效。若delegate不为nil,则委托方法被调用如下所示,
-DiceGameTracker遵循了DiceGameDelegate协议+class DiceGameTracker: DiceGameDelegate { +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") } } -+
DiceGameTracker实现了DiceGameDelegate协议的方法要求,用来记录游戏已经进行的轮数。 当游戏开始时,numberOfTurns属性被赋值为0;在每新一轮中递加;游戏结束后,输出打印游戏的总轮数。
DiceGameTracker实现了DiceGameDelegate协议的方法要求,用来记录游戏已经进行的轮数。 当游戏开始时,numberOfTurns属性被赋值为0;在每新一轮中递加;游戏结束后,输出打印游戏的总轮数。
gameDidStart方法从game参数获取游戏信息并输出。game在方法中被当做DiceGame类型而不是SnakeAndLadders类型,所以方法中只能访问DiceGame协议中的成员。-
DiceGameTracker的运行情况,如下所示:+“let tracker = DiceGameTracker() ++// The game lasted for 4 turns +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” -在扩展中添加协议成员
即便无法修改源代码,依然可以通过
扩展(Extension)来扩充已存在类型(译者注: 类,结构体,枚举等)。扩展可以为已存在的类型添加属性,方法,下标,协议等成员。详情请在扩展章节中查看。-注意:
-通过
+扩展为已存在的类型遵循协议时,该类型的所有实例也会随之添加协议中的方法注意: +通过
扩展为已存在的类型遵循协议时,该类型的所有实例也会随之添加协议中的方法-
TextRepresentable协议含有一个asText,如下所示:+ +protocol TextRepresentable { +protocol TextRepresentable { func asText() -> String } -通过
-扩展为上一节中提到的Dice类遵循TextRepresentable协议+extension Dice: TextRepresentable { - func asText() -> String { +通过
+扩展为上一节中提到的Dice类遵循TextRepresentable协议extension Dice: TextRepresentable { + cun asText() -> String { return "A \(sides)-sided dice" } } -从现在起,
-Dice类型的实例可被当作TextRepresentable类型:+let d12 = Dice(sides: 12,generator: LinearCongruentialGenerator()) +从现在起,
+Dice类型的实例可被当作TextRepresentable类型:let d12 = Dice(sides: 12,generator: LinearCongruentialGenerator()) println(d12.asText()) // 输出 "A 12-sided dice" --
SnakesAndLadders类也可以通过扩展的方式来遵循协议:+extension SnakeAndLadders: TextRepresentable { ++
SnakesAndLadders类也可以通过扩展的方式来遵循协议:-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" -通过延展补充协议声明
+通过扩展补充协议声明
当一个类型已经实现了协议中的所有要求,却没有声明时,可以通过
-扩展来补充协议声明:+struct Hamster { +struct Hamster { var name: String func asText() -> String { return "A hamster named \(name)" } } extension Hamster: TextRepresentabl {} -从现在起,
-Hamster的实例可以作为TextRepresentable类型使用+let simonTheHamster = Hamster(name: "Simon") +从现在起,
+Hamster的实例可以作为TextRepresentable类型使用let simonTheHamster = Hamster(name: "Simon") let somethingTextRepresentable: TextRepresentabl = simonTheHamester println(somethingTextRepresentable.asText()) // 输出 "A hamster named Simon" --注意:
-即时满足了协议的所有要求,类型也不会自动转变,因此你必须为它做出明显的协议声明
++注意: +即时满足了协议的所有要求,类型也不会自动转变,因此你必须为它做出明显的协议声明
集合中的协议类型
协议类型可以被集合使用,表示集合中的元素均为协议类型:
-let things: TextRepresentable[] = [game,d12,simoTheHamster] -如下所示,
-things数组可以被直接遍历,并调用其中元素的asText()函数:+for thing in things { ++let things: TextRepresentable[] = [game,d12,simoTheHamster] +如下所示,
+things数组可以被直接遍历,并调用其中元素的asText()函数:for thing in things { println(thing.asText()) } // A game of Snakes and Ladders with 25 squares // A 12-sided dice // A hamster named Simon -+
thing被当做是TextRepresentable类型而不是Dice,DiceGame,Hamster等类型。因此能且仅能调用asText方法
thing被当做是TextRepresentable类型而不是Dice,DiceGame,Hamster等类型。因此能且仅能调用asText方法协议的继承
协议能够继承一到多个其他协议。语法与类的继承相似,多个协议间用逗号
-,分隔+protocol InheritingProtocol: SomeProtocol, AnotherProtocol { +protocol InheritingProtocol: SomeProtocol, AnotherProtocol { // 协议定义 } -如下所示,
-PrettyTextRepresentable协议继承了TextRepresentable协议+protocol PrettyTextRepresentable: TextRepresentable { +如下所示,
+PrettyTextRepresentable协议继承了TextRepresentable协议protocol PrettyTextRepresentable: TextRepresentable { func asPrettyText() -> String } -+
遵循``PrettyTextRepresentable协议的同时,也需要遵循TextRepresentable`协议。
遵循``PrettyTextRepresentable协议的同时,也需要遵循TextRepresentable`协议。如下所示,用
-扩展为SnakesAndLadders遵循PrettyTextRepresentable协议:+extension SnakesAndLadders: PrettyTextRepresentable { +extension SnakesAndLadders: PrettyTextRepresentable { func asPrettyText() -> String { var output = asText() + ":\n" for index in 1...finalSquare { @@ -938,21 +966,23 @@ println(somethingTextRepresentable.asText()) return output } } -在
+for in中迭代出了board数组中的每一个元素:在
for in中迭代出了board数组中的每一个元素:
- 当从数组中迭代出的元素的值大于0时,用
▲表示- 当从数组中迭代出的元素的值小于0时,用
▼表示- 当从数组中迭代出的元素的值等于0时,用
○表示任意
-SankesAndLadders的实例都可以使用asPrettyText()方法。+println(game.asPrettyText()) ++println(game.asPrettyText()) // A game of Snakes and Ladders with 25 squares: // ○ ○ ▲ ○ ○ ▲ ○ ○ ▲ ▲ ○ ○ ○ ▼ ○ ○ ○ ○ ▼ ○ ○ ▼ ○ ▼ ○ -协议合成
一个协议可由多个协议采用
protocol<SomeProtocol, AnotherProtocol>这样的格式进行组合,称为协议合成(protocol composition)。举个例子:
-+protocol Named { +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! -+
Named协议包含String类型的name属性;Aged协议包含Int类型的age属性。Person结构体遵循了这两个协议。
Named协议包含String类型的name属性;Aged协议包含Int类型的age属性。Person结构体遵循了这两个协议。
wishHappyBirthday函数的形参celebrator的类型为protocol<Named,Aged>。可以传入任意遵循这两个协议的类型的实例-注意:
-+
协议合成并不会生成一个新协议类型,而是将多个协议合成为一个临时的协议,超出范围后立即失效。注意: +
协议合成并不会生成一个新协议类型,而是将多个协议合成为一个临时的协议,超出范围后立即失效。检验协议的一致性
@@ -982,14 +1013,15 @@ wishHappyBirthday(birthdayPerson)as?返回一个可选值,当实例遵循协议时,返回该协议类型;否则返回nil- as用以强制向下转换型。+@objc protocol HasArea { +@objc protocol HasArea { var area: Double { get } } --注意:
-+
@objc用来表示协议是可选的,也可以用来表示暴露给Objective-C的代码,此外,@objc型协议只对类有效,因此只能在类中检查协议的一致性。详情查看Using Siwft with Cocoa and Objectivei-c。+-注意: +
@objc用来表示协议是可选的,也可以用来表示暴露给Objective-C的代码,此外,@objc型协议只对类有效,因此只能在类中检查协议的一致性。详情查看Using Siwft with Cocoa and Objectivei-c。+class Circle: HasArea { +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 } } -+
Circle和Country都遵循了HasArea协议,前者把area写为计算型属性(computed property),后者则把area写为存储型属性(stored property)。
Circle和Country都遵循了HasArea协议,前者把area写为计算型属性(computed property),后者则把area写为存储型属性(stored property)。如下所示,
-Animal类没有实现任何协议+class Animal { +class Animal { var legs: Int init(legs: Int) { self.legs = legs } } --
Circle,Country,Animal并没有一个相同的基类,所以采用AnyObject类型的数组来装载在它们的实例,如下所示:+let objects: AnyObject[] = [ ++
Circle,Country,Animal并没有一个相同的基类,所以采用AnyObject类型的数组来装载在它们的实例,如下所示:let objects: AnyObject[] = [ Circle(radius: 2.0), Country(area: 243_610), Animal(legs: 4) ] -如下所示,在迭代时检查
-object数组的元素是否遵循了HasArea协议:+for object in objects { +如下所示,在迭代时检查
+object数组的元素是否遵循了HasArea协议: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 -当数组中的元素遵循
+HasArea协议时,通过as?操作符将其可选绑定(optional binding)到objectWithArea常量上。当数组中的元素遵循
HasArea协议时,通过as?操作符将其可选绑定(optional binding)到objectWithArea常量上。
objects数组中元素的类型并不会因为向下转型而改变,当它们被赋值给objectWithArea时只被视为HasArea类型,因此只有area属性能够被访问。可选协议要求
@@ -1030,21 +1066,22 @@ class Country: HasArea {可选协议在调用时使用
可选链,详细内容在可选链章节中查看。像
someOptionalMethod?(someArgument)一样,你可以在可选方法名称后加上?来检查该方法是否被实现。可选方法和可选属性都会返回一个可选值(optional value),当其不可访问时,?之后语句不会执行,并返回nil。-注意:
-可选协议只能在含有
+@objc前缀的协议中生效。且@objc的协议只能被类遵循。注意: +可选协议只能在含有
@objc前缀的协议中生效。且@objc的协议只能被类遵循。-
Counter类使用CounterDataSource类型的外部数据源来提供增量值(increment amount),如下所示:+@objc protocol CounterDataSource { +@objc protocol CounterDataSource { @optional func incrementForCount(count: Int) -> Int @optional var fixedIncrement: Int { get } } -+
CounterDataSource含有incrementForCount的可选方法和fiexdIncrement的可选属性。
CounterDataSource含有incrementForCount的可选方法和fiexdIncrement的可选属性。-注意:
-+
CounterDataSource中的属性和方法都是可选的,因此可以在类中声明但不实现这些成员,尽管技术上允许这样做,不过最好不要这样写。注意: +
CounterDataSource中的属性和方法都是可选的,因此可以在类中声明但不实现这些成员,尽管技术上允许这样做,不过最好不要这样写。-
Counter类含有CounterDataSource?类型的可选属性dataSource,如下所示:+@objc class Counter { +@objc class Counter { var count = 0 var dataSource: CounterDataSource? func increment() { @@ -1055,7 +1092,8 @@ class Country: HasArea { } } } -+
count属性用于存储当前的值,increment方法用来为count赋值。
count属性用于存储当前的值,increment方法用来为count赋值。
increment方法通过可选链,尝试从两种可选成员中获取count。
- 由于
@@ -1064,11 +1102,12 @@ class Country: HasArea {dataSource可能为nil,因此在dataSource后边加上了?标记来表明只在dataSource非空时才去调用incrementForCount`方法。在调用
incrementForCount方法后,Int型可选值通过可选绑定(optional binding)自动拆包并赋值给常量amount。当
incrementForCount不能被调用时,尝试使用可选属性``fixedIncrement来代替。-
ThreeSource实现了CounterDataSource协议,如下所示:class ThreeSource: CounterDataSource { +class ThreeSource: CounterDataSource { let fixedIncrement = 3 } -使用
-ThreeSource作为数据源开实例化一个Counter:+var counter = Counter() +使用
+ThreeSource作为数据源开实例化一个Counter:var counter = Counter() counter.dataSource = ThreeSource() for _ in 1...4 { counter.increment() @@ -1078,8 +1117,9 @@ for _ in 1...4 { // 6 // 9 // 12 --
TowardsZeroSource实现了CounterDataSource协议中的incrementForCount方法,如下所示:+class TowardsZeroSource: CounterDataSource { ++
TowardsZeroSource实现了CounterDataSource协议中的incrementForCount方法,如下所示:class TowardsZeroSource: CounterDataSource { func incrementForCount(count: Int) -> Int { if count == 0 { return 0 @@ -1090,8 +1130,9 @@ func incrementForCount(count: Int) -> Int { } } } -下边是执行的代码:
-+counter.count = -4 +下边是执行的代码:
++counter.count = -4 counter.dataSource = TowardsZeroSource() for _ in 1...5 { counter.increment() @@ -1103,6 +1144,7 @@ for _ in 1...5 { // 0 // 0+@@ -587,11 +587,10 @@-+ diff --git a/chapter2/23_Advanced_Operators.html b/chapter2/23_Advanced_Operators.html index fc2a9ced..09195103 100644 --- a/chapter2/23_Advanced_Operators.html +++ b/chapter2/23_Advanced_Operators.html @@ -10,7 +10,7 @@ - + @@ -46,7 +46,7 @@ - -翻译:takalard
-校对:lifedim
+泛型
@@ -611,20 +610,22 @@泛型所解决的问题
这里是一个标准的,非泛型函数
-swapTwoInts,用来交换两个Int值:+func swapTwoInts(inout a: Int, inout b: Int) - let temporaryA = a - a = b - b = temporaryA +func swapTwoInts(inout a: Int, inout b: Int) + let temporaryA = a + a = b + b = temporaryA } -这个函数使用写入读出(in-out)参数来交换
+a和b的值,请参考[写入读出参数][1]。这个函数使用写入读出(in-out)参数来交换
a和b的值,请参考[写入读出参数][1]。-
swapTwoInts函数可以交换b的原始值到a,也可以交换a的原始值到b,你可以调用这个函数交换两个Int变量值:+var someInt = 3 +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) { ++
swapTwoInts函数是非常有用的,但是它只能交换Int值,如果你想要交换两个String或者Double,就不得不写更多的函数,如swapTwoStrings和swapTwoDoublesfunctions,如同如下所示: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 } -你可能注意到
+swapTwoInts、swapTwoStrings和swapTwoDoubles函数功能都是相同的,唯一不同之处就在于传入的变量类型不同,分别是Int、String和Double。你可能注意到
swapTwoInts、swapTwoStrings和swapTwoDoubles函数功能都是相同的,唯一不同之处就在于传入的变量类型不同,分别是Int、String和Double。但实际应用中通常需要一个用处更强大并且尽可能的考虑到更多的灵活性单个函数,可以用来交换两个任何类型值,很幸运的是,泛型代码帮你解决了这种问题。(一个这种泛型函数后面已经定义好了。)
-注意: -在所有三个函数中,
+a和b的类型是一样的。如果a和b不是相同的类型,那它们俩就不能互换值。Swift 是类型安全的语言,所以它不允许一个String类型的变量和一个Double类型的变量互相交换值。如果一定要做,Swift 将报编译错误。注意:
在所有三个函数中,a和b的类型是一样的。如果a和b不是相同的类型,那它们俩就不能互换值。Swift 是类型安全的语言,所以它不允许一个String类型的变量和一个Double类型的变量互相交换值。如果一定要做,Swift 将报编译错误。泛型函数
-
泛型函数可以工作于任何类型,这里是一个上面swapTwoInts函数的泛型版本,用于交换两个值:+func swapTwoValues<T>(inout a: T, inout b: T) { +func swapTwoValues<T>(inout a: T, inout b: T) { let temporaryA = a a = b b = temporaryA } --
swapTwoValues函数主体和swapTwoInts函数是一样的,它只在第一行稍微有那么一点点不同于swapTwoInts,如下所示:+func swapTwoInts(inout a: Int, inout b: Int) ++
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来表示)来代替实际类型名(如In、String或Doubl)。占位类型名没有提示T必须是什么类型,但是它提示了a和b必须是同一类型T,而不管T表示什么类型。只有swapTwoValues函数在每次调用时所传入的实际类型才能决定T所代表的类型。另外一个不同之处在于这个泛型函数名后面跟着的展位类型名字(T)是用尖括号括起来的(
)。这个尖括号告诉 Swift 那个 T是swapTwoValues函数所定义的一个类型。因为T是一个占位命名类型,Swift 不会去查找命名为T的实际类型。
swapTwoValues函数除了要求传入的两个任何类型值是同一类型外,也可以作为swapTwoInts函数被调用。每次swapTwoValues被调用,T所代表的类型值都会传给函数。在下面的两个例子中,
-T分别代表Int和String:+var someInt = 3 ++var someInt = 3 var anotherInt = 107 swapTwoValues(&someInt, &anotherInt) // someInt is now 107, and anotherInt is now 3 - -var someString = "hello" +var someString = "hello" var anotherString = "world" swapTwoValues(&someString, &anotherString) // someString is now "world", and anotherString is now "hello" --注意 -上面定义的函数
+swapTwoValues是受swap函数启发而实现的。swap函数存在于 Swift 标准库,并可以在其它类中任意使用。如果你在自己代码中需要类似swapTwoValues函数的功能,你可以使用已存在的交换函数swap函数。+注意
上面定义的函数swapTwoValues是受swap函数启发而实现的。swap函数存在于 Swift 标准库,并可以在其它类中任意使用。如果你在自己代码中需要类似swapTwoValues函数的功能,你可以使用已存在的交换函数swap函数。类型参数
@@ -679,16 +682,14 @@ swapTwoValues(&someString, &anotherString)在简单的情况下,泛型函数或泛型类型需要指定一个占位类型(如上面的
swapTwoValues泛型函数,或一个存储单一类型的泛型集,如数组),通常用一单个字母T来命名类型参数。不过,你可以使用任何有效的标识符来作为类型参数名。如果你使用多个参数定义更复杂的泛型函数或泛型类型,那么使用更多的描述类型参数是非常有用的。例如,Swift 字典(Dictionary)类型有两个类型参数,一个是键,另外一个是值。如果你自己写字典,你或许会定义这两个类型参数为
KeyType和ValueType,用来记住它们在你的泛型代码中的作用。-注意 -请始终使用大写字母开头的驼峰式命名法(例如
+T和KeyType)来给类型参数命名,以表明它们是类型的占位符,而非类型值。注意
请始终使用大写字母开头的驼峰式命名法(例如T和KeyType)来给类型参数命名,以表明它们是类型的占位符,而非类型值。泛型类型
通常在泛型函数中,Swift 允许你定义你自己的泛型类型。这些自定义类、结构体和枚举作用于任何类型,如同
Array和Dictionary的用法。这部分向你展示如何写一个泛型集类型--
Stack(栈)。一个栈是一系列值域的集合,和Array(数组)类似,但其是一个比 Swift 的Array类型更多限制的集合。一个数组可以允许其里面任何位置的插入/删除操作,而栈,只允许在集合的末端添加新的项(如同push一个新值进栈)。同样的一个栈也只能从末端移除项(如同pop一个值出栈)。-注意 -栈的概念已被
+UINavigationController类使用来模拟试图控制器的导航结构。你通过调用UINavigationController的pushViewController:animated:方法来为导航栈添加(add)新的试图控制器;而通过popViewControllerAnimated:的方法来从导航栈中移除(pop)某个试图控制器。每当你需要一个严格的后进先出方式来管理集合,堆栈都是最实用的模型。注意
栈的概念已被UINavigationController类使用来模拟试图控制器的导航结构。你通过调用UINavigationController的pushViewController:animated:方法来为导航栈添加(add)新的试图控制器;而通过popViewControllerAnimated:的方法来从导航栈中移除(pop)某个试图控制器。每当你需要一个严格的后进先出方式来管理集合,堆栈都是最实用的模型。下图展示了一个栈的压栈(push)/出栈(pop)的行为:
![此处输入图片的描述][2]
@@ -700,7 +701,7 @@ swapTwoValues(&someString, &anotherString)移除掉一个值后,现在栈又重新只有三个值。 这里展示了如何写一个非泛型版本的栈,
-Int值型的栈:+struct IntStack { +struct IntStack { var items = Int[]() mutating func push(item: Int) { items.append(item) @@ -709,10 +710,11 @@ swapTwoValues(&someString, &anotherString) return items.removeLast() } } -这个结构体在栈中使用一个
+Array性质的items存储值。Stack提供两个方法:push和pop,从栈中压进一个值和移除一个值。这些方法标记为可变的,因为它们需要修改(或转换)结构体的items数组。这个结构体在栈中使用一个
Array性质的items存储值。Stack提供两个方法:push和pop,从栈中压进一个值和移除一个值。这些方法标记为可变的,因为它们需要修改(或转换)结构体的items数组。上面所展现的
IntStack类型只能用于Int值,不过,其对于定义一个泛型Stack类(可以处理任何类型值的栈)是非常有用的。这里是一个相同代码的泛型版本:
-+struct Stack<T> { +struct Stack<T> { var items = T[]() mutating func push(item: T) { items.append(item) @@ -721,7 +723,8 @@ swapTwoValues(&someString, &anotherString) return items.removeLast() } } -注意到
+Stack的泛型版本基本上和非泛型版本相同,但是泛型版本的占位类型参数为T代替了实际Int类型。这种类型参数包含在一对尖括号里(<T>),紧随在结构体名字后面。注意到
Stack的泛型版本基本上和非泛型版本相同,但是泛型版本的占位类型参数为T代替了实际Int类型。这种类型参数包含在一对尖括号里(<T>),紧随在结构体名字后面。
T定义了一个名为“某种类型T”的节点提供给后来用。这种将来类型可以在结构体的定义里任何地方表示为“T”。在这种情况下,T在如下三个地方被用作节点:
- 创建一个名为
@@ -729,18 +732,20 @@ swapTwoValues(&someString, &anotherString)items的属性,使用空的T类型值数组对其进行初始化;- 指定一个
pop方法的返回值,该返回值将是一个T类型值。当创建一个新单例并初始化时, 通过用一对紧随在类型名后的尖括号里写出实际指定栈用到类型,创建一个
-Stack实例,同创建Array和Dictionary一样:+var stackOfStrings = Stack<String>() +var stackOfStrings = Stack<String>() stackOfStrings.push("uno") stackOfStrings.push("dos") stackOfStrings.push("tres") stackOfStrings.push("cuatro") // 现在栈已经有4个string了 -下图将展示
+stackOfStrings如何push这四个值进栈的过程:下图将展示
stackOfStrings如何push这四个值进栈的过程:![此处输入图片的描述][3]
从栈中
-pop并移除值"cuatro":+let fromTheTop = stackOfStrings.pop() +let fromTheTop = stackOfStrings.pop() // fromTheTop is equal to "cuatro", and the stack now contains 3 strings -下图展示了如何从栈中pop一个值的过程: +
下图展示了如何从栈中pop一个值的过程: ![此处输入图片的描述][4]
由于
@@ -751,13 +756,14 @@ stackOfStrings.push("cuatro")Stack是泛型类型,所以在 Swift 中其可以用来创建任何有效类型的栈,这种方式如同Array和Dictionary。当你创建自定义泛型类型时,你可以定义你自己的类型约束,当然,这些约束要支持泛型编程的强力特征中的多数。抽象概念如
可哈希具有的类型特征是根据它们概念特征来界定的,而不是它们的直接类型特征。类型约束语法
你可以写一个在一个类型参数名后面的类型约束,通过冒号分割,来作为类型参数链的一部分。这种作用于泛型函数的类型约束的基础语法如下所示(和泛型类型的语法相同):
-+func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) { +func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) { // function body goes here } -上面这个假定函数有两个类型参数。第一个类型参数
+T,有一个需要T必须是SomeClass子类的类型约束;第二个类型参数U,有一个需要U必须遵循SomeProtocol协议的类型约束。上面这个假定函数有两个类型参数。第一个类型参数
T,有一个需要T必须是SomeClass子类的类型约束;第二个类型参数U,有一个需要U必须遵循SomeProtocol协议的类型约束。类型约束行为
这里有个名为
-findStringIndex的非泛型函数,该函数功能是去查找包含一给定String值的数组。若查找到匹配的字符串,findStringIndex函数返回该字符串在数组中的索引值(Int),反之则返回nil:+func findStringIndex(array: String[], valueToFind: String) -> Int? { +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 } --
findStringIndex函数可以作用于查找一字符串数组中的某个字符串:+let strings = ["cat", "dog", "llama", "parakeet", "terrapin"] ++
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值替换掉提到的字符串。如果只是针对字符串而言查找在数组中的某个值的索引,用处不是很大,不过,你可以写出相同功能的泛型函数
findIndex,用某个类型T值替换掉提到的字符串。这里展示如何写一个你或许期望的
-findStringIndex的泛型版本findIndex。请注意这个函数仍然返回Int,是不是有点迷惑呢,而不是泛型类型?那是因为函数返回的是一个可选的索引数,而不是从数组中得到的一个可选值。需要提醒的是,这个函数不会编译,原因在例子后面会说明:+func findIndex<T>(array: T[], valueToFind: T) -> Int? { +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 } -上面所写的函数不会编译。这个问题的位置在等式的检查上,
+“if value == valueToFind”。不是所有的 Swift 中的类型都可以用等式符(==)进行比较。例如,如果你创建一个你自己的类或结构体来表示一个复杂的数据模型,那么 Swift 没法猜到对于这个类或结构体而言“等于”的意思。正因如此,这部分代码不能可能保证工作于每个可能的类型T,当你试图编译这部分代码时估计会出现相应的错误。上面所写的函数不会编译。这个问题的位置在等式的检查上,
“if value == valueToFind”。不是所有的 Swift 中的类型都可以用等式符(==)进行比较。例如,如果你创建一个你自己的类或结构体来表示一个复杂的数据模型,那么 Swift 没法猜到对于这个类或结构体而言“等于”的意思。正因如此,这部分代码不能可能保证工作于每个可能的类型T,当你试图编译这部分代码时估计会出现相应的错误。不过,所有的这些并不会让我们无从下手。Swift 标准库中定义了一个
Equatable协议,该协议要求任何遵循的类型实现等式符(==)和不等符(!=)对任何两个该类型进行比较。所有的 Swift 标准类型自动支持Equatable协议。任何
-Equatable类型都可以安全的使用在findIndex函数中,因为其保证支持等式操作。为了说明这个事实,当你定义一个函数时,你可以写一个Equatable类型约束作为类型参数定义的一部分:+func findIndex<T: Equatable>(array: T[], valueToFind: T) -> Int? { +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 } -+
findIndex中这个单个类型参数写做:T: Equatable,也就意味着“任何T类型都遵循Equatable协议”。
findIndex中这个单个类型参数写做:T: Equatable,也就意味着“任何T类型都遵循Equatable协议”。-
findIndex函数现在则可以成功的编译过,并且作用于任何遵循Equatable的类型,如Double或String:+let doubleIndex = findIndex([3.14159, 0.1, 0.25], 9.3) ++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 -关联类型
当定义一个协议时,有的时候声明一个或多个关联类型作为协议定义的一部分是非常有用的。一个关联类型给定作用于协议部分的类型一个节点名(或别名)。作用于关联类型上实际类型是不需要指定的,直到该协议接受。关联类型被指定为
typealias关键字。关联类型行为
这里是一个
-Container协议的例子,定义了一个ItemType关联类型:+protocol Container { +protocol Container { typealias ItemType mutating func append(item: ItemType) var count: Int { get } subscript(i: Int) -> ItemType { get } } -+
Container协议定义了三个任何容器必须支持的兼容要求:
Container协议定义了三个任何容器必须支持的兼容要求:
- 必须可能通过
append方法添加一个新item到容器里;- 必须可能通过使用
@@ -820,7 +832,7 @@ let stringIndex = findIndex(["Mike", "Malcolm", "Andreacount属性获取容器里items的数量,并返回一个Int值;为了定义这三个条件,
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 { +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] } } -+
IntStack类型实现了Container协议的所有三个要求,在IntStack类型的每个包含部分的功能都满足这些要求。
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 { +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] } } -这个时候,占位类型参数
+T被用作append方法的item参数和下标的返回类型。Swift 因此可以推断出被用作这个特定容器的ItemType的T的合适类型。这个时候,占位类型参数
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来使用。+extension Array: Container {} +如同上面的泛型
Stack类型一样,Array的append方法和下标保证Swift可以推断出ItemType所使用的适用的类型。定义了这个扩展后,你可以将任何Array当作Container来使用。Where 语句
[类型约束][8]中描述的类型约束确保你定义关于类型参数的需求和一泛型函数或类型有关联。
对于关联类型的定义需求也是非常有用的。你可以通过这样去定义where语句作为一个类型参数队列的一部分。一个
where语句使你能够要求一个关联类型遵循一个特定的协议,以及(或)那个特定的类型参数和关联类型可以是相同的。你可写一个where语句,通过紧随放置where关键字在类型参数队列后面,其后跟着一个或者多个针对关联类型的约束,以及(或)一个或多个类型和关联类型的等于关系。下面的列子定义了一个名为
allItemsMatch的泛型函数,用来检查是否两个Container单例包含具有相同顺序的相同元素。如果匹配到所有的元素,那么返回一个为true的Boolean值,反之,则相反。这两个容器可以被检查出是否是相同类型的容器(虽然它们可以是),但它们确实拥有相同类型的元素。这个需求通过一个类型约束和
-where语句结合来表示:+func allItemsMatch< +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 } -这个函数用了两个参数:
+someContainer和anotherContainer。someContainer参数是类型C1,anotherContainer参数是类型C2。C1和C2是容器的两个占位类型参数,决定了这个函数何时被调用。这个函数用了两个参数:
someContainer和anotherContainer。someContainer参数是类型C1,anotherContainer参数是类型C2。C1和C2是容器的两个占位类型参数,决定了这个函数何时被调用。这个函数的类型参数列紧随在两个类型参数需求的后面:
- @@ -918,7 +934,7 @@ let stringIndex = findIndex(["Mike", "Malcolm", "Andrea
C1必须遵循Container协议 (写作C1: Container)。检查完之后,函数通过
for-in循环和半闭区间操作(..)来迭代someContainer中的所有元素。对于每个元素,函数检查是否someContainer中的元素不等于对应的anotherContainer中的元素,如果这两个元素不等,则这两个容器不匹配,返回false。如果循环体结束后未发现没有任何的不匹配,那表明两个容器匹配,函数返回
true。这里演示了allItemsMatch函数运算的过程:
-+var stackOfStrings = Stack<String>() +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." -上面的例子创建一个
+Stack单例来存储String,然后压了三个字符串进栈。这个例子也创建了一个Array单例,并初始化包含三个同栈里一样的原始字符串。即便栈和数组否是不同的类型,但它们都遵循Container协议,而且它们都包含同样的类型值。你因此可以调用allItemsMatch函数,用这两个容器作为它的参数。在上面的例子中,allItemsMatch函数正确的显示了所有的这两个容器的items匹配。上面的例子创建一个
Stack单例来存储String,然后压了三个字符串进栈。这个例子也创建了一个Array单例,并初始化包含三个同栈里一样的原始字符串。即便栈和数组否是不同的类型,但它们都遵循Container协议,而且它们都包含同样的类型值。你因此可以调用allItemsMatch函数,用这两个容器作为它的参数。在上面的例子中,allItemsMatch函数正确的显示了所有的这两个容器的items匹配。+@@ -587,11 +587,10 @@-+ diff --git a/chapter2/chapter2.html b/chapter2/chapter2.html index 7810fbbb..f89d3a11 100644 --- a/chapter2/chapter2.html +++ b/chapter2/chapter2.html @@ -10,7 +10,7 @@ - + @@ -46,7 +46,7 @@ - -翻译:xielingwang
-校对:numbbbbb
+翻译:xielingwang
校对:numbbbbb高级运算符
@@ -776,8 +775,7 @@ let y = x &/ 0计算结果为 4。
查阅Swift运算符的优先级和结合性的完整列表,请看表达式。
-注意:
-Swift的运算符较C语言和Objective-C来得更简单和保守,这意味着跟基于C的语言可能不一样。所以,在移植已有代码到Swift时,注意去确保代码按你想的那样去执行。
+注意:
Swift的运算符较C语言和Objective-C来得更简单和保守,这意味着跟基于C的语言可能不一样。所以,在移植已有代码到Swift时,注意去确保代码按你想的那样去执行。运算符函数
@@ -841,8 +839,7 @@ let afterIncrement = ++toIncrement // afterIncrement 现在也是 (4.0, 5.0)-注意:
-默认的赋值符是不可重载的。只有组合赋值符可以重载。三目条件运算符
+a?b:c也是不可重载。注意:
默认的赋值符是不可重载的。只有组合赋值符可以重载。三目条件运算符a?b:c也是不可重载。比较运算符
Swift无所知道自定义类型是否相等或不等,因为等于或者不等于由你的代码说了算了。所以自定义的类和结构要使用比较符
@@ -895,7 +892,7 @@ let secondVector = Vector2D(x: 3.0, y: 4.0) let plusMinusVector = firstVector +- secondVector // plusMinusVector 此时的值为 (4.0, -2.0) -==或!=就需要重载。这个运算符把两个向量的
+x相加,把向量的y相减。因为它实际是属于加减运算,所以让它保持了和加法一样的结合性和优先级(left和140)。查阅完整的Swift默认结合性和优先级的设置,请移步表达式;这个运算符把两个向量的
x相加,把向量的y相减。因为他实际是属于加减运算,所以让它保持了和加法一样的结合性和优先级(left和140)。查阅完整的Swift默认结合性和优先级的设置,请移步表达式;+@@ -587,7 +587,7 @@-+ Swift 教程
本章介绍了 Swift 的各种特性及其使用方法,是全书的核心部分。
diff --git a/chapter3/01_About_the_Language_Reference.html b/chapter3/01_About_the_Language_Reference.html index 341f3133..6d5611fe 100644 --- a/chapter3/01_About_the_Language_Reference.html +++ b/chapter3/01_About_the_Language_Reference.html @@ -10,7 +10,7 @@ - + @@ -46,7 +46,7 @@ -+@@ -587,11 +587,10 @@-+ -翻译:ChildhoodAndy
-校对:numbbbbb
+翻译:dabing1022
校对:numbbbbb关于语言附注
@@ -604,8 +603,8 @@如何阅读语法
用来描述Swift编程语言形式语法的记法遵循下面几个约定:
+-](https://github.com/numbbbbb)箭头(→)用来标记语法产式,可以被理](https://github.com/numbbbbb)解为“可以包含”。
-
- 箭头(→)用来标记语法产式,可以被理解为“可以包含”。
- 句法范畴由斜体文字表示,并出现在一个语法产式规则两侧。
- 义词和标点符号由粗体固定宽度的文本显示和只出现在一个语法产式规则的右边。
- 选择性的语法产式由竖线(|)分隔。当可选用的语法产式太多时,为了阅读方便,它们将被拆分为多行语法产式规则。
@@ -614,14 +613,11 @@举个例子,getter-setter的语法块的定义如下:
-GRAMMAR OF A GETTER-SETTER BLOCK
-getter-setter-block → { getter-clause setter-clauseopt } | { setter-clause getter-clause}
+GRAMMAR OF A GETTER-SETTER BLOCK
getter-setter-block → { getter-clause setter-clauseopt } | { setter-clause getter-clause}这个定义表明,一个getter-setter方法块可以由一个getter子句后跟一个可选的setter子句构成,用大括号括起来,或者由一个setter子句后跟一个getter子句构成,用大括号括起来。上述的文法产生等价于下面的两个产生,明确阐明如何二中择一:
-diff --git a/chapter3/02_Lexical_Structure.html b/chapter3/02_Lexical_Structure.html index be954633..a2e6da75 100644 --- a/chapter3/02_Lexical_Structure.html +++ b/chapter3/02_Lexical_Structure.html @@ -10,7 +10,7 @@ - + @@ -46,7 +46,7 @@ -GRAMMAR OF A GETTER-SETTER BLOCK
-getter-setter-block → { getter-clause setter-clauseopt }
-getter-setter-block → { setter-clause getter-clause}
+GRAMMAR OF A GETTER-SETTER BLOCK
getter-setter-block → { getter-clause setter-clauseopt }
getter-setter-block → { setter-clause getter-clause}+@@ -587,11 +587,10 @@-+ -翻译:superkam
-校对:numbbbbb
+词法结构
@@ -615,44 +614,16 @@使用保留字(reserved word)作为标识符,需要在其前后增加反引号
`。例如,class不是合法的标识符,但可以使用`class`。反引号不属于标识符的一部分,`x`和x表示同一标识符。闭包(closure)中如果没有明确指定参数名称,参数将被隐式命名为
$0、$1、$2... 这些命名在闭包作用域内是合法的标识符。-标识符语法
-identifier → identifier-head identifier-characters opt
-identifier → ` identifier-head identifier-characters opt `
-identifier → implicit-parameter-name
-identifier-list → identifier | identifier , 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
-identifier-characters → identifier-character identifier-characters opt
-implicit-parameter-name → $ decimal-digits
+标识符语法
标识符 → 标识符头(Head) 标识符字符列表 可选
标识符 → ` 标识符头(Head) 标识符字符列表 可选 `
标识符 → 隐式参数名
标识符列表 → 标识符 | 标识符 , 标识符列表
标识符头(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)
标识符字符列表 → 标识符字符 标识符字符列表 可选
隐式参数名 → $ 十进制数字列表关键字
被保留的关键字(keywords)不允许用作标识符,除非被反引号转义,参见 标识符。
-
- -
用作声明的关键字: 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,这些关键字在特定上下文之外可以被用于标识符。
-- 用作声明的关键字: 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,这些关键字在特定上下文之外可以被用于标识符。
字面量
@@ -662,8 +633,7 @@ "Hello, world!" // 文本型字面量-字面量语法
-literal → integer-literal | floating-point-literal | string-literal
+整型字面量
整型字面量(integer literals)表示未指定精度整型数的值。整型字面量默认用十进制表示,可以加前缀来指定其他的进制,二进制字面量加
@@ -675,28 +645,7 @@0b,八进制字面量加0o,十六进制字面量加0x。除非特殊指定,整型字面量的默认类型为 Swift 标准库类型中的
Int。Swift 标准库还定义了其他不同长度以及是否带符号的整数类型,请参考 整数类型。-整型字面量语法
-integer-literal → binary-literal
-integer-literal → octal-literal
-integer-literal → decimal-literal
-integer-literal → hexadecimal-literal
-binary-literal → 0b binary-digit binary-literal-characters opt
-binary-digit → 数字 0 或 1
-binary-literal-character → binary-digit | _
-binary-literal-characters → binary-literal-character binary-literal-characters opt
-octal-literal → 0o octal-digit octal-literal-characters opt
-octal-digit → 数字 0 至 7
-octal-literal-character → octal-digit | _
-octal-literal-characters → octal-literal-character octal-literal-characters opt
-decimal-literal → decimal-digit decimal-literal-characters opt
-decimal-digit → 数字 0 至 9
-decimal-digits → decimal-digit decimal-digits opt
-decimal-literal-character → decimal-digit | _
-decimal-literal-characters → decimal-literal-character decimal-literal-characters opt
-hexadecimal-literal → 0x hexadecimal-digit hexadecimal-literal-characters opt
-hexadecimal-digit → 数字 0 到 9, a 到 f, 或 A 到 F
-hexadecimal-literal-character → hexadecimal-digit | _
-hexadecimal-literal-characters → hexadecimal-literal-character hexadecimal-literal-characters opt
+整型字面量语法
整型字面量 → 二进制字面量
整型字面量 → 八进制字面量
整型字面量 → 十进制字面量
整型字面量 → 十六进制字面量
二进制字面量 → 0b 二进制数字 二进制字面量字符列表 可选
二进制数字 → 数值 0 到 1
二进制字面量字符 → 二进制数字 | _
二进制字面量字符列表 → 二进制字面量字符 二进制字面量字符列表 可选
八进制字面量 → 0o 八进字数字 八进制字符列表 可选
八进字数字 → 数值 0 到 7
八进制字符 → 八进字数字 | _
八进制字符列表 → 八进制字符 八进制字符列表 可选
十进制字面量 → 十进制数字 十进制字符列表 可选
十进制数字 → 数值 0 到 9
十进制数字列表 → 十进制数字 十进制数字列表 可选
十进制字符 → 十进制数字 | _
十进制字符列表 → 十进制字符 十进制字符列表 可选
十六进制字面量 → 0x 十六进制数字 十六进制字面量字符列表 可选
十六进制数字 → 数值 0 到 9, a through f, or A through F
十六进制字符 → 十六进制数字 | _
十六进制字面量字符列表 → 十六进制字符 十六进制字面量字符列表 可选浮点型字面量
浮点型字面量(floating-point literals)表示未指定精度浮点数的值。
@@ -710,21 +659,13 @@除非特殊指定,浮点型字面量的默认类型为 Swift 标准库类型中的
Double,表示64位浮点数。Swift 标准库也定义Float类型,表示32位浮点数。-浮点型字面量语法
-floating-point-literal → decimal-literal decimal-fraction opt decimal-exponent opt
-floating-point-literal → hexadecimal-literal hexadecimal-fraction opt hexadecimal-exponent
-decimal-fraction → . decimal-literal
-decimal-exponent → floating-point-e sign opt decimal-literal
-hexadecimal-fraction → . hexadecimal-literal opt
-hexadecimal-exponent → floating-point-p sign opt hexadecimal-literal
-floating-point-e → e | E
-floating-point-p → p | P
-sign → + | -
+浮点型字面量语法
浮点数字面量 → 十进制字面量 十进制分数 可选 十进制指数 可选
浮点数字面量 → 十六进制字面量 十六进制分数 可选 十六进制指数
十进制分数 → . 十进制字面量
十进制指数 → 浮点数e 正负号 可选 十进制字面量
十六进制分数 → . 十六进制字面量 可选
十六进制指数 → 浮点数p 正负号 可选 十六进制字面量
浮点数e → e | E
浮点数p → p | P
正负号 → + | -文本型字面量
文本型字面量(string literal)由双引号中的字符串组成,形式如下:
-"characters" -文本型字面量中不能包含未转义的双引号
+"、未转义的反斜线\、回车符(carriage return)或换行符(line feed)。+"characters" +文本型字面量中不能包含未转义的双引号
"、未转义的反斜线\、回车符(carriage return)或换行符(line feed)。可以在文本型字面量中使用的转义特殊符号如下:
- 空字符(Null Character)
@@ -751,16 +692,7 @@ var x = 3; "1 2 \(x)"\0文本型字面量的默认类型为
String。组成字符串的字符类型为Character。更多有关String和Character的信息请参照 字符串和字符。-文本型字面量语法
-string-literal → " quoted-text "
-quoted-text → quoted-text-item quoted-text opt
-quoted-text-item → escaped-character
-quoted-text-item → ( expression )
-quoted-text-item → 除
-"、\、U+000A或U+000D以外的任何 Unicode 扩展字符集escaped-character → \0 | \ | \t | \n | \r | \" | \'
-escaped-character → \x hexadecimal-digit hexadecimal-digit
-escaped-character → \u hexadecimal-digit hexadecimal-digit hexadecimal-digit hexadecimal-digit
-escaped-character → \U hexadecimal-digit hexadecimal-digit hexadecimal-digit hexadecimal-digit hexadecimal-digit hexadecimal-digit hexadecimal-digit hexadecimal-digit
+字符型字面量语法
字符串字面量 → " 引用文本 "
引用文本 → 引用文本条目 引用文本 可选
引用文本条目 → 转义字符
引用文本条目 → ( 表达式 )
引用文本条目 → 除了", \, U+000A, or U+000D的所有Unicode的字符
转义字符 → \0 | \ | \t | \n | \r | \" | \'
转义字符 → \x 十六进制数字 十六进制数字
转义字符 → \u 十六进制数字 十六进制数字 十六进制数字 十六进制数字
转义字符 → \U 十六进制数字 十六进制数字 十六进制数字 十六进制数字 十六进制数字 十六进制数字 十六进制数字 十六进制数字运算符
@@ -769,26 +701,17 @@ var x = 3; "1 2 \(x)"/、=、-、+、!、*、%、<、>、&、|、^、~、.。也就是说,标记=,->、//、/*、*/、.以及一元前缀运算符&属于保留字,这些标记不能被重写或用于自定义运算符。运算符两侧的空白被用来区分该运算符是否为前缀运算符(prefix operator)、后缀运算符(postfix operator)或二元运算符(binary operator)。规则总结如下:
-
- -
如果运算符两侧都有空白或两侧都无空白,将被看作二元运算符。例如:
-a+b和a + b中的运算符+被看作二元运算符。- -
如果运算符只有左侧空白,将被看作前缀一元运算符。例如
-a ++b中的++被看作前缀一元运算符。- -
如果运算符只有右侧空白,将被看作后缀一元运算符。例如
-a++ b中的++被看作后缀一元运算符。- +
如果运算符左侧没有空白并紧跟
-.,将被看作后缀一元运算符。例如a++.b中的++被看作后缀一元运算符(同理,a++ . b中的++是后缀一元运算符而a ++ .b中的++不是).- 如果运算符两侧都有空白或两侧都无空白,将被看作二元运算符。例如:
+a+b和a + b中的运算符+被看作二元运算符。- 如果运算符只有左侧空白,将被看作前缀一元运算符。例如
+a ++b中的++被看作前缀一元运算符。- 如果运算符只有右侧空白,将被看作后缀一元运算符。例如
+a++ b中的++被看作后缀一元运算符。- 如果运算符左侧没有空白并紧跟
.,将被看作后缀一元运算符。例如a++.b中的++被看作后缀一元运算符(同理,a++ . b中的++是后缀一元运算符而a ++ .b中的++不是).鉴于这些规则,运算符前的字符
(、[和{;运算符后的字符)、]和}以及字符,、;和:都将用于空白检测。以上规则需注意一点,如果运算符
!或?左侧没有空白,则不管右侧是否有空白都将被看作后缀运算符。如果将?用作可选类型(optional type)修饰,左侧必须无空白。如果用于条件运算符? :,必须两侧都有空白。在特定构成中 ,以
<或>开头的运算符会被分离成两个或多个标记,剩余部分以同样的方式会被再次分离。因此,在Dictionary<String, Array<Int>>中没有必要添加空白来消除闭合字符>的歧义。在这个例子中, 闭合字符>被看作单字符标记,而不会被误解为移位运算符>>。要学习如何自定义新的运算符,请参考 自定义操作符 和 运算符声明。学习如何重写现有运算符,请参考 运算符方法。
-diff --git a/chapter3/03_Types.html b/chapter3/03_Types.html index a4bdce45..1addc70e 100644 --- a/chapter3/03_Types.html +++ b/chapter3/03_Types.html @@ -10,7 +10,7 @@ - + @@ -46,7 +46,7 @@ -运算符语法
-operator → operator-character operator opt
-operator-character → / | = | - | + | ! | * | % | < | > | & | | | ^ | ~ | .
-binary-operator → operator
-prefix-operator → operator
-postfix-operator → operator
+运算符语法语法
运算符 → 运算符字符 运算符 可选
运算符字符 → / | = | - | + | ! | * | % | < | > | & | | | ^ | ~ | .
二元运算符 → 运算符
前置运算符 → 运算符
后置运算符 → 运算符+@@ -587,11 +587,10 @@-+ -翻译:lyuka
-校对:numbbbbb
+类型(Types)
@@ -614,36 +613,32 @@复合型类型是没有名字的类型,它由 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
+类型语法
类型 → 数组类型 | 函数类型 | 类型标识 | 元组类型 | 可选类型 | 隐式解析可选类型 | 协议合成类型 | 元型类型类型注解
类型注解显式地指定一个变量或表达式的值。类型注解始于冒号
-:终于类型,比如下面两个例子:let someTuple:(Double, Double) = (3.14159, 2.71828) -func someFunction(a: Int){ /* ... */ } +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
+类型注解语法
类型注解 → : 特性(Attributes)列表 可选 类型类型标识符
类型标识符引用命名型类型或者是命名型/复合型类型的别名。
大多数情况下,类型标识符引用的是同名的命名型类型。例如类型标识符
Int引用命名型类型Int,同样,类型标识符Dictionary<String, Int>引用命名型类型Dictionary<String, Int>。在两种情况下类型标识符引用的不是同名的类型。情况一,类型标识符引用的是命名型/复合型类型的类型别名。比如,在下面的例子中,类型标识符使用
-Point来引用元组(Int, Int):typealias Point = (Int, Int) -let origin: Point = (0, 0) +typealias Point = (Int, Int) +let origin: Point = (0, 0)情况二,类型标识符使用dot(
-.)语法来表示在其它模块(modules)或其它类型嵌套内声明的命名型类型。例如,下面例子中的类型标识符引用在ExampleModule模块中声明的命名型类型MyType:var someValue: ExampleModule.MyType +var someValue: ExampleModule.MyType-类型标识符的语法: -type-identifier → type-name generic-argument-clause[opt] | type-name generic-argument-clause[opt].type-identifier -type-name → identifier
+元组类型
@@ -651,12 +646,7 @@ func someFunction(a: Int){ /* ... */ }你可以使用元组类型作为一个函数的返回类型,这样就可以使函数返回多个值。你也可以命名元组类型中的元素,然后用这些名字来引用每个元素的值。元素的名字由一个标识符和
:组成。“函数和多返回值”章节里有一个展示上述特性的例子。
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
+元组类型语法
元组类型 → ( 元组类型主体 可选 )
元组类型主体 → 元组类型的元素列表 ... 可选
元组类型的元素列表 → 元组类型的元素 | 元组类型的元素 , 元组类型的元素列表
元组类型的元素 → 特性(Attributes)列表 可选 inout 可选 类型 | inout 可选 元素名 类型注解
元素名 → 标识符函数类型
@@ -666,81 +656,78 @@ func someFunction(a: Int){ /* ... */ }由于 参数类型 和 返回值类型 可以是元组类型,所以函数类型可以让函数与方法支持多参数与多返回值。
你可以对函数类型应用带有参数类型
-()并返回表达式类型的auto_closure属性(见类型属性章节)。一个自动闭包函数捕获特定表达式上的隐式闭包而非表达式本身。下面的例子使用auto_closure属性来定义一个很简单的assert函数:func simpleAssert(condition: @auto_closure () -> Bool, message: String){ - if !condition(){ +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." +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:func addTwoNumbers(a: Int)(b: Int) -> Int{ - return a + b +func addTwoNumbers(a: Int)(b: Int) -> Int{ + return a + b } -addTwoNumbers(4)(5) // returns 9 +addTwoNumbers(4)(5) // returns 9柯里化函数的函数类型从右向左组成一组。例如,函数类型
-Int -> Int -> Int可以被理解为Int -> (Int -> Int)——也就是说,一个函数传入一个Int然后输出作为另一个函数的输入,然后又返回一个Int。例如,你可以使用如下嵌套函数来重写柯里化函数addTwoNumbers()():func addTwoNumbers(a: Int) -> (Int -> Int){ +func addTwoNumbers(a: Int) -> (Int -> Int){ func addTheSecondNumber(b: Int) -> Int{ - return a + b + return a + b } - return addTheSecondNumber + return addTheSecondNumber } -addTwoNumbers(4)(5) // Returns 9 +addTwoNumbers(4)(5) // Returns 9-函数类型的语法: -function-type → type -> type
+数组类型
Swift语言使用类型名紧接中括号
-[]来简化标准库中定义的命名型类型Array<T>。换句话说,下面两个声明是等价的:let someArray: String[] = ["Alex", "Brian", "Dave"] -let someArray: Array<String> = ["Alex", "Brian", "Dave"] +let someArray: String[] = ["Alex", "Brian", "Dave"] +let someArray: Array<String> = ["Alex", "Brian", "Dave"]上面两种情况下,常量
someArray都被声明为字符串数组。数组的元素也可以通过[]获取访问:someArray[0]是指第0个元素“Alex”。上面的例子同时显示,你可以使用
-[]作为初始值构造数组,空的[]则用来来构造指定类型的空数组。var emptyArray: Double[] = [] +var emptyArray: Double[] = []你也可以使用链接起来的多个
-[]集合来构造多维数组。例如,下例使用三个[]集合来构造三维整型数组:var array3D: Int[][][] = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]] +var array3D: Int[][][] = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]访问一个多维数组的元素时,最左边的下标指向最外层数组的相应位置元素。接下来往右的下标指向第一层嵌入的相应位置元素,依次类推。这就意味着,在上面的例子中,
array3D[0]是指[[1, 2], [3, 4]],array3D[0][1]是指[3, 4],array3D[0][1][1]则是指值4。关于Swift标准库中
Array类型的细节讨论,见章节Arrays。-数组类型的语法: -array-type → type
+[ ]| array-type[ ]可选类型
Swift定义后缀
-?来作为标准库中的定义的命名型类型Optional<T>的简写。换句话说,下面两个声明是等价的:var optionalInteger: Int? -var optionalInteger: Optional<Int> +var optionalInteger: Int? +var optionalInteger: Optional<Int>在上述两种情况下,变量
optionalInteger都被声明为可选整型类型。注意在类型和?之间没有空格。类型
Optional<T>是一个枚举,有两种形式,None和Some(T),又来代表可能出现或可能不出现的值。任意类型都可以被显式的声明(或隐式的转换)为可选类型。当声明一个可选类型时,确保使用括号给?提供合适的作用范围。比如说,声明一个整型的可选数组,应写作(Int[])?,写成Int[]?的话则会出错。如果你在声明或定义可选变量或特性的时候没有提供初始值,它的值则会自动赋成缺省值
nil。可选符合
LogicValue协议,因此可以出现在布尔值环境下。此时,如果一个可选类型T?实例包含有类型为T的值(也就是说值为Optional.Some(T)),那么此可选类型就为true,否则为false。如果一个可选类型的实例包含一个值,那么你就可以使用后缀操作符
-!来获取该值,正如下面描述的:optionalInteger = 42 -optionalInteger! // 42 +optionalInteger = 42 +optionalInteger! // 42使用
!操作符获取值为nil的可选项会导致运行错误(runtime error)。你也可以使用可选链和可选绑定来选择性的执行可选表达式上的操作。如果值为
nil,不会执行任何操作因此也就没有运行错误产生。更多细节以及更多如何使用可选类型的例子,见章节“可选”。
-可选类型语法: -optional-type → type?
+可选类型语法
可选类型 → 类型 ?隐式解析可选类型
Swift语言定义后缀
-!作为标准库中命名类型ImplicitlyUnwrappedOptional<T>的简写。换句话说,下面两个声明等价:var implicitlyUnwrappedString: String! -var implicitlyUnwrappedString: ImplicitlyUnwrappedOptional<String> +var implicitlyUnwrappedString: String! +var implicitlyUnwrappedString: ImplicitlyUnwrappedOptional<String>上述两种情况下,变量
implicitlyUnwrappedString被声明为一个隐式解析可选类型的字符串。注意类型与!之间没有空格。你可以在使用可选的地方同样使用隐式解析可选。比如,你可以将隐式解析可选的值赋给变量、常量和可选特性,反之亦然。
@@ -749,47 +736,42 @@ optionalInteger! // 42使用可选链会选择性的执行隐式解析可选表达式上的某一个操作。如果值为
nil,就不会执行任何操作,因此也不会产生运行错误。关于隐式解析可选的更多细节,见章节“隐式解析可选”。
-隐式解析可选的语法: -implicitly-unwrapped-optional-type → type!
+隐式解析可选类型(Implicitly Unwrapped Optional Type)语法
隐式解析可选类型 → 类型 !协议合成类型
协议合成类型是一种符合每个协议的指定协议列表类型。协议合成类型可能会用在类型注解和泛型参数中。
协议合成类型的形式如下:
-protocol<Protocol 1, Procotol 2> +protocol<Protocol 1, Procotol 2>协议合成类型允许你指定一个值,其类型可以适配多个协议的条件,而且不需要定义一个新的命名型协议来继承其它想要适配的各个协议。比如,协议合成类型
protocol<Protocol A, Protocol B, Protocol C>等效于一个从Protocol A,Protocol B,Protocol C继承而来的新协议Protocol D,很显然这样做有效率的多,甚至不需引入一个新名字。协议合成列表中的每项必须是协议名或协议合成类型的类型别名。如果列表为空,它就会指定一个空协议合成列表,这样每个类型都能适配。
-协议合成类型的语法: -protocol-composition-type → protocol <protocol-identifier-list[opt]> -protocol-identifier-list → protocol-identifier | protocol-identifier, protocol-identifier-list -protocol-identifier → type-identifier
+协议合成类型语法
协议合成类型 → protocol < 协议标识符列表 可选 >
协议标识符列表 → 协议标识符 | 协议标识符 , 协议标识符列表
协议标识符 → 类型标识元类型
元类型是指所有类型的类型,包括类、结构体、枚举和协议。
类、结构体或枚举类型的元类型是相应的类型名紧跟
.Type。协议类型的元类型——并不是运行时适配该协议的具体类型——是该协议名字紧跟.Protocol。比如,类SomeClass的元类型就是SomeClass.Type,协议SomeProtocol的元类型就是SomeProtocal.Protocol。你可以使用后缀
-self表达式来获取类型。比如,SomeClass.self返回SomeClass本身,而不是SomeClass的一个实例。同样,SomeProtocol.self返回SomeProtocol本身,而不是运行时适配SomeProtocol的某个类型的实例。还可以对类型的实例使用dynamicType表达式来获取该实例在运行阶段的类型,如下所示:class SomeBaseClass { - class func printClassName() { - println("SomeBaseClass") +class SomeBaseClass { + class func printClassName() { + println("SomeBaseClass") } } -class SomeSubClass: SomeBaseClass { - override class func printClassName() { - println("SomeSubClass") +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 +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 +// prints "SomeSubClass-元类型的语法: -metatype-type → type.Type | type.Protocol
+类型继承子句
@@ -798,17 +780,15 @@ someInstance.dynamicType.printClassName()其它命名型类型可能只继承或适配一个协议列表。协议类型可能继承于其它任意数量的协议。当一个协议类型继承于其它协议时,其它协议的条件集合会被集成在一起,然后其它从当前协议继承的任意类型必须适配所有这些条件。
枚举定义中的类型继承子句可以是一个协议列表,或是指定原始值的枚举,一个单独的指定原始值类型的命名型类型。使用类型继承子句来指定原始值类型的枚举定义的例子,见章节“原始值”。
-类型继承子句的语法: -type-inheritance-clause → :type-inheritance-list -type-inheritance-list → type-identifier | type-identifier, type-inheritance-list
+类型推断
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。let e = 2.71828 // The type of e is inferred to be Double. -let eFloat: Float = 2.71828 // The type of eFloat is Float. +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中的类型推断在单独的表达式或语句水平上进行。这意味着所有用于推断类型的信息必须可以从表达式或其某个子表达式的类型检查中获取。
diff --git a/chapter3/04_Expressions.html b/chapter3/04_Expressions.html index 193b885f..d63f5c38 100644 --- a/chapter3/04_Expressions.html +++ b/chapter3/04_Expressions.html @@ -10,7 +10,7 @@ - + @@ -46,7 +46,7 @@ -+@@ -587,11 +587,10 @@-+ -翻译:sg552
-校对:numbbbbb
+表达式(Expressions)
@@ -608,9 +607,7 @@Swift 中存在四种表达式: 前缀(prefix)表达式,二元(binary)表达式,主要(primary)表达式和后缀(postfix)表达式。表达式可以返回一个值,以及运行某些逻辑(causes a side effect)。
前缀表达式和二元表达式就是对某些表达式使用各种运算符(operators)。 主要表达式是最短小的表达式,它提供了获取(变量的)值的一种途径。 后缀表达式则允许你建立复杂的表达式,例如配合函数调用和成员访问。 每种表达式都在下面有详细论述~
-表达式的语法
-expression → prefix-expressionbinary-expressions(opt) -expression-list → expression| expression,expression-list
+前缀表达式(Prefix Expressions)
@@ -627,15 +624,14 @@对于这些操作符的使用,请参见: Basic Operators and Advanced Operators
作为对上面标准库运算符的补充,你也可以对 某个函数的参数使用 '&'运算符。 更多信息,请参见: "In-Out parameters".
-前缀表达式的语法
-prefix-expression → prefix-operator (opt) postfix-expression -prefix-expression → in-out-expression -in-out-expression → &identifier
+前置表达式语法
前置表达式 → 前置运算符 可选 后置表达式
前置表达式 → 写入写出(in-out)表达式
写入写出(in-out)表达式 → & 标识符二元表达式(Binary Expressions)
二元表达式由 "左边参数" + "二元运算符" + "右边参数" 组成, 它有如下的形式:
-+
left-hand argumentoperatorright-hand argument++
left-hand argumentoperatorright-hand argumentSwift 标准库提供了如下的二元运算符:
- 求幂相关(无结合,优先级160)
@@ -731,55 +727,50 @@
关于这些运算符(operators)的更多信息,请参见:Basic Operators and Advanced Operators.
---注意
-在解析时, 一个二元表达式表示为一个一级数组(a flat list), 这个数组(List)根据运算符的先后顺序,被转换成了一个tree. 例如: 2 + 3 5 首先被认为是: 2, + ,
+3, , 5. 随后它被转换成 tree (2 + (3 * 5))注意
在解析时, 一个二元表达式表示为一个一级数组(a flat list), 这个数组(List)根据运算符的先后顺序,被转换成了一个tree. 例如: 2 + 3 5 首先被认为是: 2, + ,3, , 5. 随后它被转换成 tree (2 + (3 * 5))二元表达式的语法
-binary-expression → binary-operatorprefix-expression -binary-expression → assignment-operatorprefix-expression -binary-expression → conditional-operatorprefix-expression -binary-expression → type-casting-operator -binary-expressions → binary-expressionbinary-expressions(opt)
+ + ++二元表达式语法
二元表达式 → 二元运算符 前置表达式
二元表达式 → 赋值运算符 前置表达式
二元表达式 → 条件运算符 前置表达式
二元表达式 → 类型转换运算符
二元表达式列表 → 二元表达式 二元表达式列表 可选赋值表达式(Assignment Operator)
The assigment operator sets a new value for a given expression. It has the following form: 赋值表达式会对某个给定的表达式赋值。 它有如下的形式;
++
expression=value就是把右边的 value 赋值给左边的 expression. 如果左边的expression 需要接收多个参数(是一个tuple ),那么右边必须也是一个具有同样数量参数的tuple. (允许嵌套的tuple)
(a, _, (b, c)) = ("test", 9.45, (12, 3)) // a is "test", b is 12, c is 3, and 9.45 is ignored赋值运算符不返回任何值。
-赋值表达式的语法
-assignment-operator → =
+赋值运算符语法
赋值运算符 → =三元条件运算符(Ternary Conditional Operator)
三元条件运算符 是根据条件来获取值。 形式如下:
-`condition` ? `expression used if true` : `expression used if false` -如果
+condition是true, 那么返回 第一个表达式的值(此时不会调用第二个表达式), 否则返回第二个表达式的值(此时不会调用第一个表达式)。+++
condition?expression used if true:expression used if false如果
condition是true, 那么返回 第一个表达式的值(此时不会调用第二个表达式), 否则返回第二个表达式的值(此时不会调用第一个表达式)。想看三元条件运算符的例子,请参见: Ternary Conditional Operator.
-三元条件表达式
-+
conditional-operator→ ?expression:三元条件运算符语法
三元条件运算符 → ? 表达式 :类型转换运算符(Type-Casting Operators)
有两种类型转换操作符: as 和 is. 它们有如下的形式:
-`expression` as `type` -`expression` as? `type` -`expression` is `type` -as 运算符会把
+目标表达式转换成指定的类型(specified type),过程如下:+++
expressionastypeexpressionas?typeexpressionistypeas 运算符会把
目标表达式转换成指定的类型(specified type),过程如下:-
- -
如果类型转换成功, 那么目标表达式就会返回指定类型的实例(instance). 例如:把子类(subclass)变成父类(superclass)时.
-- -
如果转换失败,则会抛出编译错误( compile-time error)。
-- +
如果上述两个情况都不是(也就是说,编译器在编译时期无法确定转换能否成功,) 那么目标表达式就会变成指定的类型的optional. (is an optional of the specified type ) 然后在运行时,如果转换成功, 目标表达式就会作为 optional的一部分来返回, 否则,目标表达式返回nil. 对应的例子是: 把一个 superclass 转换成一个 subclass.
-- 如果类型转换成功, 那么目标表达式就会返回指定类型的实例(instance). 例如:把子类(subclass)变成父类(superclass)时.
+- 如果转换失败,则会抛出编译错误( compile-time error)。
+- 如果上述两个情况都不是(也就是说,编译器在编译时期无法确定转换能否成功,) 那么目标表达式就会变成指定的类型的optional. (is an optional of the specified type ) 然后在运行时,如果转换成功, 目标表达式就会作为 optional的一部分来返回, 否则,目标表达式返回nil. 对应的例子是: 把一个 superclass 转换成一个 subclass.
class SomeSuperType {} class SomeType: SomeSuperType {} @@ -802,22 +793,13 @@ let y2: SomeType = x // Type information from an annotation关于类型转换的更多内容和例子,请参见: Type Casting.
--类型转换的语法
-type-casting-operator → istype| as?(opt)type
+类型转换运算符(type-casting-operator)语法
类型转换运算符 → is 类型 | as ? 可选 类型主要表达式(Primary Expressions)
-+
主要表达式是最基本的表达式。 它们可以跟 前缀表达式,二元表达式,后缀表达式以及其他主要表达式组合使用。主表达式(Primary Expressions)
+
主表达式是最基本的表达式。 它们可以跟 前缀表达式,二元表达式,后缀表达式以及其他主要表达式组合使用。-主要表达式的语法
-primary-expression → identifiergeneric-argument-clause(opt) -primary-expression → literal-expression -primary-expression → self-expression -primary-expression → superclass-expression -primary-expression → closure-expression -primary-expression → parenthesized-expression -primary-expression → implicit-member-expression -primary-expression → wildcard-expression
+主表达式语法
主表达式 → 标识符 泛型参数子句 可选
主表达式 → 字面量表达式
主表达式 → self表达式
主表达式 → 超类表达式
主表达式 → 闭包表达式
主表达式 → 圆括号表达式
主表达式 → 隐式成员表达式
主表达式 → 通配符表达式字符型表达式(Literal Expression)
由这些内容组成:普通的字符(string, number) , 一个字符的字典或者数组,或者下面列表中的特殊字符。
@@ -854,31 +836,22 @@ let y2: SomeType = x // Type information from an annotation在某个函数(function)中,
__FUNCTION__会返回当前函数的名字。 在某个方法(method)中,它会返回当前方法的名字。 在某个property 的getter/setter中会返回这个属性的名字。 在init/subscript中 只有的特殊成员(member)中会返回这个keyword的名字,在某个文件的顶端(the top level of a file),它返回的是当前module的名字。一个array literal,是一个有序的值的集合。 它的形式是:
-[`value 1`, `value 2`, `...`] -数组中的最后一个表达式可以紧跟一个逗号(','). []表示空数组 。 array literal的type是 T[], 这个T就是数组中元素的type. 如果该数组中有多种type, T则是跟这些type的公共supertype最接近的type.(closest common supertype)
-一个
-dictionary literal是一个包含无序的键值对(key-value pairs)的集合,它的形式是:[`key 1`: `value 1`, `key 2`: `value 2`, `...`] -dictionary 的最后一个表达式可以是一个逗号(','). [:] 表示一个空的dictionary. 它的type是 Dictionary
(这里KeyType表示 key的type, ValueType表示 value的type) 如果这个dictionary 中包含多种 types, 那么KeyType, Value 则对应着它们的公共supertype最接近的type( closest common supertype). -+字符型表达式的语法
-literal-expression → literal -literal-expression → array-literal| dictionary-literal -literal-expression → _FILE_| _LINE_| _COLUMN_| _FUNCTION_ -array-literal → [array-literal-itemsopt] -array-literal-items → array-literal-item,(opt) | array-literal-item,array-literal-items -array-literal-item → expression -dictionary-literal → [dictionary-literal-items] [:] -dictionary-literal-items → dictionary-literal-item,(opt)| dictionary-literal-item,dictionary-literal-items -dictionary-literal-item → expression:expression
+[
+value 1,value 2,...]数组中的最后一个表达式可以紧跟一个逗号(','). []表示空数组 。 array literal的type是 T[], 这个T就是数组中元素的type. 如果该数组中有多种type, T则是跟这些type的公共supertype最接近的type.(closest common supertype)
+一个
+dictionary literal是一个包含无序的键值对(key-value pairs)的集合,它的形式是:++[
+key 1:value 1,key 2:value 2,...]dictionary 的最后一个表达式可以是一个逗号(','). [:] 表示一个空的dictionary. 它的type是 Dictionary
+(这里KeyType表示 key的type, ValueType表示 value的type) 如果这个dictionary 中包含多种 types, 那么KeyType, Value 则对应着它们的公共supertype最接近的type( closest common supertype). +字面量表达式语法
字面量表达式 → 字面量
字面量表达式 → 数组字面量 | 字典字面量
字面量表达式 → __FILE__ | __LINE__ | __COLUMN__ | __FUNCTION__
数组字面量 → [ 数组字面量项列表 可选 ]
数组字面量项列表 → 数组字面量项 , 可选 | 数组字面量项 , 数组字面量项列表
数组字面量项 → 表达式
字典字面量 → [ 字典字面量项列表 ] | [ : ]
字典字面量项列表 → 字典字面量项 , 可选 | 字典字面量项 , 字典字面量项列表
字典字面量项 → 表达式 : 表达式self表达式(Self Expression)
self表达式是对 当前type 或者当前instance的引用。它的形式如下:
-self -self.
+member name-self[subscript index] -self(initializer arguments) -self.init(initializer arguments)self
self.member name
self[subscript index]
self(initializer arguments)
self.init(initializer arguments)如果在 initializer, subscript, instance method中,self等同于当前type的instance. 在一个静态方法(static method), 类方法(class method)中, self等同于当前的type.
当访问 member(成员变量时), self 用来区分重名变量(例如函数的参数). 例如, @@ -899,31 +872,23 @@ self.init(
}initializer arguments)-self表达式的语法
-self-expression → self -self-expression → self.identifier -self-expression → self[expression] -self-expression → self.init
+Self 表达式语法
self表达式 → self
self表达式 → self . 标识符
self表达式 → self [ 表达式 ]
self表达式 → self . init超类表达式(Superclass Expression)
超类表达式可以使我们在某个class中访问它的超类. 它有如下形式:
-super.`member name` -super[`subscript index`] -super.init(`initializer arguments`) -形式1 用来访问超类的某个成员(member). 形式2 用来访问该超类的 subscript 实现。 形式3 用来访问该超类的 initializer.
+++super.
+member name
super[subscript index]
super.init(initializer arguments)形式1 用来访问超类的某个成员(member). 形式2 用来访问该超类的 subscript 实现。 形式3 用来访问该超类的 initializer.
子类(subclass)可以通过超类(superclass)表达式在它们的 member, subscripting 和 initializers 中来利用它们超类中的某些实现(既有的方法或者逻辑)。
-GRAMMAR OF A SUPERCLASS EXPRESSION
-superclass-expression → superclass-method-expression | superclass-subscript-expression| superclass-initializer-expression -superclass-method-expression → super.identifier -superclass-subscript-expression → super[expression] -superclass-initializer-expression → super.init
+超类(superclass)表达式语法
超类表达式 → 超类方法表达式 | 超类下标表达式 | 超类构造器表达式
超类方法表达式 → super . 标识符
超类下标表达式 → super [ 表达式 ]
超类构造器表达式 → super . init闭包表达式(Closure Expression)
闭包(closure) 表达式可以建立一个闭包(在其他语言中也叫 lambda, 或者 匿名函数(anonymous function)). 跟函数(function)的声明一样, 闭包(closure)包含了可执行的代码(跟方法主体(statement)类似) 以及接收(capture)的参数。 它的形式如下:
-{ (parameters) -> return type in - statements - } +{ (parameters) -> return type in + statements +}闭包的参数声明形式跟方法中的声明一样, 请参见:Function Declaration.
闭包还有几种特殊的形式, 让使用更加简洁:
@@ -960,36 +925,28 @@ myFunction { [weak parent = self.parent] in print(parent!.title) }关于闭包表达式的更多信息和例子,请参见: Closure Expressions.
-闭包表达式的语法
-closure-expression → {closure-signatureoptstatements} -closure-signature → parameter-clausefunction-result(opt)in -closure-signature → identifier-listfunction-result(opt)in -closure-signature → capture-listparameter-clausefunction-result(opt)in -closure-signature → capture-listidentifier-listfunction-result(opt)in -closure-signature → capture-listin -capture-list → [capture-specifierexpression] -capture-specifier → weak| unowned| unowned(safe)| unowned(unsafe)
+闭包表达式语法
闭包表达式 → { 闭包签名(Signational) 可选 多条语句(Statements) }
闭包签名(Signational) → 参数子句 函数结果 可选 in
闭包签名(Signational) → 标识符列表 函数结果 可选 in
闭包签名(Signational) → 捕获(Capature)列表 参数子句 函数结果 可选 in
闭包签名(Signational) → 捕获(Capature)列表 标识符列表 函数结果 可选 in
闭包签名(Signational) → 捕获(Capature)列表 in
捕获(Capature)列表 → [ 捕获(Capature)说明符 表达式 ]
捕获(Capature)说明符 → weak | unowned | unowned(safe) | unowned(unsafe)隐式成员表达式(Implicit Member Expression)
在可以判断出类型(type)的上下文(context)中,隐式成员表达式是访问某个type的member( 例如 class method, enumeration case) 的简洁方法。 它的形式是:
+.
+member name例子:
var x = MyEnumeration.SomeValue x = .AnotherValue-隐式成员表达式的语法
-implicit-member-expression → .identifier
+隐式成员表达式语法
隐式成员表达式 → . 标识符圆括号表达式(Parenthesized Expression)
圆括号表达式由多个子表达式和逗号','组成。 每个子表达式前面可以有 identifier x: 这样的可选前缀。形式如下:
+(
+identifier 1:expression 1,identifier 2:expression 2,...)圆括号表达式用来建立tuples , 然后把它做为参数传递给 function. 如果某个圆括号表达式中只有一个 子表达式,那么它的type就是 子表达式的type。例如: (1)的 type是Int, 而不是(Int)
-圆括号表达式的语法
-parenthesized-expression → (expression-element-list (opt)) -expression-element-list → expression-element| expression-element,expression-element-list -expression-element → expression| identifier:expression
+圆括号表达式(Parenthesized Expression)语法
圆括号表达式 → ( 表达式元素列表 可选 )
表达式元素列表 → 表达式元素 | 表达式元素 , 表达式元素列表
表达式元素 → 表达式 | 标识符 : 表达式通配符表达式(Wildcard Expression)
通配符表达式用来忽略传递进来的某个参数。例如:下面的代码中,10被传递给x, 20被忽略(译注:好奇葩的语法。。。)
@@ -997,8 +954,7 @@ x = .AnotherValue // x is 10, 20 is ignored-通配符表达式的语法
-wildcard-expression → _
+通配符表达式语法
通配符表达式 → _后缀表达式(Postfix Expressions)
@@ -1010,25 +966,19 @@ x = .AnotherValue对于这些操作符的使用,请参见: Basic Operators and Advanced Operators
-后缀表达式的语法
-postfix-expression → primary-expression -postfix-expression → postfix-expressionpostfix-operator -postfix-expression → function-call-expression -postfix-expression → initializer-expression -postfix-expression → explicit-member-expression -postfix-expression → postfix-self-expression -postfix-expression → dynamic-type-expression -postfix-expression → subscript-expression -postfix-expression → forced-value-expression -postfix-expression → optional-chaining-expression
+后置表达式语法
后置表达式 → 主表达式
后置表达式 → 后置表达式 后置运算符
后置表达式 → 函数调用表达式
后置表达式 → 构造器表达式
后置表达式 → 显示成员表达式
后置表达式 → 后置self表达式
后置表达式 → 动态类型表达式
后置表达式 → 下标表达式
后置表达式 → 强制取值(Forced Value)表达式
后置表达式 → 可选链(Optional Chaining)表达式函数调用表达式(Function Call Expression)
函数调用表达式由函数名和参数列表组成。它的形式如下:
++
function name(argument value 1,argument value 2)The function name can be any expression whose value is of a function type. (不用翻译了, 太罗嗦)
如果该function 的声明中指定了参数的名字,那么在调用的时候也必须得写出来. 例如:
++
function name(argument name 1:argument value 1,argument name 2:argument value 2)可以在 函数调用表达式的尾部(最后一个参数之后)加上 一个闭包(closure) , 该闭包会被目标函数理解并执行。它具有如下两种写法:
// 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}-GRAMMAR OF A FUNCTION CALL EXPRESSION
-function-call-expression → postfix-expressionparenthesized-expression -function-call-expression → postfix-expressionparenthesized-expression(opt)trailing-closure -trailing-closure → closure-expression
+函数调用表达式语法
函数调用表达式 → 后置表达式 圆括号表达式
函数调用表达式 → 后置表达式 圆括号表达式 可选 后置闭包(Trailing Closure)
后置闭包(Trailing Closure) → 闭包表达式初始化函数表达式(Initializer Expression)
Initializer表达式用来给某个Type初始化。 它的形式如下:
++
expression.init(initializer arguments)(Initializer表达式用来给某个Type初始化。) 跟函数(function)不同, initializer 不能返回值。
+var x = SomeClass.someClassFunction // ok var y = SomeClass.init // error -```swift - -可以通过 initializer 表达式来委托调用(delegate to )到superclass的initializers. - -```swift -class SomeSubClass: SomeSuperClass { +可以通过 initializer 表达式来委托调用(delegate to )到superclass的initializers.
+class SomeSubClass: SomeSuperClass { init() { // subclass initialization goes here super.init() @@ -1064,12 +1010,13 @@ class SomeSubClass: SomeSuperClass { }-initializer表达式的语法
-initializer-expression → postfix-expression.init
+构造器表达式语法
构造器表达式 → 后置表达式 . init显式成员表达式(Explicit Member Expression)
显示成员表达式允许我们访问type, tuple, module的成员变量。它的形式如下:
++
expression.member name该member 就是某个type在声明时候所定义(declaration or extension) 的变量, 例如:
class SomeClass { var someProperty = 42 @@ -1085,24 +1032,24 @@ t.0 = t.1The members of a module access the top-level declarations of that module. (不确定:对于某个module的member的调用,只能调用在top-level声明中的member.)
-显示成员表达式的语法
-explicit-member-expression → postfix-expression.decimal-digit -explicit-member-expression → postfix-expression.identifiergeneric-argument-clause(opt)
+显式成员表达式语法
显示成员表达式 → 后置表达式 . 十进制数字
显示成员表达式 → 后置表达式 . 标识符 泛型参数子句 可选后缀self表达式(Postfix Self Expression)
后缀表达式由 某个表达式 + '.self' 组成. 形式如下:
-+
expression.self -type.self++
expression.selftype.self形式1 表示会返回 expression 的值。例如: x.self 返回 x
形式2:返回对应的type。我们可以用它来动态的获取某个instance的type。
-后缀self表达式的语法
-postfix-self-expression → postfix-expression.self
+后置Self 表达式语法
后置self表达式 → 后置表达式 . selfdynamic表达式(Dynamic Type Expression)
(因为dynamicType是一个独有的方法,所以这里保留了英文单词,未作翻译, --- 类似与self expression)
dynamicType 表达式由 某个表达式 + '.dynamicType' 组成。
++
expression.dynamicType上面的形式中, expression 不能是某type的名字(当然了,如果我都知道它的名字了还需要动态来获取它吗)。动态类型表达式会返回"运行时"某个instance的type, 具体请看下面的列子:
class SomeBaseClass { class func printClassName() { @@ -1122,29 +1069,32 @@ someInstance.dynamicType.printClassName() // prints "SomeSubClass"-dynamic type 表达式
-dynamic-type-expression → postfix-expression.dynamicType
+动态类型表达式语法
动态类型表达式 → 后置表达式 . dynamicType下标脚本表达式(Subscript Expression)
下标脚本表达式提供了通过下标脚本访问getter/setter 的方法。它的形式是:
++
expression[index expressions]可以通过下标脚本表达式通过getter获取某个值,或者通过setter赋予某个值.
关于subscript的声明,请参见: Protocol Subscript Declaration.
-下标脚本表达式的语法
-subscript-expression → postfix-expression[expression-list]
+强制取值表达式(Forced-Value Expression)
强制取值表达式用来获取某个目标表达式的值(该目标表达式的值必须不是nil )。它的形式如下:
++
expression!如果该表达式的值不是nil, 则返回对应的值。 否则,抛出运行时错误(runtime error)。
-强制取值表达式的语法
-forced-value-expression → postfix-expression!
+强制取值(Forced Value)语法
强制取值(Forced Value)表达式 → 后置表达式 !可选链表达式(Optional-Chaining Expression)
可选链表达式由目标表达式 + '?' 组成,形式如下:
++
expression?后缀'?' 返回目标表达式的值,把它做为可选的参数传递给后续的表达式
如果某个后缀表达式包含了可选链表达式,那么它的执行过程就比较特殊: 首先先判断该可选链表达式的值,如果是 nil, 整个后缀表达式都返回 nil, 如果该可选链的值不是nil, 则正常返回该后缀表达式的值(依次执行它的各个子表达式)。在这两种情况下,该后缀表达式仍然是一个optional type(In either case, the value of the postfix expression is still of an optional type)
如果某个"后缀表达式"的"子表达式"中包含了"可选链表达式",那么只有最外层的表达式返回的才是一个optional type. 例如,在下面的例子中, 如果c 不是nil, 那么 c?.property.performAction() 这句代码在执行时,就会先获得c 的property方法,然后调用 performAction()方法。 然后对于 "c?.property.performAction()" 这个整体,它的返回值是一个optional type.
@@ -1157,8 +1107,7 @@ var result: Bool? = c?.property.performAction() }-diff --git a/chapter3/05_Declarations.html b/chapter3/05_Declarations.html index 4554f9b6..5094df91 100644 --- a/chapter3/05_Declarations.html +++ b/chapter3/05_Declarations.html @@ -10,7 +10,7 @@ - + @@ -46,7 +46,7 @@ -可选链表达式的语法
-optional-chaining-expression → postfix-expression?
+可选链表达式语法
可选链表达式 → 后置表达式 ?+@@ -587,11 +587,10 @@-+ -翻译:marsprince
-校对:numbbbbb, stanzhai
+翻译:marsprince
校对:numbbbbb, stanzhai声明
@@ -632,10 +631,10 @@代码块
代码块用来将一些声明和控制结构的语句组织在一起。它有如下的形式:
-{ - `statements` -} -代码块中的语句包括声明,表达式和各种其他类型的语句,它们按照在源码中的出现顺序被依次执行。
+++{
+
statements
}代码块中的语句包括声明,表达式和各种其他类型的语句,它们按照在源码中的出现顺序被依次执行。
@@ -643,30 +642,39 @@代码块语法
代码块 → { 多条语句(Statements) 可选 }引入声明
引入声明使你可以使用在其他文件中声明的内容。引入语句的基本形式是引入整个代码模块;它由import关键字开始,后面 紧跟一个模块名:
-import module -你可以提供更多的细节来限制引入的符号,如声明一个特殊的子模块或者在一个模块或子模块中做特殊的声明。(待改进) +
++import
+module你可以提供更多的细节来限制引入的符号,如声明一个特殊的子模块或者在一个模块或子模块中做特殊的声明。(待改进) 当你使用了这些细节后,在当前的程序汇总只有引入的符号是可用的(并不是声明的整个模块)。
-import import kind module.symbol name -import module.submodule -+++ + +import
+import kindmodule.symbol name
importmodule.submodule导入(Import)声明语法
导入声明 → 特性(Attributes)列表 可选 import 导入类型 可选 导入路径
导入类型 → typealias | struct | class | enum | protocol | var | func
导入路径 → 导入路径标识符 | 导入路径标识符 . 导入路径
导入路径标识符 → 标识符 | 运算符常量声明
常量声明可以在你的程序里命名一个常量。常量以关键词let来声明,遵循如下的格式:
-let constant name: type = expression -当常量的值被给定后,常量就将常量名称和表达式初始值不变的结合在了一起,而且不能更改。 +
++let
+constant name:type=expression当常量的值被给定后,常量就将常量名称和表达式初始值不变的结合在了一起,而且不能更改。 这意味着如果常量以类的形式被初始化,类本身的内容是可以改变的,但是常量和类之间的结合关系是不能改变的。 当一个常量被声明为全局变量,它必须被给定一个初始值。当一个常量在类或者结构体中被声明时,它被认为是一个常量 属性。常量并不是可计算的属性,因此不包含getters和setters。(译者注:getters和setters不知道怎么翻译,待改进)
如果常量名是一个元祖形式,元祖中的每一项初始化表达式中都要有对应的值
-let (firstNumber, secondNumber) = (10, 42) -在上例中,firstNumber是一个值为10的常量,secnodeName是一个值为42的常量。所有常量都可以独立的使用:
-+println("The first number is \(firstNumber).") ++let (firstNumber, secondNumber) = (10, 42) +在上例中,firstNumber是一个值为10的常量,secnodeName是一个值为42的常量。所有常量都可以独立的使用:
+println("The first number is \(firstNumber).") // prints "The first number is 10." println("The second number is \(secondNumber).") // prints "The second number is 42." -类型注释(:type)在常量声明中是一个可选项,它可以用来描述在类型推断(type inference)中找到的类型。
+类型注释(:type)在常量声明中是一个可选项,它可以用来描述在类型推断(type inference)中找到的类型。
声明一个静态常量要使用关键字static。静态属性在类型属性(type propetries)中有介绍。
如果还想获得更多关于常量的信息或者想在使用中获得帮助,请查看常量和变量(constants and variables), 存储属性(stored properties)等节。
@@ -679,28 +687,24 @@ println("The second number is \(secondNumber).") 变量和属性,存储变量和属性监视,和静态变量属性,有着不同的声明形式。(待改进) 所使用的声明形式取决于变量所声明的范围和你打算声明的变量类型。-注意:
-你也可以在协议声明的上下文声明属性,详情参见类型属性声明。
+注意:
你也可以在协议声明的上下文声明属性,详情参见类型属性声明。存储型变量和存储型属性
下面的形式声明了一个存储型变量或存储型变量属性
-var variable name: type = expression -你可以在全局,函数内,或者在类和结构体的声明(context)中使用这种形式来声明一个变量。当变量以这种形式 +
++var
+variable name:type=expression你可以在全局,函数内,或者在类和结构体的声明(context)中使用这种形式来声明一个变量。当变量以这种形式 在全局或者一个函数内被声明时,它代表一个存储型变量。当它在类或者结构体中被声明时,它代表一个存储型变量属性。
构造器表达式可以被
和常量声明相比,如果变量名是一个元祖类型,元祖的每一项的名字都要和初始化表达式一致。
正如名字一样,存储型变量的值或存储型变量属性存储在内存中。
计算型变量和计算型属性
如下形式声明一个一个存储型变量或存储型属性:
-var variable name: type { -get { - statements -} -set(setter name) { - statements -} -} -你可以在全局,函数体内或者类,结构体,枚举,扩展声明的上下文中使用这种形式的声明。 +
++var
+variable name:type{
get {
statements
}
set(setter name) {
statements
}
}你可以在全局,函数体内或者类,结构体,枚举,扩展声明的上下文中使用这种形式的声明。 当变量以这种形式在全局或者一个函数内被声明时,它代表一个计算型变量。当它在类,结构体,枚举,扩展声明的上下文 中中被声明时,它代表一个计算型变量属性。
getter用来读取变量值,setter用来写入变量值。setter子句是可选择的,只有getter是必需的,你可以将这些语句 @@ -712,15 +716,10 @@ setter的初始名为newValue,正如在seter声明速记(shorthand setter decl
获得更多信息,查看更多关于计算型属性的例子,请查看计算型属性(computed properties)一节。
存储型变量监视器和属性监视器
你可以用willset和didset监视器来声明一个存储型变量或属性。一个包含监视器的存储型变量或属性按如下的形式声明:
-var variable name: type = expression { -willSet(setter name) { - statements -} -didSet(setter name { - statements -} -} -你可以在全局,函数体内或者类,结构体,枚举,扩展声明的上下文中使用这种形式的声明。 +
++var
+variable name:type= expression {
willSet(setter name) {
statements
}
didSet(setter name) {
statements
}
}你可以在全局,函数体内或者类,结构体,枚举,扩展声明的上下文中使用这种形式的声明。 当变量以这种形式在全局或者一个函数内被声明时,监视器代表一个存储型变量监视器; 当它在类,结构体,枚举,扩展声明的上下文中被声明时,监视器代表属性监视器。
你可以为适合的监视器添加任何存储型属性。你也可以通过重写子类属性的方式为适合的监视器添加任何继承的属性 @@ -746,8 +745,10 @@ willset监视器初始名为newvalue,didset监视器初始名为oldvalue。
类型的别名声明类型别名的声明可以在你的程序里为一个已存在的类型声明一个别名。类型的别名声明以关键字typealias开始,遵循如下的 形式:
-typealias name = existing type -当一个类型被别名被声明后,你可以在你程序的任何地方使用别名来代替已存在的类型。已存在的类型可以是已经被命名的 +
+++
typealias name=existing type当一个类型被别名被声明后,你可以在你程序的任何地方使用别名来代替已存在的类型。已存在的类型可以是已经被命名的 类型或者是混合类型。类型的别名不产生新的类型,它只是简单的和已存在的类型做名称替换。
查看更多Protocol Associated Type Declaration.
@@ -757,14 +758,14 @@ willset监视器初始名为newvalue,didset监视器初始名为oldvalue。函数声明你可以使用函数声明在你的程序里引入新的函数。函数可以在类的上下文,结构体,枚举,或者作为方法的协议中被声明。 函数声明使用关键字func,遵循如下的形式:
-func function name(parameters) -> return type { - statements -} -如果函数不返回任何值,返回类型可以被忽略,如下所示:
-func function name(parameters) { - statements -} -每个参数的类型都要标明,它们不能被推断出来。初始时函数的参数是常值。在这些参数前面添加var使它们成为变量, +
++func
+function name(parameters) ->return type{
statements
}如果函数不返回任何值,返回类型可以被忽略,如下所示:
+++func
+function name(parameters) {
statements
}每个参数的类型都要标明,它们不能被推断出来。初始时函数的参数是常值。在这些参数前面添加var使它们成为变量, 作用域内任何对变量的改变只在函数体内有效,或者用inout使的这些改变可以在调用域内生效。 更多关于in-out参数的讨论,参见in-out参数(in-out parameters)
函数可以使用元组类型作为返回值来返回多个变量。
@@ -772,36 +773,39 @@ willset监视器初始名为newvalue,didset监视器初始名为oldvalue。参数名函数的参数是一个以逗号分隔的列表 。函数调用是的变量顺序必须和函数声明时的参数顺序一致。 最简单的参数列表有着如下的形式:
-parameter name: parameter type -对于函数参数来讲,参数名在函数体内被使用,而不是在函数调用时使用。对于方法参数,参数名在函数体内被使用, +
+++
parameter name:parameter type对于函数参数来讲,参数名在函数体内被使用,而不是在函数调用时使用。对于方法参数,参数名在函数体内被使用, 同时也在方法被调用时作为标签被使用。该方法的第一个参数名仅仅在函数体内被使用,就像函数的参数一样,举例来讲:
-+func f(x: Int, y: String) -> String { ++func f(x: Int, y: String) -> String { return y + String(x) } f(7, "hello") // x and y have no name - -class C { +class C { func f(x: Int, y: String) -> String { return y + String(x) } } let c = C() c.f(7, y: "hello") // x没有名称,y有名称 -你可以按如下的形式,重写参数名被使用的过程:
-external parameter name local parameter name: parameter type -#parameter name: parameter type -_ local parameter name: parameter type -在本地参数前命名的第二名称(second name)使得参数有一个扩展名。且不同于本地的参数名。 +
你可以按如下的形式,重写参数名被使用的过程:
++++
external parameter namelocal parameter name:parameter type
#parameter name:parameter type
_local parameter name:parameter type在本地参数前命名的第二名称(second name)使得参数有一个扩展名。且不同于本地的参数名。 扩展参数名在函数被调用时必须被使用。对应的参数在方法或函数被调用时必须有扩展名 。
在参数名前所写的哈希符号(#)代表着这个参数名可以同时作为外部或本体参数名来使用。等同于书写两次本地参数名。 在函数或方法调用时,与其对应的语句必须包含这个名字。
本地参数名前的强调字符(_)使参数在函数被调用时没有名称。在函数或方法调用时,与其对应的语句必须没有名字。
特殊类型的参数
参数可以被忽略,值可以是变化的,并且提供一个初始值,这种方法有着如下的形式:
-_ : <#parameter type#. -parameter name: parameter type... -parameter name: parameter type = default argument value -以强调符(_)命名的参数明确的在函数体内不能被访问。
+++_ : <#parameter type#.
+parameter name:parameter type...parameter name:parameter type=default argument value以强调符(_)命名的参数明确的在函数体内不能被访问。
一个以基础类型名的参数,如果紧跟着三个点(...),被理解为是可变参数。一个函数至多可以拥有一个可变参数, 且必须是最后一个参数。可变参数被作为该基本类型名的数组来看待。举例来讲,可变参数int...被看做是int[]。 查看可变参数的使用例子,详见可变参数(variadic parameters)一节。
@@ -815,11 +819,11 @@ f()和f(x:7)都是只有一个变量x的函数的有效调用,但是f(7)是非和类型相关而不是和类型实例相关的方法必须在static声明的结构以或枚举内,亦或是以class关键字定义的类内。
柯里化函数和方法
柯里化函数或方法有着如下的形式:
-func function name(parameters)(parameters) -> return type { - statements -} -以这种形式定义的函数的返回值是另一个函数。举例来说,下面的两个声明时等价的:
-+func addTwoNumbers(a: Int)(b: Int) -> Int { +++func
+function name(parameters)(parameters) ->return type{
statements
}以这种形式定义的函数的返回值是另一个函数。举例来说,下面的两个声明时等价的:
+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 -多级柯里化应用如下
++addTwoNumbers(4)(5) // Returns 9 +多级柯里化应用如下
@@ -847,29 +852,28 @@ addTwoNumbers(4)(5) // Returns 9函数声明语法
函数声明 → 函数头 函数名 泛型参数子句 可选 函数签名(Signature) 函数体
函数头 → 特性(Attributes)列表 可选 声明描述符(Specifiers)列表 可选 func
函数名 → 标识符 | 运算符
函数签名(Signature) → parameter-clauses 函数结果 可选
函数结果 → -> 特性(Attributes)列表 可选 类型
函数体 → 代码块
parameter-clauses → 参数子句 parameter-clauses 可选
参数子句 → ( ) | ( 参数列表 ... 可选 )
参数列表 → 参数 | 参数 , 参数列表
参数 → inout 可选 let 可选 # 可选 参数名 本地参数名 可选 类型注解 默认参数子句 可选
参数 → inout 可选 var # 可选 参数名 本地参数名 可选 类型注解 默认参数子句 可选
参数 → 特性(Attributes)列表 可选 类型
参数名 → 标识符 | _
本地参数名 → 标识符 | _
默认参数子句 → = 表达式你可以扩展枚举类型,正如在扩展名声明(Extension Declaration)中讨论的一样。
任意事件类型的枚举
如下的形式声明了一个包含任意类型枚举时间的枚举变量
-enum enumeration name { - case enumeration case 1 - case enumeration case 2(associated value types) -} -这种形式的枚举声明在其他语言中有时被叫做可识别联合(discrinminated)。
+++enum
+enumeration name{
caseenumeration case 1
caseenumeration case 2(associated value types)
}这种形式的枚举声明在其他语言中有时被叫做可识别联合(discrinminated)。
这种形式中,每一个事件块由关键字case开始,后面紧接着一个或多个以逗号分隔的枚举事件。每一个事件名必须是 独一无二的。每一个事件也可以指定它所存储的指定类型的值,这些类型在关联值类型的元祖里被指定,立即书写在事件 名后。获得更多关于关联值类型的信息和例子,请查看关联值(associated values)一节。
使用原始事件值的枚举
以下的形式声明了一个包含相同基础类型的枚举事件的枚举:
-enum enumeration name: raw value type { - case enumeration case 1 = raw value 1 - case enumeration case 2 = raw value 2 -} -在这种形式中,每一个事件块由case关键字开始,后面紧接着一个或多个以逗号分隔的枚举事件。和第一种形式的枚举 +
++enum
+enumeration name:raw value type{
caseenumeration case 1=raw value 1
caseenumeration case 2=raw value 2
}在这种形式中,每一个事件块由case关键字开始,后面紧接着一个或多个以逗号分隔的枚举事件。和第一种形式的枚举 事件不同,这种形式的枚举事件包含一个同类型的基础值,叫做原始值(raw value)。这些值的类型在原始值类型(raw value type) 中被指定,必须是字面上的整数,浮点数,字符或者字符串。
每一个事件必须有唯一的名字,必须有一个唯一的初始值。如果初始值类型被指定为int,则不必为事件显式的指定值, 它们会隐式的被标为值0,1,2等。每一个没有被赋值的Int类型时间会隐式的赋予一个初始值,它们是自动递增的。
-+num ExampleEnum: Int { +num ExampleEnum: Int { case A, B, C = 5, D } -在上面的例子中,ExampleEnum.A的值是0,ExampleEnum.B的值是。因为ExampleEnum.C的值被显式的设定为5,因此 +
在上面的例子中,ExampleEnum.A的值是0,ExampleEnum.B的值是。因为ExampleEnum.C的值被显式的设定为5,因此 ExampleEnum.D的值会自动增长为6.
枚举事件的初始值可以调用方法roRaw获得,如ExampleEnum.B.toRaw()。你也可以通过调用fromRaw方法来使用初始值找到 其对应的事件,并返回一个可选的事件。查看更多信息和获取初始值类型事件的信息,参阅初始值(raw values)。
@@ -884,10 +888,10 @@ ExampleEnum.D的值会自动增长为6.结构体声明
使用结构体声明可以在你的程序里引入一个结构体类型。结构体声明使用struct关键字,遵循如下的形式:
-struct structure name: adopted protocols { - declarations -} -结构体内包含零或多个声明。这些声明可以包括存储型和计算型属性,静态属性,实例方法,静态方法,构造器, +
++struct
+structure name:adopted protocols{
declarations
}结构体内包含零或多个声明。这些声明可以包括存储型和计算型属性,静态属性,实例方法,静态方法,构造器, 类型别名,甚至其他结构体,类,和枚举声明。结构体声明不能包含析构器或者协议声明。详细讨论和包含多种结构体 声明的实例,参见类和结构体一节。
结构体可以包含任意数量的协议,但是不能继承自类,枚举或者其他结构体。
@@ -906,10 +910,10 @@ ExampleEnum.D的值会自动增长为6.类声明
你可以在你的程序中使用类声明来引入一个类。类声明使用关键字class,遵循如下的形式:
-class class name: superclass, adopted protocols { - declarations -} -一个类内包含零或多个声明。这些声明可以包括存储型和计算型属性,实例方法,类方法,构造器,单独的析构器方法, +
++class
+class name:superclass,adopted protocols{
declarations
}一个类内包含零或多个声明。这些声明可以包括存储型和计算型属性,实例方法,类方法,构造器,单独的析构器方法, 类型别名,甚至其他结构体,类,和枚举声明。类声明不能包含协议声明。详细讨论和包含多种类声明的实例,参见类和 结构体一节。
一个类只能继承一个父类,超类,但是可以包含任意数量的协议。这些超类第一次在type-inheritance-clause出现,遵循任意协议。
@@ -920,8 +924,10 @@ ExampleEnum.D的值会自动增长为6.虽然超类的属性和方法声明可以被当前类继承,但是超类声明的指定构造器却不能。这意味着,如果当前类重写了超类 的所有指定构造器,它就继承了超类的方便构造器。Swift的类并不是继承自一个全局基础类。
有两种方法来创建已声明的类的实例:
--调用类的一个构造器,参见构造器(initializers)。
--如果没有声明构造器,而且类的所有属性都被赋予了初始值,调用类的默认构造器,参见默认构造器(default initializers).
++
- 调用类的一个构造器,参见构造器(initializers)。
+- 如果没有声明构造器,而且类的所有属性都被赋予了初始值,调用类的默认构造器,参见默认构造器(default initializers).
+类实例属性可以用点(.)来获得,详情参见获得属性(Accessing Properties)一节。
类是引用类型;当被赋予常量或变量,函数调用时,类的实例是被引用,而不是复制。获得更多关于引用类型的信息, 结构体和枚举都是值类型(Structures and Enumerations Are Value Types)一节。
@@ -932,22 +938,19 @@ ExampleEnum.D的值会自动增长为6.协议声明(translated by 小一)
一个协议声明为你的程序引入一个命名了的协议类型。协议声明使用
-protocol关键词来进行声明并有下面这样的形式:+protocol protocol name: inherited protocols { - protocol member declarations -} -+protocol
+protocol name:inherited protocols{
protocol member declarations
}协议的主体包含零或多个协议成员声明,这些成员描述了任何采用该协议必须满足的一致性要求。特别的,一个协议可以声明必须实现某些属性、方法、初始化程序及下标脚本的一致性类型。协议也可以声明专用种类的类型别名,叫做关联类型,它可以指定协议的不同声明之间的关系。协议成员声明会在下面的详情里进行讨论。
协议类型可以从很多其它协议那继承。当一个协议类型从其它协议那继承的时候,来自其它协议的所有要求就集合了,而且从当前协议继承的任何类型必须符合所有的这些要求。对于如何使用协议继承的例子,查看协议继承
-注意:
- +你可以通过采用在类型的扩展声明中的协议来为之前声明的类型添加协议一致性。在扩展中你必须实现所有采用协议的要求。如果该类型已经实现了所有的要求,你可以让这个扩展声明的主题留空。
默认地,符合某一个协议的类型必须实现所有声明在协议中的属性、方法和下标脚本。也就是说,你可以用
optional属性标注这些协议成员声明以指定它们的一致性类型实现是可选的。optional属性仅仅可以用于使用objc属性标记过的协议。这样的结果就是仅仅类类型可以采用并符合包含可选成员要求的协议。更多关于如何使用optional属性的信息及如何访问可选协议成员的指导——比如当你不能肯定是否一致性的类型实现了它们——参见可选协议要求为了限制协议的采用仅仅针对类类型,需要使用
class_protocol属性标记整个协议声明。任意继承自标记有class_protocol属性协议的协议都可以智能地仅能被类类型采用。-注意:
-如果协议已经用
+object属性标记了,class_protocol属性就隐性地应用于该协议;没有必要再明确地使用class_protocol属性来标记该协议了。注意:
如果协议已经用object属性标记了,class_protocol属性就隐性地应用于该协议;没有必要再明确地使用class_protocol属性来标记该协议了。协议是命名的类型,因此它们可以以另一个命名类型出现在你代码的所有地方,就像协议类型里讨论的那样。然而你不能构造一个协议的实例,因为协议实际上不提供它们指定的要求的实现。
你可以使用协议来声明一个类的代理的方法或者应该实现的结构,就像委托(代理)模式描述的那样。
@@ -957,8 +960,10 @@ ExampleEnum.D的值会自动增长为6.协议属性声明
协议声明了一致性类型必须在协议声明的主体里通过引入一个协议属性声明来实现一个属性。协议属性声明有一种特殊的类型声明形式:
-var property name: type { get set } -同其它协议成员声明一样,这些属性声明仅仅针对符合该协议的类型声明了
+getter和setter要求。结果就是你不需要在协议里它被声明的地方实现getter和setter。++var
+property name:type{ get set }同其它协议成员声明一样,这些属性声明仅仅针对符合该协议的类型声明了
getter和setter要求。结果就是你不需要在协议里它被声明的地方实现getter和setter。
getter和setter要求可以通过一致性类型以各种方式满足。如果属性声明包含get和set关键词,一致性类型就可以用可读写(实现了getter和setter)的存储型变量属性或计算型属性,但是属性不能以常量属性或只读计算型属性实现。如果属性声明仅仅包含get关键词的话,它可以作为任意类型的属性被实现。比如说实现了协议的属性要求的一致性类型,参见属性要求更多参见变量声明
@@ -985,8 +990,10 @@ ExampleEnum.D的值会自动增长为6.协议下标脚本声明
协议声明了一致性类型必须在协议声明的主体里通过引入一个协议下标脚本声明来实现一个下标脚本。协议属性声明 对下标脚本声明有一个特殊的形式:
-subscript (parameters) -> return type { get set } -下标脚本声明只为和协议一致的类型声明了必需的最小数量的的getter和setter。如果下标脚本申明包含get和set关键字, +
++subscript (
+parameters) ->return type{ get set }下标脚本声明只为和协议一致的类型声明了必需的最小数量的的getter和setter。如果下标脚本申明包含get和set关键字, 一致的类型也必须有一个getter和setter语句。如果下标脚本声明值包含get关键字,一致的类型必须至少包含一个 getter语句,可以选择是否包含setter语句。
更多参阅下标脚本声明。
@@ -1006,19 +1013,19 @@ getter语句,可以选择是否包含setter语句。结构体,枚举,类可以有任意数量的构造器,但是类的构造器的规则和行为是不一样的。不像结构体和枚举那样,类 有两种结构体,designed initializers 和convenience initializers,参见构造器一节。
如下的形式声明了结构体,枚举和类的指定构造器:
-init(parameters) { - statements -} -类的指定构造器将类的所有属性直接初始化。如果类有超类,它不能调用该类的其他构造器,它只能调用超类的一个 +
++init(
+parameters) {
statements
}类的指定构造器将类的所有属性直接初始化。如果类有超类,它不能调用该类的其他构造器,它只能调用超类的一个 指定构造器。如果该类从它的超类处继承了任何属性,这些属性在当前类内被赋值或修饰时,必须带哦用一个超类的 指定构造器。
指定构造器可以在类声明的上下文中声明,因此它不能用扩展声明的方法加入一个类中。
结构体和枚举的构造器可以带哦用其他的已声明的构造器,来委托其中一个火全部进行初始化过程。
以关键字convenience来声明一个类的便利构造器:
-convenience init(parameters) { - statements -} -便利构造器可以将初始化过程委托给另一个便利构造器或类的一个指定构造器。这意味着,类的初始化过程必须 +
++convenience init(
+parameters) {
statements
}便利构造器可以将初始化过程委托给另一个便利构造器或类的一个指定构造器。这意味着,类的初始化过程必须 以一个将所有类属性完全初始化的指定构造器的调用作为结束。便利构造器不能调用超类的构造器。
你可以使用requierd关键字,将便利构造器和指定构造器标记为每个子类的构造器都必须拥有的。因为指定构造器 不被子类继承,它们必须被立即执行。当子类直接执行所有超类的指定构造器(或使用便利构造器重写指定构造器)时, @@ -1031,10 +1038,10 @@ overrride关键字。
析构声明
析构声明为类声明了一个析构器。析构器没有参数,遵循如下的格式:
-deinit { - statements -} -当类没有任何语句时将要被释放时,析构器会自动的被调用。析构器在类的声明体内只能被声明一次——但是不能在 +
++deinit {
+
statements
}当类没有任何语句时将要被释放时,析构器会自动的被调用。析构器在类的声明体内只能被声明一次——但是不能在 类的扩展声明内,每个类最多只能有一个。
子类继承了它的超类的析构器,在子类将要被释放时隐式的调用。子类在所有析构器被执行完毕前不会被释放。
析构器不会被直接调用。
@@ -1045,10 +1052,10 @@ overrride关键字。扩展声明
扩展声明用于扩展一个现存的类,结构体,枚举的行为。扩展声明以关键字extension开始,遵循如下的规则:
-extension type: adopted protocols { - declarations -} -一个扩展声明体包括零个或多个声明。这些声明可以包括计算型属性,计算型静态属性,实例方法,静态和类方法,构造器, +
++extension
+type:adopted protocols{
declarations
}一个扩展声明体包括零个或多个声明。这些声明可以包括计算型属性,计算型静态属性,实例方法,静态和类方法,构造器, 下标脚本声明,甚至其他结构体,类,和枚举声明。扩展声明不能包含析构器,协议声明,存储型属性,属性监测器或其他 的扩展属性。详细讨论和查看包含多种扩展声明的实例,参见扩展一节。
扩展声明可以向现存的类,结构体,枚举内添加一致的协议。扩展声明不能向一个类中添加继承的类,因此 @@ -1061,37 +1068,11 @@ type-inheritance-clause是一个只包含协议列表的扩展声明。
下标脚本声明(translated by 林)
-<<<<<<< HEAD -附属脚本用于向特定类型添加附属脚本支持,通常为访问集合,列表和序列的元素时提供语法便利。附属脚本声明使用关键字
+subscript,声明形式如下:附属脚本用于向特定类型添加附属脚本支持,通常为访问集合,列表和序列的元素时提供语法便利。附属脚本声明使用关键字
subscript,声明形式如下:--subscript (
-parameter) -> (return type){
get{
statements
}
set(setter name){
statements
}
}附属脚本声明只能在类,结构体,枚举,扩展和协议声明的上下文进行声明。
-下标脚本用于向特定类型添加下标脚本支持,通常为访问集合,列表和序列的元素时提供语法便利。下标脚本声明使用关键字
-subscript,声明形式如下:-+subscript (
-parameter) -> (return type){ - get{ -statements- } - set(setter name){ -statements- } -} -下标脚本声明只能在类,结构体,枚举,扩展和协议声明的上下文进行声明。-+----------a516af6a531a104ec88da0d236ecf389a5ec72af
-subscript (
parameter) -> (return type){
get{
statements
}
set(setter name){
statements
}
}附属脚本声明只能在类,结构体,枚举,扩展和协议声明的上下文进行声明。
变量(parameters)指定一个或多个用于在相关类型的下标脚本中访问元素的索引(例如,表达式
object[i]中的i)。尽管用于元素访问的索引可以是任意类型的,但是每个变量必须包含一个用于指定每种索引类型的类型标注。返回类型(return type)指定被访问的元素的类型。和计算性属性一样,下标脚本声明支持对访问元素的读写操作。getter用于读取值,setter用于写入值。setter子句是可选的,当仅需要一个getter子句时,可以将二者都忽略且直接返回请求的值即可。也就是说,如果使用了setter子句,就必须使用getter子句。
setter的名字和封闭的括号是可选的。如果使用了setter名称,它会被当做传给setter的变量的名称。如果不使用setter名称,那么传给setter的变量的名称默认是
@@ -1099,7 +1080,7 @@ type-inheritance-clause是一个只包含协议列表的扩展声明。value。setter名称的类型必须与返回类型(return type)的类型相同。同样可以在协议声明的上下文中声明下标脚本,Protocol Subscript Declaration中有所描述。
更多关于下标脚本和下标脚本声明的例子,请参考Subscripts。
-附属脚本声明语法
+
附属脚本声明 → 附属脚本头(Head) 附属脚本结果(Result) 代码块
附属脚本声明 → 附属脚本头(Head) 附属脚本结果(Result) getter-setter块
附属脚本声明 → 附属脚本头(Head) 附属脚本结果(Result) getter-setter关键字(Keyword)块
附属脚本头(Head) → 特性(Attributes)列表 可选 subscript 参数子句
附属脚本结果(Result) → -> 特性(Attributes)列表 可选 类型附属脚本声明语法
附属脚本声明 → 附属脚本头(Head) 附属脚本结果(Result) 代码块
附属脚本声明 → 附属脚本头(Head) 附属脚本结果(Result) getter-setter块
附属脚本声明 → 附属脚本头(Head) 附属脚本结果(Result) getter-setter关键字(Keyword)块
附属脚本头(Head) → 特性(Attributes)列表 可选 subscript 参数子句
附属脚本结果(Result) → -> 特性(Attributes)列表 可选 类型运算符声明(translated by 林)
@@ -1108,10 +1089,7 @@ type-inheritance-clause是一个只包含协议列表的扩展声明。 运算符声明有三种基本形式,每种缀性各一种。运算符的缀性通过在operator和运算符之间添加上下文关键字infix,prefix或postfix来指定。每种形式中,运算符的名字只能包含Operators中定义的运算符字符。下面的这种形式声明了一个新的中缀运算符:
-operator infix
+operator name{ - precedenceprecedence level- associativityassociativity- }operator infix
operator name{
previewprecedenceprecedence level
associativityassociativity
}中缀运算符是二元运算符,它可以被置于两个操作数之间,比如表达式
1 + 2中的加法运算符(+)。中缀运算符可以可选地指定优先级,结合性,或两者同时指定。
@@ -1121,13 +1099,13 @@ type-inheritance-clause是一个只包含协议列表的扩展声明。声明时不指定任何优先级或结合性的中缀运算符,它们的优先级会被初始化为100,结合性被初始化为
none。下面的这种形式声明了一个新的前缀运算符:
-operator prefix
+operator name{}operator prefix
operator name{}紧跟在操作数前边的前缀运算符(prefix operator)是一元运算符,例如表达式
++i中的前缀递增运算符(++)。前缀运算符的声明中不指定优先级。前缀运算符是非结合的。
下面的这种形式声明了一个新的后缀运算符:
-operator postfix
+operator name{}operator postfix
operator name{}紧跟在操作数后边的后缀运算符(postfix operator)是一元运算符,例如表达式
i++中的前缀递增运算符(++)。和前缀运算符一样,后缀运算符的声明中不指定优先级。后缀运算符是非结合的。
diff --git a/chapter3/06_Attributes.html b/chapter3/06_Attributes.html index 7175af55..1cf652e0 100644 --- a/chapter3/06_Attributes.html +++ b/chapter3/06_Attributes.html @@ -10,7 +10,7 @@ - + @@ -46,7 +46,7 @@ -+@@ -587,11 +587,10 @@-+ -翻译:Hawstein
-校对:numbbbbb
+特性
@@ -602,9 +601,10 @@特性提供了关于声明和类型的更多信息。在Swift中有两类特性,用于修饰声明的以及用于修饰类型的。例如,
required特性,当应用于一个类的指定或便利初始化器声明时,表明它的每个子类都必须实现那个初始化器。再比如noreturn特性,当应用于函数或方法类型时,表明该函数或方法不会返回到它的调用者。通过以下方式指定一个特性:符号
-@后面跟特性名,如果包含参数,则把参数带上:@attribute name -@attribute name(attribute arguments) -有些声明特性通过接收参数来指定特性的更多信息以及它是如何修饰一个特定的声明的。这些特性的参数写在小括号内,它们的格式由它们所属的特性来定义。
+++@
+attribute name
@attribute name(attribute arguments)有些声明特性通过接收参数来指定特性的更多信息以及它是如何修饰一个特定的声明的。这些特性的参数写在小括号内,它们的格式由它们所属的特性来定义。
声明特性
声明特性只能应用于声明。然而,你也可以将
@@ -631,7 +631,7 @@noreturn特性应用于函数或方法类型。该特性用于修饰任意可以在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 +@objc class ExampleClass { var enabled: Bool { @objc(isEnabled) get { @@ -639,7 +639,8 @@ class ExampleClass { } } } -+
optional
optional用该特性修饰协议的属性,方法或下标成员,表示实现这些成员并不需要一致性类型(conforming type)。
你只能用
optional特性修饰那些标记了objc特性的协议。因此,只有类类型可以adopt和comform to那些包含可选成员需求的协议。更多关于如何使用optional特性以及如何访问可选协议成员的指导,例如,当你不确定一个conforming类型是否实现了它们,请见:可选协议需求。@@ -656,7 +657,7 @@ class ExampleClass {
required
noreturn该特性用于修饰函数或方法的类型,表明该函数或方法不会返回到它的调用者中去。你也可以用它标记函数或方法的声明,表示函数或方法的相应类型,
T,是@noreturn T。-diff --git a/chapter3/07_Patterns.html b/chapter3/07_Patterns.html index 38320e75..53abf8fa 100644 --- a/chapter3/07_Patterns.html +++ b/chapter3/07_Patterns.html @@ -10,7 +10,7 @@ - + @@ -46,7 +46,7 @@ -特性语法
+
特色 → @ 特性名 特性参数子句 可选
特性名 → 标识符
特性参数子句 → ( 平衡令牌列表 可选 )
特性(Attributes)列表 → 特色 特性(Attributes)列表 可选
平衡令牌列表 → 平衡令牌 平衡令牌列表 可选
平衡令牌 → ( 平衡令牌列表 可选 )
平衡令牌 → [ 平衡令牌列表 可选 ]
平衡令牌 → { 平衡令牌列表 可选 }
平衡令牌 → 任意标识符, 关键字, 字面量或运算符
平衡令牌 → 任意标点除了(, ), [, ], {, 或 }特性语法
特性 → @ 特性名 特性参数子句 可选
特性名 → 标识符
特性参数子句 → ( 平衡令牌列表 可选 )
特性(Attributes)列表 → 特色 特性(Attributes)列表 可选
平衡令牌列表 → 平衡令牌 平衡令牌列表 可选
平衡令牌 → ( 平衡令牌列表 可选 )
平衡令牌 → [ 平衡令牌列表 可选 ]
平衡令牌 → { 平衡令牌列表 可选 }
平衡令牌 → 任意标识符, 关键字, 字面量或运算符
平衡令牌 → 任意标点除了(, ), [, ], {, 或 }+@@ -587,11 +587,10 @@-+ -翻译:honghaoz
-校对:numbbbbb, stanzhai
+模式(Patterns)
@@ -614,17 +613,19 @@通配符模式(Wildcard Pattern)
通配符模式匹配并忽略任何值,包含一个下划线(_)。当你不关心被匹配的值时,可以使用此模式。例如,下面这段代码进行了
-1...3的循环,并忽略了每次循环的值:+for _ in 1...3 { +for _ in 1...3 { // Do something three times. } -+通配符模式语法
通配符模式 → _标识符模式(Identifier Pattern)
标识符模式匹配任何值,并将匹配的值和一个变量或常量绑定起来。例如,在下面的常量申明中,
-someValue是一个标识符模式,匹配了类型是Int的42。let someValue = 42 -当匹配成功时,
+42被绑定(赋值)给常量someValue。+let someValue = 42 +当匹配成功时,
42被绑定(赋值)给常量someValue。当一个变量或常量申明的左边是标识符模式时,此时,标识符模式是隐式的值绑定模式(value-binding pattern)。
标识符模式语法
@@ -633,14 +634,15 @@
标识符模式 → 标识符值绑定模式(Value-Binding Pattern)
值绑定模式绑定匹配的值到一个变量或常量。当绑定匹配值给常量时,用关键字
let,绑定给变量时,用关键之var。标识符模式包含在值绑定模式中,绑定新的变量或常量到匹配的值。例如,你可以分解一个元组的元素,并把每个元素绑定到相应的标识符模式中。
-+let point = (3, 2) +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):匹配的值是一样的。在上面这个例子中,
let将元组模式(x, y)分配到各个标识符模式。因为这种行为,switch语句中case let (x, y):和case (let x, let y):匹配的值是一样的。@@ -649,16 +651,18 @@ case let (x, y):元组模式是逗号分隔的列表,包含一个或多个模式,并包含在一对圆括号中。元组模式匹配相应元组类型的值。
你可以使用类型注释来限制一个元组模式来匹配某种元组类型。例如,在常量申明
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)] +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) = 2 // a: Int = 2 let (a): Int = 2 // a: Int = 2 -+@@ -671,9 +675,10 @@ let (a): Int = 2 // a: Int = 2元组模式语法
元组模式 → ( 元组模式元素列表 可选 )
元组模式元素列表 → 元组模式元素 | 元组模式元素 , 元组模式元素列表
元组模式元素 → 模式类型转换模式(Type-Casting Patterns)
有两种类型转换模式,
-is模式和as模式。这两种模式均只出现在switch语句中的case标签中。is模式和as模式有以下形式:is type -pattern as type -+
is模式匹配一个值,如果这个值的类型在运行时(runtime)和is模式右边的指定类型(或者那个类型的子类)是一致的。is模式和is操作符一样,它们都进行类型转换,但是抛弃了返回的类型。++is
+typepatternastype
is模式匹配一个值,如果这个值的类型在运行时(runtime)和is模式右边的指定类型(或者那个类型的子类)是一致的。is模式和is操作符一样,它们都进行类型转换,但是抛弃了返回的类型。
as模式匹配一个值,如果这个值的类型在运行时(runtime)和as模式右边的指定类型(或者那个类型的子类)是一致的。一旦匹配成功,匹配的值的类型被转换成as模式左边指定的模式。关于使用
switch语句来匹配is模式和as模式值的例子,请参阅Type Casting for Any and AnyObject。@@ -683,7 +688,7 @@ pattern as type表达式模式(Expression Pattern)
表达式模式代表了一个表达式的值。这个模式只出现在
switch语句中的case标签中。由表达式模式所代表的表达式用Swift标准库中的
-~=操作符与输入表达式的值进行比较。如果~=操作符返回true,则匹配成功。默认情况下,~=操作符使用==操作符来比较两个相同类型的值。它也可以匹配一个整数值与一个Range对象中的整数范围,正如下面这个例子所示:+let point = (1, 2) +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.” -你可以重载
-~=操作符来提供自定义的表达式行为。例如,你可以重写上面的例子,以实现用字符串表达的点来比较point表达式。+// Overload the ~= operator to match a string with an integer +你可以重载
+~=操作符来提供自定义的表达式行为。例如,你可以重写上面的例子,以实现用字符串表达的点来比较point表达式。// 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.” -+diff --git a/chapter3/08_Generic_Parameters_and_Arguments.html b/chapter3/08_Generic_Parameters_and_Arguments.html index 4d35c378..49e02344 100644 --- a/chapter3/08_Generic_Parameters_and_Arguments.html +++ b/chapter3/08_Generic_Parameters_and_Arguments.html @@ -10,7 +10,7 @@ - + @@ -46,7 +46,7 @@ -表达式模式语法
表达式模式 → 表达式+@@ -587,11 +587,10 @@-+ -翻译:fd5788
-校对:yankuangshi
+翻译:fd5788
校对:yankuangshi, stanzhai泛型参数
@@ -605,22 +604,27 @@泛型形参子句
泛型形参子句指定泛型类型或函数的类型形参,以及这些参数的关联约束和要求。泛型形参子句用尖括号(<>)包住,并且有以下两种形式:
-<generic parameter list> -<generic parameter list where requirements > -泛型形参列表中泛型形参用逗号分开,每一个采用以下形式:
-type parameter : constrain -泛型形参由两部分组成:类型形参及其后的可选约束。类型形参只是占位符类型(如T,U,V,KeyType,ValueType等)的名字而已。你可以在泛型类型、函数的其余部分或者构造器声明,以及函数或构造器的签名中使用它。
+++<
+generic parameter list>
<generic parameter listwhererequirements>泛型形参列表中泛型形参用逗号分开,每一个采用以下形式:
++++
type parameter:constrain泛型形参由两部分组成:类型形参及其后的可选约束。类型形参只是占位符类型(如T,U,V,KeyType,ValueType等)的名字而已。你可以在泛型类型、函数的其余部分或者构造器声明,以及函数或构造器的签名中使用它。
约束用于指明该类型形参继承自某个类或者遵守某个协议或协议的一部分。例如,在下面的泛型中,泛型形参
-T: Comparable表示任何用于替代类型形参T的类型实参必须满足Comparable协议。+func simpleMin<T: COmparable>(x: T, y: T) -> T { +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 +如,
+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子句中的要求用于指明该类型形参继承自某个类或遵守某个协议或协议的一部分。尽管where子句有助于表达类型形参上的简单约束(如T: Comparable等同于T where T: Comparable,等等),但是依然可以用来对类型形参及其关联约束提供更复杂的约束。如,<T where T: C, T: P>表示泛型类型T继承自类C且遵守协议P。如上所述,可以强制约束类型形参的关联类型遵守某个协议。
@@ -634,15 +638,19 @@ simpleMin(3.14159, 2.71828) // T is inferred to be Double<T: Generator where T.Element: Equatable>表示T遵守Generator协议,而且T的关联类型T.Element遵守Eauatable协议(T有关联类型是因为Generator声明了Element,而T遵守Generator协议)。泛型实参子句
泛型实参子句指定泛型类型的类型实参。泛型实参子句用尖括号(<>)包住,形式如下:
-< generic argument list > -泛型实参列表中类型实参有逗号分开。类型实参是实际具体类型的名字,用来替代泛型类型的泛型形参子句中的相应的类型形参。从而得到泛型类型的一个特化版本。如,Swift标准库的泛型字典类型定义如下:
-+struct Dictionary<KeyTypel: Hashable, ValueType>: Collection, DictionaryLiteralConvertible { +++<
+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协议。泛型
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]] -如泛型形参子句所述,不能用泛型实参子句来指定泛型函数或构造器的类型实参。
++let arrayOfArrays: Array<Array<Int>> = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] +如泛型形参子句所述,不能用泛型实参子句来指定泛型函数或构造器的类型实参。
diff --git a/chapter3/09_Summary_of_the_Grammar.html b/chapter3/09_Summary_of_the_Grammar.html index 52104cd9..1d430619 100644 --- a/chapter3/09_Summary_of_the_Grammar.html +++ b/chapter3/09_Summary_of_the_Grammar.html @@ -10,7 +10,7 @@ - + @@ -44,7 +44,7 @@ -泛型实参子句语法
(泛型参数子句Generic Argument Clause) → < 泛型参数列表 >
泛型参数列表 → 泛型参数 | 泛型参数 , 泛型参数列表
泛型参数 → 类型+@@ -585,11 +585,10 @@-+ -翻译:stanzhai
-校对:xielingwang
+翻译:stanzhai
校对:xielingwang语法总结
@@ -609,72 +608,72 @@- +语句语法
语句 → 表达式 ; 可选
语句 → 声明 ; 可选
语句 → 循环语句 ; 可选
语句 → 分支语句 ; 可选
语句 → 标记语句(Labeled Statement)
语句 → 控制转移语句 ; 可选
多条语句(Statements) → 语句 多条语句(Statements) 可选- +循环语句语法
循环语句 → for语句
循环语句 → for-in语句
循环语句 → while语句
循环语句 → do-while语句- +For 循环语法
for语句 → for for初始条件 可选 ; 表达式 可选 ; 表达式 可选 代码块
for语句 → for ( for初始条件 可选 ; 表达式 可选 ; 表达式 可选 ) 代码块
for初始条件 → 变量声明 | 表达式列表- +- +- +- +- +If语句语法
if语句 → if if条件 代码块 else子句(Clause) 可选
if条件 → 表达式 | 声明
else子句(Clause) → else 代码块 | else if语句- +Switch语句语法
switch语句 → switch 表达式 { SwitchCase列表 可选 }
SwitchCase列表 → SwitchCase SwitchCase列表 可选
SwitchCase → case标签 多条语句(Statements) | default标签 多条语句(Statements)
SwitchCase → case标签 ; | default标签 ;
case标签 → case case项列表 :
case项列表 → 模式 guard-clause 可选 | 模式 guard-clause 可选 , case项列表
default标签 → default :
guard-clause → where guard-expression
guard-expression → 表达式-- +标记语句语法
+
标记语句(Labeled Statement) → 语句标签 循环语句 | 语句标签 switch语句
语句标签 → 标签名称 :
标签名称 → 标识符标记语句语法
标记语句(Labeled Statement) → 语句标签 循环语句 | 语句标签 switch语句
语句标签 → 标签名称 :
标签名称 → 标识符-- +控制传递语句(Control Transfer Statement) 语法
+
控制转移语句 → break语句
控制转移语句 → continue语句
控制转移语句 → fallthrough语句
控制转移语句 → return语句控制传递语句(Control Transfer Statement) 语法
控制传递语句 → break语句
控制传递语句 → continue语句
控制传递语句 → fallthrough语句
控制传递语句 → return语句- +Break 语句语法
break语句 → break 标签名称 可选- +Continue 语句语法
continue语句 → continue 标签名称 可选- +Fallthrough 语句语法
fallthrough语句 → fallthroughReturn 语句语法
@@ -684,7 +683,7 @@
return语句 → return 表达式 可选- +泛型形参子句(Generic Parameter Clause) 语法
泛型参数子句 → < 泛型参数列表 约束子句 可选 >
泛型参数列表 → 泛形参数 | 泛形参数 , 泛型参数列表
泛形参数 → 类型名称
泛形参数 → 类型名称 : 类型标识
泛形参数 → 类型名称 : 协议合成类型
约束子句 → where 约束列表
约束列表 → 约束 | 约束 , 约束列表
约束 → 一致性约束 | 同类型约束
一致性约束 → 类型标识 : 类型标识
一致性约束 → 类型标识 : 协议合成类型
同类型约束 → 类型标识 == 类型标识泛型实参子句语法
@@ -694,147 +693,147 @@
(泛型参数子句Generic Argument Clause) → < 泛型参数列表 >
泛型参数列表 → 泛型参数 | 泛型参数 , 泛型参数列表
泛型参数 → 类型