diff --git a/README.md b/README.md index 0c8dbb3d..08a83afa 100755 --- a/README.md +++ b/README.md @@ -13,7 +13,26 @@ # 当前阶段 -已经更新到 Swift 2.2。 + + +已经更新到 Swift 3.0。 2016-09-23 + +# 3.0 译者记录 +相关[issue](https://github.com/numbbbbb/the-swift-programming-language-in-chinese/issues/628) +- Functions: [crayygy](https://github.com/crayygy) +- Control Flow: [Realank](https://github.com/Realank) +- Closures: [LanfordCai](https://github.com/LanfordCai) +- Protocols: [chenmingbiao](https://github.com/chenmingbiao) +- The Basics:[chenmingbiao](https://github.com/chenmingbiao) +- Advanced Operators: [mmoaay](https://github.com/mmoaay) + +Language Reference: +- Attributes: [WhoJave](https://github.com/WhoJave) +- Statements: [chenmingjia](https://github.com/chenmingjia) +- Declarations: [chenmingjia](https://github.com/chenmingjia) +- Expressions: [chenmingjia](https://github.com/chenmingjia) +- Types: [lettleprince](https://github.com/lettleprince) +- Generic Parameters and Arguments: [chenmingjia](https://github.com/chenmingjia) # 2.1 & 2.2 译者记录 diff --git a/cover/cover_3.0.jpg b/cover/cover_3.0.jpg new file mode 100644 index 00000000..76bcfd84 Binary files /dev/null and b/cover/cover_3.0.jpg differ diff --git a/source/README.md b/source/README.md index d1cf2e96..875989cb 100755 --- a/source/README.md +++ b/source/README.md @@ -1,76 +1,22 @@ -> 已同步更新到 Swift 2.2 +> 2016.9.23: 已经更新到 Swift 3.0。 +# 3.0 更新说明 +Swift 3.0 是自 Swift 开源以来第一个大的版本更新。从语言角度不兼容之前的 Swift 2.2 和 Swift 2.3 版本。Swift 3.0 的更新说明,大家可以查看[官方blog的说明](https://swift.org/blog/swift-3-0-released/),也可以关注 [SwiftGG](http://swift.gg) 最新的文章。学习官方文档,是掌握语言特性点的最佳途径,感谢翻译的小伙伴们为 Swift 社区所做贡献! -# 2.0 新的开始 +# 3.0 译者记录 +相关[issue](https://github.com/numbbbbb/the-swift-programming-language-in-chinese/issues/628) +- Functions: [crayygy](https://github.com/crayygy) +- Control Flow: [Realank](https://github.com/Realank) +- Closures: [LanfordCai](https://github.com/LanfordCai) +- Protocols: [chenmingbiao](https://github.com/chenmingbiao) +- The Basics:[chenmingbiao](https://github.com/chenmingbiao) +- Advanced Operators: [mmoaay](https://github.com/mmoaay) -> Swift 兴趣交流群:`131595168`, `146932759`, `151336833`, `153549217`. **加入一个群即可,请勿重复添加** +Language Reference: +- Attributes: [WhoJave](https://github.com/WhoJave) +- Statements: [chenmingjia](https://github.com/chenmingjia) +- Declarations: [chenmingjia](https://github.com/chenmingjia) +- Expressions: [chenmingjia](https://github.com/chenmingjia) +- Types: [lettleprince](https://github.com/lettleprince) +- Generic Parameters and Arguments: [chenmingjia](https://github.com/chenmingjia) -> 订阅 Swift 开发者周报,每周获取最新 Swift 资源 - - -> 如果您觉得这个项目不错,请点击Star一下,您的支持是我们最大的动力。 - - -## 1 - -开源项目完成难,维护更难。 - -大家看到的是发布时的瞩目和荣耀,却没有看到项目本身质量不高、错误频出。这并不是翻译者和校对者的问题,他们已经付出了足够的努力。真正的问题在我,没有建立起长期的维护团队,因此后期的校对和更新都难以实施。 - -1.0发布之后,我们就再也没能和苹果的文档同步。语法错误、编译不通过、语言不通顺,阅读量直线下降,最低时每天只有不到1000人访问。 - -6月9日,calvingit发了一个issue“准备翻译2.0版本吗”,我没有回复,应该已经没人关注这个项目了吧,我想。 - -## 2 - -我错了。 - -![1](https://cloud.githubusercontent.com/assets/1472352/10558349/74eb84de-74fe-11e5-99d2-155dfacff068.png) -![2](https://cloud.githubusercontent.com/assets/1472352/10558351/79101476-74fe-11e5-82d5-feb815d0b86b.png) -![3](https://cloud.githubusercontent.com/assets/1472352/10558353/7c983272-74fe-11e5-8397-97c5176261ca.png) -![4](https://cloud.githubusercontent.com/assets/1472352/10558354/7cd63ae0-74fe-11e5-9b9d-1d7ec6516319.png) -![5](https://cloud.githubusercontent.com/assets/1472352/10558358/7fe50914-74fe-11e5-9b2b-a7b9129ea638.png) -![6](https://cloud.githubusercontent.com/assets/1472352/10558360/83a8b064-74fe-11e5-9c2f-2e79d3309f62.png) - - -在我没有任何回复的情况下,不到一天时间,有五位朋友报名。看到这些回复的时候我真的很惊讶,也很感动,无论这个项目存在多少问题,只要有人关注,有人愿意为它付出,那我还有什么理由放弃呢? - -6月28日8点55分,Swift 2.0翻译正式启动。按下发送按钮后,我不停的刷新页面,半个小时过去了,一个回复都没有。“还是不行啊”“如果再过一个小时没人回复我就把issue删掉”,类似的念头不断出现,又不断消失。 - -9:35,xtymichael第一个回复,而且一下就认领了三篇!接下来就是不断的回复认领,到中午已经有超过一半章节被认领。 - -第二天早晨,37个章节全部认领完毕。 - -## 3 - -经过一个多月的努力,我们终于完成了文档的更新。听起来似乎没什么,确实,从1到n总是没有从0到1那么振奋人心。不过真正参与了才知道,修改往往比创造更麻烦,一个需要耐心,一个需要激情,前者往往得不到应有的重视。 - -但是我还是想尽最大可能去感谢他们,这个项目能走到今天,靠的不是我,是那个issue,是那些回复,是这几十个兄弟在工作学习的空闲敲下的每一个字符。而我能做的,只是在每篇文章的开头,那个所有人都会忽略的地方,加上他们的ID。 - -下次你再打开这篇文档,可以多看看那些列在最上方的ID,哪怕不去follow和star,只是看一眼就好。他们的所有努力和付出,就存在于这短暂的一瞥中。 - -Swift 2.0 参与者名单(按照章节顺序): -- [xtymichael](https://github.com/xtymichael) -- [AlanMelody](https://github.com/AlanMelody) -- [DianQK](https://github.com/DianQK) -- [dreamkidd](https://github.com/dreamkidd) -- [100mango](https://github.com/100mango) -- [futantan](https://github.com/futantan) -- [SkyJean](https://github.com/SkyJean) -- [yangsiy](https://github.com/yangsiy) -- [shanksyang](https://github.com/shanksyang) -- [chenmingbiao](https://github.com/chenmingbiao) -- [Channe](https://github.com/Channe) -- [lyojo](https://github.com/lyojo) -- [SergioChan](https://github.com/SergioChan) -- [mmoaay](https://github.com/mmoaay) -- [buginux](https://github.com/buginux) -- [KYawn](https://github.com/KYawn) -- [EudeMorgen](https://github.com/EudeMorgen) -- [littledogboy](https://github.com/littledogboy) -- [Lenhoon](https://github.com/Lenhoon) -- [ray16897188](https://github.com/ray16897188) -- [wardenNScaiyi](https://github.com/wardenNScaiyi) -- [miaosiqi](https://github.com/miaosiqi) -- [Realank](https://github.com/Realank) - -最后,感谢[极客学院](http://wiki.jikexueyuan.com)提供的wiki系统,在国内访问起来速度很快,优化后的样式看起来也更舒服。 +感谢阅读! diff --git a/source/chapter1/01_swift.md b/source/chapter1/01_swift.md index 1b5d9928..f6fb88ea 100755 --- a/source/chapter1/01_swift.md +++ b/source/chapter1/01_swift.md @@ -7,16 +7,18 @@ > 2.0 > 翻译+校对:[xtymichael](https://github.com/xtymichael) +> +> 3.0 翻译+校对:[shanks](http://codebuild.me),2016-10-06 -Swift 是一种新的编程语言,用于编写 iOS,OS X 和 watchOS应用程序。Swift 结合了 C 和 Objective-C 的优点并且不受 C 兼容性的限制。Swift 采用安全的编程模式并添加了很多新特性,这将使编程更简单,更灵活,也更有趣。Swift 是基于成熟而且倍受喜爱的 Cocoa 和 Cocoa Touch 框架,它的降临将重新定义软件开发。 +Swift 是一种新的编程语言,用于编写 iOS,MacOS, watchOS 和tvOS 的应用程序。Swift 结合了 C 和 Objective-C 的优点并且不受 C 兼容性的限制。Swift 采用安全的编程模式并添加了很多新特性,这将使编程更简单,更灵活,也更有趣。Swift 是基于成熟而且倍受喜爱的 Cocoa 和 Cocoa Touch 框架,它的降临将重新定义软件开发。 Swift 的开发从很久之前就开始了。为了给 Swift 打好基础,苹果公司改进了编译器,调试器和框架结构。我们使用自动引用计数(Automatic Reference Counting, ARC)来简化内存管理。我们在 Foundation 和 Cocoa 的基础上构建框架栈使其完全现代化和标准化。 Objective-C 本身支持块、集合语法和模块,所以框架可以轻松支持现代编程语言技术。正是得益于这些基础工作,我们现在才能发布这样一个用于未来苹果软件开发的新语言。 Objective-C 开发者对 Swift 并不会感到陌生。它采用了 Objective-C 的命名参数以及动态对象模型,可以无缝对接到现有的 Cocoa 框架,并且可以兼容 Objective-C 代码。在此基础之上,Swift 还有许多新特性并且支持过程式编程和面向对象编程。 -Swift 对于初学者来说也很友好。它是第一个既满足工业标准又像脚本语言一样充满表现力和趣味的脚本语言。它支持代码预览,这个革命性的特性可以允许程序员在不编译和运行应用程序的前提下运行 Swift 代码并实时查看结果。 +Swift 对于初学者来说也很友好。它是第一个既满足工业标准又像脚本语言一样充满表现力和趣味的系统编程语言。它支持代码预览(playgrounds),这个革命性的特性可以允许程序员在不编译和运行应用程序的前提下运行 Swift 代码并实时查看结果。 Swift 将现代编程语言的精华和苹果工程师文化的智慧结合了起来。编译器对性能进行了优化,编程语言对开发进行了优化,两者互不干扰,鱼与熊掌兼得。Swift 既可以用于开发 “hello, world” 这样的小程序,也可以用于开发一套完整的操作系统。所有的这些特性让 Swift 对于开发者和苹果来说都是一项值得的投资。 -Swift 是编写 iOS,OS X 和 watchOS应用的极佳手段,并将伴随着新的特性和功能持续演进。我们对 Swift 充满信心,你还在等什么! +Swift 是编写 iOS,MacOS,watchOS 和 tvOS 应用的极佳手段,并将伴随着新的特性和功能持续演进。我们对 Swift 充满信心,你还在等什么! diff --git a/source/chapter1/02_a_swift_tour.md b/source/chapter1/02_a_swift_tour.md index 8e78f23b..a34ee03b 100755 --- a/source/chapter1/02_a_swift_tour.md +++ b/source/chapter1/02_a_swift_tour.md @@ -11,6 +11,9 @@ > 2.2 > 翻译:[175](https://github.com/Brian175),2016-04-09 校对:[SketchK](https://github.com/SketchK),2016-05-11 +> +> 3.0 +> 翻译+校对:[shanks](http://codebuild.me),2016-10-06 本页内容包括: @@ -34,7 +37,7 @@ print("Hello, world!") 这个教程会通过一系列编程例子来让你对 Swift 有初步了解,如果你有什么不理解的地方也不用担心——任何本章介绍的内容都会在后面的章节中详细讲解。 > 注意: -> 在 Mac 上,下载 Playground 并双击文件在 Xcode 里打开:[https://developer.apple.com/go/?id=swift-tour](https://developer.apple.com/go/?id=swift-tour) +> 最佳实践是,在 Xcode 作为 playground 打开本章,Playgrounds允许你编辑你的代码并且立即得到结果,[下载 Playground](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/GuidedTour.playground.zip) ## 简单值 @@ -143,10 +146,11 @@ if let name = optionalName { ``` > 练习: -> 把`optionalName`改成`nil`,greeting会是什么?添加一个`else`语句,当`optionalName`是`nil`时给greeting赋一个不同的值。 +> 把`optionalName`改成`nil`,greeting会是什么?添加一个`else`语句,当`optionalName`是`nil`时给 greeting 赋一个不同的值。 如果变量的可选值是`nil`,条件会判断为`false`,大括号中的代码会被跳过。如果不是`nil`,会将值赋给`let`后面的常量,这样代码块中就可以使用这个值了。 -另一种处理可选值的方法是通过使用 ?? 操作符来提供一个默认值。如果可选值缺失的话,可以使用默认值来代替。 +另一种处理可选值的方法是通过使用 `??` 操作符来提供一个默认值。如果可选值缺失的话,可以使用默认值来代替。 + ```swift let nickName: String? = nil let fullName: String = "John Appleseed" @@ -236,12 +240,21 @@ print(total) func greet(name: String, day: String) -> String { return "Hello \(name), today is \(day)." } -greet("Bob", day: "Tuesday") +greet(name:"Bob", day: "Tuesday") ``` > 练习: > 删除`day`参数,添加一个参数来表示今天吃了什么午饭。 +默认情况下,函数使用它们的参数名称作为它们参数的标签,在参数名称前可以自定义参数标签,或者使用 `_` 表示不使用参数标签。 + +``` +func greet(_ person: String, on day: String) -> String { + return "Hello \(person), today is \(day)." +} +greet("John", on: "Wednesday") +``` + 使用元组来让一个函数返回多个值。该元组的元素可以用名称或数字来表示。 ```swift @@ -261,7 +274,7 @@ func calculateStatistics(scores: [Int]) -> (min: Int, max: Int, sum: Int) { return (min, max, sum) } -let statistics = calculateStatistics([5, 3, 100, 3, 9]) +let statistics = calculateStatistics(scores:[5, 3, 100, 3, 9]) print(statistics.sum) print(statistics.2) ``` @@ -277,7 +290,7 @@ func sumOf(numbers: Int...) -> Int { return sum } sumOf() -sumOf(42, 597, 12) +sumOf(numbers: 42, 597, 12) ``` > 练习: @@ -300,7 +313,7 @@ returnFifteen() 函数是第一等类型,这意味着函数可以作为另一个函数的返回值。 ```swift -func makeIncrementer() -> (Int -> Int) { +func makeIncrementer() -> ((Int) -> Int) { func addOne(number: Int) -> Int { return 1 + number } @@ -313,7 +326,7 @@ increment(7) 函数也可以当做参数传入另一个函数。 ```swift -func hasAnyMatches(list: [Int], condition: Int -> Bool) -> Bool { +func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool { for item in list { if condition(item) { return true @@ -325,7 +338,7 @@ func lessThanTen(number: Int) -> Bool { return number < 10 } var numbers = [20, 19, 7, 12] -hasAnyMatches(numbers, condition: lessThanTen) +hasAnyMatches(list: numbers, condition: lessThanTen) ``` 函数实际上是一种特殊的闭包:它是一段能之后被调取的代码。闭包中的代码能访问闭包所建作用域中能得到的变量和函数,即使闭包是在一个不同的作用域被执行的 - 你已经在嵌套函数例子中所看到。你可以使用`{}`来创建一个匿名闭包。使用`in`将参数和返回值类型声明与闭包函数体进行分离。 @@ -339,7 +352,7 @@ numbers.map({ ``` > 练习: -> 重写闭包,对所有奇数返回0。 +> 重写闭包,对所有奇数返回 0。 有很多种创建更简洁的闭包的方法。如果一个闭包的类型已知,比如作为一个回调函数,你可以忽略参数的类型和返回值。单个语句闭包会把它语句的值当做结果返回。 @@ -468,7 +481,7 @@ print(triangle.sideLength) 1. 设置子类声明的属性值 2. 调用父类的构造器 -3. 改变父类定义的属性值。其他的工作比如调用方法、getters和setters也可以在这个阶段完成。 +3. 改变父类定义的属性值。其他的工作比如调用方法、getters 和 setters 也可以在这个阶段完成。 如果你不需要计算属性,但是仍然需要在设置一个新值之前或者之后运行代码,使用`willSet`和`didSet`。 @@ -689,10 +702,10 @@ print(protocolValue.simpleDescription) ## 错误处理 -使用采用`ErrorType`协议的类型来表示错误。 +使用采用`Error`协议的类型来表示错误。 ```swift -enum PrinterError: ErrorType { +enum PrinterError: Error { case OutOfPaper case NoToner case OnFire @@ -755,13 +768,13 @@ let printerFailure = try? sendToPrinter("Never Has Toner") var fridgeIsOpen = false let fridgeContent = ["milk", "eggs", "leftovers"] -func fridgeContains(itemName: String) -> Bool { +func fridgeContains(_ food: String) -> Bool { fridgeIsOpen = true defer { fridgeIsOpen = false } - let result = fridgeContent.contains(itemName) + let result = fridgeContent.contains(food) return result } fridgeContains("banana") @@ -774,20 +787,20 @@ print(fridgeIsOpen) 在尖括号里写一个名字来创建一个泛型函数或者类型。 ```swift -func repeatItem(item: Item, numberOfTimes: Int) -> [Item] { +func repeatItem(repeating item: Item, numberOfTimes: Int) -> [Item] { var result = [Item]() for _ in 0.. { case None case Some(Wrapped) diff --git a/source/chapter1/03_revision_history.md b/source/chapter1/03_revision_history.md index 00a796fe..ba299dc7 100644 --- a/source/chapter1/03_revision_history.md +++ b/source/chapter1/03_revision_history.md @@ -25,11 +25,14 @@ > > 2.2 > 翻译+校对:[changkun](http://changkun.us/about/) +> +> 3.0 +> 翻译+校对:[shanks](http://codebuild.me),2016-10-06 本页面根据 [Document Revision History](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/RevisionHistory.html) 进行适配更新。 本页内容包括: - +- [Swift 3.0 更新](#swift_3_0) - [Swift 2.2 更新](#swift_2_2) - [Swift 2.1 更新](#swift_2_1) - [Swift 2.0 更新](#swift_2_0) @@ -37,6 +40,10 @@ - [Swift 1.1 更新](#swift_1_1) - [Swift 1.0 更新](#swift_1_0) + + +### Swift 3.0 更新 + ### Swift 2.2 更新 @@ -48,6 +55,109 @@ + + 2016-09-13 +
    +
  • + 更新至 Swift 3.0。 +
  • +
  • + 更新函数章节中关于函数的讨论,在函数定义一节中,标明所有函数参数默认都有函数标签。 +
  • +
  • + 更新高级操作符章节中关于操作符的讨论,现在你可以作为类型函数来实现,替代之前的全局函数实现方式。 +
  • +
  • + 增加访问控制章节中关于对新的访问级别描述符openfileprivate的信息 +
  • +
  • + 更新函数定义一节中关于inout的讨论,标明它放在参数类型的前面,替代之前放在参数名称前面的方式。 +
  • +
  • + 更新逃逸闭包自动闭包还有属性章节中关于@noescape@autoclosure的讨论,现在他们是类型属性,而不是定义属性。 +
  • +
  • + 增加高级操作符章节中自定义中缀操作符的优先级一节和定义章节中优先级组声明一节中关于操作符优先级组的信息。 + +
  • +
  • + 更新一些讨论:使用 macOS 替换掉 OS X, Error 替换掉 ErrorProtocol,和更新一些协议名称,比如使用 ExpressibleByStringLiteral 替换掉 StringLiteralConvertible。 + +
  • +
  • + 更新泛型章节中泛型 Where 语句一节和泛型形参和实参章节,现在泛型的 where 语句写在一个声明的最后。 + + +
  • +
  • + 更新逃逸闭包一节,现在闭包默认为非逃逸的(noescaping)。 +
  • +
  • + 更新基础部分章节中可选绑定一节和语句章节中While 语句一节,现在 if,while 和 guard 语句使用逗号分隔条件列表,不需要使用 where 语句。 +
  • +
  • + 增加控制流章节中Switch一节和语句章节中Switch 语句一节关于 switch cases 可以使用多模式的信息。 +
  • +
  • + 更新函数类型一节,现在函数参数标签不包含在函数类型中。 +
  • +
  • + 更新协议章节中协议组合一节和类型章节中协议组合类型一节关于使用新的 Protocol1 & Protocol2 语法的信息。 +
  • +
  • + 更新动态类型表达式一节使用新的 type(of:) 表达式的信息。 +
  • +
  • + 更新行控制表达式一节使用 #sourceLocation(file:line:) 表达式的信息。 +
  • +
  • + 更新永不返回函数一节使用 新的 Never 类型的信息。 +
  • +
  • + 增加字面量表达式一节关于 playground 字面量的信息。 +
  • +
  • + 更新In-Out 参数一节,标明只有非逃逸闭包能捕获 in-out 参数。 +
  • +
  • + 更新默认参数值一节,现在默认参数不能在调用时候重新排序。 +
  • +
  • + 更新属性章节中关于属性参数使用分号的说明。 +
  • +
  • + 增加重新抛出函数和方法一节中关于在 catch 代码块中抛出错误的重新抛出函数的信息。 +
  • +
  • + 增加Selector 表达式一节中关于访问 Objective-C 中 Selector 的 getter 和 setter 的信息。 +
  • +
  • + 增加类型别名声明一节,标明函数类型作为参数类型必须使用括号包裹。 +
  • +
  • + 增加函数类型一节中关于泛型类型别名和在协议内使用类型别名的信息。 +
  • +
  • + 更新属性章节,标明 @IBAction,@IBOutlet 和 @NSManaged 隐式含有 @objc 属性。 +
  • +
  • + 增加声明属性一节中关于 @GKInspectable 的信息。 +
  • +
  • + 更新可选协议要求一节中关于只能在与 Objective-C 交互的代码中才能使用可选协议要求的信息。 +
  • +
  • + 删除函数声明一节中关于显式使用 let 关键字作为函数参数的信息。 +
  • +
  • + 删除语句一节中关于 Boolean 协议的信息, 现在这个协议已经被 Swift 标准库删除。 +
  • +
  • + 更正声明一节中关于 @NSApplicationMain 协议的信息。 +
  • +
+ + 2016-03-21
    diff --git a/source/chapter2/01_The_Basics.md b/source/chapter2/01_The_Basics.md index bd48d27f..5b24eb62 100755 --- a/source/chapter2/01_The_Basics.md +++ b/source/chapter2/01_The_Basics.md @@ -13,7 +13,12 @@ > 校对:[shanks](http://codebuild.me),[overtrue](https://github.com/overtrue) > > 2.2 -> 校对:[SketchK](https://github.com/SketchK) 2016-05-11 +> 校对:[SketchK](https://github.com/SketchK) +> +> 3.0 +> 校对:[CMB](https://github.com/chenmingbiao) +> +> 版本日期:2016-09-13 本页包含内容: @@ -45,27 +50,27 @@ - [错误处理](#error_handling) - [断言](#assertions) -Swift 是一门开发 iOS, OS X 和 watchOS 应用的新语言。然而,如果你有 C 或者 Objective-C 开发经验的话,你会发现 Swift 的很多内容都是你熟悉的。 +Swift 是一门开发 iOS, macOS, watchOS 和 tvOS 应用的新语言。然而,如果你有 C 或者 Objective-C 开发经验的话,你会发现 Swift 的很多内容都是你熟悉的。 -Swift 包含了 C 和 Objective-C 上所有基础数据类型,`Int`表示整型值;`Double`和`Float`表示浮点型值;`Bool`是布尔型值;`String`是文本型数据。Swift 还提供了三个基本的集合类型,`Array`,`Set`和`Dictionary`,详见[集合类型](04_Collection_Types.html)。 +Swift 包含了 C 和 Objective-C 上所有基础数据类型,`Int`表示整型值; `Double` 和 `Float` 表示浮点型值; `Bool` 是布尔型值;`String` 是文本型数据。 Swift 还提供了三个基本的集合类型,`Array` ,`Set` 和 `Dictionary` ,详见[集合类型](04_Collection_Types.html)。 就像 C 语言一样,Swift 使用变量来进行存储并通过变量名来关联值。在 Swift 中,广泛的使用着值不可变的变量,它们就是常量,而且比 C 语言的常量更强大。在 Swift 中,如果你要处理的值不需要改变,那使用常量可以让你的代码更加安全并且更清晰地表达你的意图。 除了我们熟悉的类型,Swift 还增加了 Objective-C 中没有的高阶数据类型比如元组(Tuple)。元组可以让你创建或者传递一组数据,比如作为函数的返回值时,你可以用一个元组可以返回多个值。 -Swift 还增加了可选(Optional)类型,用于处理值缺失的情况。可选表示“那儿有一个值,并且它等于 x ”或者“那儿没有值”。可选有点像在 Objective-C 中使用`nil`,但是它可以用在任何类型上,不仅仅是类。可选类型比 Objective-C 中的`nil`指针更加安全也更具表现力,它是 Swift 许多强大特性的重要组成部分。 +Swift 还增加了可选(Optional)类型,用于处理值缺失的情况。可选表示 “那儿有一个值,并且它等于 x ” 或者 “那儿没有值” 。可选有点像在 Objective-C 中使用 `nil` ,但是它可以用在任何类型上,不仅仅是类。可选类型比 Objective-C 中的 `nil` 指针更加安全也更具表现力,它是 Swift 许多强大特性的重要组成部分。 -Swift 是一门类型安全的语言,可选类型就是一个很好的例子。Swift 可以让你清楚地知道值的类型。如果你的代码期望得到一个`String`,类型安全会阻止你不小心传入一个`Int`。你可以在开发阶段尽早发现并修正错误。 +Swift 是一门类型安全的语言,可选类型就是一个很好的例子。Swift 可以让你清楚地知道值的类型。如果你的代码期望得到一个 `String` ,类型安全会阻止你不小心传入一个 `Int` 。同样的,如果你的代码期望得到一个 `String`,类型安全会阻止你意外传入一个可选的 `String` 。你可以在开发阶段尽早发现并修正错误。 ## 常量和变量 -常量和变量把一个名字(比如`maximumNumberOfLoginAttempts`或者`welcomeMessage`)和一个指定类型的值(比如数字`10`或者字符串`"Hello"`)关联起来。常量的值一旦设定就不能改变,而变量的值可以随意更改。 +常量和变量把一个名字(比如 `maximumNumberOfLoginAttempts` 或者 `welcomeMessage` )和一个指定类型的值(比如数字 `10` 或者字符串 `"Hello"` )关联起来。常量的值一旦设定就不能改变,而变量的值可以随意更改。 ### 声明常量和变量 -常量和变量必须在使用前声明,用`let`来声明常量,用`var`来声明变量。下面的例子展示了如何用常量和变量来记录用户尝试登录的次数: +常量和变量必须在使用前声明,用 `let` 来声明常量,用 `var` 来声明变量。下面的例子展示了如何用常量和变量来记录用户尝试登录的次数: ```swift let maximumNumberOfLoginAttempts = 10 @@ -74,7 +79,7 @@ var currentLoginAttempt = 0 这两行代码可以被理解为: -“声明一个名字是`maximumNumberOfLoginAttempts`的新常量,并给它一个值`10`。然后,声明一个名字是`currentLoginAttempt`的变量并将它的值初始化为`0`。” +“声明一个名字是 `maximumNumberOfLoginAttempts` 的新常量,并给它一个值 `10` 。然后,声明一个名字是 `currentLoginAttempt` 的变量并将它的值初始化为 `0` 。” 在这个例子中,允许的最大尝试登录次数被声明为一个常量,因为这个值不会改变。当前尝试登录次数被声明为一个变量,因为每次尝试登录失败的时候都需要增加这个值。 @@ -84,15 +89,15 @@ var currentLoginAttempt = 0 var x = 0.0, y = 0.0, z = 0.0 ``` ->注意: -如果你的代码中有不需要改变的值,请使用`let`关键字将它声明为常量。只将需要改变的值声明为变量。 +> 注意: +> 如果你的代码中有不需要改变的值,请使用 `let` 关键字将它声明为常量。只将需要改变的值声明为变量。 ### 类型标注 当你声明常量或者变量的时候可以加上类型标注(type annotation),说明常量或者变量中要存储的值的类型。如果要添加类型标注,需要在常量或者变量名后面加上一个冒号和空格,然后加上类型名称。 -这个例子给`welcomeMessage`变量添加了类型标注,表示这个变量可以存储`String`类型的值: +这个例子给 `welcomeMessage` 变量添加了类型标注,表示这个变量可以存储 `String` 类型的值: ```swift var welcomeMessage: String @@ -100,11 +105,11 @@ var welcomeMessage: String 声明中的冒号代表着“是...类型”,所以这行代码可以被理解为: -“声明一个类型为`String`,名字为`welcomeMessage`的变量。” +“声明一个类型为 `String` ,名字为 `welcomeMessage` 的变量。” -“类型为`String`”的意思是“可以存储任意`String`类型的值。” +“类型为 `String` ”的意思是“可以存储任意 `String` 类型的值。” -`welcomeMessage`变量现在可以被设置成任意字符串: +`welcomeMessage` 变量现在可以被设置成任意字符串: ```swift welcomeMessage = "Hello" @@ -117,7 +122,7 @@ var red, green, blue: Double ``` > 注意: -一般来说你很少需要写类型标注。如果你在声明常量或者变量的时候赋了一个初始值,Swift可以推断出这个常量或者变量的类型,请参考[类型安全和类型推断](#type_safety_and_type_inference)。在上面的例子中,没有给`welcomeMessage`赋初始值,所以变量`welcomeMessage`的类型是通过一个类型标注指定的,而不是通过初始值推断的。 +> 一般来说你很少需要写类型标注。如果你在声明常量或者变量的时候赋了一个初始值,Swift可以推断出这个常量或者变量的类型,请参考[类型安全和类型推断](#type_safety_and_type_inference)。在上面的例子中,没有给 `welcomeMessage` 赋初始值,所以变量 `welcomeMessage` 的类型是通过一个类型标注指定的,而不是通过初始值推断的。 ### 常量和变量的命名 @@ -135,7 +140,7 @@ let 🐶🐮 = "dogcow" 一旦你将常量或者变量声明为确定的类型,你就不能使用相同的名字再次进行声明,或者改变其存储的值的类型。同时,你也不能将常量与变量进行互转。 > 注意: -如果你需要使用与Swift保留关键字相同的名称作为常量或者变量名,你可以使用反引号(`)将关键字包围的方式将其作为名字使用。无论如何,你应当避免使用关键字作为常量或变量名,除非你别无选择。 +> 如果你需要使用与Swift保留关键字相同的名称作为常量或者变量名,你可以使用反引号(`)将关键字包围的方式将其作为名字使用。无论如何,你应当避免使用关键字作为常量或变量名,除非你别无选择。 你可以更改现有的变量值为其他同类型的值,在下面的例子中,`friendlyWelcome`的值从`"Hello!"`改为了`"Bonjour!"`: @@ -163,7 +168,7 @@ print(friendlyWelcome) // 输出 "Bonjour!" ``` -`print(_:separator:terminator:)`是一个用来输出一个或多个值到适当输出区的全局函数。如果你用 Xcode,`print(_:separator:terminator:)`将会输出内容到“console”面板上。`separator`和`terminator`参数具有默认值,因此你调用这个函数的时候可以忽略它们。默认情况下,该函数通过添加换行符来结束当前行。如果不想换行,可以传递一个空字符串给`terminator`参数--例如,`print(someValue, terminator:"")`。关于参数默认值的更多信息,请参考[默认参数值](./06_Functions.html#default_parameter_values)。 +`print(_:separator:terminator:)` 是一个用来输出一个或多个值到适当输出区的全局函数。如果你用 Xcode,`print(_:separator:terminator:)` 将会输出内容到“console”面板上。`separator` 和 `terminator` 参数具有默认值,因此你调用这个函数的时候可以忽略它们。默认情况下,该函数通过添加换行符来结束当前行。如果不想换行,可以传递一个空字符串给 `terminator` 参数--例如,`print(someValue, terminator:"")` 。关于参数默认值的更多信息,请参考[默认参数值](./06_Functions.html#default_parameter_values)。 Swift 用字符串插值(string interpolation)的方式把常量名或者变量名当做占位符加入到长字符串中,Swift 会用当前常量或变量的值替换这些占位符。将常量或变量名放入圆括号中,并在开括号前使用反斜杠将其转义: @@ -214,38 +219,39 @@ let cat = "🐱"; print(cat) ## 整数 -整数就是没有小数部分的数字,比如`42`和`-23`。整数可以是`有符号`(正、负、零)或者`无符号`(正、零)。 +整数就是没有小数部分的数字,比如 `42` 和 `-23` 。整数可以是 `有符号`(正、负、零)或者 `无符号`(正、零)。 -Swift 提供了8,16,32和64位的有符号和无符号整数类型。这些整数类型和 C 语言的命名方式很像,比如8位无符号整数类型是`UInt8`,32位有符号整数类型是`Int32`。就像 Swift 的其他类型一样,整数类型采用大写命名法。 +Swift 提供了8,16,32和64位的有符号和无符号整数类型。这些整数类型和 C 语言的命名方式很像,比如8位无符号整数类型是`UInt8`,32位有符号整数类型是 `Int32` 。就像 Swift 的其他类型一样,整数类型采用大写命名法。 ### 整数范围 -你可以访问不同整数类型的`min`和`max`属性来获取对应类型的最小值和最大值: +你可以访问不同整数类型的 `min` 和 `max` 属性来获取对应类型的最小值和最大值: ```swift let minValue = UInt8.min // minValue 为 0,是 UInt8 类型 let maxValue = UInt8.max // maxValue 为 255,是 UInt8 类型 ``` -`min`和`max`所传回值的类型,正是其所对的整数类型(如上例UInt8, 所传回的类型是UInt8),可用在表达式中相同类型值旁。 + +`min` 和 `max` 所传回值的类型,正是其所对的整数类型(如上例UInt8, 所传回的类型是UInt8),可用在表达式中相同类型值旁。 ### Int 一般来说,你不需要专门指定整数的长度。Swift 提供了一个特殊的整数类型`Int`,长度与当前平台的原生字长相同: -* 在32位平台上,`Int`和`Int32`长度相同。 -* 在64位平台上,`Int`和`Int64`长度相同。 +* 在32位平台上,`Int` 和 `Int32` 长度相同。 +* 在64位平台上,`Int` 和 `Int64` 长度相同。 -除非你需要特定长度的整数,一般来说使用`Int`就够了。这可以提高代码一致性和可复用性。即使是在32位平台上,`Int`可以存储的整数范围也可以达到`-2,147,483,648`~`2,147,483,647`,大多数时候这已经足够大了。 +除非你需要特定长度的整数,一般来说使用 `Int` 就够了。这可以提高代码一致性和可复用性。即使是在32位平台上,`Int` 可以存储的整数范围也可以达到 `-2,147,483,648` ~ `2,147,483,647` ,大多数时候这已经足够大了。 ### UInt -Swift 也提供了一个特殊的无符号类型`UInt`,长度与当前平台的原生字长相同: +Swift 也提供了一个特殊的无符号类型 `UInt`,长度与当前平台的原生字长相同: -* 在32位平台上,`UInt`和`UInt32`长度相同。 -* 在64位平台上,`UInt`和`UInt64`长度相同。 +* 在32位平台上,`UInt` 和 `UInt32` 长度相同。 +* 在64位平台上,`UInt` 和 `UInt64` 长度相同。 > 注意: 尽量不要使用`UInt`,除非你真的需要存储一个和当前平台原生字长相同的无符号整数。除了这种情况,最好使用`Int`,即使你要存储的值已知是非负的。统一使用`Int`可以提高代码的可复用性,避免不同类型数字之间的转换,并且匹配数字的类型推断,请参考[类型安全和类型推断](#type_safety_and_type_inference)。 @@ -253,15 +259,15 @@ Swift 也提供了一个特殊的无符号类型`UInt`,长度与当前平台 ## 浮点数 -浮点数是有小数部分的数字,比如`3.14159`,`0.1`和`-273.15`。 +浮点数是有小数部分的数字,比如 `3.14159` ,`0.1` 和 `-273.15`。 -浮点类型比整数类型表示的范围更大,可以存储比`Int`类型更大或者更小的数字。Swift 提供了两种有符号浮点数类型: +浮点类型比整数类型表示的范围更大,可以存储比 `Int` 类型更大或者更小的数字。Swift 提供了两种有符号浮点数类型: * `Double`表示64位浮点数。当你需要存储很大或者很高精度的浮点数时请使用此类型。 * `Float`表示32位浮点数。精度要求不高的话可以使用此类型。 > 注意: -`Double`精确度很高,至少有15位数字,而`Float`只有6位数字。选择哪个类型取决于你的代码需要处理的值的范围。 +`Double`精确度很高,至少有15位数字,而`Float`只有6位数字。选择哪个类型取决于你的代码需要处理的值的范围,在两种类型都匹配的情况下,将优先选择 `Double`。 ## 类型安全和类型推断 @@ -274,32 +280,32 @@ Swift 是一个*类型安全(type safe)*的语言。类型安全的语言可 因为有类型推断,和 C 或者 Objective-C 比起来 Swift 很少需要声明类型。常量和变量虽然需要明确类型,但是大部分工作并不需要你自己来完成。 -当你声明常量或者变量并赋初值的时候类型推断非常有用。当你在声明常量或者变量的时候赋给它们一个字面量(literal value 或 literal)即可触发类型推断。(字面量就是会直接出现在你代码中的值,比如`42`和`3.14159`。) +当你声明常量或者变量并赋初值的时候类型推断非常有用。当你在声明常量或者变量的时候赋给它们一个字面量(literal value 或 literal)即可触发类型推断。(字面量就是会直接出现在你代码中的值,比如 `42` 和 `3.14159` 。) -例如,如果你给一个新常量赋值`42`并且没有标明类型,Swift 可以推断出常量类型是`Int`,因为你给它赋的初始值看起来像一个整数: +例如,如果你给一个新常量赋值 `42` 并且没有标明类型,Swift 可以推断出常量类型是 `Int` ,因为你给它赋的初始值看起来像一个整数: ```swift let meaningOfLife = 42 // meaningOfLife 会被推测为 Int 类型 ``` -同理,如果你没有给浮点字面量标明类型,Swift 会推断你想要的是`Double`: +同理,如果你没有给浮点字面量标明类型,Swift 会推断你想要的是 `Double`: ```swift let pi = 3.14159 // pi 会被推测为 Double 类型 ``` -当推断浮点数的类型时,Swift 总是会选择`Double`而不是`Float`。 +当推断浮点数的类型时,Swift 总是会选择 `Double` 而不是`Float`。 -如果表达式中同时出现了整数和浮点数,会被推断为`Double`类型: +如果表达式中同时出现了整数和浮点数,会被推断为 `Double` 类型: ```swift let anotherPi = 3 + 0.14159 // anotherPi 会被推测为 Double 类型 ``` -原始值`3`没有显式声明类型,而表达式中出现了一个浮点字面量,所以表达式会被推断为`Double`类型。 +原始值 `3` 没有显式声明类型,而表达式中出现了一个浮点字面量,所以表达式会被推断为 `Double` 类型。 ## 数值型字面量 @@ -320,13 +326,15 @@ let octalInteger = 0o21 // 八进制的17 let hexadecimalInteger = 0x11 // 十六进制的17 ``` -浮点字面量可以是十进制(没有前缀)或者是十六进制(前缀是`0x`)。小数点两边必须有至少一个十进制数字(或者是十六进制的数字)。十进制浮点数也可以有一个可选的指数(exponent),通过大写或者小写的 `e` 来指定;十六进制浮点数必须有一个指数,通过大写或者小写的 `p` 来指定。 +浮点字面量可以是十进制(没有前缀)或者是十六进制(前缀是 `0x` )。小数点两边必须有至少一个十进制数字(或者是十六进制的数字)。十进制浮点数也可以有一个可选的指数(exponent),通过大写或者小写的 `e` 来指定;十六进制浮点数必须有一个指数,通过大写或者小写的 `p` 来指定。 + +如果一个十进制数的指数为 `exp`,那这个数相当于基数和10^exp的乘积: -如果一个十进制数的指数为`exp`,那这个数相当于基数和10^exp的乘积: * `1.25e2` 表示 1.25 × 10^2,等于 `125.0`。 * `1.25e-2` 表示 1.25 × 10^-2,等于 `0.0125`。 如果一个十六进制数的指数为`exp`,那这个数相当于基数和2^exp的乘积: + * `0xFp2` 表示 15 × 2^2,等于 `60.0`。 * `0xFp-2` 表示 15 × 2^-2,等于 `3.75`。 @@ -375,9 +383,9 @@ let one: UInt8 = 1 let twoThousandAndOne = twoThousand + UInt16(one) ``` -现在两个数字的类型都是`UInt16`,可以进行相加。目标常量`twoThousandAndOne`的类型被推断为`UInt16`,因为它是两个`UInt16`值的和。 +现在两个数字的类型都是 `UInt16`,可以进行相加。目标常量 `twoThousandAndOne` 的类型被推断为 `UInt16`,因为它是两个 `UInt16` 值的和。 -`SomeType(ofInitialValue)`是调用 Swift 构造器并传入一个初始值的默认方法。在语言内部,`UInt16`有一个构造器,可以接受一个`UInt8`类型的值,所以这个构造器可以用现有的`UInt8`来创建一个新的`UInt16`。注意,你并不能传入任意类型的值,只能传入`UInt16`内部有对应构造器的值。不过你可以扩展现有的类型来让它可以接收其他类型的值(包括自定义类型),请参考[扩展](./20_Extensions.html)。 +`SomeType(ofInitialValue)` 是调用 Swift 构造器并传入一个初始值的默认方法。在语言内部,`UInt16` 有一个构造器,可以接受一个`UInt8`类型的值,所以这个构造器可以用现有的 `UInt8` 来创建一个新的 `UInt16`。注意,你并不能传入任意类型的值,只能传入 `UInt16` 内部有对应构造器的值。不过你可以扩展现有的类型来让它可以接收其他类型的值(包括自定义类型),请参考[扩展](./20_Extensions.html)。 ### 整数和浮点数转换 @@ -391,16 +399,16 @@ let pi = Double(three) + pointOneFourOneFiveNine // pi 等于 3.14159,所以被推测为 Double 类型 ``` -这个例子中,常量`three`的值被用来创建一个`Double`类型的值,所以加号两边的数类型须相同。如果不进行转换,两者无法相加。 +这个例子中,常量 `three` 的值被用来创建一个 `Double` 类型的值,所以加号两边的数类型须相同。如果不进行转换,两者无法相加。 -浮点数到整数的反向转换同样行,整数类型可以用`Double`或者`Float`类型来初始化: +浮点数到整数的反向转换同样行,整数类型可以用 `Double` 或者 `Float` 类型来初始化: ```swift let integerPi = Int(pi) // integerPi 等于 3,所以被推测为 Int 类型 ``` -当用这种方式来初始化一个新的整数值时,浮点值会被截断。也就是说`4.75`会变成`4`,`-3.9`会变成`-3`。 +当用这种方式来初始化一个新的整数值时,浮点值会被截断。也就是说 `4.75` 会变成 `4`,`-3.9` 会变成 `-3`。 > 注意: 结合数字类常量和变量不同于结合数字类字面量。字面量`3`可以直接和字面量`0.14159`相加,因为数字字面量本身没有明确的类型。它们的类型只在编译器需要求值的时候被推测。 @@ -428,16 +436,16 @@ var maxAmplitudeFound = AudioSample.min ## 布尔值 -Swift 有一个基本的布尔(Boolean)类型,叫做`Bool`。布尔值指逻辑上的值,因为它们只能是真或者假。Swift 有两个布尔常量,`true`和`false`: +Swift 有一个基本的布尔(Boolean)类型,叫做`Bool`。布尔值指逻辑上的值,因为它们只能是真或者假。Swift 有两个布尔常量,`true` 和 `false`: ```swift let orangesAreOrange = true let turnipsAreDelicious = false ``` -`orangesAreOrange`和`turnipsAreDelicious`的类型会被推断为`Bool`,因为它们的初值是布尔字面量。就像之前提到的`Int`和`Double`一样,如果你创建变量的时候给它们赋值`true`或者`false`,那你不需要将常量或者变量声明为`Bool`类型。初始化常量或者变量的时候如果所赋的值类型已知,就可以触发类型推断,这让 Swift 代码更加简洁并且可读性更高。 +`orangesAreOrange` 和 `turnipsAreDelicious` 的类型会被推断为 `Bool`,因为它们的初值是布尔字面量。就像之前提到的 `Int` 和 `Double` 一样,如果你创建变量的时候给它们赋值 `true` 或者 `false`,那你不需要将常量或者变量声明为 `Bool` 类型。初始化常量或者变量的时候如果所赋的值类型已知,就可以触发类型推断,这让 Swift 代码更加简洁并且可读性更高。 -当你编写条件语句比如`if`语句的时候,布尔值非常有用: +当你编写条件语句比如 `if` 语句的时候,布尔值非常有用: ```swift if turnipsAreDelicious { @@ -450,7 +458,7 @@ if turnipsAreDelicious { 条件语句,例如`if`,请参考[控制流](./05_Control_Flow.html)。 -如果你在需要使用`Bool`类型的地方使用了非布尔值,Swift 的类型安全机制会报错。下面的例子会报告一个编译时错误: +如果你在需要使用 `Bool` 类型的地方使用了非布尔值,Swift 的类型安全机制会报错。下面的例子会报告一个编译时错误: ```swift let i = 1 @@ -468,7 +476,7 @@ if i == 1 { } ``` -`i == 1`的比较结果是`Bool`类型,所以第二个例子可以通过类型检查。类似`i == 1`这样的比较,请参考[基本操作符](./05_Control_Flow.html)。 +`i == 1` 的比较结果是 `Bool` 类型,所以第二个例子可以通过类型检查。类似 `i == 1` 这样的比较,请参考[基本操作符](./05_Control_Flow.html)。 和 Swift 中的其他类型安全的例子一样,这个方法可以避免错误并保证这块代码的意图总是清晰的。 @@ -477,16 +485,16 @@ if i == 1 { *元组(tuples)*把多个值组合成一个复合值。元组内的值可以是任意类型,并不要求是相同类型。 -下面这个例子中,`(404, "Not Found")`是一个描述 *HTTP 状态码(HTTP status code)*的元组。HTTP 状态码是当你请求网页的时候 web 服务器返回的一个特殊值。如果你请求的网页不存在就会返回一个`404 Not Found`状态码。 +下面这个例子中,`(404, "Not Found")` 是一个描述 *HTTP 状态码(HTTP status code)*的元组。HTTP 状态码是当你请求网页的时候 web 服务器返回的一个特殊值。如果你请求的网页不存在就会返回一个 `404 Not Found` 状态码。 ```swift let http404Error = (404, "Not Found") // http404Error 的类型是 (Int, String),值是 (404, "Not Found") ``` -`(404, "Not Found")`元组把一个`Int`值和一个`String`值组合起来表示 HTTP 状态码的两个部分:一个数字和一个人类可读的描述。这个元组可以被描述为“一个类型为`(Int, String)`的元组”。 +`(404, "Not Found")` 元组把一个 `Int` 值和一个 `String` 值组合起来表示 HTTP 状态码的两个部分:一个数字和一个人类可读的描述。这个元组可以被描述为“一个类型为 `(Int, String)` 的元组”。 -你可以把任意顺序的类型组合成一个元组,这个元组可以包含所有类型。只要你想,你可以创建一个类型为`(Int, Int, Int)`或者`(String, Bool)`或者其他任何你想要的组合的元组。 +你可以把任意顺序的类型组合成一个元组,这个元组可以包含所有类型。只要你想,你可以创建一个类型为 `(Int, Int, Int)` 或者 `(String, Bool)` 或者其他任何你想要的组合的元组。 你可以将一个元组的内容分解(decompose)成单独的常量和变量,然后你就可以正常使用它们了: @@ -530,7 +538,7 @@ print("The status message is \(http200Status.description)") // 输出 "The status message is OK" ``` -作为函数返回值时,元组非常有用。一个用来获取网页的函数可能会返回一个`(Int, String)`元组来描述是否获取成功。和只能返回一个类型的值比较起来,一个包含两个不同类型值的元组可以让函数的返回信息更有用。请参考[函数参数与返回值](./06_Functions.html#Function_Parameters_and_Return_Values)。 +作为函数返回值时,元组非常有用。一个用来获取网页的函数可能会返回一个 `(Int, String)` 元组来描述是否获取成功。和只能返回一个类型的值比较起来,一个包含两个不同类型值的元组可以让函数的返回信息更有用。请参考[函数参数与返回值](./06_Functions.html#Function_Parameters_and_Return_Values)。 > 注意: 元组在临时组织值的时候很有用,但是并不适合创建复杂的数据结构。如果你的数据结构并不是临时使用,请使用类或者结构体而不是元组。请参考[类和结构体](./09_Classes_and_Structures.html)。 @@ -547,11 +555,11 @@ print("The status message is \(http200Status.description)") * 没有值 > 注意: -C 和 Objective-C 中并没有可选类型这个概念。最接近的是 Objective-C 中的一个特性,一个方法要不返回一个对象要不返回`nil`,`nil`表示“缺少一个合法的对象”。然而,这只对对象起作用——对于结构体,基本的 C 类型或者枚举类型不起作用。对于这些类型,Objective-C 方法一般会返回一个特殊值(比如`NSNotFound`)来暗示值缺失。这种方法假设方法的调用者知道并记得对特殊值进行判断。然而,Swift 的可选类型可以让你暗示_任意类型_的值缺失,并不需要一个特殊值。 +C 和 Objective-C 中并没有可选类型这个概念。最接近的是 Objective-C 中的一个特性,一个方法要不返回一个对象要不返回`nil`,`nil`表示“缺少一个合法的对象”。然而,这只对对象起作用——对于结构体,基本的 C 类型或者枚举类型不起作用。对于这些类型,Objective-C 方法一般会返回一个特殊值(比如`NSNotFound`)来暗示值缺失。这种方法假设方法的调用者知道并记得对特殊值进行判断。然而,Swift 的可选类型可以让你暗示*任意类型*的值缺失,并不需要一个特殊值。 -来看一个例子。Swift 的`Int`类型有一种构造器,作用是将一个`String`值转换成一个`Int`值。然而,并不是所有的字符串都可以转换成一个整数。字符串`"123"`可以被转换成数字`123`,但是字符串`"hello, world"`不行。 +来看一个例子。Swift 的 `Int` 类型有一种构造器,作用是将一个 `String` 值转换成一个 `Int` 值。然而,并不是所有的字符串都可以转换成一个整数。字符串 `"123"` 可以被转换成数字 `123` ,但是字符串 `"hello, world"` 不行。 -下面的例子使用这种构造器来尝试将一个`String`转换成`Int`: +下面的例子使用这种构造器来尝试将一个 `String` 转换成 `Int`: ```swift let possibleNumber = "123" @@ -559,7 +567,7 @@ let convertedNumber = Int(possibleNumber) // convertedNumber 被推测为类型 "Int?", 或者类型 "optional Int" ``` -因为该构造器可能会失败,所以它返回一个_可选类型_(optional)`Int`,而不是一个`Int`。一个可选的`Int`被写作`Int?`而不是`Int`。问号暗示包含的值是可选类型,也就是说可能包含`Int`值也可能*不包含值*。(不能包含其他任何值比如`Bool`值或者`String`值。只能是`Int`或者什么都没有。) +因为该构造器可能会失败,所以它返回一个*可选类型*(optional)`Int`,而不是一个 `Int`。一个可选的 `Int` 被写作 `Int?` 而不是 `Int`。问号暗示包含的值是可选类型,也就是说可能包含 `Int` 值也可能*不包含值*。(不能包含其他任何值比如 `Bool` 值或者 `String` 值。只能是 `Int` 或者什么都没有。) ### nil @@ -576,7 +584,7 @@ serverResponseCode = nil > 注意: `nil`不能用于非可选的常量和变量。如果你的代码中有常量或者变量需要处理值缺失的情况,请把它们声明成对应的可选类型。 -如果你声明一个可选常量或者变量但是没有赋值,它们会自动被设置为`nil`: +如果你声明一个可选常量或者变量但是没有赋值,它们会自动被设置为 `nil`: ```swift var surveyAnswer: String? @@ -584,14 +592,14 @@ var surveyAnswer: String? ``` > 注意: -Swift 的`nil`和 Objective-C 中的`nil`并不一样。在 Objective-C 中,`nil`是一个指向不存在对象的指针。在 Swift 中,`nil`不是指针——它是一个确定的值,用来表示值缺失。任何类型的可选状态都可以被设置为`nil`,不只是对象类型。 +Swift 的 `nil` 和 Objective-C 中的 `nil` 并不一样。在 Objective-C 中,`nil` 是一个指向不存在对象的指针。在 Swift 中,`nil` 不是指针——它是一个确定的值,用来表示值缺失。任何类型的可选状态都可以被设置为 `nil`,不只是对象类型。 ### if 语句以及强制解析 -你可以使用`if`语句和`nil`比较来判断一个可选值是否包含值。你可以使用“相等”(`==`)或“不等”(`!=`)来执行比较。 +你可以使用 `if` 语句和 `nil` 比较来判断一个可选值是否包含值。你可以使用“相等”(`==`)或“不等”(`!=`)来执行比较。 -如果可选类型有值,它将不等于`nil`: +如果可选类型有值,它将不等于 `nil`: ```swift if convertedNumber != nil { @@ -599,7 +607,8 @@ if convertedNumber != nil { } // 输出 "convertedNumber contains some integer value." ``` -当你确定可选类型确实包含值之后,你可以在可选的名字后面加一个感叹号(`!`)来获取值。这个惊叹号表示“我知道这个可选有值,请使用它。”这被称为可选值的_强制解析(forced unwrapping)_: + +当你确定可选类型确实包含值之后,你可以在可选的名字后面加一个感叹号(`!`)来获取值。这个惊叹号表示“我知道这个可选有值,请使用它。”这被称为可选值的*强制解析(forced unwrapping)*: ```swift if convertedNumber != nil { @@ -608,17 +617,17 @@ if convertedNumber != nil { // 输出 "convertedNumber has an integer value of 123." ``` -更多关于`if`语句的内容,请参考[控制流](05_Control_Flow.html)。 +更多关于 `if` 语句的内容,请参考[控制流](05_Control_Flow.html)。 > 注意: -使用`!`来获取一个不存在的可选值会导致运行时错误。使用`!`来强制解析值之前,一定要确定可选包含一个非`nil`的值。 +使用 `!` 来获取一个不存在的可选值会导致运行时错误。使用 `!` 来强制解析值之前,一定要确定可选包含一个非 `nil` 的值。 ### 可选绑定 -使用*可选绑定(optional binding)*来判断可选类型是否包含值,如果包含就把值赋给一个临时常量或者变量。可选绑定可以用在`if`和`while`语句中,这条语句不仅可以用来判断可选类型中是否有值,同时可以将可选类型中的值赋给一个常量或者变量。`if`和`while`语句,请参考[控制流](./05_Control_Flow.html)。 +使用*可选绑定(optional binding)*来判断可选类型是否包含值,如果包含就把值赋给一个临时常量或者变量。可选绑定可以用在 `if` 和 `while` 语句中,这条语句不仅可以用来判断可选类型中是否有值,同时可以将可选类型中的值赋给一个常量或者变量。`if` 和 `while` 语句,请参考[控制流](./05_Control_Flow.html)。 -像下面这样在`if`语句中写一个可选绑定: +像下面这样在 `if` 语句中写一个可选绑定: ```swift if let constantName = someOptional { @@ -626,7 +635,7 @@ if let constantName = someOptional { } ``` -你可以像上面这样使用可选绑定来重写`possibleNumber`这个[例子](./01_The_Basics.html#optionals): +你可以像上面这样使用可选绑定来重写 `possibleNumber` 这个[例子](./01_The_Basics.html#optionals): ```swift if let actualNumber = Int(possibleNumber) { @@ -639,25 +648,37 @@ if let actualNumber = Int(possibleNumber) { 这段代码可以被理解为: -“如果`Int(possibleNumber)`返回的可选`Int`包含一个值,创建一个叫做`actualNumber`的新常量并将可选包含的值赋给它。” +“如果 `Int(possibleNumber)` 返回的可选 `Int` 包含一个值,创建一个叫做 `actualNumber` 的新常量并将可选包含的值赋给它。” -如果转换成功,`actualNumber`常量可以在`if`语句的第一个分支中使用。它已经被可选类型 _包含的_ 值初始化过,所以不需要再使用`!`后缀来获取它的值。在这个例子中,`actualNumber`只被用来输出转换结果。 +如果转换成功,`actualNumber` 常量可以在 `if` 语句的第一个分支中使用。它已经被可选类型 *包含的* 值初始化过,所以不需要再使用 `!` 后缀来获取它的值。在这个例子中,`actualNumber` 只被用来输出转换结果。 -你可以在可选绑定中使用常量和变量。如果你想在`if`语句的第一个分支中操作`actualNumber`的值,你可以改成`if var actualNumber`,这样可选类型包含的值就会被赋给一个变量而非常量。 +你可以在可选绑定中使用常量和变量。如果你想在`if`语句的第一个分支中操作 `actualNumber` 的值,你可以改成 `if var actualNumber`,这样可选类型包含的值就会被赋给一个变量而非常量。 -你可以包含多个可选绑定在`if`语句中,并使用`where`子句做布尔值判断。 +你可以包含多个可选绑定或多个布尔条件在一个 `if` 语句中,只要使用逗号分开就行。只要有任意一个可选绑定的值为`nil`,或者任意一个布尔条件为`false`,则整个`if`条件判断为`false`,这时你就需要使用嵌套 `if` 条件语句来处理,如下所示: ```swift -if let firstNumber = Int("4"), secondNumber = Int("42") where firstNumber < secondNumber { - print("\(firstNumber) < \(secondNumber)") +if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 { + print("\(firstNumber) < \(secondNumber) < 100") } -// prints "4 < 42" +// Prints "4 < 42 < 100" + +if let firstNumber = Int("4") { + if let secondNumber = Int("42") { + if firstNumber < secondNumber && secondNumber < 100 { + print("\(firstNumber) < \(secondNumber) < 100") + } + } +} +// Prints "4 < 42 < 100" ``` +> 注意: +> 在 `if` 条件语句中使用常量和变量来创建一个可选绑定,仅在 `if` 语句的句中(`body`)中才能获取到值。相反,在 `guard` 语句中使用常量和变量来创建一个可选绑定,仅在 `guard` 语句外且在语句后才能获取到值,请参考[控制流](./05_Control_Flow#early_exit.html)。 + ### 隐式解析可选类型 -如上所述,可选类型暗示了常量或者变量可以“没有值”。可选可以通过`if`语句来判断是否有值,如果有值的话可以通过可选绑定来解析值。 +如上所述,可选类型暗示了常量或者变量可以“没有值”。可选可以通过 `if` 语句来判断是否有值,如果有值的话可以通过可选绑定来解析值。 有时候在程序架构中,第一次被赋值之后,可以确定一个可选类型_总会_有值。在这种情况下,每次都要判断和解析可选值是非常低效的,因为可以确定它总会有值。 @@ -665,7 +686,7 @@ if let firstNumber = Int("4"), secondNumber = Int("42") where firstNumber < seco 当可选类型被第一次赋值之后就可以确定之后一直有值的时候,隐式解析可选类型非常有用。隐式解析可选类型主要被用在 Swift 中类的构造过程中,请参考[无主引用以及隐式解析可选属性](./16_Automatic_Reference_Counting.html#unowned_references_and_implicitly_unwrapped_optional_properties)。 -一个隐式解析可选类型其实就是一个普通的可选类型,但是可以被当做非可选类型来使用,并不需要每次都使用解析来获取可选值。下面的例子展示了可选类型`String`和隐式解析可选类型`String`之间的区别: +一个隐式解析可选类型其实就是一个普通的可选类型,但是可以被当做非可选类型来使用,并不需要每次都使用解析来获取可选值。下面的例子展示了可选类型 `String` 和隐式解析可选类型 `String` 之间的区别: ```swift let possibleString: String? = "An optional string." @@ -678,7 +699,7 @@ let implicitString: String = assumedString // 不需要感叹号 你可以把隐式解析可选类型当做一个可以自动解析的可选类型。你要做的只是声明的时候把感叹号放到类型的结尾,而不是每次取值的可选名字的结尾。 > 注意: -如果你在隐式解析可选类型没有值的时候尝试取值,会触发运行时错误。和你在没有值的普通可选类型后面加一个惊叹号一样。 +> 如果你在隐式解析可选类型没有值的时候尝试取值,会触发运行时错误。和你在没有值的普通可选类型后面加一个惊叹号一样。 你仍然可以把隐式解析可选类型当做普通可选类型来判断它是否包含值: @@ -699,11 +720,11 @@ if let definiteString = assumedString { ``` > 注意: -如果一个变量之后可能变成`nil`的话请不要使用隐式解析可选类型。如果你需要在变量的生命周期中判断是否是`nil`的话,请使用普通可选类型。 +> 如果一个变量之后可能变成`nil`的话请不要使用隐式解析可选类型。如果你需要在变量的生命周期中判断是否是`nil`的话,请使用普通可选类型。 ## 错误处理 -你可以使用*错误处理(error handling)*来应对程序执行中可能会遇到的错误条件。 +你可以使用 *错误处理(error handling)* 来应对程序执行中可能会遇到的错误条件。 相对于可选中运用值的存在与缺失来表达函数的成功与失败,错误处理可以推断失败的原因,并传播至程序的其他部分。 @@ -734,35 +755,35 @@ do { func makeASandwich() throws { // ... } - + do { try makeASandwich() eatASandwich() -} catch Error.OutOfCleanDishes { +} catch SandwichError.outOfCleanDishes { washDishes() -} catch Error.MissingIngredients(let ingredients) { +} catch SandwichError.missingIngredients(let ingredients) { buyGroceries(ingredients) } ``` -在此例中,`makeASandwich()`(做一个三明治)函数会抛出一个错误消息如果没有干净的盘子或者某个原料缺失。因为`makeASandwich()`抛出错误,函数调用被包裹在`try`表达式中。将函数包裹在一个`do`语句中,任何被抛出的错误会被传播到提供的`catch`从句中。 +在此例中,`makeASandwich()`(做一个三明治)函数会抛出一个错误消息如果没有干净的盘子或者某个原料缺失。因为 `makeASandwich()` 抛出错误,函数调用被包裹在 `try` 表达式中。将函数包裹在一个 `do` 语句中,任何被抛出的错误会被传播到提供的 `catch` 从句中。 -如果没有错误被抛出, `eatASandwich()`函数会被调用。如果一个匹配`Error.OutOfCleanDishes`的错误被抛出,`washDishes`函数会被调用。如果一个匹配`Error.MissingIngredients`的错误被抛出,`buyGroceries(_:)`函数会被调用,并且使用`catch`所捕捉到的关联值`[String]`作为参数。 +如果没有错误被抛出,`eatASandwich()` 函数会被调用。如果一个匹配 `SandwichError.outOfCleanDishes` 的错误被抛出,`washDishes()` 函数会被调用。如果一个匹配 `SandwichError.missingIngredients` 的错误被抛出,`buyGroceries(_:)` 函数会被调用,并且使用 `catch` 所捕捉到的关联值 `[String]` 作为参数。 抛出,捕捉,以及传播错误会在[错误处理](./18_Error_Handling.html)章节详细说明。 ## 断言 -可选类型可以让你判断值是否存在,你可以在代码中优雅地处理值缺失的情况。然而,在某些情况下,如果值缺失或者值并不满足特定的条件,你的代码可能没办法继续执行。这时,你可以在你的代码中触发一个*断言(assertion)*来结束代码运行并通过调试来找到值缺失的原因。 +可选类型可以让你判断值是否存在,你可以在代码中优雅地处理值缺失的情况。然而,在某些情况下,如果值缺失或者值并不满足特定的条件,你的代码可能没办法继续执行。这时,你可以在你的代码中触发一个 *断言(assertion)* 来结束代码运行并通过调试来找到值缺失的原因。 ### 使用断言进行调试 -断言会在运行时判断一个逻辑条件是否为`true`。从字面意思来说,断言“断言”一个条件是否为真。你可以使用断言来保证在运行其他代码之前,某些重要的条件已经被满足。如果条件判断为`true`,代码运行会继续进行;如果条件判断为`false`,代码执行结束,你的应用被终止。 +断言会在运行时判断一个逻辑条件是否为 `true`。从字面意思来说,断言“断言”一个条件是否为真。你可以使用断言来保证在运行其他代码之前,某些重要的条件已经被满足。如果条件判断为 `true`,代码运行会继续进行;如果条件判断为 `false`,代码执行结束,你的应用被终止。 如果你的代码在调试环境下触发了一个断言,比如你在 Xcode 中构建并运行一个应用,你可以清楚地看到不合法的状态发生在哪里并检查断言被触发时你的应用的状态。此外,断言允许你附加一条调试信息。 -你可以使用全局`assert(_:_:file:line:)`函数来写一个断言。向这个函数传入一个结果为`true`或者`false`的表达式以及一条信息,当表达式的结果为`false`的时候这条信息会被显示: +你可以使用全局 `assert(_:_:file:line:)` 函数来写一个断言。向这个函数传入一个结果为 `true` 或者 `false` 的表达式以及一条信息,当表达式的结果为 `false` 的时候这条信息会被显示: ```swift let age = -3 @@ -770,7 +791,7 @@ assert(age >= 0, "A person's age cannot be less than zero") // 因为 age < 0,所以断言会触发 ``` -在这个例子中,只有`age >= 0`为`true`的时候,即`age`的值非负的时候,代码才会继续执行。如果`age`的值是负数,就像代码中那样,`age >= 0`为`false`,断言被触发,终止应用。 +在这个例子中,只有 `age >= 0` 为 `true` 的时候,即 `age` 的值非负的时候,代码才会继续执行。如果 `age` 的值是负数,就像代码中那样,`age >= 0` 为 `false`,断言被触发,终止应用。 如果不需要断言信息,可以省略,就像这样: @@ -779,7 +800,7 @@ assert(age >= 0) ``` > 注意: -当代码使用优化编译的时候,断言将会被禁用,例如在 Xcode 中,使用默认的 target Release 配置选项来 build 时,断言会被禁用。 +> 当代码使用优化编译的时候,断言将会被禁用,例如在 Xcode 中,使用默认的 target Release 配置选项来 build 时,断言会被禁用。 ### 何时使用断言 @@ -787,11 +808,9 @@ assert(age >= 0) * 整数类型的下标索引被传入一个自定义下标实现,但是下标索引值可能太小或者太大。 * 需要给函数传入一个值,但是非法的值可能导致函数不能正常执行。 -* 一个可选值现在是`nil`,但是后面的代码运行需要一个非`nil`值。 +* 一个可选值现在是 `nil`,但是后面的代码运行需要一个非 `nil` 值。 请参考[下标](./12_Subscripts.html)和[函数](./06_Functions.html)。 > 注意: -断言可能导致你的应用终止运行,所以你应当仔细设计你的代码来让非法条件不会出现。然而,在你的应用发布之前,有时候非法条件可能出现,这时使用断言可以快速发现问题。 - - +> 断言可能导致你的应用终止运行,所以你应当仔细设计你的代码来让非法条件不会出现。然而,在你的应用发布之前,有时候非法条件可能出现,这时使用断言可以快速发现问题。 diff --git a/source/chapter2/03_Strings_and_Characters.md b/source/chapter2/03_Strings_and_Characters.md index d13f4a99..12c31586 100755 --- a/source/chapter2/03_Strings_and_Characters.md +++ b/source/chapter2/03_Strings_and_Characters.md @@ -17,6 +17,8 @@ > 3.0 > 校对:[CMB](https://github.com/chenmingbiao) +> +> 版本日期:2016-09-13 本页包含内容: diff --git a/source/chapter2/04_Collection_Types.md b/source/chapter2/04_Collection_Types.md index 68875522..b055f99a 100755 --- a/source/chapter2/04_Collection_Types.md +++ b/source/chapter2/04_Collection_Types.md @@ -12,7 +12,10 @@ > 校对:[shanks](http://codebuild.me) > 2.2 -> 校对:[SketchK](https://github.com/SketchK) 2016-05-11 +> 校对:[SketchK](https://github.com/SketchK) 2016-05-11 +> +> 3.0 +> 校对:[shanks](http://codebuild.me) ,2016-10-09 本页包含内容: @@ -46,7 +49,7 @@ Swift 的`Arrays`、`Sets`和`Dictionaries`类型被实现为*泛型集合*。 > 注意: Swift 的`Array`类型被桥接到`Foundation`中的`NSArray`类。 - 更多关于在`Foundation`和`Cocoa`中使用`Array`的信息,参见 [*Using Swift with Cocoa and Obejective-C*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 一书。 + 更多关于在`Foundation`和`Cocoa`中使用`Array`的信息,参见 [*Using Swift with Cocoa and Obejective-C(Swift 3)*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中[使用 Cocoa 数据类型](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)部分。 ### 数组的简单语法 @@ -81,7 +84,7 @@ someInts = [] Swift 中的`Array`类型还提供一个可以创建特定大小并且所有数据都被默认的构造方法。我们可以把准备加入新数组的数据项数量(`count`)和适当类型的初始值(`repeatedValue`)传入数组构造函数: ```swift -var threeDoubles = [Double](count: 3, repeatedValue:0.0) +var threeDoubles = Array(repeating: 0.0, count: 3) // threeDoubles 是一种 [Double] 数组,等价于 [0.0, 0.0, 0.0] ``` @@ -91,7 +94,7 @@ var threeDoubles = [Double](count: 3, repeatedValue:0.0) 我们可以使用加法操作符(`+`)来组合两种已存在的相同类型数组。新数组的数据类型会被从两个数组的数据类型中推断出来: ```swift -var anotherThreeDoubles = [Double](count: 3, repeatedValue: 2.5) +var anotherThreeDoubles = Array(repeating: 2.5, count: 3) // anotherThreeDoubles 被推断为 [Double],等价于 [2.5, 2.5, 2.5] var sixDoubles = threeDoubles + anotherThreeDoubles @@ -194,20 +197,20 @@ shoppingList[4...6] = ["Bananas", "Apples"] 不可以用下标访问的形式去在数组尾部添加新项。 -调用数组的`insert(_:atIndex:)`方法来在某个具体索引值之前添加数据项: +调用数组的`insert(_:at:)`方法来在某个具体索引值之前添加数据项: ```swift -shoppingList.insert("Maple Syrup", atIndex: 0) +shoppingList.insert("Maple Syrup", at: 0) // shoppingList 现在有7项 // "Maple Syrup" 现在是这个列表中的第一项 ``` -这次`insert(_:atIndex:)`方法调用把值为`"Maple Syrup"`的新数据项插入列表的最开始位置,并且使用`0`作为索引值。 +这次`insert(_:at:)`方法调用把值为`"Maple Syrup"`的新数据项插入列表的最开始位置,并且使用`0`作为索引值。 -类似的我们可以使用`removeAtIndex(_:)`方法来移除数组中的某一项。这个方法把数组在特定索引值中存储的数据项移除并且返回这个被移除的数据项(我们不需要的时候就可以无视它): +类似的我们可以使用`remove(at:)`方法来移除数组中的某一项。这个方法把数组在特定索引值中存储的数据项移除并且返回这个被移除的数据项(我们不需要的时候就可以无视它): ```swift -let mapleSyrup = shoppingList.removeAtIndex(0) +let mapleSyrup = remove(at: 0) // 索引值为0的数据项被移除 // shoppingList 现在只有6项,而且不包括 Maple Syrup // mapleSyrup 常量的值等于被移除数据项的值 "Maple Syrup" @@ -222,7 +225,7 @@ firstItem = shoppingList[0] // firstItem 现在等于 "Six eggs" ``` -如果我们只想把数组中的最后一项移除,可以使用`removeLast()`方法而不是`removeAtIndex(_:)`方法来避免我们需要获取数组的`count`属性。就像后者一样,前者也会返回被移除的数据项: +如果我们只想把数组中的最后一项移除,可以使用`removeLast()`方法而不是`remove(at:)`方法来避免我们需要获取数组的`count`属性。就像后者一样,前者也会返回被移除的数据项: ```swift let apples = shoppingList.removeLast() @@ -269,7 +272,7 @@ for (index, value) in shoppingList.enumerate() { > 注意: > Swift的`Set`类型被桥接到`Foundation`中的`NSSet`类。 -> 关于使用`Foundation`和`Cocoa`中`Set`的知识,请看 [*Using Swift with Cocoa and Objective-C*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216)。 +> 关于使用`Foundation`和`Cocoa`中`Set`的知识,参见 [*Using Swift with Cocoa and Obejective-C(Swift 3)*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中[使用 Cocoa 数据类型](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)部分。 #### 集合类型的哈希值 @@ -410,10 +413,10 @@ for genre in favoriteGenres { 更多关于`for-in`循环的信息,参见[For 循环](./05_Control_Flow.html#for_loops)。 -Swift 的`Set`类型没有确定的顺序,为了按照特定顺序来遍历一个`Set`中的值可以使用`sort()`方法,它将返回一个有序数组,这个数组的元素排列顺序由操作符'<'对元素进行比较的结果来确定. +Swift 的`Set`类型没有确定的顺序,为了按照特定顺序来遍历一个`Set`中的值可以使用`sorted()`方法,它将返回一个有序数组,这个数组的元素排列顺序由操作符'<'对元素进行比较的结果来确定. ```swift -for genre in favoriteGenres.sort() { +for genre in favoriteGenres.sorted() { print("\(genre)") } // prints "Classical" @@ -433,10 +436,10 @@ for genre in favoriteGenres.sort() { ![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/setVennDiagram_2x.png) -* 使用`intersect(_:)`方法根据两个集合中都包含的值创建的一个新的集合。 -* 使用`exclusiveOr(_:)`方法根据在一个集合中但不在两个集合中的值创建一个新的集合。 +* 使用`intersection(_:)`方法根据两个集合中都包含的值创建的一个新的集合。 +* 使用`symmetricDifference(_:)`方法根据在一个集合中但不在两个集合中的值创建一个新的集合。 * 使用`union(_:)`方法根据两个集合的值创建一个新的集合。 -* 使用`subtract(_:)`方法根据不在该集合中的值创建一个新的集合。 +* 使用`subtracting(_:)`方法根据不在该集合中的值创建一个新的集合。 ```swift let oddDigits: Set = [1, 3, 5, 7, 9] @@ -445,11 +448,11 @@ let singleDigitPrimeNumbers: Set = [2, 3, 5, 7] oddDigits.union(evenDigits).sort() // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] -oddDigits.intersect(evenDigits).sort() +oddDigits. intersection(evenDigits).sorted() // [] -oddDigits.subtract(singleDigitPrimeNumbers).sort() +oddDigits.subtracting(singleDigitPrimeNumbers).sorted() // [1, 9] -oddDigits.exclusiveOr(singleDigitPrimeNumbers).sort() +oddDigits. symmetricDifference(singleDigitPrimeNumbers).sorted() // [1, 2, 9] ``` @@ -461,21 +464,21 @@ oddDigits.exclusiveOr(singleDigitPrimeNumbers).sort() ![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/setEulerDiagram_2x.png) * 使用“是否相等”运算符(`==`)来判断两个集合是否包含全部相同的值。 -* 使用`isSubsetOf(_:)`方法来判断一个集合中的值是否也被包含在另外一个集合中。 -* 使用`isSupersetOf(_:)`方法来判断一个集合中包含另一个集合中所有的值。 -* 使用`isStrictSubsetOf(_:)`或者`isStrictSupersetOf(_:)`方法来判断一个集合是否是另外一个集合的子集合或者父集合并且两个集合并不相等。 -* 使用`isDisjointWith(_:)`方法来判断两个集合是否不含有相同的值(是否没有交集)。 +* 使用`isSubset(of:)`方法来判断一个集合中的值是否也被包含在另外一个集合中。 +* 使用`isSuperset(of:)`方法来判断一个集合中包含另一个集合中所有的值。 +* 使用`isStrictSubset(of:)`或者`isStrictSuperset(of:)`方法来判断一个集合是否是另外一个集合的子集合或者父集合并且两个集合并不相等。 +* 使用`isDisjoint(with:)`方法来判断两个集合是否不含有相同的值(是否没有交集)。 ```swift let houseAnimals: Set = ["🐶", "🐱"] let farmAnimals: Set = ["🐮", "🐔", "🐑", "🐶", "🐱"] let cityAnimals: Set = ["🐦", "🐭"] -houseAnimals.isSubsetOf(farmAnimals) +houseAnimals.isSubset(of: farmAnimals) // true -farmAnimals.isSupersetOf(houseAnimals) +farmAnimals.isSuperset(of: houseAnimals) // true -farmAnimals.isDisjointWith(cityAnimals) +farmAnimals.isDisjoint(with: cityAnimals) // true ``` @@ -486,7 +489,7 @@ farmAnimals.isDisjointWith(cityAnimals) > 注意: > Swift 的`Dictionary`类型被桥接到`Foundation`的`NSDictionary`类。 -> 更多关于在`Foundation`和`Cocoa`中使用`Dictionary`类型的信息,参见 [*Using Swift with Cocoa and Objective-C (Swift 2.1)*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 一书。 +> 更多关于在`Foundation`和`Cocoa`中使用`Dictionary`类型的信息,参见 [*Using Swift with Cocoa and Obejective-C(Swift 3)*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中[使用 Cocoa 数据类型](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)部分。 ## 字典类型快捷语法 @@ -624,10 +627,10 @@ airports["APL"] = nil // APL 现在被移除了 ``` -此外,`removeValueForKey(_:)`方法也可以用来在字典中移除键值对。这个方法在键值对存在的情况下会移除该键值对并且返回被移除的值或者在没有值的情况下返回`nil`: +此外,`removeValue(forKey:)`方法也可以用来在字典中移除键值对。这个方法在键值对存在的情况下会移除该键值对并且返回被移除的值或者在没有值的情况下返回`nil`: ```swift -if let removedValue = airports.removeValueForKey("DUB") { +if let removedValue = airports. removeValue(forKey: "DUB") { print("The removed airport's name is \(removedValue).") } else { print("The airports dictionary does not contain a value for DUB.") @@ -676,4 +679,4 @@ let airportNames = [String](airports.values) // airportNames 是 ["Toronto Pearson", "London Heathrow"] ``` -Swift 的字典类型是无序集合类型。为了以特定的顺序遍历字典的键或值,可以对字典的`keys`或`values`属性使用`sort()`方法。 +Swift 的字典类型是无序集合类型。为了以特定的顺序遍历字典的键或值,可以对字典的`keys`或`values`属性使用`sorted()`方法。 diff --git a/source/chapter2/06_Functions.md b/source/chapter2/06_Functions.md index 81d3e15e..323fbb9c 100755 --- a/source/chapter2/06_Functions.md +++ b/source/chapter2/06_Functions.md @@ -16,7 +16,8 @@ > 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-12 > 3.0 -> 翻译: [crayygy](https://github.com/crayygy) 2016-09-12 +> 翻译: [crayygy](https://github.com/crayygy) 2016-09-12 +> 校对: [shanks](http://codebuild.me) 2016-09-27 本页包含内容: - [函数定义与调用(Defining and Calling Functions)](#Defining_and_Calling_Functions) @@ -40,7 +41,7 @@ Swift 统一的函数语法非常的灵活,可以用来表示任何函数, 每个函数有个函数名,用来描述函数执行的任务。要使用一个函数时,用函数名来“调用”这个函数,并传给它匹配的输入值(称作 *实参* ,*arguments*)。函数的实参必须与函数参数表里参数的顺序一致。 -下面例子中的函数的名字是`sayHello(_:)`,之所以叫这个名字,是因为这个函数用一个人的名字当做输入,并返回向这个人问候的语句。为了完成这个任务,你需要定义一个输入参数——一个叫做 `personName` 的 `String` 值,和一个包含给这个人问候语的 `String` 类型的返回值: +下面例子中的函数的名字是`greet(_:)`,之所以叫这个名字,是因为这个函数用一个人的名字当做输入,并返回向这个人问候的语句。为了完成这个任务,你需要定义一个输入参数——一个叫做 `personName` 的 `String` 值,和一个包含给这个人问候语的 `String` 类型的返回值: ```swift @@ -61,15 +62,15 @@ print(greet(person: "Brian")) // 打印 "Hello, Brian!" ``` -调用 `sayHello(_:)` 函数时,在圆括号中传给它一个 `String` 类型的实参,例如 `sayHello("Anna")`。正如上面所示,因为这个函数返回一个 `String` 类型的值,所以`sayHello` 可以被包含在 `print(_:separator:terminator:)` 的调用中,用来输出这个函数的返回值。 +调用 `greet(_:)` 函数时,在圆括号中传给它一个 `String` 类型的实参,例如 `greet("Anna")`。正如上面所示,因为这个函数返回一个 `String` 类型的值,所以`greet ` 可以被包含在 `print(_:separator:terminator:)` 的调用中,用来输出这个函数的返回值。 >注意 `print(_:separator:terminator:)` 函数的第一个参数并没有设置一个标签,而其他的参数因为已经有了默认值,因此是可选的。关于这些函数语法上的变化详见下方关于 函数参数标签和参数名 以及 默认参数值。 -在 `sayHello(_:)` 的函数体中,先定义了一个新的名为 `greeting` 的 `String` 常量,同时,把对 `personName` 的问候消息赋值给了 `greeting` 。然后用 `return` 关键字把这个问候返回出去。一旦 `return greeting` 被调用,该函数结束它的执行并返回 `greeting` 的当前值。 +在 `greet(_:)` 的函数体中,先定义了一个新的名为 `greeting` 的 `String` 常量,同时,把对 `personName` 的问候消息赋值给了 `greeting` 。然后用 `return` 关键字把这个问候返回出去。一旦 `return greeting` 被调用,该函数结束它的执行并返回 `greeting` 的当前值。 -你可以用不同的输入值多次调用 `sayHello(_:)`。上面的例子展示的是用`"Anna"`和`"Brian"`调用的结果,该函数分别返回了不同的结果。 +你可以用不同的输入值多次调用 `greet(_:)`。上面的例子展示的是用`"Anna"`和`"Brian"`调用的结果,该函数分别返回了不同的结果。 为了简化这个函数的定义,可以将问候消息的创建和返回写成一句: @@ -120,12 +121,12 @@ print(greet(person: "Tim", alreadyGreeted: true)) // 打印 "Hello again, Tim!" ``` -你可以通过在括号内使用逗号分隔来传递一个`String`参数值和一个标识为`alreadyGreeted`的`Bool`值,来调用`sayHello(_:alreadyGreeted:)`函数。注意这个函数和上面`greet(person:)`是不同的。虽然它们都有着同样的名字`greet`,但是`greet(person:alreadyGreeted:)`函数需要两个参数,而`greet(person:)`只需要一个参数。 +你可以通过在括号内使用逗号分隔来传递一个`String`参数值和一个标识为`alreadyGreeted`的`Bool`值,来调用`greet(person:alreadyGreeted:)`函数。注意这个函数和上面`greet(person:)`是不同的。虽然它们都有着同样的名字`greet`,但是`greet(person:alreadyGreeted:)`函数需要两个参数,而`greet(person:)`只需要一个参数。 ### 无返回值函数 (Functions Without Return Values) -函数可以没有返回值。下面是 `sayHello(_:)` 函数的另一个版本,叫 `sayGoodbye(_:)`,这个函数直接打印一个`String`值,而不是返回它: +函数可以没有返回值。下面是 `greet(person:)` 函数的另一个版本,这个函数直接打印一个`String`值,而不是返回它: ```swift func greet(person: String) { @@ -138,7 +139,7 @@ greet(person: "Dave") 因为这个函数不需要返回值,所以这个函数的定义中没有返回箭头(->)和返回类型。 >注意 -严格上来说,虽然没有返回值被定义,`sayGoodbye(_:)` 函数依然返回了值。没有定义返回类型的函数会返回一个特殊的`Void`值。它其实是一个空的元组(tuple),没有任何元素,可以写成()。 +严格上来说,虽然没有返回值被定义,`greet(person:)` 函数依然返回了值。没有定义返回类型的函数会返回一个特殊的`Void`值。它其实是一个空的元组(tuple),没有任何元素,可以写成()。 被调用时,一个函数的返回值可以被忽略: @@ -157,7 +158,7 @@ printWithoutCounting(string: "hello, world") // 打印 "hello, world" 但是没有返回任何值 ``` -第一个函数 `printAndCount(_:)`,输出一个字符串并返回 `Int` 类型的字符数。第二个函数 `printWithoutCounting`调用了第一个函数,但是忽略了它的返回值。当第二个函数被调用时,消息依然会由第一个函数输出,但是返回值不会被用到。 +第一个函数 `printAndCount(string:)`,输出一个字符串并返回 `Int` 类型的字符数。第二个函数 `printWithoutCounting(string:)`调用了第一个函数,但是忽略了它的返回值。当第二个函数被调用时,消息依然会由第一个函数输出,但是返回值不会被用到。 >注意 返回值可以被忽略,但定义了有返回值的函数必须返回一个值,如果在函数定义底部没有返回任何值,将导致编译时错误(compile-time error)。 @@ -168,7 +169,7 @@ printWithoutCounting(string: "hello, world") 你可以用元组(tuple)类型让多个值作为一个复合值从函数中返回。 -下例中定义了一个名为 `minMax(_:)` 的函数,作用是在一个 `Int` 类型的数组中找出最小值与最大值。 +下例中定义了一个名为 `minMax(array:)` 的函数,作用是在一个 `Int` 类型的数组中找出最小值与最大值。 ```swift func minMax(array: [Int]) -> (min: Int, max: Int) { @@ -185,9 +186,9 @@ func minMax(array: [Int]) -> (min: Int, max: Int) { } ``` - `minMax(_:)` 函数返回一个包含两个 `Int` 值的元组,这些值被标记为 `min` 和 `max` ,以便查询函数的返回值时可以通过名字访问它们。 + `minMax(array:)` 函数返回一个包含两个 `Int` 值的元组,这些值被标记为 `min` 和 `max` ,以便查询函数的返回值时可以通过名字访问它们。 -在 `minMax(_:)` 的函数体中,在开始的时候设置两个工作变量 `currentMin` 和 `currentMax` 的值为数组中的第一个数。然后函数会遍历数组中剩余的值并检查该值是否比 `currentMin` 和 `currentMax` 更小或更大。最后数组中的最小值与最大值作为一个包含两个 `Int` 值的元组返回。 +在 `minMax(array:)` 的函数体中,在开始的时候设置两个工作变量 `currentMin` 和 `currentMax` 的值为数组中的第一个数。然后函数会遍历数组中剩余的值并检查该值是否比 `currentMin` 和 `currentMax` 更小或更大。最后数组中的最小值与最大值作为一个包含两个 `Int` 值的元组返回。 因为元组的成员值已被命名,因此可以通过 `.` 语法来检索找到的最小值与最大值: @@ -208,9 +209,9 @@ print("min is \(bounds.min) and max is \(bounds.max)") 可选元组类型如 `(Int, Int)?` 与元组包含可选类型如 `(Int?, Int?)` 是不同的.可选的元组类型,整个元组是可选的,而不只是元组中的每个元素值。 -前面的 `minMax(_:)` 函数返回了一个包含两个 `Int` 值的元组。但是函数不会对传入的数组执行任何安全检查,如果 `array` 参数是一个空数组,如上定义的 `minMax(_:)` 在试图访问 `array[0]` 时会触发一个运行时错误(runtime error)。 +前面的 `minMax(array:)` 函数返回了一个包含两个 `Int` 值的元组。但是函数不会对传入的数组执行任何安全检查,如果 `array` 参数是一个空数组,如上定义的 `minMax(array:)` 在试图访问 `array[0]` 时会触发一个运行时错误(runtime error)。 -为了安全地处理这个“空数组”问题,将 `minMax(_:)` 函数改写为使用可选元组返回类型,并且当数组为空时返回 `nil`: +为了安全地处理这个“空数组”问题,将 `minMax(array:)` 函数改写为使用可选元组返回类型,并且当数组为空时返回 `nil`: ```swift @@ -229,7 +230,7 @@ func minMax(array: [Int]) -> (min: Int, max: Int)? { } ``` -你可以使用可选绑定来检查 `minMax(_:)` 函数返回的是一个存在的元组值还是 `nil`: +你可以使用可选绑定来检查 `minMax(array:)` 函数返回的是一个存在的元组值还是 `nil`: ```swift if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) { @@ -296,7 +297,7 @@ someFunction(1, secondParameterName: 2) ### 默认参数值 (Default Parameter Values) -你可以在函数体中通过给参数赋值来为任意一个参数定义默认值(Deafult Values)。当默认值被定义后,调用这个函数时可以忽略这个参数。 +你可以在函数体中通过给参数赋值来为任意一个参数定义*默认值(Deafult Value)*。当默认值被定义后,调用这个函数时可以忽略这个参数。 ```swift func someFunction(parameterWithoutDefault: Int, parameterWithDefault: Int = 12) { @@ -312,7 +313,7 @@ someFunction(parameterWithoutDefault: 4) // parameterWithDefault is 12 ### 可变参数 (Variadic Parameters) -一个可变参数(variadic parameter)可以接受零个或多个值。函数调用时,你可以用可变参数来指定函数参数可以被传入不确定数量的输入值。通过在变量类型名后面加入(`...`)的方式来定义可变参数。 +一个*可变参数(variadic parameter)*可以接受零个或多个值。函数调用时,你可以用可变参数来指定函数参数可以被传入不确定数量的输入值。通过在变量类型名后面加入(`...`)的方式来定义可变参数。 可变参数的传入值在函数体中变为此类型的一个数组。例如,一个叫做 `numbers` 的 `Double...` 型可变参数,在函数体内可以当做一个叫 `numbers` 的 `[Double]` 型的数组常量。 @@ -338,7 +339,7 @@ arithmeticMean(3, 8.25, 18.75) ### 输入输出参数(In-Out Parameters) -函数参数默认是常量。试图在函数体中更改参数值将会导致编译错误(compile-time error)。这意味着你不能错误地更改参数值。如果你想要一个函数可以修改参数的值,并且想要在这些修改在函数调用结束后仍然存在,那么就应该把这个参数定义为输入输出参数(In-Out Parameters)。 +函数参数默认是常量。试图在函数体中更改参数值将会导致编译错误(compile-time error)。这意味着你不能错误地更改参数值。如果你想要一个函数可以修改参数的值,并且想要在这些修改在函数调用结束后仍然存在,那么就应该把这个参数定义为*输入输出参数(In-Out Parameters)*。 定义一个输入输出参数时,在参数定义前加 `inout` 关键字。一个`输入输出参数`有传入函数的值,这个值被函数修改,然后被传出函数,替换原来的值。想获取更多的关于输入输出参数的细节和相关的编译器优化,请查看`输入输出参数`一节。 @@ -470,7 +471,7 @@ printMathResult(addTwoInts, 3, 5) 你可以用函数类型作为另一个函数的返回类型。你需要做的是在返回箭头(->)后写一个完整的函数类型。 -下面的这个例子中定义了两个简单函数,分别是 `stepForward` 和 `stepBackward`。`stepForward`函数返回一个比输入值大 `1` 的值。`stepBackward` 函数返回一个比输入值小 `1` 的值。这两个函数的类型都是 `(Int) -> Int`: +下面的这个例子中定义了两个简单函数,分别是 `stepForward(_:)` 和 `stepBackward(_:)`。`stepForward(_:)`函数返回一个比输入值大 `1` 的值。`stepBackward(_:)` 函数返回一个比输入值小 `1` 的值。这两个函数的类型都是 `(Int) -> Int`: ```swift func stepForward(_ input: Int) -> Int { @@ -481,7 +482,7 @@ func stepBackward(_ input: Int) -> Int { } ``` -如下名为 `chooseStepFunction(_:)` 的函数,它的返回类型是 `(Int) -> Int` 类型的函数。`chooseStepFunction(_:)` 根据布尔值 `backwards` 来返回 `stepForward(_:)` 函数或 `stepBackward(_:)` 函数: +如下名为 `chooseStepFunction(backward:)` 的函数,它的返回类型是 `(Int) -> Int` 类型的函数。`chooseStepFunction(backward:)` 根据布尔值 `backwards` 来返回 `stepForward(_:)` 函数或 `stepBackward(_:)` 函数: ```swift func chooseStepFunction(backward: Bool) -> (Int) -> Int { @@ -489,7 +490,7 @@ func chooseStepFunction(backward: Bool) -> (Int) -> Int { } ``` -你现在可以用 `chooseStepFunction(_:)` 来获得两个函数其中的一个: +你现在可以用 `chooseStepFunction(backward:)` 来获得两个函数其中的一个: ```swift var currentValue = 3 @@ -519,11 +520,11 @@ print("zero!") ## 嵌套函数 (Nested Functions) -到目前为止本章中你所见到的所有函数都叫`全局`函数(global functions),它们定义在`全局域`中。你也可以把函数定义在别的函数体中,称作 `嵌套函数`(nested functions)。 +到目前为止本章中你所见到的所有函数都叫*全局函数(global functions)*,它们定义在全局域中。你也可以把函数定义在别的函数体中,称作 *嵌套函数(nested functions)*。 默认情况下,嵌套函数是对外界不可见的,但是可以被它们的外围函数(enclosing function)调用。一个外围函数也可以返回它的某一个嵌套函数,使得这个函数可以在其他域中被使用。 -你可以用返回嵌套函数的方式重写 `chooseStepFunction(_:)` 函数: +你可以用返回嵌套函数的方式重写 `chooseStepFunction(backward:)` 函数: ```swift func chooseStepFunction(backward: Bool) -> (Int) -> Int { diff --git a/source/chapter2/07_Closures.md b/source/chapter2/07_Closures.md index 3a5ebf9b..3fc7d74f 100755 --- a/source/chapter2/07_Closures.md +++ b/source/chapter2/07_Closures.md @@ -14,6 +14,9 @@ > > 2.2 > 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-12 +> +> 3.0 +> 翻译:[Lanford](https://github.com/LanfordCai) 2016-09-19 本页包含内容: @@ -21,15 +24,15 @@ - [尾随闭包(Trailing Closures)](#trailing_closures) - [值捕获(Capturing Values)](#capturing_values) - [闭包是引用类型(Closures Are Reference Types)](#closures_are_reference_types) -- [非逃逸闭包(Nonescaping Closures) ](#nonescaping_closures) +- [逃逸闭包(Escaping Closures) ](#escaping_closures) - [自动闭包(Autoclosures)](#autoclosures) -闭包是自包含的函数代码块,可以在代码中被传递和使用。Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的匿名函数比较相似。 +*闭包*是自包含的函数代码块,可以在代码中被传递和使用。Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的匿名函数比较相似。 -闭包可以捕获和存储其所在上下文中任意常量和变量的引用。这就是所谓的闭合并包裹着这些常量和变量,俗称闭包。Swift 会为您管理在捕获过程中涉及到的所有内存操作。 +闭包可以捕获和存储其所在上下文中任意常量和变量的引用。*闭合、包裹*常量和变量,所谓闭包也。Swift 会为你管理在捕获过程中涉及到的所有内存操作。 -> 注意 -> 如果您不熟悉捕获(capturing)这个概念也不用担心,您可以在[值捕获](#capturing_values)章节对其进行详细了解。 +> 注意 +> 如果你不熟悉捕获(capturing)这个概念也不用担心,你可以在[值捕获](#capturing_values)章节对其进行详细了解。 在[函数](./06_Functions.html)章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之一: @@ -40,7 +43,7 @@ Swift 的闭包表达式拥有简洁的风格,并鼓励在常见场景中进行语法优化,主要优化如下: * 利用上下文推断参数和返回值类型 -* 隐式返回单表达式闭包,即单表达式闭包可以省略`return`关键字 +* 隐式返回单表达式闭包,即单表达式闭包可以省略 `return` 关键字 * 参数名称缩写 * 尾随(Trailing)闭包语法 @@ -48,43 +51,43 @@ Swift 的闭包表达式拥有简洁的风格,并鼓励在常见场景中进 ## 闭包表达式(Closure Expressions) -[嵌套函数](./06_Functions.html#nested_function)是一个在较复杂函数中方便进行命名和定义自包含代码模块的方式。当然,有时候撰写小巧的没有完整定义和命名的类函数结构也是很有用处的,尤其是在您处理一些函数并需要将另外一些函数作为该函数的参数时。 +[嵌套函数](./06_Functions.html#nested_function)是一个在较复杂函数中方便进行命名和定义自包含代码模块的方式。当然,有时候编写小巧的没有完整定义和命名的类函数结构也是很有用处的,尤其是在你处理一些函数并需要将另外一些函数作为该函数的参数时。 -闭包表达式是一种利用简洁语法构建内联闭包的方式。闭包表达式提供了一些语法优化,使得撰写闭包变得简单明了。下面闭包表达式的例子通过使用几次迭代展示了`sort(_:)`方法定义和语法优化的方式。每一次迭代都用更简洁的方式描述了相同的功能。 +*闭包表达式*是一种利用简洁语法构建内联闭包的方式。闭包表达式提供了一些语法优化,使得撰写闭包变得简单明了。下面闭包表达式的例子通过使用几次迭代展示了 `sorted(by:)` 方法定义和语法优化的方式。每一次迭代都用更简洁的方式描述了相同的功能。 -### sort 方法(The Sort Method) +### sorted 方法(The Sorted Method) -Swift 标准库提供了名为`sort`的方法,会根据您提供的用于排序的闭包函数将已知类型数组中的值进行排序。一旦排序完成,`sort(_:)`方法会返回一个与原数组大小相同,包含同类型元素且元素已正确排序的新数组。原数组不会被`sort(_:)`方法修改。 +Swift 标准库提供了名为 `sorted(by:)` 的方法,它会根据你所提供的用于排序的闭包函数将已知类型数组中的值进行排序。一旦排序完成,`sorted(by:)` 方法会返回一个与原数组大小相同,包含同类型元素且元素已正确排序的新数组。原数组不会被 `sorted(by:)` 方法修改。 -下面的闭包表达式示例使用`sort(_:)`方法对一个`String`类型的数组进行字母逆序排序.以下是初始数组值: +下面的闭包表达式示例使用 `sorted(by:)` 方法对一个 `String` 类型的数组进行字母逆序排序。以下是初始数组: ```swift let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"] ``` -`sort(_:)`方法接受一个闭包,该闭包函数需要传入与数组元素类型相同的两个值,并返回一个布尔类型值来表明当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值前面,排序闭包函数需要返回`true`,反之返回`false`。 +`sorted(by:)` 方法接受一个闭包,该闭包函数需要传入与数组元素类型相同的两个值,并返回一个布尔类型值来表明当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值*前面*,排序闭包函数需要返回`true`,反之返回`false`。 -该例子对一个`String`类型的数组进行排序,因此排序闭包函数类型需为`(String, String) -> Bool`。 +该例子对一个 `String` 类型的数组进行排序,因此排序闭包函数类型需为 `(String, String) -> Bool`。 -提供排序闭包函数的一种方式是撰写一个符合其类型要求的普通函数,并将其作为`sort(_:)`方法的参数传入: +提供排序闭包函数的一种方式是撰写一个符合其类型要求的普通函数,并将其作为 `sorted(by:)` 方法的参数传入: ```swift -func backwards(s1: String, s2: String) -> Bool { +func backward(_ s1: String, _ s2: String) -> Bool { return s1 > s2 } -var reversed = names.sort(backwards) -// reversed 为 ["Ewa", "Daniella", "Chris", "Barry", "Alex"] +var reversedNames = names.sorted(by: backward) +// reversedNames 为 ["Ewa", "Daniella", "Chris", "Barry", "Alex"] ``` -如果第一个字符串(`s1`)大于第二个字符串(`s2`),`backwards(_:_:)`函数返回`true`,表示在新的数组中`s1`应该出现在`s2`前。对于字符串中的字符来说,“大于”表示“按照字母顺序较晚出现”。这意味着字母`"B"`大于字母`"A"`,字符串`"Tom"`大于字符串`"Tim"`。该闭包将进行字母逆序排序,`"Barry"`将会排在`"Alex"`之前。 +如果第一个字符串(`s1`)大于第二个字符串(`s2`),`backward(_:_:)` 函数会返回 `true`,表示在新的数组中 `s1` 应该出现在 `s2` 前。对于字符串中的字符来说,“大于”表示“按照字母顺序较晚出现”。这意味着字母 `"B"` 大于字母 `"A"` ,字符串 `"Tom"` 大于字符串 `"Tim"`。该闭包将进行字母逆序排序,`"Barry"` 将会排在 `"Alex"` 之前。 -然而,这是一个相当冗长的方式,本质上只是写了一个单表达式函数 (`a > b`)。在下面的例子中,利用闭包表达式语法可以更好地构造一个内联排序闭包。 +然而,以这种方式来编写一个实际上很简单的表达式(`a > b`),确实太过繁琐了。对于这个例子来说,利用闭包表达式语法可以更好地构造一个内联排序闭包。 ### 闭包表达式语法(Closure Expression Syntax) -闭包表达式语法有如下一般形式: +闭包表达式语法有如下的一般形式: ```swift { (parameters) -> returnType in @@ -92,81 +95,80 @@ var reversed = names.sort(backwards) } ``` -闭包表达式语法可以使用常量、变量和`inout`类型作为参数,不能提供默认值。也可以在参数列表的最后使用可变参数。元组也可以作为参数和返回值。 +闭包表达式的参数可以是inout参数,但不能设定默认值。也可以使用具名的可变参数(译者注:但是如果可变参数不放在参数列表的最后一位的话,调用闭包的时时编译器将报错。可参考[这里](http://stackoverflow.com/questions/39548852/swift-3-0-closure-expression-what-if-the-variadic-parameters-not-at-the-last-pl))。元组也可以作为参数和返回值。 -下面的例子展示了之前`backwards(_:_:)`函数对应的闭包表达式版本的代码: +下面的例子展示了之前 `backward(_:_:)` 函数对应的闭包表达式版本的代码: ```swift -reversed = names.sort({ (s1: String, s2: String) -> Bool in +reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 }) ``` -需要注意的是内联闭包参数和返回值类型声明与`backwards(_:_:)`函数类型声明相同。在这两种方式中,都写成了`(s1: String, s2: String) -> Bool`。然而在内联闭包表达式中,函数和返回值类型都写在大括号内,而不是大括号外。 +需要注意的是内联闭包参数和返回值类型声明与 `backward(_:_:)` 函数类型声明相同。在这两种方式中,都写成了 `(s1: String, s2: String) -> Bool`。然而在内联闭包表达式中,函数和返回值类型都写在*大括号内*,而不是大括号外。 闭包的函数体部分由关键字`in`引入。该关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始。 由于这个闭包的函数体部分如此短,以至于可以将其改写成一行代码: ```swift -reversed = names.sort( { (s1: String, s2: String) -> Bool in return s1 > s2 } ) +reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 } ) ``` -该例中`sort(_:)`方法的整体调用保持不变,一对圆括号仍然包裹住了方法的整个参数。然而,参数现在变成了内联闭包。 +该例中 `sorted(by:)` 方法的整体调用保持不变,一对圆括号仍然包裹住了方法的整个参数。然而,参数现在变成了内联闭包。 ### 根据上下文推断类型(Inferring Type From Context) -因为排序闭包函数是作为`sort(_:)`方法的参数传入的,Swift 可以推断其参数和返回值的类型。`sort(_:)`方法被一个字符串数组调用,因此其参数必须是`(String, String) -> Bool`类型的函数。这意味着`(String, String)`和`Bool`类型并不需要作为闭包表达式定义的一部分。因为所有的类型都可以被正确推断,返回箭头(`->`)和围绕在参数周围的括号也可以被省略: +因为排序闭包函数是作为 `sorted(by:)` 方法的参数传入的,Swift 可以推断其参数和返回值的类型。`sorted(by:)` 方法被一个字符串数组调用,因此其参数必须是 `(String, String) -> Bool` 类型的函数。这意味着 `(String, String)` 和 `Bool` 类型并不需要作为闭包表达式定义的一部分。因为所有的类型都可以被正确推断,返回箭头(`->`)和围绕在参数周围的括号也可以被省略: ```swift -reversed = names.sort( { s1, s2 in return s1 > s2 } ) +reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } ) ``` -实际上任何情况下,通过内联闭包表达式构造的闭包作为参数传递给函数或方法时,都可以推断出闭包的参数和返回值类型。 -这意味着闭包作为函数或者方法的参数时,您几乎不需要利用完整格式构造内联闭包。 +实际上,通过内联闭包表达式构造的闭包作为参数传递给函数或方法时,总是能够推断出闭包的参数和返回值类型。这意味着闭包作为函数或者方法的参数时,你几乎不需要利用完整格式构造内联闭包。 -尽管如此,您仍然可以明确写出有着完整格式的闭包。如果完整格式的闭包能够提高代码的可读性,则可以采用完整格式的闭包。而在`sort(_:)`方法这个例子里,闭包的目的就是排序。由于这个闭包是为了处理字符串数组的排序,因此读者能够推测出这个闭包是用于字符串处理的。 +尽管如此,你仍然可以明确写出有着完整格式的闭包。如果完整格式的闭包能够提高代码的可读性,则我们更鼓励采用完整格式的闭包。而在 `sorted(by:)` 方法这个例子里,显然闭包的目的就是排序。由于这个闭包是为了处理字符串数组的排序,因此读者能够推测出这个闭包是用于字符串处理的。 -### 单表达式闭包隐式返回(Implicit Return From Single-Expression Clossures) +### 单表达式闭包隐式返回(Implicit Returns From Single-Expression Closures) -单行表达式闭包可以通过省略`return`关键字来隐式返回单行表达式的结果,如上版本的例子可以改写为: +单行表达式闭包可以通过省略 `return` 关键字来隐式返回单行表达式的结果,如上版本的例子可以改写为: ```swift -reversed = names.sort( { s1, s2 in s1 > s2 } ) +reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } ) ``` -在这个例子中,`sort(_:)`方法的参数类型明确了闭包必须返回一个`Bool`类型值。因为闭包函数体只包含了一个单一表达式(`s1 > s2`),该表达式返回`Bool`类型值,因此这里没有歧义,`return`关键字可以省略。 +在这个例子中,`sorted(by:)` 方法的参数类型明确了闭包必须返回一个 `Bool` 类型值。因为闭包函数体只包含了一个单一表达式(`s1 > s2`),该表达式返回 `Bool` 类型值,因此这里没有歧义,`return` 关键字可以省略。 ### 参数名称缩写(Shorthand Argument Names) -Swift 自动为内联闭包提供了参数名称缩写功能,您可以直接通过`$0`,`$1`,`$2`来顺序调用闭包的参数,以此类推。 +Swift 自动为内联闭包提供了参数名称缩写功能,你可以直接通过 `$0`,`$1`,`$2` 来顺序调用闭包的参数,以此类推。 -如果您在闭包表达式中使用参数名称缩写,您可以在闭包参数列表中省略对其的定义,并且对应参数名称缩写的类型会通过函数类型进行推断。`in`关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成: +如果你在闭包表达式中使用参数名称缩写,你可以在闭包定义中省略参数列表,并且对应参数名称缩写的类型会通过函数类型进行推断。`in`关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成: ```swift -reversed = names.sort( { $0 > $1 } ) +reversedNames = names.sorted(by: { $0 > $1 } ) ``` -在这个例子中,`$0`和`$1`表示闭包中第一个和第二个`String`类型的参数。 +在这个例子中,`$0`和`$1`表示闭包中第一个和第二个 `String` 类型的参数。 - -### 运算符函数(Operator Functions) + +### 运算符方法(Operator Methods) -实际上还有一种更简短的方式来撰写上面例子中的闭包表达式。Swift 的`String`类型定义了关于大于号(`>`)的字符串实现,其作为一个函数接受两个`String`类型的参数并返回`Bool`类型的值。而这正好与`sort(_:)`方法的参数需要的函数类型相符合。因此,您可以简单地传递一个大于号,Swift 可以自动推断出您想使用大于号的字符串函数实现: +实际上还有一种更简短的方式来编写上面例子中的闭包表达式。Swift 的 `String` 类型定义了关于大于号(`>`)的字符串实现,其作为一个函数接受两个 `String` 类型的参数并返回 `Bool` 类型的值。而这正好与 `sorted(by:)` 方法的参数需要的函数类型相符合。因此,你可以简单地传递一个大于号,Swift 可以自动推断出你想使用大于号的字符串函数实现: ```swift -reversed = names.sort(>) +reversedNames = names.sorted(by: >) ``` -更多关于运算符表达式的内容请查看[运算符函数](./25_Advanced_Operators.html#operator_functions)。 +更多关于运算符方法的内容请查看[运算符方法](./25_Advanced_Operators.html#operator_methods)。 ## 尾随闭包(Trailing Closures) -如果您需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用*尾随闭包*来增强函数的可读性。尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用: +如果你需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用*尾随闭包*来增强函数的可读性。尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。在使用尾随闭包时,你不用写出它的参数标签: ```swift func someFunctionThatTakesAClosure(closure: () -> Void) { @@ -174,7 +176,7 @@ func someFunctionThatTakesAClosure(closure: () -> Void) { } // 以下是不使用尾随闭包进行函数调用 -someFunctionThatTakesAClosure({ +someFunctionThatTakesAClosure(closure: { // 闭包主体部分 }) @@ -184,23 +186,23 @@ someFunctionThatTakesAClosure() { } ``` -在[闭包表达式语法](#closure_expression_syntax)一节中作为`sort(_:)`方法参数的字符串排序闭包可以改写为: +在[闭包表达式语法](#closure_expression_syntax)一节中作为 `sorted(by:)` 方法参数的字符串排序闭包可以改写为: ```swift -reversed = names.sort() { $0 > $1 } +reversedNames = names.sorted() { $0 > $1 } ``` -如果函数只需要闭包表达式一个参数,当您使用尾随闭包时,您甚至可以把`()`省略掉: +如果闭包表达式是函数或方法的唯一参数,则当你使用尾随闭包时,你甚至可以把 `()` 省略掉: ```swift -reversed = names.sort { $0 > $1 } +reversedNames = names.sorted { $0 > $1 } ``` -当闭包非常长以至于不能在一行中进行书写时,尾随闭包变得非常有用。举例来说,Swift 的`Array`类型有一个`map(_:)`方法,其获取一个闭包表达式作为其唯一参数。该闭包函数会为数组中的每一个元素调用一次,并返回该元素所映射的值。具体的映射方式和返回值类型由闭包来指定。 +当闭包非常长以至于不能在一行中进行书写时,尾随闭包变得非常有用。举例来说,Swift 的 `Array` 类型有一个 `map(_:)` 方法,这个方法获取一个闭包表达式作为其唯一参数。该闭包函数会为数组中的每一个元素调用一次,并返回该元素所映射的值。具体的映射方式和返回值类型由闭包来指定。 -当提供给数组的闭包应用于每个数组元素后,`map(_:)`方法将返回一个新的数组,数组中包含了与原数组中的元素一一对应的映射后的值。 +当提供给数组的闭包应用于每个数组元素后,`map(_:)` 方法将返回一个新的数组,数组中包含了与原数组中的元素一一对应的映射后的值。 -下例介绍了如何在`map(_:)`方法中使用尾随闭包将`Int`类型数组`[16, 58, 510]`转换为包含对应`String`类型的值的数组`["OneSix", "FiveEight", "FiveOneZero"]`: +下例介绍了如何在 `map(_:)` 方法中使用尾随闭包将 `Int` 类型数组 `[16, 58, 510]` 转换为包含对应 `String` 类型的值的数组`["OneSix", "FiveEight", "FiveOneZero"]`: ```swift let digitNames = [ @@ -210,92 +212,90 @@ let digitNames = [ let numbers = [16, 58, 510] ``` -如上代码创建了一个数字位和它们英文版本名字相映射的字典。同时还定义了一个准备转换为字符串数组的整型数组。 +如上代码创建了一个整型数位和它们英文版本名字相映射的字典。同时还定义了一个准备转换为字符串数组的整型数组。 -您现在可以通过传递一个尾随闭包给`numbers`的`map(_:)`方法来创建对应的字符串版本数组: +你现在可以通过传递一个尾随闭包给 `numbers` 数组的 `map(_:)` 方法来创建对应的字符串版本数组: ```swift let strings = numbers.map { (number) -> String in var number = number var output = "" - while number > 0 { + repeat { output = digitNames[number % 10]! + output number /= 10 - } + } while number > 0 return output } // strings 常量被推断为字符串类型数组,即 [String] // 其值为 ["OneSix", "FiveEight", "FiveOneZero"] ``` -`map(_:)`为数组中每一个元素调用了闭包表达式。您不需要指定闭包的输入参数`number`的类型,因为可以通过要映射的数组类型进行推断。 +`map(_:)` 为数组中每一个元素调用了一次闭包表达式。你不需要指定闭包的输入参数 `number` 的类型,因为可以通过要映射的数组类型进行推断。 -在该例中,局部变量`number`的值由闭包中的`number`参数获得,因此可以在闭包函数体内对其进行修改,(闭包或者函数的参数总是常量),闭包表达式指定了返回类型为`String`,以表明存储映射值的新数组类型为`String`。 +在该例中,局部变量 `number` 的值由闭包中的 `number` 参数获得,因此可以在闭包函数体内对其进行修改,(闭包或者函数的参数总是常量),闭包表达式指定了返回类型为 `String`,以表明存储映射值的新数组类型为 `String`。 -闭包表达式在每次被调用的时候创建了一个叫做`output`的字符串并返回。其使用求余运算符(`number % 10`)计算最后一位数字并利用`digitNames`字典获取所映射的字符串。 +闭包表达式在每次被调用的时候创建了一个叫做 `output` 的字符串并返回。其使用求余运算符(`number % 10`)计算最后一位数字并利用 `digitNames` 字典获取所映射的字符串。这个闭包能够用于创建任意正整数的字符串表示。 -> 注意 -> 字典`digitNames`下标后跟着一个叹号(`!`),因为字典下标返回一个可选值(optional value),表明该键不存在时会查找失败。在上例中,由于可以确定`number % 10`总是`digitNames`字典的有效下标,因此叹号可以用于强制解包 (force-unwrap) 存储在下标的可选类型的返回值中的`String`类型的值。 +> 注意 +> 字典 `digitNames` 下标后跟着一个叹号(`!`),因为字典下标返回一个可选值(optional value),表明该键不存在时会查找失败。在上例中,由于可以确定 `number % 10` 总是 `digitNames` 字典的有效下标,因此叹号可以用于强制解包 (force-unwrap) 存储在下标的可选类型的返回值中的`String`类型的值。 -从`digitNames`字典中获取的字符串被添加到`output`的前部,逆序建立了一个字符串版本的数字。(在表达式`number % 10`中,如果`number`为`16`,则返回`6`,`58`返回`8`,`510`返回`0`。) +从 `digitNames` 字典中获取的字符串被添加到 `output` 的*前部*,逆序建立了一个字符串版本的数字。(在表达式 `number % 10` 中,如果 `number` 为 `16`,则返回 `6`,`58` 返回 `8`,`510` 返回 `0`。) -`number`变量之后除以`10`。因为其是整数,在计算过程中未除尽部分被忽略。因此`16`变成了`1`,`58`变成了`5`,`510`变成了`51`。 +`number` 变量之后除以 `10`。因为其是整数,在计算过程中未除尽部分被忽略。因此 `16` 变成了 `1`,`58` 变成了 `5`,`510` 变成了 `51`。 -整个过程重复进行,直到`number /= 10`为`0`,这时闭包会将字符串`output`返回,而`map(_:)`方法则会将字符串添加到所映射的数组中。 +整个过程重复进行,直到 `number /= 10` 为 `0`,这时闭包会将字符串 `output` 返回,而 `map(_:)` 方法则会将字符串添加到映射数组中。 -在上面的例子中,通过尾随闭包语法,优雅地在函数后封装了闭包的具体功能,而不再需要将整个闭包包裹在`map(_:)`方法的括号内。 +在上面的例子中,通过尾随闭包语法,优雅地在函数后封装了闭包的具体功能,而不再需要将整个闭包包裹在 `map(_:)` 方法的括号内。 -## 捕获值(Capturing Values) +## 值捕获(Capturing Values) 闭包可以在其被定义的上下文中*捕获*常量或变量。即使定义这些常量和变量的原作用域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。 Swift 中,可以捕获值的闭包的最简单形式是嵌套函数,也就是定义在其他函数的函数体内的函数。嵌套函数可以捕获其外部函数所有的参数以及定义的常量和变量。 -举个例子,这有一个叫做`makeIncrementor`的函数,其包含了一个叫做`incrementor`的嵌套函数。嵌套函数`incrementor()`从上下文中捕获了两个值,`runningTotal`和`amount`。捕获这些值之后,`makeIncrementor`将`incrementor`作为闭包返回。每次调用`incrementor`时,其会以`amount`作为增量增加`runningTotal`的值。 +举个例子,这有一个叫做 `makeIncrementor` 的函数,其包含了一个叫做 `incrementor` 的嵌套函数。嵌套函数 `incrementor()` 从上下文中捕获了两个值,`runningTotal` 和 `amount`。捕获这些值之后,`makeIncrementor` 将 `incrementor` 作为闭包返回。每次调用 `incrementor` 时,其会以 `amount` 作为增量增加 `runningTotal` 的值。 ```swift -func makeIncrementor(forIncrement amount: Int) -> () -> Int { +func makeIncrementer(forIncrement amount: Int) -> () -> Int { var runningTotal = 0 - func incrementor() -> Int { + func incrementer() -> Int { runningTotal += amount return runningTotal } - return incrementor + return incrementer } ``` -`makeIncrementor`返回类型为`() -> Int`。这意味着其返回的是一个函数,而不是一个简单类型的值。该函数在每次调用时不接受参数,只返回一个`Int`类型的值。关于函数返回其他函数的内容,请查看[函数类型作为返回类型](./06_Functions.html#function_types_as_return_types)。 +`makeIncrementor` 返回类型为 `() -> Int`。这意味着其返回的是一个*函数*,而非一个简单类型的值。该函数在每次调用时不接受参数,只返回一个 `Int` 类型的值。关于函数返回其他函数的内容,请查看[函数类型作为返回类型](./06_Functions.html#function_types_as_return_types)。 -`makeIncrementer(forIncrement:)`函数定义了一个初始值为`0`的整型变量`runningTotal`,用来存储当前跑步总数。该值通过`incrementor`返回。 +`makeIncrementer(forIncrement:)` 函数定义了一个初始值为 `0` 的整型变量 `runningTotal`,用来存储当前总计数值。该值为 `incrementor` 的返回值。 -`makeIncrementer(forIncrement:)`有一个`Int`类型的参数,其外部参数名为`forIncrement`,内部参数名为`amount`,该参数表示每次`incrementor`被调用时`runningTotal`将要增加的量。 +`makeIncrementer(forIncrement:)` 有一个 `Int` 类型的参数,其外部参数名为 `forIncrement`,内部参数名为 `amount`,该参数表示每次 `incrementor` 被调用时 `runningTotal` 将要增加的量。`makeIncrementer` 函数还定义了一个嵌套函数 `incrementor`,用来执行实际的增加操作。该函数简单地使 `runningTotal` 增加 `amount`,并将其返回。 -嵌套函数`incrementor`用来执行实际的增加操作。该函数简单地使`runningTotal`增加`amount`,并将其返回。 - -如果我们单独看这个函数,会发现看上去不同寻常: +如果我们单独考虑嵌套函数 `incrementer()`,会发现它有些不同寻常: ```swift -func incrementor() -> Int { +func incrementer() -> Int { runningTotal += amount return runningTotal } ``` -`incrementer()`函数并没有任何参数,但是在函数体内访问了`runningTotal`和`amount`变量。这是因为它从外围函数捕获了`runningTotal`和`amount`变量的引用。捕获引用保证了`runningTotal`和`amount`变量在调用完`makeIncrementer`后不会消失,并且保证了在下一次执行`incrementer`函数时,`runningTotal`依旧存在。 +`incrementer()` 函数并没有任何参数,但是在函数体内访问了 `runningTotal` 和 `amount` 变量。这是因为它从外围函数捕获了 `runningTotal` 和 `amount` 变量的*引用*。捕获引用保证了 `runningTotal` 和 `amount` 变量在调用完 `makeIncrementer` 后不会消失,并且保证了在下一次执行 `incrementer` 函数时,`runningTotal` 依旧存在。 -> 注意 -> 为了优化,如果一个值是不可变的,Swift 可能会改为捕获并保存一份对值的拷贝。 +> 注意 +> 为了优化,如果一个值不会被闭包改变,或者在闭包创建后不会改变,Swift 可能会改为捕获并保存一份对值的拷贝。 > Swift 也会负责被捕获变量的所有内存管理工作,包括释放不再需要的变量。 -下面是一个使用`makeIncrementor`的例子: +下面是一个使用 `makeIncrementor` 的例子: ```swift let incrementByTen = makeIncrementor(forIncrement: 10) ``` -该例子定义了一个叫做`incrementByTen`的常量,该常量指向一个每次调用会将`runningTotal`变量增加`10`的`incrementor`函数。调用这个函数多次可以得到以下结果: +该例子定义了一个叫做 `incrementByTen` 的常量,该常量指向一个每次调用会将其 `runningTotal` 变量增加 `10` 的 `incrementor` 函数。调用这个函数多次可以得到以下结果: ```swift incrementByTen() @@ -306,7 +306,7 @@ incrementByTen() // 返回的值为30 ``` -如果您创建了另一个`incrementor`,它会有属于它自己的一个全新、独立的`runningTotal`变量的引用: +如果你创建了另一个 `incrementor`,它会有属于自己的引用,指向一个全新、独立的 `runningTotal` 变量: ```swift let incrementBySeven = makeIncrementor(forIncrement: 7) @@ -314,7 +314,7 @@ incrementBySeven() // 返回的值为7 ``` -再次调用原来的`incrementByTen`会在原来的变量`runningTotal`上继续增加值,该变量和`incrementBySeven`中捕获的变量没有任何联系: +再次调用原来的 `incrementByTen` 会继续增加它自己的 `runningTotal` 变量,该变量和 `incrementBySeven` 中捕获的变量没有任何联系: ```swift incrementByTen() @@ -322,16 +322,16 @@ incrementByTen() ``` > 注意 -> 如果您将闭包赋值给一个类实例的属性,并且该闭包通过访问该实例或其成员而捕获了该实例,您将创建一个在闭包和该实例间的循环强引用。Swift 使用捕获列表来打破这种循环强引用。更多信息,请参考[闭包引起的循环强引用](./16_Automatic_Reference_Counting.html#strong_reference_cycles_for_closures)。 +> 如果你将闭包赋值给一个类实例的属性,并且该闭包通过访问该实例或其成员而捕获了该实例,你将在闭包和该实例间创建一个循环强引用。Swift 使用捕获列表来打破这种循环强引用。更多信息,请参考[闭包引起的循环强引用](./16_Automatic_Reference_Counting.html#strong_reference_cycles_for_closures)。 ## 闭包是引用类型(Closures Are Reference Types) -上面的例子中,`incrementBySeven`和`incrementByTen`是常量,但是这些常量指向的闭包仍然可以增加其捕获的变量的值。这是因为函数和闭包都是*引用类型*。 +上面的例子中,`incrementBySeven` 和 `incrementByTen` 都是常量,但是这些常量指向的闭包仍然可以增加其捕获的变量的值。这是因为函数和闭包都是*引用类型*。 -无论您将函数或闭包赋值给一个常量还是变量,您实际上都是将常量或变量的值设置为对应函数或闭包的引用。上面的例子中,指向闭包的引用`incrementByTen`是一个常量,而并非闭包内容本身。 +无论你将函数或闭包赋值给一个常量还是变量,你实际上都是将常量或变量的值设置为对应函数或闭包的*引用*。上面的例子中,指向闭包的引用 `incrementByTen` 是一个常量,而并非闭包内容本身。 -这也意味着如果您将闭包赋值给了两个不同的常量或变量,两个值都会指向同一个闭包: +这也意味着如果你将闭包赋值给了两个不同的常量或变量,两个值都会指向同一个闭包: ```swift let alsoIncrementByTen = incrementByTen @@ -339,121 +339,118 @@ alsoIncrementByTen() // 返回的值为50 ``` - -## 非逃逸闭包(Nonescaping Closures) + +## 逃逸闭包(Escaping Closures) -当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中*逃逸*。当你定义接受闭包作为参数的函数时,你可以在参数名之前标注`@noescape`,用来指明这个闭包是不允许“逃逸”出这个函数的。将闭包标注`@noescape`能使编译器知道这个闭包的生命周期(译者注:闭包只能在函数体中被执行,不能脱离函数体执行,所以编译器明确知道运行时的上下文),从而可以进行一些比较激进的优化。 +当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中*逃逸*。当你定义接受闭包作为参数的函数时,你可以在参数名之前标注 `@escaping`,用来指明这个闭包是允许“逃逸”出这个函数的。 -```swift -func someFunctionWithNoescapeClosure(@noescape closure: () -> Void) { - closure() -} -``` +一种能使闭包“逃逸”出函数的方法是,将这个闭包保存在一个函数外部定义的变量中。举个例子,很多启动异步操作的函数接受一个闭包参数作为 completion handler。这类函数会在异步操作开始之后立刻返回,但是闭包直到异步操作结束后才会被调用。在这种情况下,闭包需要“逃逸”出函数,因为闭包需要在函数返回之后被调用。例如: -举个例子,`sort(_:)`方法接受一个用来进行元素比较的闭包作为参数。这个参数被标注了`@noescape`,因为它确保自己在排序结束之后就没用了。 - -一种能使闭包“逃逸”出函数的方法是,将这个闭包保存在一个函数外部定义的变量中。举个例子,很多启动异步操作的函数接受一个闭包参数作为 completion handler。这类函数会在异步操作开始之后立刻返回,但是闭包直到异步操作结束后才会被调用。在这种情况下,闭包需要“逃逸”出函数,因为闭包需要在函数返回之后被调用。例如: - ```swift var completionHandlers: [() -> Void] = [] -func someFunctionWithEscapingClosure(completionHandler: () -> Void) { +func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) { completionHandlers.append(completionHandler) } ``` -`someFunctionWithEscapingClosure(_:)`函数接受一个闭包作为参数,该闭包被添加到一个函数外定义的数组中。如果你试图将这个参数标注为`@noescape`,你将会获得一个编译错误。 +`someFunctionWithEscapingClosure(_:)` 函数接受一个闭包作为参数,该闭包被添加到一个函数外定义的数组中。如果你不将这个参数标记为 `@escaping`,就会得到一个编译错误。 + +将一个闭包标记为 `@escaping` 意味着你必须在闭包中显式地引用 `self`。比如说,在下面的代码中,传递到 `someFunctionWithEscapingClosure(_:)` 中的闭包是一个逃逸闭包,这意味着它需要显式地引用 `self`。相对的,传递到 `someFunctionWithNonescapingClosure(_:)` 中的闭包是一个非逃逸闭包,这意味着它可以隐式引用 `self`。 -将闭包标注为`@noescape`使你能在闭包中隐式地引用`self`。 ```swift +func someFunctionWithNonescapingClosure(closure: () -> Void) { + closure() +} + class SomeClass { var x = 10 func doSomething() { someFunctionWithEscapingClosure { self.x = 100 } - someFunctionWithNoescapeClosure { x = 200 } + someFunctionWithNonescapingClosure { x = 200 } } } - + let instance = SomeClass() instance.doSomething() print(instance.x) -// prints "200" - +// 打印出 "200" + completionHandlers.first?() print(instance.x) -// prints "100" +// 打印出 "100" ``` ## 自动闭包(Autoclosures) -*自动闭包*是一种自动创建的闭包,用于包装传递给函数作为参数的表达式。这种闭包不接受任何参数,当它被调用的时候,会返回被包装在其中的表达式的值。这种便利语法让你能够用一个普通的表达式来代替显式的闭包,从而省略闭包的花括号。 +*自动闭包*是一种自动创建的闭包,用于包装传递给函数作为参数的表达式。这种闭包不接受任何参数,当它被调用的时候,会返回被包装在其中的表达式的值。这种便利语法让你能够省略闭包的花括号,用一个普通的表达式来代替显式的闭包。 -我们经常会调用一个接受闭包作为参数的函数,但是很少实现那样的函数。举个例子来说,`assert(condition:message:file:line:)`函数接受闭包作为它的`condition`参数和`message`参数;它的`condition`参数仅会在 debug 模式下被求值,它的`message`参数仅当`condition`参数为`false`时被计算求值。 +我们经常会*调用*采用自动闭包的函数,但是很少去*实现*这样的函数。举个例子来说,`assert(condition:message:file:line:)` 函数接受自动闭包作为它的 `condition` 参数和 `message` 参数;它的 `condition` 参数仅会在 debug 模式下被求值,它的 `message` 参数仅当 `condition` 参数为 `false` 时被计算求值。 -自动闭包让你能够延迟求值,因为代码段不会被执行直到你调用这个闭包。延迟求值对于那些有副作用(Side Effect)和代价昂贵的代码来说是很有益处的,因为你能控制代码什么时候执行。下面的代码展示了闭包如何延时求值。 +自动闭包让你能够延迟求值,因为直到你调用这个闭包,代码段才会被执行。延迟求值对于那些有副作用(Side Effect)和高计算成本的代码来说是很有益处的,因为它使得你能控制代码的执行时机。下面的代码展示了闭包如何延时求值。 ```swift var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"] print(customersInLine.count) -// prints "5" - -let customerProvider = { customersInLine.removeAtIndex(0) } +// 打印出 "5" + +let customerProvider = { customersInLine.remove(at: 0) } print(customersInLine.count) -// prints "5" - +// 打印出 "5" + print("Now serving \(customerProvider())!") -// prints "Now serving Chris!" +// Prints "Now serving Chris!" print(customersInLine.count) -// prints "4" +// 打印出 "4" ``` -尽管在闭包的代码中,`customersInLine`的第一个元素被移除了,不过在闭包被调用之前,这个元素是不会被移除的。如果这个闭包永远不被调用,那么在闭包里面的表达式将永远不会执行,那意味着列表中的元素永远不会被移除。请注意,`customerProvider`的类型不是`String`,而是`() -> String`,一个没有参数且返回值为`String`的函数。 +尽管在闭包的代码中,`customersInLine` 的第一个元素被移除了,不过在闭包被调用之前,这个元素是不会被移除的。如果这个闭包永远不被调用,那么在闭包里面的表达式将永远不会执行,那意味着列表中的元素永远不会被移除。请注意,`customerProvider` 的类型不是 `String`,而是 `() -> String`,一个没有参数且返回值为 `String` 的函数。 -将闭包作为参数传递给函数时,你能获得同样的延时求值行为。 +将闭包作为参数传递给函数时,你能获得同样的延时求值行为。 ```swift // customersInLine is ["Alex", "Ewa", "Barry", "Daniella"] -func serveCustomer(customerProvider: () -> String) { +func serve(customer customerProvider: () -> String) { print("Now serving \(customerProvider())!") } -serveCustomer( { customersInLine.removeAtIndex(0) } ) -// prints "Now serving Alex!" +serve(customer: { customersInLine.remove(at: 0) } ) +// 打印出 "Now serving Alex!" ``` -`serveCustomer(_:)`接受一个返回顾客名字的显式的闭包。下面这个版本的`serveCustomer(_:)`完成了相同的操作,不过它并没有接受一个显式的闭包,而是通过将参数标记为`@autoclosure`来接收一个自动闭包。现在你可以将该函数当做接受`String`类型参数的函数来调用。`customerProvider`参数将自动转化为一个闭包,因为该参数被标记了`@autoclosure`特性。 +上面的 `serve(customer:)` 函数接受一个返回顾客名字的显式的闭包。下面这个版本的 `serve(customer:)` 完成了相同的操作,不过它并没有接受一个显式的闭包,而是通过将参数标记为 `@autoclosure` 来接收一个自动闭包。现在你可以将该函数当作接受 `String` 类型参数(而非闭包)的函数来调用。`customerProvider` 参数将自动转化为一个闭包,因为该参数被标记了 `@autoclosure` 特性。 ```swift // customersInLine is ["Ewa", "Barry", "Daniella"] -func serveCustomer(@autoclosure customerProvider: () -> String) { +func serve(customer customerProvider: @autoclosure () -> String) { print("Now serving \(customerProvider())!") } -serveCustomer(customersInLine.removeAtIndex(0)) -// prints "Now serving Ewa!" +serve(customer: customersInLine.remove(at: 0)) +// 打印出 "Now serving Ewa!" ``` -> 注意 -> 过度使用`autoclosures`会让你的代码变得难以理解。上下文和函数名应该能够清晰地表明求值是被延迟执行的。 +> 注意 +> 过度使用 `autoclosures` 会让你的代码变得难以理解。上下文和函数名应该能够清晰地表明求值是被延迟执行的。 -`@autoclosure`特性暗含了`@noescape`特性,这个特性在[非逃逸闭包](#nonescaping_closures)一节中有描述。如果你想让这个闭包可以“逃逸”,则应该使用`@autoclosure(escaping)`特性. +如果你想让一个自动闭包可以“逃逸”,则应该同时使用 `@autoclosure` 和 `@escaping` 属性。`@escaping` 属性的讲解见上面的[逃逸闭包](#escaping_closures)。 ```swift // customersInLine is ["Barry", "Daniella"] var customerProviders: [() -> String] = [] -func collectCustomerProviders(@autoclosure(escaping) customerProvider: () -> String) { +func collectCustomerProviders(_ customerProvider: @autoclosure @escaping () -> String) { customerProviders.append(customerProvider) } -collectCustomerProviders(customersInLine.removeAtIndex(0)) -collectCustomerProviders(customersInLine.removeAtIndex(0)) - +collectCustomerProviders(customersInLine.remove(at: 0)) +collectCustomerProviders(customersInLine.remove(at: 0)) + print("Collected \(customerProviders.count) closures.") -// prints "Collected 2 closures." +// 打印出 "Collected 2 closures." for customerProvider in customerProviders { print("Now serving \(customerProvider())!") } -// prints "Now serving Barry!" -// prints "Now serving Daniella!" +// 打印出 "Now serving Barry!" +// 打印出 "Now serving Daniella!" ``` -在上面的代码中,`collectCustomerProviders(_:)`函数并没有调用传入的`customerProvider`闭包,而是将闭包追加到了`customerProviders`数组中。这个数组定义在函数作用域范围外,这意味着数组内的闭包将会在函数返回之后被调用。因此,`customerProvider`参数必须允许“逃逸”出函数作用域。 +在上面的代码中,`collectCustomerProviders(_:)` 函数并没有调用传入的 `customerProvider` 闭包,而是将闭包追加到了 `customerProviders` 数组中。这个数组定义在函数作用域范围外,这意味着数组内的闭包能够在函数返回之后被调用。因此,`customerProvider` 参数必须允许“逃逸”出函数作用域。 diff --git a/source/chapter2/08_Enumerations.md b/source/chapter2/08_Enumerations.md index 7943563e..13df5cc1 100755 --- a/source/chapter2/08_Enumerations.md +++ b/source/chapter2/08_Enumerations.md @@ -10,11 +10,15 @@ > 2.1 > 翻译:[Channe](https://github.com/Channe) -> 校对:[shanks](http://codebuild.me), +> 校对:[shanks](http://codebuild.me) > 2.2 > 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-13 + +> 3.0 +> 翻译+校对:[shanks](https://codebuild.me) 2016-09-24 + 本页内容包含: - [枚举语法(Enumeration Syntax)](#enumeration_syntax) @@ -27,11 +31,11 @@ 如果你熟悉 C 语言,你会知道在 C 语言中,枚举会为一组整型值分配相关联的名称。Swift 中的枚举更加灵活,不必给每一个枚举成员提供一个值。如果给枚举成员提供一个值(称为“原始”值),则该值的类型可以是字符串,字符,或是一个整型值或浮点数。 -此外,枚举成员可以指定任意类型的关联值存储到枚举成员中,就像其他语言中的联合体(unions)和变体(variants)。每一个枚举成员都可以有适当类型的关联值。 +此外,枚举成员可以指定*任意*类型的关联值存储到枚举成员中,就像其他语言中的联合体(unions)和变体(variants)。你可以在一个枚举中定义一组相关的枚举成员,每一个枚举成员都可以有适当类型的关联值。 -在 Swift 中,枚举类型是一等(first-class)类型。它们采用了很多在传统上只被类(class)所支持的特性,例如计算型属性(computed properties),用于提供枚举值的附加信息,实例方法(instance methods),用于提供和枚举值相关联的功能。枚举也可以定义构造函数(initializers)来提供一个初始值;可以在原始实现的基础上扩展它们的功能;还可以遵守协议(protocols)来提供标准的功能。 +在 Swift 中,枚举类型是一等(first-class)类型。它们采用了很多在传统上只被类(class)所支持的特性,例如计算属性(computed properties),用于提供枚举值的附加信息,实例方法(instance methods),用于提供和枚举值相关联的功能。枚举也可以定义构造函数(initializers)来提供一个初始值;可以在原始实现的基础上扩展它们的功能;还可以遵循协议(protocols)来提供标准的功能。 -欲了解更多相关信息,请参见[属性(Properties)](./10_Properties.html),[方法(Methods)](./11_Methods.html),[构造过程(Initialization)](./14_Initialization.html),[扩展(Extensions)](./21_Extensions.html)和[协议(Protocols)](./22_Protocols.html)。 +想了解更多相关信息,请参见[属性(Properties)](./10_Properties.html),[方法(Methods)](./11_Methods.html),[构造过程(Initialization)](./14_Initialization.html),[扩展(Extensions)](./21_Extensions.html)和[协议(Protocols)](./22_Protocols.html)。 ## 枚举语法 @@ -48,36 +52,36 @@ enum SomeEnumeration { ```swift enum CompassPoint { - case North - case South - case East - case West + case north + case south + case east + case west } ``` -枚举中定义的值(如 `North`,`South`,`East`和`West`)是这个枚举的*成员值*(或*成员*)。你使用`case`关键字来定义一个新的枚举成员值。 +枚举中定义的值(如 `north `,`south`,`east`和`west`)是这个枚举的*成员值*(或*成员*)。你可以使用`case`关键字来定义一个新的枚举成员值。 > 注意 -> 与 C 和 Objective-C 不同,Swift 的枚举成员在被创建时不会被赋予一个默认的整型值。在上面的`CompassPoint`例子中,`North`,`South`,`East`和`West`不会被隐式地赋值为`0`,`1`,`2`和`3`。相反,这些枚举成员本身就是完备的值,这些值的类型是已经明确定义好的`CompassPoint`类型。 +> 与 C 和 Objective-C 不同,Swift 的枚举成员在被创建时不会被赋予一个默认的整型值。在上面的`CompassPoint`例子中,`north`,`south`,`east`和`west`不会被隐式地赋值为`0`,`1`,`2`和`3`。相反,这些枚举成员本身就是完备的值,这些值的类型是已经明确定义好的`CompassPoint`类型。 多个成员值可以出现在同一行上,用逗号隔开: ```swift enum Planet { - case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune + case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune } ``` 每个枚举定义了一个全新的类型。像 Swift 中其他类型一样,它们的名字(例如`CompassPoint`和`Planet`)应该以一个大写字母开头。给枚举类型起一个单数名字而不是复数名字,以便于读起来更加容易理解: ```swift -var directionToHead = CompassPoint.West +var directionToHead = CompassPoint.west ``` `directionToHead`的类型可以在它被`CompassPoint`的某个值初始化时推断出来。一旦`directionToHead`被声明为`CompassPoint`类型,你可以使用更简短的点语法将其设置为另一个`CompassPoint`的值: ```swift -directionToHead = .East +directionToHead = .east ``` 当`directionToHead`的类型已知时,再次为其赋值可以省略枚举类型名。在使用具有显式类型的枚举值时,这种写法让代码具有更好的可读性。 @@ -88,15 +92,15 @@ directionToHead = .East 你可以使用`switch`语句匹配单个枚举值: ```swift -directionToHead = .South +directionToHead = .south switch directionToHead { - case .North: + case .north: print("Lots of planets have a north") - case .South: + case .south: print("Watch out for penguins") - case .East: + case .east: print("Where the sun rises") - case .West: + case .west: print("Where the skies are blue") } // 输出 "Watch out for penguins” @@ -104,18 +108,18 @@ switch directionToHead { 你可以这样理解这段代码: -“判断`directionToHead`的值。当它等于`.North`,打印`“Lots of planets have a north”`。当它等于`.South`,打印`“Watch out for penguins”`。” +“判断`directionToHead`的值。当它等于`.north`,打印`“Lots of planets have a north”`。当它等于`.south`,打印`“Watch out for penguins”`。” ……以此类推。 -正如在[控制流(Control Flow)](./05_Control_Flow.html)中介绍的那样,在判断一个枚举类型的值时,`switch`语句必须穷举所有情况。如果忽略了`.West`这种情况,上面那段代码将无法通过编译,因为它没有考虑到`CompassPoint`的全部成员。强制穷举确保了枚举成员不会被意外遗漏。 +正如在[控制流(Control Flow)](./05_Control_Flow.html)中介绍的那样,在判断一个枚举类型的值时,`switch`语句必须穷举所有情况。如果忽略了`.west`这种情况,上面那段代码将无法通过编译,因为它没有考虑到`CompassPoint`的全部成员。强制穷举确保了枚举成员不会被意外遗漏。 当不需要匹配每个枚举成员的时候,你可以提供一个`default`分支来涵盖所有未明确处理的枚举成员: ```swift -let somePlanet = Planet.Earth +let somePlanet = Planet.earth switch somePlanet { -case .Earth: +case .earth: print("Mostly harmless") default: print("Not a safe place for humans") @@ -126,7 +130,7 @@ default: ## 关联值(Associated Values) -上一小节的例子演示了如何定义和分类枚举的成员。你可以为`Planet.Earth`设置一个常量或者变量,并在赋值之后查看这个值。然而,有时候能够把其他类型的*关联值*和成员值一起存储起来会很有用。这能让你连同成员值一起存储额外的自定义信息,并且你每次在代码中使用该枚举成员时,还可以修改这个关联值。 +上一小节的例子演示了如何定义和分类枚举的成员。你可以为`Planet.earth`设置一个常量或者变量,并在赋值之后查看这个值。然而,有时候能够把其他类型的*关联值*和成员值一起存储起来会很有用。这能让你连同成员值一起存储额外的自定义信息,并且你每次在代码中使用该枚举成员时,还可以修改这个关联值。 你可以定义 Swift 枚举来存储任意类型的关联值,如果需要的话,每个枚举成员的关联值类型可以各不相同。枚举的这种特性跟其他语言中的可识别联合(discriminated unions),标签联合(tagged unions),或者变体(variants)相似。 @@ -138,46 +142,46 @@ default: -这便于库存跟踪系统用包含四个整型值的元组存储 UPC-A 码,以及用任意长度的字符串储存 QR 码。 +这便于库存跟踪系统用包含四个整型值的元组存储 UPC 码,以及用任意长度的字符串储存 QR 码。 在 Swift 中,使用如下方式定义表示两种商品条形码的枚举: ```swift enum Barcode { - case UPCA(Int, Int, Int, Int) - case QRCode(String) + case upc(Int, Int, Int, Int) + case qrCode(String) } ``` 以上代码可以这么理解: -“定义一个名为`Barcode`的枚举类型,它的一个成员值是具有`(Int,Int,Int,Int)`类型关联值的`UPCA`,另一个成员值是具有`String`类型关联值的`QRCode`。” +“定义一个名为`Barcode`的枚举类型,它的一个成员值是具有`(Int,Int,Int,Int)`类型关联值的`upc`,另一个成员值是具有`String`类型关联值的`qrCode`。” -这个定义不提供任何`Int`或`String`类型的关联值,它只是定义了,当`Barcode`常量和变量等于`Barcode.UPCA`或`Barcode.QRCode`时,可以存储的关联值的类型。 +这个定义不提供任何`Int`或`String`类型的关联值,它只是定义了,当`Barcode`常量和变量等于`Barcode.upc`或`Barcode.qrCode`时,可以存储的关联值的类型。 然后可以使用任意一种条形码类型创建新的条形码,例如: ```swift -var productBarcode = Barcode.UPCA(8, 85909, 51226, 3) +var productBarcode = Barcode.upc(8, 85909, 51226, 3) ``` -上面的例子创建了一个名为`productBarcode`的变量,并将`Barcode.UPCA`赋值给它,关联的元组值为`(8, 85909, 51226, 3)`。 +上面的例子创建了一个名为`productBarcode`的变量,并将`Barcode.upc`赋值给它,关联的元组值为`(8, 85909, 51226, 3)`。 同一个商品可以被分配一个不同类型的条形码,例如: ```swift -productBarcode = .QRCode("ABCDEFGHIJKLMNOP") +productBarcode = .qrCode("ABCDEFGHIJKLMNOP") ``` -这时,原始的`Barcode.UPCA`和其整数关联值被新的`Barcode.QRCode`和其字符串关联值所替代。`Barcode`类型的常量和变量可以存储一个`.UPCA`或者一个`.QRCode`(连同它们的关联值),但是在同一时间只能存储这两个值中的一个。 +这时,原始的`Barcode.upc`和其整数关联值被新的`Barcode.qrCode`和其字符串关联值所替代。`Barcode`类型的常量和变量可以存储一个`.upc`或者一个`.qrCode`(连同它们的关联值),但是在同一时间只能存储这两个值中的一个。 像先前那样,可以使用一个 switch 语句来检查不同的条形码类型。然而,这一次,关联值可以被提取出来作为 switch 语句的一部分。你可以在`switch`的 case 分支代码中提取每个关联值作为一个常量(用`let`前缀)或者作为一个变量(用`var`前缀)来使用: ```swift switch productBarcode { -case .UPCA(let numberSystem, let manufacturer, let product, let check): - print("UPC-A: \(numberSystem), \(manufacturer), \(product), \(check).") -case .QRCode(let productCode): +case .upc(let numberSystem, let manufacturer, let product, let check): + print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).") +case .qrCode(let productCode): print("QR code: \(productCode).") } // 输出 "QR code: ABCDEFGHIJKLMNOP." @@ -187,9 +191,9 @@ case .QRCode(let productCode): ```swift switch productBarcode { -case let .UPCA(numberSystem, manufacturer, product, check): - print("UPC-A: \(numberSystem), \(manufacturer), \(product), \(check).") -case let .QRCode(productCode): +case let .upc(numberSystem, manufacturer, product, check): + print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).") +case let .qrCode(productCode): print("QR code: \(productCode).") } // 输出 "QR code: ABCDEFGHIJKLMNOP." @@ -204,9 +208,9 @@ case let .QRCode(productCode): ```swift enum ASCIIControlCharacter: Character { - case Tab = "\t" - case LineFeed = "\n" - case CarriageReturn = "\r" + case tab = "\t" + case lineFeed = "\n" + case carriageReturn = "\r" } ``` @@ -229,11 +233,11 @@ enum ASCIIControlCharacter: Character { ```swift enum Planet: Int { - case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune + case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune } ``` -在上面的例子中,`Plant.Mercury`的显式原始值为`1`,`Planet.Venus`的隐式原始值为`2`,依次类推。 +在上面的例子中,`Plant.mercury`的显式原始值为`1`,`Planet.venus`的隐式原始值为`2`,依次类推。 当使用字符串作为枚举类型的原始值时,每个枚举成员的隐式原始值为该枚举成员的名称。 @@ -241,20 +245,20 @@ enum Planet: Int { ```swift enum CompassPoint: String { - case North, South, East, West + case north, south, east, west } ``` -上面例子中,`CompassPoint.South`拥有隐式原始值`South`,依次类推。 +上面例子中,`CompassPoint.south`拥有隐式原始值`south`,依次类推。 使用枚举成员的`rawValue`属性可以访问该枚举成员的原始值: ```swift -let earthsOrder = Planet.Earth.rawValue +let earthsOrder = Planet.earth.rawValue // earthsOrder 值为 3 -let sunsetDirection = CompassPoint.West.rawValue -// sunsetDirection 值为 "West" +let sunsetDirection = CompassPoint.west.rawValue +// sunsetDirection 值为 "west" ``` @@ -262,11 +266,11 @@ let sunsetDirection = CompassPoint.West.rawValue 如果在定义枚举类型的时候使用了原始值,那么将会自动获得一个初始化方法,这个方法接收一个叫做`rawValue`的参数,参数类型即为原始值类型,返回值则是枚举成员或`nil`。你可以使用这个初始化方法来创建一个新的枚举实例。 -这个例子利用原始值`7`创建了枚举成员`Uranus`: +这个例子利用原始值`7`创建了枚举成员`uranus`: ```swift let possiblePlanet = Planet(rawValue: 7) -// possiblePlanet 类型为 Planet? 值为 Planet.Uranus +// possiblePlanet 类型为 Planet? 值为 Planet.uranus ``` 然而,并非所有`Int`值都可以找到一个匹配的行星。因此,原始值构造器总是返回一个*可选*的枚举成员。在上面的例子中,`possiblePlanet`是`Planet?`类型,或者说“可选的`Planet`”。 @@ -274,13 +278,13 @@ let possiblePlanet = Planet(rawValue: 7) > 注意 > 原始值构造器是一个可失败构造器,因为并不是每一个原始值都有与之对应的枚举成员。更多信息请参见[可失败构造器](../chapter3/05_Declarations.html#failable_initializers) -如果你试图寻找一个位置为`9`的行星,通过原始值构造器返回的可选`Planet`值将是`nil`: +如果你试图寻找一个位置为`11`的行星,通过原始值构造器返回的可选`Planet`值将是`nil`: ```swift -let positionToFind = 9 +let positionToFind = 11 if let somePlanet = Planet(rawValue: positionToFind) { switch somePlanet { - case .Earth: + case .earth: print("Mostly harmless") default: print("Not a safe place for humans") @@ -288,10 +292,10 @@ if let somePlanet = Planet(rawValue: positionToFind) { } else { print("There isn't a planet at position \(positionToFind)") } -// 输出 "There isn't a planet at position 9 +// 输出 "There isn't a planet at position 11 ``` -这个例子使用了可选绑定(optional binding),试图通过原始值`9`来访问一个行星。`if let somePlanet = Planet(rawValue: 9)`语句创建了一个可选`Planet`,如果可选`Planet`的值存在,就会赋值给`somePlanet`。在这个例子中,无法检索到位置为`9`的行星,所以`else`分支被执行。 +这个例子使用了可选绑定(optional binding),试图通过原始值`11`来访问一个行星。`if let somePlanet = Planet(rawValue: 11)`语句创建了一个可选`Planet`,如果可选`Planet`的值存在,就会赋值给`somePlanet`。在这个例子中,无法检索到位置为`11`的行星,所以`else`分支被执行。 ## 递归枚举(Recursive Enumerations) @@ -303,9 +307,9 @@ if let somePlanet = Planet(rawValue: positionToFind) { ```swift enum ArithmeticExpression { - case Number(Int) - indirect case Addition(ArithmeticExpression, ArithmeticExpression) - indirect case Multiplication(ArithmeticExpression, ArithmeticExpression) + case number(Int) + indirect case addition(ArithmeticExpression, ArithmeticExpression) + indirect case multiplication(ArithmeticExpression, ArithmeticExpression) } ``` @@ -313,31 +317,31 @@ enum ArithmeticExpression { ```swift indirect enum ArithmeticExpression { - case Number(Int) - case Addition(ArithmeticExpression, ArithmeticExpression) - case Multiplication(ArithmeticExpression, ArithmeticExpression) + case number(Int) + case addition(ArithmeticExpression, ArithmeticExpression) + case multiplication(ArithmeticExpression, ArithmeticExpression) } ``` -上面定义的枚举类型可以存储三种算术表达式:纯数字、两个表达式相加、两个表达式相乘。枚举成员`Addition`和`Multiplication`的关联值也是算术表达式——这些关联值使得嵌套表达式成为可能。例如,表达式`(5 + 4) * 2`,乘号右边是一个数字,左边则是另一个表达式。因为数据是嵌套的,因而用来存储数据的枚举类型也需要支持这种嵌套——这意味着枚举类型需要支持递归。下面的代码展示了使用`ArithmeticExpression `这个递归枚举创建表达式`(5 + 4) * 2` +上面定义的枚举类型可以存储三种算术表达式:纯数字、两个表达式相加、两个表达式相乘。枚举成员`addition`和`multiplication`的关联值也是算术表达式——这些关联值使得嵌套表达式成为可能。例如,表达式`(5 + 4) * 2`,乘号右边是一个数字,左边则是另一个表达式。因为数据是嵌套的,因而用来存储数据的枚举类型也需要支持这种嵌套——这意味着枚举类型需要支持递归。下面的代码展示了使用`ArithmeticExpression `这个递归枚举创建表达式`(5 + 4) * 2` ```swift -let five = ArithmeticExpression.Number(5) -let four = ArithmeticExpression.Number(4) -let sum = ArithmeticExpression.Addition(five, four) -let product = ArithmeticExpression.Multiplication(sum, ArithmeticExpression.Number(2)) +let five = ArithmeticExpression.number(5) +let four = ArithmeticExpression.number(4) +let sum = ArithmeticExpression.addition(five, four) +let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2)) ``` 要操作具有递归性质的数据结构,使用递归函数是一种直截了当的方式。例如,下面是一个对算术表达式求值的函数: ```swift -func evaluate(expression: ArithmeticExpression) -> Int { +func evaluate(_ expression: ArithmeticExpression) -> Int { switch expression { - case .Number(let value): + case let .number(value): return value - case .Addition(let left, let right): + case let .addition(left, right): return evaluate(left) + evaluate(right) - case .Multiplication(let left, let right): + case let .multiplication(left, right): return evaluate(left) * evaluate(right) } } diff --git a/source/chapter2/18_Error_Handling.md b/source/chapter2/18_Error_Handling.md index a501ca6f..8f909cb8 100755 --- a/source/chapter2/18_Error_Handling.md +++ b/source/chapter2/18_Error_Handling.md @@ -7,6 +7,9 @@ > > 2.2 > 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-15 +> +> 3.0 +> 翻译+校对:[shanks](http://codebuild.me) 2016-09-24 本页包含内容: @@ -14,38 +17,38 @@ - [处理错误](#handling_errors) - [指定清理操作](#specifying_cleanup_actions) -*错误处理(Error handling)*是响应错误以及从错误中恢复的过程。Swift 提供了在运行时对可恢复错误的抛出、捕获、传递和操作的一流支持。 +*错误处理(Error handling)*是响应错误以及从错误中恢复的过程。Swift 提供了在运行时对可恢复错误的抛出、捕获、传递和操作的一等公民支持。 某些操作无法保证总是执行完所有代码或总是生成有用的结果。可选类型可用来表示值缺失,但是当某个操作失败时,最好能得知失败的原因,从而可以作出相应的应对。 举个例子,假如有个从磁盘上的某个文件读取数据并进行处理的任务,该任务会有多种可能失败的情况,包括指定路径下文件并不存在,文件不具有可读权限,或者文件编码格式不兼容。区分这些不同的失败情况可以让程序解决并处理某些错误,然后把它解决不了的错误报告给用户。 > 注意 -Swift 中的错误处理涉及到错误处理模式,这会用到 Cocoa 和 Objective-C 中的`NSError`。关于这个类的更多信息请参见 [Using Swift with Cocoa and Objective-C (Swift 2.2)](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中的[错误处理](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html#//apple_ref/doc/uid/TP40014216-CH7-ID10)。 +Swift 中的错误处理涉及到错误处理模式,这会用到 Cocoa 和 Objective-C 中的`NSError`。关于这个类的更多信息请参见 [Using Swift with Cocoa and Objective-C (Swift 3)](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中的[错误处理](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html#//apple_ref/doc/uid/TP40014216-CH7-ID10)。 -##表示并抛出错误 +## 表示并抛出错误 -在 Swift 中,错误用符合`ErrorType`协议的类型的值来表示。这个空协议表明该类型可以用于错误处理。 +在 Swift 中,错误用符合`Error`协议的类型的值来表示。这个空协议表明该类型可以用于错误处理。 Swift 的枚举类型尤为适合构建一组相关的错误状态,枚举的关联值还可以提供错误状态的额外信息。例如,你可以这样表示在一个游戏中操作自动贩卖机时可能会出现的错误状态: ```swift -enum VendingMachineError: ErrorType { - case InvalidSelection //选择无效 - case InsufficientFunds(coinsNeeded: Int) //金额不足 - case OutOfStock //缺货 +enum VendingMachineError: Error { + case invalidSelection //选择无效 + case insufficientFunds(coinsNeeded: Int) //金额不足 + case outOfStock //缺货 } ``` 抛出一个错误可以让你表明有意外情况发生,导致正常的执行流程无法继续执行。抛出错误使用`throw`关键字。例如,下面的代码抛出一个错误,提示贩卖机还需要`5`个硬币: ```swift -throw VendingMachineError.InsufficientFunds(coinsNeeded: 5) +throw VendingMachineError. insufficientFunds(coinsNeeded: 5) ``` -##处理错误 +## 处理错误 某个错误被抛出时,附近的某部分代码必须负责处理这个错误,例如纠正这个问题、尝试另外一种方式、或是向用户报告错误。 @@ -109,7 +112,7 @@ class VendingMachine { newItem.count -= 1 inventory[name] = newItem - dispenseSnack(name) + print("Dispensing \(name)") } } ``` @@ -145,7 +148,7 @@ struct PurchasedSnack { ``` -###用 Do-Catch 处理错误 +### 用 Do-Catch 处理错误 可以使用一个`do-catch`语句运行一段闭包代码来处理错误。如果在`do`子句中的代码抛出了一个错误,这个错误会与`catch`子句做匹配,从而决定哪条子句能处理它。 @@ -183,7 +186,7 @@ do { 上面的例子中,`buyFavoriteSnack(_:vendingMachine:)`函数在一个`try`表达式中调用,因为它能抛出错误。如果错误被抛出,相应的执行会马上转移到`catch`子句中,并判断这个错误是否要被继续传递下去。如果没有错误抛出,`do`子句中余下的语句就会被执行。 -###将错误转换成可选值 +### 将错误转换成可选值 可以使用`try?`通过将错误转换成一个可选值来处理错误。如果在评估`try?`表达式时一个错误被抛出,那么表达式的值就是`nil`。例如,在下面的代码中,`x`和`y`有着相同的数值和等价的含义: @@ -225,7 +228,7 @@ let photo = try! loadImage("./Resources/John Appleseed.jpg") ``` -##指定清理操作 +## 指定清理操作 可以使用`defer`语句在即将离开当前代码块时执行一系列语句。该语句让你能执行一些必要的清理工作,不管是以何种方式离开当前代码块的——无论是由于抛出错误而离开,还是由于诸如`return`或者`break`的语句。例如,你可以用`defer`语句来确保文件描述符得以关闭,以及手动分配的内存得以释放。 diff --git a/source/chapter2/22_Protocols.md b/source/chapter2/22_Protocols.md index ad3a04ee..deb7a420 100644 --- a/source/chapter2/22_Protocols.md +++ b/source/chapter2/22_Protocols.md @@ -10,10 +10,15 @@ > 2.1 > 翻译:[小铁匠Linus](https://github.com/kevin833752) -> 校对:[shanks](http://codebuild.me),2015-11-01 +> 校对:[shanks](http://codebuild.me) > > 2.2 -> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-16 +> 翻译+校对:[SketchK](https://github.com/SketchK) +> +> 3.0 +> 校对:[CMB](https://github.com/chenmingbiao) +> +> 版本日期:2016-09-13 本页包含内容: @@ -614,7 +619,7 @@ protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol { ## 协议合成 -有时候需要同时采纳多个协议,你可以将多个协议采用 `protocol` 这样的格式进行组合,称为 *协议合成(protocol composition)*。你可以在 `<>` 中罗列任意多个你想要采纳的协议,以逗号分隔。 +有时候需要同时采纳多个协议,你可以将多个协议采用 `SomeProtocol & AnotherProtocol` 这样的格式进行组合,称为 *协议合成(protocol composition)*。你可以罗列任意多个你想要采纳的协议,以与符号(`&`)分隔。 下面的例子中,将 `Named` 和 `Aged` 两个协议按照上述语法组合成一个协议,作为函数参数的类型: @@ -629,17 +634,17 @@ struct Person: Named, Aged { var name: String var age: Int } -func wishHappyBirthday(celebrator: protocol) { - print("Happy birthday \(celebrator.name) - you're \(celebrator.age)!") +func wishHappyBirthday(to celebrator: Named & Aged) { + print("Happy birthday, \(celebrator.name), you're \(celebrator.age)!") } let birthdayPerson = Person(name: "Malcolm", age: 21) -wishHappyBirthday(birthdayPerson) +wishHappyBirthday(to: birthdayPerson) // 打印 “Happy birthday Malcolm - you're 21!” ``` `Named` 协议包含 `String` 类型的 `name` 属性。`Aged` 协议包含 `Int` 类型的 `age` 属性。`Person` 结构体采纳了这两个协议。 -`wishHappyBirthday(_:)` 函数的参数 `celebrator` 的类型为 `protocol`。这意味着它不关心参数的具体类型,只要参数符合这两个协议即可。 +`wishHappyBirthday(_:)` 函数的参数 `celebrator` 的类型为 `Named & Aged`。这意味着它不关心参数的具体类型,只要参数符合这两个协议即可。 上面的例子创建了一个名为 `birthdayPerson` 的 `Person` 的实例,作为参数传递给了 `wishHappyBirthday(_:)` 函数。因为 `Person` 同时符合这两个协议,所以这个参数合法,函数将打印生日问候语。 diff --git a/source/chapter2/25_Advanced_Operators.md b/source/chapter2/25_Advanced_Operators.md index ee0757f6..04d698de 100644 --- a/source/chapter2/25_Advanced_Operators.md +++ b/source/chapter2/25_Advanced_Operators.md @@ -13,6 +13,9 @@ > > 2.2 > 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-17 +> +> 3.0 +> 翻译+校对:[mmoaay](https://github.com/mmoaay) 2016-09-20 本页内容包括: @@ -320,12 +323,15 @@ signedOverflow = signedOverflow &- 1 struct Vector2D { var x = 0.0, y = 0.0 } -func + (left: Vector2D, right: Vector2D) -> Vector2D { - return Vector2D(x: left.x + right.x, y: left.y + right.y) + +extension Vector2D { + static func + (left: Vector2D, right: Vector2D) -> Vector2D { + return Vector2D(x: left.x + right.x, y: left.y + right.y) + } } ``` -该运算符函数被定义为一个全局函数,并且函数的名字与它要进行重载的 `+` 名字一致。因为算术加法运算符是双目运算符,所以这个运算符函数接收两个类型为 `Vector2D` 的参数,同时有一个 `Vector2D` 类型的返回值。 +该运算符函数被定义为 `Vector2D` 上的一个类方法,并且函数的名字与它要进行重载的 `+` 名字一致。因为加法运算并不是一个向量必需的功能,所以这个类方法被定义在 `Vector2D` 的一个扩展中,而不是 `Vector2D` 结构体声明内。而算术加法运算符是双目运算符,所以这个运算符函数接收两个类型为 `Vector2D` 的参数,同时有一个 `Vector2D` 类型的返回值。 在这个实现中,输入参数分别被命名为 `left` 和 `right`,代表在 `+` 运算符左边和右边的两个 `Vector2D` 实例。函数返回了一个新的 `Vector2D` 实例,这个实例的 `x` 和 `y` 分别等于作为参数的两个实例的 `x` 和 `y` 的值之和。 @@ -350,8 +356,10 @@ let combinedVector = vector + anotherVector 要实现前缀或者后缀运算符,需要在声明运算符函数的时候在 `func` 关键字之前指定 `prefix` 或者 `postfix` 修饰符: ```swift -prefix func - (vector: Vector2D) -> Vector2D { - return Vector2D(x: -vector.x, y: -vector.y) +extension Vector2D { + static prefix func - (vector: Vector2D) -> Vector2D { + return Vector2D(x: -vector.x, y: -vector.y) + } } ``` @@ -372,8 +380,10 @@ let alsoPositive = -negative 复合赋值运算符将赋值运算符(`=`)与其它运算符进行结合。例如,将加法与赋值结合成加法赋值运算符(`+=`)。在实现的时候,需要把运算符的左参数设置成 `inout` 类型,因为这个参数的值会在运算符函数内直接被修改。 ```swift -func += (inout left: Vector2D, right: Vector2D) { - left = left + right +extension Vector2D { + static func += (left: inout Vector2D, right: Vector2D) { + left = left + right + } } ``` @@ -398,11 +408,13 @@ original += vectorToAdd 为了使用等价运算符能对自定义的类型进行判等运算,需要为其提供自定义实现,实现的方法与其它中缀运算符一样: ```swift -func == (left: Vector2D, right: Vector2D) -> Bool { - return (left.x == right.x) && (left.y == right.y) -} -func != (left: Vector2D, right: Vector2D) -> Bool { - return !(left == right) +extension Vector2D { + static func == (left: Vector2D, right: Vector2D) -> Bool { + return (left.x == right.x) && (left.y == right.y) + } + static func != (left: Vector2D, right: Vector2D) -> Bool { + return !(left == right) + } } ``` @@ -430,18 +442,17 @@ if twoThree == anotherTwoThree { prefix operator +++ {} ``` -上面的代码定义了一个新的名为 `+++` 的前缀运算符。对于这个运算符,在 Swift 中并没有意义,因此我们针对 `Vector2D` 的实例来定义它的意义。对这个示例来讲,`+++` 被实现为“前缀双自增”运算符。它使用了前面定义的复合加法运算符来让矩阵对自身进行相加,从而让 `Vector2D` 实例的 `x` 属性和 `y` 属性的值翻倍: +上面的代码定义了一个新的名为 `+++` 的前缀运算符。对于这个运算符,在 Swift 中并没有意义,因此我们针对 `Vector2D` 的实例来定义它的意义。对这个示例来讲,`+++` 被实现为“前缀双自增”运算符。它使用了前面定义的复合加法运算符来让矩阵对自身进行相加,从而让 `Vector2D` 实例的 `x` 属性和 `y` 属性的值翻倍。实现 `+++` 运算符的方式如下: ```swift -prefix func +++ (inout vector: Vector2D) -> Vector2D { - vector += vector - return vector +extension Vector2D { + static prefix func +++ (vector: inout Vector2D) -> Vector2D { + vector += vector + return vector + } } -``` -`Vector2D` 的 `+++` 的实现和 `++` 的实现很相似,唯一不同的是前者对自身进行相加,而后者是与另一个值为 `(1.0, 1.0)` 的向量相加。 -```swift var toBeDoubled = Vector2D(x: 1.0, y: 4.0) let afterDoubling = +++toBeDoubled // toBeDoubled 现在的值为 (2.0, 8.0) @@ -449,20 +460,20 @@ let afterDoubling = +++toBeDoubled ``` -### 自定义中缀运算符的优先级和结合性 +### 自定义中缀运算符的优先级 -自定义的中缀运算符也可以指定优先级和结合性。[优先级和结合性](#precedence_and_associativity)中详细阐述了这两个特性是如何对中缀运算符的运算产生影响的。 +每个自定义中缀运算符都属于某个优先级组。这个优先级组指定了这个运算符和其他中缀运算符的优先级和结合性。[优先级和结合性](#precedence_and_associativity)中详细阐述了这两个特性是如何对中缀运算符的运算产生影响的。 -结合性可取的值有` left`,`right` 和 `none`。当左结合运算符跟其他相同优先级的左结合运算符写在一起时,会跟左边的值进行结合。同理,当右结合运算符跟其他相同优先级的右结合运算符写在一起时,会跟右边的值进行结合。而非结合运算符不能跟其他相同优先级的运算符写在一起。 +而没有明确放入优先级组的自定义中缀运算符会放到一个默认的优先级组内,其优先级高于三元运算符。 -结合性的默认值是 `none`,优先级的默认值 `100`。 - -以下例子定义了一个新的中缀运算符 `+-`,此运算符的结合性为 `left`,并且它的优先级为 `140`: +以下例子定义了一个新的自定义中缀运算符 `+-`,此运算符属于 `AdditionPrecedence` 优先组: ```swift -infix operator +- { associativity left precedence 140 } -func +- (left: Vector2D, right: Vector2D) -> Vector2D { - return Vector2D(x: left.x + right.x, y: left.y - right.y) +infix operator +-: AdditionPrecedence +extension Vector2D { + static func +- (left: Vector2D, right: Vector2D) -> Vector2D { + return Vector2D(x: left.x + right.x, y: left.y - right.y) + } } let firstVector = Vector2D(x: 1.0, y: 2.0) let secondVector = Vector2D(x: 3.0, y: 4.0) @@ -470,7 +481,7 @@ let plusMinusVector = firstVector +- secondVector // plusMinusVector 是一个 Vector2D 实例,并且它的值为 (4.0, -2.0) ``` -这个运算符把两个向量的 `x` 值相加,同时用第一个向量的 `y` 值减去第二个向量的 `y` 值。因为它本质上是属于“相加型”运算符,所以将它的结合性和优先级被分别设置为 `left` 和 `140`,这与 `+` 和 `-` 等默认的中缀“相加型”运算符是相同的。关于 Swift 标准库提供的运算符的结合性与优先级,请参考 [Swift Standard Library Operators Reference](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_StandardLibrary_Operators/index.html#//apple_ref/doc/uid/TP40016054)。 +这个运算符把两个向量的 `x` 值相加,同时用第一个向量的 `y` 值减去第二个向量的 `y` 值。因为它本质上是属于“相加型”运算符,所以将它放置 `+` 和 `-` 等默认的中缀“相加型”运算符相同的优先级组中。关于 Swift 标准库提供的运算符,以及完整的运算符优先级组和结合性设置,请参考 [Swift Standard Library Operators Reference](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_StandardLibrary_Operators/index.html#//apple_ref/doc/uid/TP40016054)。而更多关于优先级组以及自定义操作符和优先级组的语法,请参考[运算符声明](#operator_declaration) > 注意 > 当定义前缀与后缀运算符的时候,我们并没有指定优先级。然而,如果对同一个值同时使用前缀与后缀运算符,则后缀运算符会先参与运算。 diff --git a/source/chapter3/03_Types.md b/source/chapter3/03_Types.md index 8f90eb25..498b36f7 100644 --- a/source/chapter3/03_Types.md +++ b/source/chapter3/03_Types.md @@ -26,7 +26,7 @@ - [类型继承子句](#type_inheritance_clause) - [类型推断](#type_inference) -Swift 语言存在两种类型:命名型类型和复合型类型。命名型类型是指定义时可以给定名字的类型。命名型类型包括类、结构体、枚举和协议。比如,一个用户定义的类 MyClass 的实例拥有类型 MyClass。除了用户定义的命名型类型,Swift 标准库也定义了很多常用的命名型类型,包括那些表示数组、字典和可选值的类型。 +Swift 语言存在两种类型:命名型类型和复合型类型。命名型类型是指定义时可以给定名字的类型。命名型类型包括类、结构体、枚举和协议。比如,一个用户定义的类 `MyClass` 的实例拥有类型 `MyClass`。除了用户定义的命名型类型,Swift 标准库也定义了很多常用的命名型类型,包括那些表示数组、字典和可选值的类型。 那些通常被其它语言认为是基本或原始的数据型类型,比如表示数字、字符和字符串的类型,实际上就是命名型类型,这些类型在 Swift 标准库中是使用结构体来定义和实现的。因为它们是命名型类型,因此你可以按照 [扩展](../chapter2/21_Extensions.html) 和 [扩展声明](05_Declarations.html#extension_declaration) 中讨论的那样,声明一个扩展来增加它们的行为以满足你程序的需求。 @@ -36,7 +36,7 @@ Swift 语言存在两种类型:命名型类型和复合型类型。命名型 > 类型语法 -> *类型* → [*数组类型*](#array-type) | [*字典类型*](#dictionary-type) | [*函数类型*](#function-type) | [*类型标识*](#type-identifier) | [*元组类型*](#tuple-type) | [*可选类型*](#optional-type) | [*隐式解析可选类型*](#implicitly-unwrapped-optional-type) | [*协议合成类型*](#protocol-composition-type) | [*元型类型*](#metatype-type) +> *类型* → [*数组类型*](#array-type) | [*字典类型*](#dictionary-type) | [*函数类型*](#function-type) | [*类型标识*](#type-identifier) | [*元组类型*](#tuple-type) | [*可选类型*](#optional-type) | [*隐式解析可选类型*](#implicitly-unwrapped-optional-type) | [*协议合成类型*](#protocol-composition-type) | [*元型类型*](#metatype-type) | **任意类型** | **自身类型** ## 类型注解 @@ -53,7 +53,7 @@ func someFunction(a: Int) { /* ... */ } > 类型注解语法 -> *类型注解* → **:** [*特性列表*](06_Attributes.html#attributes)可选 [*类型*](#type) +> *类型注解* → **:** [*特性列表*](06_Attributes.html#attributes)可选 **输入输出参数**可选 [*类型*](#type) ## 类型标识符 @@ -87,20 +87,27 @@ var someValue: ExampleModule.MyType 元组类型是使用括号括起来的零个或多个类型,类型间用逗号隔开。 你可以使用元组类型作为一个函数的返回类型,这样就可以使函数返回多个值。你也可以命名元组类型中的元素,然后用这些名字来引用每个元素的值。元素的名字由一个标识符紧跟一个冒号 `(:)` 组成。[函数和多返回值](../chapter2/06_Functions.html#functions_with_multiple_return_values) 章节里有一个展示上述特性的例子。 + +当一个元组类型的元素有名字的时候,这个名字就是类型的一部分。 + +```swift +var someTuple = (top: 10, bottom: 12) // someTuple 的类型为 (top: Int, bottom: Int) +someTuple = (top: 4, bottom: 42) // 正确:命名类型匹配 +someTuple = (9, 99) // 正确:命名类型被自动推断 +someTuple = (left: 5, right: 5) // 错误:命名类型不匹配 +``` `Void` 是空元组类型 `()` 的别名。如果括号内只有一个元素,那么该类型就是括号内元素的类型。比如,`(Int)` 的类型是 `Int` 而不是 `(Int)`。所以,只有当元组类型包含的元素个数在两个及以上时才可以命名元组元素。 -> 元组类型语法 +> 元组类型语法 -> *元组类型* → **(** [*元组类型主体*](#tuple-type-body)可选 **)** - -> *元组类型主体* → [*元组类型元素列表*](#tuple-type-element-list) **...**可选 +> *元组类型* → **(** [*元组类型元素列表*](#tuple-type-element-list) 可选 **)** > *元组类型元素列表* → [*元组类型元素*](#tuple-type-element) | [*元组类型元素*](#tuple-type-element) **,** [*元组类型元素列表*](#tuple-type-element-list) -> *元组类型元素* → [*特性列表*](06_Attributes.html#attributes)可选 **inout**可选 [*类型*](#type) | **inout**可选 [*元素名*](#element-name) [*类型注解*](#type-annotation) +> *元组类型元素* → [*元素名*](#element-name) [*类型注解*](#type-annotation) | [*类型*](#type) -> *元素名* → [*标识符*](02_Lexical_Structure.html#identifier) +> *元素名* → [*标识符*](02_Lexical_Structure.html#identifier) ## 函数类型 @@ -109,22 +116,49 @@ var someValue: ExampleModule.MyType > `参数类型` -> `返回值类型` -由于参数类型和返回值类型可以是元组类型,所以函数类型支持多参数与多返回值的函数与方法。 +参数类型是由逗号间隔的类型列表。由于参数类型和返回值类型可以是元组类型,所以函数类型支持多参数与多返回值的函数与方法。 你可以对函数参数使用 `autoclosure` 特性。这会自动将参数表达式转化为闭包,表达式的结果即闭包返回值。这从语法结构上提供了一种便捷:延迟对表达式的求值,直到其值在函数体中被使用。以自动闭包做为参数的函数类型的例子详见 [自动闭包](../chapter2/07_Closures.html#autoclosures) 。 函数类型可以拥有一个可变长参数作为参数类型中的最后一个参数。从语法角度上讲,可变长参数由一个基础类型名字紧随三个点(`...`)组成,如 `Int...`。可变长参数被认为是一个包含了基础类型元素的数组。即 `Int...` 就是 `[Int]`。关于使用可变长参数的例子,请参阅 [可变参数](../chapter2/06_Functions.html#variadic_parameters)。 为了指定一个 `in-out` 参数,可以在参数类型前加 `inout` 前缀。但是你不可以对可变长参数或返回值类型使用 `inout`。关于这种参数的详细讲解请参阅 [输入输出参数](../chapter2/06_Functions.html#in_out_parameters)。 + +函数和方法中的参数名并不是函数类型的一部分。例如: + +```swift +func someFunction(left: Int, right: Int) {} +func anotherFunction(left: Int, right: Int) {} +func functionWithDifferentLabels(top: Int, bottom: Int) {} + +var f = someFunction // 函数f的类型为 (Int, Int) -> Void, 而不是 (left: Int, right: Int) -> Void. +f = anotherFunction // 正确 +f = functionWithDifferentLabels // 正确 + +func functionWithDifferentArgumentTypes(left: Int, right: String) {} +func functionWithDifferentNumberOfArguments(left: Int, right: Int, top: Int) {} + +f = functionWithDifferentArgumentTypes // 错误 +f = functionWithDifferentNumberOfArguments // 错误 +``` -柯里化函数的函数类型从右向左进行组合。例如,函数类型 `Int -> Int -> Int` 可以理解为 `Int -> (Int -> Int)`,也就是说,该函数类型的参数为 `Int` 类型,其返回类型是一个参数类型为 `Int`,返回类型为 `Int` 的函数类型。关于柯里化函数的讨论见章节 [柯里化函数](05_Declarations.html#curried_functions)。 +如果一个函数类型包涵多个箭头(->),那么函数类型将从右向左进行组合。例如,函数类型 `Int -> Int -> Int` 可以理解为 `Int -> (Int -> Int)`,也就是说,该函数类型的参数为 `Int` 类型,其返回类型是一个参数类型为 `Int`,返回类型为 `Int` 的函数类型。 -函数类型若要抛出错误就必须使用 `throws` 关键字来标记,若要重抛错误则必须使用 `rethrows` 关键字来标记。`throws` 关键字是函数类型的一部分,非抛出函数是抛出函数函数的一个子类型。因此,在使用抛出函数的地方也可以使用不抛出函数。对于柯里化函数,`throws` 关键字只应用于最里层的函数。抛出和重抛函数的相关描述见章节 [抛出函数与方法](05_Declarations.html#throwing_functions_and_methods) 和 [重抛函数与方法](05_Declarations.html#rethrowing_functions_and_methods)。 +函数类型若要抛出错误就必须使用 `throws` 关键字来标记,若要重抛错误则必须使用 `rethrows` 关键字来标记。`throws` 关键字是函数类型的一部分,非抛出函数是抛出函数函数的一个子类型。因此,在使用抛出函数的地方也可以使用不抛出函数。抛出和重抛函数的相关描述见章节 [抛出函数与方法](05_Declarations.html#throwing_functions_and_methods) 和 [重抛函数与方法](05_Declarations.html#rethrowing_functions_and_methods)。 > 函数类型语法 - -> *函数类型* → [*类型*](#type) **throws**可选 **->** [*类型*](#type) -> *函数类型* → [*类型*](#type) **rethrows**可选 **->** [*类型*](#type) + +> *函数类型* → [*特性列表*](06_Attributes.html#attributes)可选 [*函数类型子句*](#function-type-argument-clause) **throws**可选 **->** [*类型*](#type) +> *函数类型* → [*特性列表*](06_Attributes.html#attributes)可选 [*函数类型子句*](#function-type-argument-clause) **rethrows­** **->** [*类型*](#type) + +> *函数类型子句* → (­)­ +> *函数类型子句* → ([*函数类型参数列表*](#function-type-argument-list)*...*­可选)­ + +> *函数类型参数列表* → [*函数类型参数*](function-type-argument) | [*函数类型参数*](function-type-argument), [*函数类型参数列表*](#function-type-argument-list) + +> *函数类型参数* → [*特性列表*](06_Attributes.html#attributes)可选 **输入输出参数**可选 [*类型*](#type) | [*参数标签*](#argument-label) [*类型注解*](#type-annotation) + +> *参数标签* → [*标识符*](02_Lexical_Structure.html#identifier) ## 数组类型 @@ -194,7 +228,7 @@ var optionalInteger: Optional 在上述两种情况下,变量 `optionalInteger` 都被声明为可选整型类型。注意在类型和 `?` 之间没有空格。 -类型 `Optional` 是一个枚举,有两个成员,`None` 和 `Some(Wrapped)`,用来表示可能有也可能没有的值。任意类型都可以被显式地声明(或隐式地转换)为可选类型。如果你在声明或定义可选变量或属性的时候没有提供初始值,它的值则会自动赋为默认值 `nil`。 +类型 `Optional` 是一个枚举,有两个成员,`none` 和 `some(Wrapped)`,用来表示可能有也可能没有的值。任意类型都可以被显式地声明(或隐式地转换)为可选类型。如果你在声明或定义可选变量或属性的时候没有提供初始值,它的值则会自动赋为默认值 `nil`。 如果一个可选类型的实例包含一个值,那么你就可以使用后缀运算符 `!` 来获取该值,正如下面描述的: @@ -216,21 +250,29 @@ optionalInteger! // 42 ## 隐式解析可选类型 -Swift 语言定义后缀 `!` 作为标准库中命名类型 `ImplicitlyUnwrappedOptional` 的语法糖。换句话说,下面两个声明等价: +当可以被访问时,Swift 语言定义后缀 `!` 作为标准库中命名类型 `Optional` 的语法糖,来实现自动解包的功能。换句话说,下面两个声明等价: ```swift var implicitlyUnwrappedString: String! -var implicitlyUnwrappedString: ImplicitlyUnwrappedOptional +var explicitlyUnwrappedString: Optional ``` -上述两种情况下,变量 `implicitlyUnwrappedString` 被声明为一个隐式解析可选类型的字符串。注意类型与 `!` 之间没有空格。 - -你可以在使用可选类型的地方使用隐式解析可选类型。比如,你可以将隐式解析可选类型的值赋给变量、常量和可选属性,反之亦然。 +注意类型与 `!` 之间没有空格。 + +由于隐式解包修改了包涵其类型的声明语义,嵌套在元组类型或泛型的可选类型(比如字典元素类型或数组元素类型),不能被标记为隐式解包。例如: + +```swift +let tupleOfImplicitlyUnwrappedElements: (Int!, Int!) // 错误 +let implicitlyUnwrappedTuple: (Int, Int)! // 正确 + +let arrayOfImplicitlyUnwrappedElements: [Int!] // 错误 +let implicitlyUnwrappedArray: [Int]! // 正确 +``` + +由于隐式解析可选类型和可选类型有同样的表达式`Optional`,你可以在使用可选类型的地方使用隐式解析可选类型。比如,你可以将隐式解析可选类型的值赋给变量、常量和可选属性,反之亦然。 正如可选类型一样,你在声明隐式解析可选类型的变量或属性的时候也不用指定初始值,因为它有默认值 `nil`。 -由于隐式解析可选类型的值会在使用时自动解析,所以没必要使用操作符 `!` 来解析它。也就是说,如果你使用值为 `nil` 的隐式解析可选类型,就会导致运行错误。 - 可以使用可选链式调用来在隐式解析可选表达式上选择性地执行操作。如果值为 `nil`,就不会执行任何操作,因此也不会产生运行错误。 关于隐式解析可选类型的更多细节,请参阅 [隐式解析可选类型](../chapter2/01_The_Basics.html#implicityly_unwrapped_optionals)。 @@ -246,19 +288,19 @@ var implicitlyUnwrappedString: ImplicitlyUnwrappedOptional 协议合成类型的形式如下: -> protocol<`Protocol 1`, `Procotol 2`> +> `Protocol 1` & `Procotol 2` -协议合成类型允许你指定一个值,其类型符合多个协议的要求且不需要定义一个新的命名型协议来继承它想要符合的各个协议。比如,协议合成类型 `protocol` 等效于一个从 `Protocol A`,`Protocol B`, `Protocol C` 继承而来的新协议 `Protocol D`,很显然这样做有效率的多,甚至不需引入一个新名字。 +协议合成类型允许你指定一个值,其类型符合多个协议的要求且不需要定义一个新的命名型协议来继承它想要符合的各个协议。比如,协议合成类型 `Protocol A & Protocol B & Protocol C` 等效于一个从 `Protocol A`,`Protocol B`, `Protocol C` 继承而来的新协议 `Protocol D`,很显然这样做有效率的多,甚至不需引入一个新名字。 -协议合成列表中的每项必须是协议名或协议合成类型的类型别名。如果列表为空,它就会指定一个空协议合成列表,每个类型都符合它。 +协议合成列表中的每项必须是协议名或协议合成类型的类型别名。 > 协议合成类型语法 -> *协议合成类型* → **protocol** **<** [*协议标识符列表*](#protocol-identifier-list)可选 **>** - -> *协议标识符列表* → [*协议标识符*](#protocol-identifier) | [*协议标识符*](#protocol-identifier) **,** [*协议标识符列表*](#protocol-identifier-list) +> *协议合成类型* → [*协议标识符*](#protocol-identifier) & [*协议合成延续*](#protocol-composition-continuation) + +> *协议合成延续* → [*协议标识符*](#protocol-identifier) | [*协议合成类型*](#protocol-composition-type) -> *协议标识符* → [*类型标识符*](#type-identifier) +> *协议标识符* → [*类型标识符*](#type-identifier) ## 元类型 @@ -267,7 +309,7 @@ var implicitlyUnwrappedString: ImplicitlyUnwrappedOptional 类、结构体或枚举类型的元类型是相应的类型名紧跟 `.Type`。协议类型的元类型——并不是运行时符合该协议的具体类型——而是该协议名字紧跟 `.Protocol`。比如,类 `SomeClass` 的元类型就是 `SomeClass.Type`,协议 `SomeProtocol` 的元类型就是 `SomeProtocal.Protocol`。 -你可以使用后缀 `self` 表达式来获取类型。比如,`SomeClass.self` 返回 `SomeClass` 本身,而不是 `SomeClass` 的一个实例。同样,`SomeProtocol.self` 返回 `SomeProtocol` 本身,而不是运行时符合 `SomeProtocol` 的某个类型的实例。还可以对类型的实例使用 `dynamicType` 表达式来获取该实例在运行阶段的类型,如下所示: +你可以使用后缀 `self` 表达式来获取类型。比如,`SomeClass.self` 返回 `SomeClass` 本身,而不是 `SomeClass` 的一个实例。同样,`SomeProtocol.self` 返回 `SomeProtocol` 本身,而不是运行时符合 `SomeProtocol` 的某个类型的实例。还可以对类型的实例使用 `type(of:)` 表达式来获取该实例在运行阶段的类型,如下所示: ```swift class SomeBaseClass { @@ -283,19 +325,19 @@ class SomeSubClass: SomeBaseClass { let someInstance: SomeBaseClass = SomeSubClass() // someInstance 在编译期是 SomeBaseClass 类型, // 但是在运行期则是 SomeSubClass 类型 -someInstance.dynamicType.printClassName() +type(of: someInstance).printClassName() // 打印 “SomeSubClass” ``` 可以使用恒等运算符(`===` 和 `!==`)来测试一个实例的运行时类型和它的编译时类型是否一致。 ```swift -if someInstance.dynamicType === SomeBaseClass.self { - print("The dynamic type of someInstance is SomeBaseCass") -} else { - print("The dynamic type of someInstance isn't SomeBaseClass") -} -// 打印 “The dynamic type of someInstance isn't SomeBaseClass” +if type(of: someInstance) === someInstance.self { + print("The dynamic and static type of someInstance are the same") +} else { + print("The dynamic and static type of someInstance are different") +} +// 打印 "The dynamic and static type of someInstance are different" ``` 可以使用初始化表达式从某个类型的元类型构造出一个该类型的实例。对于类实例,被调用的构造器必须使用 `required` 关键字标记,或者整个类使用 `final` 关键字标记。 @@ -321,7 +363,7 @@ let anotherInstance = metatype.init(string: "some string") ## 类型继承子句 -类型继承子句被用来指定一个命名型类型继承自哪个类、采纳哪些协议。类型继承子句也用来指定一个类类型专属协议。类型继承子句开始于冒号 `:`,其后是类的超类或者一系列类型标识符。 +类型继承子句被用来指定一个命名型类型继承自哪个类、采纳哪些协议。类型继承子句也用来指定一个类类型专属协议。类型继承子句开始于冒号 `:`,其后是所需要的类、类型标识符列表或两者都有。 类可以继承单个超类,采纳任意数量的协议。当定义一个类时,超类的名字必须出现在类型标识符列表首位,然后跟上该类需要采纳的任意数量的协议。如果一个类不是从其它类继承而来,那么列表可以以协议开头。关于类继承更多的讨论和例子,请参阅 [继承](../chapter2/13_Inheritance.html)。 @@ -354,3 +396,5 @@ let eFloat: Float = 2.71828 // eFloat 的类型为 Float ``` Swift 中的类型推断在单独的表达式或语句上进行。这意味着所有用于类型推断的信息必须可以从表达式或其某个子表达式的类型检查中获取到。 + + diff --git a/source/chapter3/05_Declarations.md b/source/chapter3/05_Declarations.md index 3e8f602a..082166ae 100755 --- a/source/chapter3/05_Declarations.md +++ b/source/chapter3/05_Declarations.md @@ -17,6 +17,9 @@ > 2.2 > 翻译:[星夜暮晨](https://github.com/SemperIdem) +> 3.0 +> 翻译:[chenmingjia](https://github.com/chenmingjia) + 本页包含内容: - [顶级代码](#top-level_code) @@ -326,6 +329,31 @@ typealias 类型别名 = 现存类型 ``` 当声明一个类型的别名后,可以在程序的任何地方使用“别名”来代替现有类型。现有类型可以是具有命名的类型或者混合类型。类型别名不产生新的类型,它只是使用别名来引用现有类型。 +类型别名声明可以通过泛型参数来给一个现有泛型类型提供名称。类型别名为现有类型的一部分或者全部泛型参数提供具体类型。例如: +```swift +typealias StringDictionary = Dictionary + +// 下列两个字典拥有同样的类型 +var dictionary1: StringDictionary = [:] +var dictionary2: Dictionary = [:] +``` +当一个类型别名带着泛型参数一起被声明时,这些参数的约束必须与现有参数的约束完全匹配。例如: +```swift +typealias DictionaryOfInts = Dictionary +``` +因为类型别名可以和现有类型相互交换使用,类型别名不可以引入额外的类型约束。 +在协议声明中,类型别名可以为那些经常使用的类型提供一个更短更方便的名称,例如: +```swift +protocol Sequence { + associatedtype Iterator: IteratorProtocol + typealias Element = Iterator.Element +} + +func sum(_ sequence: T) -> Int where T.Element == Int { + // ... +} +``` +假如没有类型别名,sum函数将必须引用关联类型通过T.Iterator.Element的形式来替代 T.Element。 另请参阅 [协议关联类型声明](#protocol_associated_type_declaration)。 @@ -359,7 +387,7 @@ func 函数名称(参数列表) { } ``` -每个参数的类型都要标明,因为它们不能被推断出来。虽然函数的参数默认是常量,也可以在参数名前添加 `let` 来强调这一行为。如果您在某个参数名前面加上了 `inout`,那么这个参数就可以在这个函数作用域当中被修改。更多关于 `inout` 参数的讨论,请参阅 [输入输出参数](#in-out_parameters)。 +每个参数的类型都要标明,因为它们不能被推断出来。如果您在某个参数类型前面加上了 `inout`,那么这个参数就可以在这个函数作用域当中被修改。更多关于 `inout` 参数的讨论,请参阅 [输入输出参数](#in-out_parameters)。 函数可以使用元组类型作为返回类型来返回多个值。 @@ -498,7 +526,7 @@ func 函数名称(参数列表) throws -> 返回类型 { 函数或方法可以使用 `rethrows` 关键字来声明,从而表明仅当该函数或方法的一个函数类型的参数抛出错误时,该函数或方法才抛出错误。这类函数和方法被称为重抛函数和重抛方法。重新抛出错误的函数或方法必须至少有一个参数的类型为抛出函数。 ```swift -func functionWithCallback(callback: () throws -> Int) rethrows { +func someFunction(callback: () throws -> Void) rethrows { try callback() } ``` @@ -507,6 +535,14 @@ func functionWithCallback(callback: () throws -> Int) rethrows { 抛出方法不能重写重抛方法,而且抛出方法不能满足协议对于重抛方法的要求。也就是说,重抛方法可以重写抛出方法,而且重抛方法可以满足协议对于抛出方法的要求。 + +### 永不返回的函数 + +Swift定义了`Never`类型,它表示函数或者方法不会返回给它的调用者。`Never`返回类型的函数或方法可以称为不归,不归函数、方法要么引发不可恢复的错误,要么永远不停地运作,这会使调用后本应执行得代码就不再执行了。但即使是不归函数、方法,抛错函数和重抛出函数也可以将程序控制转移到合适的`catch`代码块。 + +不归函数、方法可以在guard语句的else字句中调用,具体讨论在[*Guard语句*](10_Statements.md#guard_statements)。 +你可以重载一个不归方法,但是新的方法必须保持原有的返回类型和没有返回的行为。 + > 函数声明语法 @@ -543,6 +579,9 @@ func functionWithCallback(callback: () throws -> Int) rethrows { > > *默认参数子句* → **=** [*表达式*](04_Expressions.md#expression) + + + ## 枚举声明 @@ -1118,21 +1157,12 @@ subscript (参数列表) -> 返回类型 { 下面的形式声明了一个新的中缀运算符: ```swift -infix operator 运算符名称 { - precedence 优先级 - associativity 结合性 -} +infix operator 运算符名称: 优先级组 ``` 中缀运算符是二元运算符,置于两个运算对象之间,例如加法运算符(`+`)位于表达式 `1 + 2` 的中间。 -中缀运算符可以选择指定优先级或结合性,或者两者同时指定。 - -运算符的优先级可以指定在没有括号包围的情况下,运算符与其运算对象如何结合。可以使用上下文相关的关键字 `precedence` 以及紧随其后的优先级数字来指定一个运算符的优先级。优先级可以是 `0` 到 `255` 之间的任何十进制整数。与十进制整数字面量不同的是,它不可以包含任何下划线字符。尽管优先级是一个特定的数字,但它仅用作与另一个运算符的优先级比较大小。也就是说,当两个运算符为同一个运算对象竞争时,例如 `2 + 3 * 5`,优先级更高的运算符将优先参与运算。 - -运算符的结合性可以指定在没有括号包围的情况下,多个优先级相同的运算符将如何组合。可以使用上下文相关的关键字 `associativity` 以及紧随其后的结合性关键字来指定运算符的结合性,结合性关键字也是上下文相关的,包括 `left`、`right` 和 `none`。左结合运算符以从左到右的顺序进行组合。例如,减法运算符(`-`)具有左结合性,因此 `4 - 5 - 6` 以 `(4 - 5) - 6` 的形式组合,其结果为 `-7`。右结合运算符以从右到左的顺序组合,而设置为 `none` 的运算符不进行组合。具有相同优先级的非结合运算符,不可以互相邻接。例如,表达式 `1 < 2 < 3` 是非法的。 - -声明时不指定任何优先级或结合性的中缀运算符,优先级为 `100`,结合性为 `none`。 +中缀运算符可以选择指定优先级组。如果没有为运算符设置优先级组,Swift会设置默认优先级组`DefaultPrecedence`,它的优先级比三目优先级`TernaryPrecedence`要高,更多内容参考[*优先级组声明*](#precedence_group_declaration_modifiers) 下面的形式声明了一个新的前缀运算符: @@ -1169,18 +1199,62 @@ postfix operator 运算符名称 {} > > *中缀运算符声明* → **infix** **运算符** [*运算符*](02_Lexical_Structure.md#operator) **{** [*中缀运算符属性*](#infix-operator-attributes)可选 **}** - -> *中缀运算符属性* → [*优先级子句*](#precedence-clause)可选 [*结和性子句*](#associativity-clause)可选 -> -> *优先级子句* → **precedence** [*优先级水平*](#precedence-level) -> -> *优先级水平* → 十进制整数 0 到 255,包含 0 和 255 -> -> *结和性子句* → **associativity** [*结和性*](#associativity) -> -> *结和性* → **left** | **right** | **none** + +> *中缀运算符组* → [*优先级组名称*](#precedence-group-name) + + + + +## 优先级组声明 + +*优先级组声明 (A precedence group declaration)* 会向程序的中缀运算符引入一个全新的优先级组。当没有用圆括号分组时,运算符优先级反应了运算符与它的操作数的关系的紧密程度。 +优先级组的声明如下所示: + +```swift +precedencegroup 优先级组名称{ + higherThan: 较低优先级组的名称 + lowerThan: 较高优先级组的名称 + associativity: 结合性 + assignment: 赋值性 +} +``` +较低优先级组和较高优先级组的名称说明了新建的优先级组是依赖于现存的优先级组的。 `lowerThan`优先级组的属性只可以引用当前模块外的优先级组。当两个运算符为同一个操作数竞争时,比如表达式`2 + 3 * 5`,优先级更高的运算符将优先参与运算。 + +> 注意 +> 使用较低和较高优先级组相互联系的优先级组必须保持单一层次关系,但它们不必是线性关系。这意味着优先级组也许会有未定义的相关优先级。这些优先级组的运算符在没有用圆括号分组的情况下是不能紧邻着使用的。 + +Swift定义了大量的优先级组来与标准库的运算符配合使用,例如相加(`+`)和相减(`-`)属于`AdditionPrecedence`组,相乘(`*`)和相除(`/`)属于` MultiplicationPrecedence`组,详细关于Swift标准库中一系列运算符和优先级组内容,参阅[Swift标准库操作符参考](https://developer.apple.com/reference/swift/1851035-swift_standard_library_operators)。 + +运算符的结合性表示在没有圆括号分组的情况下,同样优先级的一系列运算符是如何被分组的。你可以指定运算符的结合性通过上下文关键字`left`、`right`或者`none`,如果没有指定结合性,默认是`none`关键字。左关联性的运算符是从左至右分组的,例如,相减操作符(-)是左关联性的,所以表达式`4 - 5 - 6`被分组为`(4 - 5) - 6`,得出结果-7。右关联性的运算符是从右往左分组的,指定为`none`结合性的运算符就没有结合性。同样优先级没有结合性的运算符不能相邻出现,例如`<`运算符是`none`结合性,那表示`1 < 2 < 3`就不是一个有效表达式。 + +优先级组的赋值性表示在包含可选链操作时的运算符优先级。当设为true时,与优先级组对应的运算符在可选链操作中使用和标准库中赋值运算符同样的分组规则,当设为false或者不设置,该优先级组的运算符与不赋值的运算符遵循同样的可选链规则。 + + +> 优先级组声明语法 + + +> *优先级组声明* → **precedence**[*优先级组名称*](#precedence-group-name){[*多优先级组属性*](#precedence-group-attributes)可选 } + +> *优先级组属性* → [*优先级组属性*](#precedence-group-attribute)[*多优先级组属性*](#precedence-group-attributes)可选 **{** **}** + +> *优先级组属性* → [*优先级组关系*](#precedence-group-relation) +> *优先级组属性* → [*优先级组赋值性*](#precedence-group-assignment) +> *优先级组属性* → [*优先级组相关性*](#precedence-group-associativity) +> +> *优先级组关系* → **higherThan:**[*多优先级组名称*](#precedence-group-names) +> *优先级组关系* → **lowerThan:**[*多优先级组名称*](#precedence-group-names) +> +> *优先级组赋值* → **assignment:**[*布尔字面值*](02_Lexical_Structure.md#boolean-literal) + +> *优先级组结合性* → **associativity:left** +> *优先级组结合性* → **associativity:right** +> *优先级组结合性* → **associativity:none** + +> *多优先级组名称* → [*优先级组名称*](#precedence-group-name) | [*优先级组名称*](#precedence-group-name) | [*优先级组名称*](#precedence-group-name) + +> *优先级组名称* →[*标识符*](02_Lexical_Structure.md#identifier) + - ## 声明修饰符 声明修饰符都是关键字或上下文相关的关键字,可以修改一个声明的行为或者含义。可以在声明的特性(如果存在)和引入该声明的关键字之间,利用声明修饰符的关键字或上下文相关的关键字指定一个声明修饰符。 diff --git a/source/chapter3/06_Attributes.md b/source/chapter3/06_Attributes.md index e1f48024..bf4af714 100755 --- a/source/chapter3/06_Attributes.md +++ b/source/chapter3/06_Attributes.md @@ -14,112 +14,113 @@ 本页内容包括: - [声明特性](#declaration_attributes) - - [Interface Builder 使用的声明特性](#declaration_attributes_used_by_interface_builder) +- [Interface Builder 使用的声明特性](#declaration_attributes_used_by_interface_builder) - [类型特性](#type_attributes) -特性提供了关于声明和类型的更多信息。在 Swift 中有两类特性,分别用于修饰声明和类型。 +特性提供了有关声明和类型的更多信息。在Swift中有两种特性,分别用于修饰声明和类型。 -通过以下方式指定一个特性:符号 `@` 后面跟特性名,如果包含参数,则把参数带上: +您可以通过以下方式指定一个特性:符号`@`后跟特性的名称和特性接收的任何参数: -> @`特性名` -> @`特性名`(`特性参数`) +> @ `特性名` + +> @ `特性名`(`特性参数`) 有些声明特性通过接收参数来指定特性的更多信息以及它是如何修饰某个特定的声明的。这些特性的参数写在圆括号内,它们的格式由它们所属的特性来定义。 -## 声明特性 - -声明特性只能应用于声明。然而,你也可以将 `noreturn` 特性应用于函数或方法类型。 - -`autoclosure` - -这个特性通过把表达式自动封装成无参数的闭包来延迟表达式的计算。它可以修饰类型为返回表达式结果类型的无参数函数类型的函数参数。含有 `autoclosure` 特性的声明同时也具有 `noescape` 的特性,除非传递可选特性参数 `escaping`。关于如何使用 `autoclosure` 特性的例子,请参阅 [自动闭包](../chapter2/07_Closures.md#autoclosures) 和 [函数类型](03_Types.md#function_type)。 +##声明特性 +声明特性只能应用于声明。 `available` -将 `available` 特性用于声明时,意味着该声明的生命周期会依赖于特定的平台和操作系统版本。 +将 `available` 特性用于声明时,表示该声明的生命周期与特定的平台和操作系统版本有关。 `available` 特性经常与参数列表一同出现,该参数列表至少有两个特性参数,参数之间由逗号分隔。这些参数由以下这些平台名字中的一个起头: -- `iOS` -- `iOSApplicationExtension` -- `OSX` -- `OSXApplicationExtension` -- `watchOS` -- `watchOSApplicationExtension` -- `tvOS` -- `tvOSApplicationExtension` +- iOS +- iOSApplicationExtension +- macOS +- macOSApplicationExtension +- watchOS +- watchOSApplicationExtension +- tvOS +- tvOSApplicationExtension -当然,你也可以用一个星号(`*`)来表示上面提到的所有平台。 - -剩下的参数,可以以任何顺序出现,并且可以添加关于声明生命周期的附加信息,包括重要事件。 - -- `unavailable` 参数表示该声明在指定的平台上是无效的。 +当然,你也可以用一个星号(*)来表示上面提到的所有平台。 +其余的参数,可以按照任何顺序出现,并且可以添加关于声明生命周期的附加信息,包括重要事件。 +- `unavailable`参数表示该声明在指定的平台上是无效的。 - `introduced` 参数表示指定平台从哪一版本开始引入该声明。格式如下: - `introduced`=`版本号` +`introduced`=`版本号` -版本号由一个十进制正整数或正浮点数构成。 +*版本号*由一个或多个正整数构成,由句点分隔的。 -- `deprecated` 参数表示指定平台从哪一版本开始弃用该声明。格式如下: +- `deprecated`参数表示指定平台从哪一版本开始弃用该声明。格式如下: - `deprecated`=`版本号` +`deprecated`=`版本号` -版本号由一个十进制正整数或正浮点数构成。 +可选的*版本号*由一个或多个正整数构成,由句点分隔的。省略版本号表示该声明目前已弃用,当弃用出现时无需给出任何有关信息。如果你省略了版本号,冒号(:)也可省略。 - `obsoleted` 参数表示指定平台从哪一版本开始废弃该声明。当一个声明被废弃后,它就从平台中移除,不能再被使用。格式如下: - `obsoleted`=`版本号` +`obsoleted`=`版本号` -版本号由一个十进制正整数或正浮点数构成。 +*版本号*由一个或多个正整数构成,由句点分隔的。 - `message` 参数用来提供文本信息。当使用被弃用或者被废弃的声明时,编译器会抛出警告或错误信息。格式如下: - `message`=`信息内容` +`message`=`信息内容` -信息内容由一个字符串字面量构成。 +信息内容由一个字符串构成。 - `renamed` 参数用来提供文本信息,用以表示被重命名的声明的新名字。当使用声明的旧名字时,编译器会报错提示新名字。格式如下: - `renamed`=`新名字` +`renamed`=`新名字` -新名字由一个字符串字面量构成。 +新名字由一个字符串构成。 -你可以将 `renamed` 参数和 `unavailable` 参数以及类型别名声明组合使用,以此向用户表示某个声明已经被重命名。当某个声明的名字在一个框架或者库的不同发布版本间发生变化时,这会相当有用。 +你可以将`renamed` 参数和 `unavailable` 参数以及类型别名声明组合使用,以此向用户表示某个声明已经被重命名。当某个声明的名字在一个框架或者库的不同发布版本间发生变化时,这会相当有用。 ```swift // 首发版本 protocol MyProtocol { - // 这里是协议定义 +// 这里是协议定义 } ``` ```swift // 后续版本重命名了 MyProtocol protocol MyRenamedProtocol { - // 这里是协议定义 +// 这里是协议定义 } - -@available(*, unavailable, renamed="MyRenamedProtocol") +@available(*, unavailable, renamed:"MyRenamedProtocol") typealias MyProtocol = MyRenamedProtocol ``` -你可以在某个声明上使用多个 `available` 特性,以指定该声明在不同平台上的有效性。编译器只有在当前目标平台和 `available` 特性中指定的平台匹配时,才会使用 `available` 特性。 +你可以在某个声明上使用多个 `available` 特性,以指定该声明在不同平台上的可用性。编译器只有在当前目标平台和 `available` 特性中指定的平台匹配时,才会使用 `available` 特性。 如果 `available` 特性除了平台名称参数外,只指定了一个 `introduced` 参数,那么可以使用以下简写语法代替: -> @available(`平台名称` `版本号`, * ) +@available(平台名称 版本号,*) `available` 特性的简写语法可以简明地表达出声明在多个平台上的可用性。尽管这两种形式在功能上是相同的,但请尽可能地使用简写语法形式。 ```swift -@available(iOS 8.0, OSX 10.10, *) +@available(iOS 10.0, macOS 10.12, *) class MyClass { - // 这里是类定义 +// 这里是类定义 } ``` +`discardableResult` + +该特性用于的函数或方法声明,以抑制编译器中 函数或方法的返回值被调而没有使用其结果的警告。 + +`GKInspectable` + +应用此属性,暴露一个自定义GameplayKit组件属性给SpriteKit编辑器UI。 + `objc` 该特性用于修饰任何可以在 Objective-C 中表示的声明。比如,非嵌套类、协议、非泛型枚举(仅限原始值为整型的枚举)、类和协议中的属性和方法(包括存取方法)、构造器、析构器以及下标运算符。`objc` 特性告诉编译器这个声明可以在 Objective-C 代码中使用。 @@ -128,23 +129,17 @@ class MyClass { 如果你将 `objc` 特性应用于枚举,每一个枚举用例都会以枚举名称和用例名称组合的方式暴露在 Objective-C 代码中。例如,在 `Planet` 枚举中有一个名为 `Venus` 的用例,该用例暴露在 Objective-C 代码中时叫做 `PlanetVenus`。 -`objc` 特性有一个可选的参数,由标识符构成。当你想把 `objc` 所修饰的实体以一个不同的名字暴露给 Objective-C 时,你就可以使用这个特性参数。你可以使用这个参数来命名类、枚举类型、枚举用例、协议、方法、存取方法以及构造器。下面的例子把 `ExampleClass` 中的 `enabled` 属性的取值方法暴露给 Objective-C,名字是 `isEnabled`,而不是它原来的属性名。 +`objc` 特性有一个可选的参数,由标识符构成。当你想把 objc 所修饰的实体以一个不同的名字暴露给 Objective-C 时,你就可以使用这个特性参数。你可以使用这个参数来命名类、枚举类型、枚举用例、协议、方法、存取方法以及构造器。下面的例子把 `ExampleClass` 中的 `enabled` 属性的取值方法暴露给 Objective-C,名字是 `isEnabled`,而不是它原来的属性名。 ```swift @objc class ExampleClass: NSObject { - var enabled: Bool { - @objc(isEnabled) get { - // 返回适当的值 - } - } +var enabled: Bool { +@objc(isEnabled) get { +// 返回适当的值 } +} } ``` - -`noescape` - -在函数或者方法声明上使用该特性,它表示参数将不会被存储以供延迟执行,这将确保参数不会超出函数调用的生命周期。在使用 `noescape` 声明特性的函数类型中访问属性和方法时不需要显式地使用 `self.`。关于如何使用 `noescape` 特性的例子,请参阅 [非逃逸闭包](../chapter2/07_Closures.md#nonescaping_closures)。 - `nonobjc` 该特性用于方法、属性、下标、或构造器的声明,这些声明本可以在 Objective-C 代码中使用,而使用 `nonobjc` 特性则告诉编译器这个声明不能在 Objective-C 代码中使用。 @@ -153,77 +148,56 @@ class ExampleClass: NSObject { 标有 `nonobjc` 特性的方法不能重写标有 `objc` 特性的方法。然而,标有 `objc` 特性的方法可以重写标有 `nonobjc` 特性的方法。同样,标有 `nonobjc` 特性的方法不能满足标有 `@objc` 特性的协议中的方法要求。 -`noreturn` - -该特性用于修饰函数或方法声明,表明该函数或方法的对应类型 `T` 是 `@noreturn T`。你可以用这个特性修饰函数或方法类型,以此表明函数或方法不会返回到它的调用者。 - -对于没有用 `noreturn` 特性标记的函数或方法,你可以将它重写为用该特性标记的。相反,对于已经用 `noreturn` 特性标记的函数或方法,你不可以将它重写为没使用该特性标记的。当你在一个某个采纳协议的类型中实现协议方法时,该规则同样适用。 - `NSApplicationMain` -在类上使用该特性表示该类是应用程序委托类,使用该特性与调用 `NSApplicationMain(_:_:)` 函数并且把该类的名字作为委托类的名字传递给函数的效果相同。 +在类上使用该特性表示该类是应用程序委托类,使用该特性与调用 `NSApplicationMain`(\_:_:) 函数并且把该类的名字作为委托类的名字传递给函数的效果相同。 -如果你不想使用这个特性,可以提供一个 `main.swift` 文件,并且提供一个 `main()` 函数去调用 `NSApplicationMain(_:_:)` 函数。比如,如果你的应用程序使用一个继承于 `NSApplication` 的自定义子类作为主要类,你可以调用 `NSApplicationMain(_:_:)` 函数而不是使用该特性。 +如果你不想使用这个特性,可以提供一个 main.swift 文件,并在代码**顶层**调用`NSApplicationMain`(\_:_:) 函数,如下所示: +```swift +import AppKit +NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv) +``` `NSCopying` -该特性用于修饰一个类的存储型变量属性。该特性将使属性的设值方法使用传入值的副本进行赋值,这个值由传入值的 `copyWithZone(_:)` 方法返回。该属性的类型必需符合 `NSCopying` 协议。 +该特性用于修饰一个类的存储型变量属性。该特性将使属性的设值方法使用传入值的副本进行赋值,这个值由传入值的 `copyWithZone`(\_:) 方法返回。该属性的类型必需符合 `NSCopying` 协议。 `NSCopying` 特性的行为与 Objective-C 中的 `copy` 特性相似。 `NSManaged` -该特性用于修饰 `NSManagedObject` 子类中的实例方法或存储型变量属性,表明它们的实现由 Core Data 在运行时基于相关实体描述动态提供。对于标记了 `NSManaged` 特性的属性,Core Data 也会在运行时为其提供存储。 +该特性用于修饰 `NSManagedObject` 子类中的实例方法或存储型变量属性,表明它们的实现由 `Core Data` 在运行时基于相关实体描述动态提供。对于标记了 `NSManaged` 特性的属性,`Core Data` 也会在运行时为其提供存储。应用这个特性也意味着`objc`特性。 `testable` -在导入允许测试的编译模块时,该特性用于修饰 `import` 声明,这样就能访问被导入模块中的任何标有 `internal` 访问级别修饰符的实体,犹如它们被标记了 `public` 访问级别修饰符。 +在导入允许测试的编译模块时,该特性用于修饰 `import` 声明,这样就能访问被导入模块中的任何标有 `internal` 访问级别修饰符的实体,犹如它们被标记了 `public` 访问级别修饰符。测试也可以访问使用`internal`或者`public`访问级别修饰符标记的类和类成员,就像它们是`open`访问修饰符声明的。 `UIApplicationMain` -在类上使用该特性表示该类是应用程序委托类,使用该特性与调用 `UIApplicationMain(_:_:)` 函数并且把该类的名字作为委托类的名字传递给函数的效果相同。 +在类上使用该特性表示该类是应用程序委托类,使用该特性与调用 `UIApplicationMain`函数并且把该类的名字作为委托类的名字传递给函数的效果相同。 -如果你不想使用这个特性,可以提供一个 `main.swift` 文件,并且提供一个 `main()` 函数去调用 `UIApplicationMain(_:_:)` 函数。比如,如果你的应用程序使用一个继承于 `UIApplication` 的自定义子类作为主要类,你可以调用 `UIApplicationMain(_:_:_:)` 函数而不是使用该特性。 - -`warn_unused_result` - -该特性应用于方法或函数声明,当方法或函数被调用,但其结果未被使用时,该特性会让编译器会产生警告。 - -当某个方法有一个对应的变异版本,但是用户却还是错误地使用了非变异版本时,你可以使用这个特性提供一个警告信息。 - -`warn_unused_result` 特性可以选择接受下面两个参数之中的一个。 - -- `message` 参数用来提供警告信息。当方法或函数被调用,但其结果未被使用时,会显示警告信息。格式如下: - - `message`=`警告信息` - -警告信息由一个字符串字面量构成。 - -- `mutable_variant` 参数用于提供方法的变异版本的名称,如果非变异方法在一个可变值上调用,但是返回结果却未被使用时,应该使用此方法的变异版本。格式如下: - - `mutable_variant`=`变异版本的方法名` - -变异版本的方法名由一个字符串字面量构成。 - -比如,Swift 标准库同时为元素符合 `Comparable` 协议的集合类型提供了变异方法 `sortInPlace()` 和非变异方法 `sort()`。如果你调用了 `sort()` 方法,却没有使用它的返回结果,那么你很可能本想使用变异方法 `sortInPlace()`。 +如果你不想使用这个特性,可以提供一个 main.swift 文件,并在代码顶层调用 `UIApplicationMain`(\_:\_:\_:) 函数。比如,如果你的应用程序使用一个继承于 UIApplication 的自定义子类作为主要类,你可以调用 `UIApplicationMain`(\_:\_:\_:) 函数而不是使用该特性。 -### Interface Builder 使用的声明特性 - -Interface Builder 特性是 Interface Builder 用来与 Xcode 同步的声明特性。Swift 提供了以下的 Interface Builder 特性:`IBAction`,`IBDesignable`,`IBInspectable`,以及 `IBOutlet`。这些特性与 Objective-C 中对应的特性在概念上是相同的。 +###Interface Builder 使用的声明特性 +`Interface Builder` 特性是 `Interface Builder` 用来与 Xcode 同步的声明特性。`Swift` 提供了以下的 `Interface Builder` 特性:`IBAction`,`IBOutlet`,`IBDesignable`,以及`IBInspectable` 。这些特性与 Objective-C 中对应的特性在概念上是相同的。 `IBOutlet` 和 `IBInspectable` 用于修饰一个类的属性声明,`IBAction` 特性用于修饰一个类的方法声明,`IBDesignable` 用于修饰类的声明。 - -## 类型特性 +`IBAction` 和 `IBOutlet` 特性都意味着`objc`特性。 -类型特性只能用于修饰类型。然而,你也可以用 `noreturn` 特性去修饰函数或方法声明。 + +##类型特性 +类型特性只能用于修饰类型。 + +`autoclosure` + +这个特性通过把表达式自动封装成无参数的闭包来延迟表达式的计算。它可以修饰类型为返回表达式结果类型的无参数函数类型的函数参数。关于如何使用 autoclosure 特性的例子,请参阅 [自动闭包](http://wiki.jikexueyuan.com/project/swift/chapter2/07_Closures.html/) 和 [函数类型](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html)。 `convention` - 该特性用于修饰函数类型,它指出了函数调用的约定。 -`convention` 特性总是与下面的参数之一一起出现。 +convention 特性总是与下面的参数之一一起出现。 - `swift` 参数用于表示一个 Swift 函数引用。这是 Swift 中函数值的标准调用约定。 @@ -233,26 +207,27 @@ Interface Builder 特性是 Interface Builder 用来与 Xcode 同步的声明特 使用 C 函数调用约定的函数也可用作使用 Objective-C 块调用约定的函数,同时使用 Objective-C 块调用约定的函数也可用作使用 Swift 函数调用约定的函数。然而,只有非泛型的全局函数、局部函数以及未捕获任何局部变量的闭包,才可以被用作使用 C 函数调用约定的函数。 -`noreturn` +`escaping` +在函数或者方法声明上使用该特性,它表示参数将不会被存储以供延迟执行,这将确保参数不会超出函数调用的生命周期。在使用 `escaping` 声明特性的函数类型中访问属性和方法时不需要显式地使用 `self.`。关于如何使用 `escaping` 特性的例子,请参阅 [逃逸闭包](http://wiki.jikexueyuan.com/project/swift/chapter2/07_Closures.html)。 -该特性用于修饰函数或方法的类型,表明该函数或方法不会返回到它的调用者。你也可以用它标记函数或方法的声明,表示函数或方法的相应类型 `T` 是 `@noreturn T`。 +>特性语法 -> 特性语法 +> *特性 *→ @ 特性名 特性参数子句可选 - -> *特性* → **@** [*特性名*](#attribute-name) [*特性参数子句*](#attribute-argument-clause)可选 - -> *特性名* → [*标识符*](02_Lexical_Structure.md#identifiers) - -> *特性参数子句* → **(** [*均衡令牌列表*](#balanced-tokens)可选 **)** - -> *特性列表* → [*特性*](#attribute) [*特性列表*](#attributes)可选 +> *特性名* → 标识符 - -> *均衡令牌列表* → [*均衡令牌*](#balanced-token) [*均衡令牌列表*](#balanced-tokens)可选 - -> *均衡令牌* → **(** [*均衡令牌列表*](#balanced-tokens)可选 **)** -> *均衡令牌* → **[** [*均衡令牌列表*](#balanced-tokens)可选 **]** -> *均衡令牌* → **{** [*均衡令牌列表*](#balanced-tokens)可选 **}** -> *均衡令牌* → **任意标识符,关键字,字面量或运算符** -> *均衡令牌* → **任意标点除了 (,),[,],{,或 }** +> *特性参数子句* → ( 均衡令牌列表可选 ) + +> *特性列表* → 特性 特性列表可选 + +> *均衡令牌列表* → 均衡令牌 均衡令牌列表可选 + +> *均衡令牌* → ( 均衡令牌列表可选 ) + +> *均衡令牌* → [ 均衡令牌列表可选 ] + +> *均衡令牌* → { 均衡令牌列表可选} + +> *均衡令牌* → 任意标识符,关键字,字面量或运算符 + +> *均衡令牌* → 任意标点除了 (,),[,],{,或 } diff --git a/source/chapter3/10_Statements.md b/source/chapter3/10_Statements.md index 0b7fd6a8..ad8b3e75 100755 --- a/source/chapter3/10_Statements.md +++ b/source/chapter3/10_Statements.md @@ -12,6 +12,9 @@ > 2.2 > 翻译:[chenmingbiao](https://github.com/chenmingbiao) +> 3.0 +> 翻译:[chenmingjia](https://github.com/chenmingjia) + 本页包含内容: - [循环语句](#loop_statements) @@ -108,7 +111,7 @@ while 条件 { 由于会在执行循环体中的语句前判断条件的值,因此循环体中的语句可能会被执行若干次,也可能一次也不会被执行。 -条件的结果必须符合 `BooleanType` 协议。另外,条件语句也可以使用可选绑定,请参阅 [可选绑定](../chapter2/01_The_Basics.md#optional_binding)。 +条件的结果必须是Bool类型或者Bool的桥接类型。另外,条件语句也可以使用可选绑定,请参阅 [可选绑定](../chapter2/01_The_Basics.md#optional_binding)。 > while 语句语法 @@ -124,18 +127,13 @@ while 条件 { > *条件列表* → [*条件*](#condition) | [*条件*](#condition) **,** [*条件列表*](#condition-list) -> *条件* → [*可用性条件*](#availability-condition) | [*case条件*](#case-condition) | [*可选绑定条件*](#optional-binding-condition) +> *条件* → [*表达式*](04_Expressions.md#expression) |[*可用性条件*](#availability-condition) | [*case条件*](#case-condition) | [*可选绑定条件*](#optional-binding-condition) -> *case 条件* → **case** [*模式*](07_Patterns.md#pattern) [*构造器*](05_Declarations.md#initializer) [where子句](#where-clause)可选 +> *case 条件* → **case** [*模式*](07_Patterns.md#pattern) [*构造器*](05_Declarations.md#initializer) -> *可选绑定条件* → [*可选绑定头*](#optional-binding-head) [*可选绑定附加列表*](#optional-binding-continuation-list)可选 [*where子句*](#where-clause)可选 - -> *可选绑定头* → **let** [*模式*](07_Patterns.md#pattern) [*构造器*](05_Declarations.md#initializer) | **var** [*模式*](07_Patterns.md#pattern) [*构造器*](05_Declarations.md#initializer) - -> *可选绑定附加部分列表* → [*可选绑定附加部分*](#optional-binding-continuation) | [*可选绑定附加部分*](#optional-binding-continuation) **,** [*可选绑定附加部分列表*](#optional-binding-continuation-list) - -> *可选绑定附加部分* → [*模式*](07_Patterns.md#pattern) [*构造器*](05_Declarations.md#initializer) | [*可选绑定头*](#optional-binding-head) +> *可选绑定条件* → **let** [*模式*](07_Patterns.md#pattern) [*构造器*](05_Declarations.md#initializer) | **var** [*模式*](07_Patterns.md#pattern) [*构造器*](05_Declarations.md#initializer) + ### Repeat-While 语句 @@ -157,7 +155,7 @@ repeat { 由于条件的值是在循环体中的语句执行后才进行判断,因此循环体中的语句至少会被执行一次。 -条件的结果必须符合 `BooleanType` 协议。另外,条件语句也可以使用可选绑定,请参阅 [可选绑定](../chapter2/01_The_Basics.md#optional_binding)。 +条件的结果必须是Bool类型或者Bool的桥接类型。另外,条件语句也可以使用可选绑定,请参阅 [可选绑定](../chapter2/01_The_Basics.md#optional_binding)。 > repeat-while 语句语法 @@ -213,7 +211,7 @@ if 条件1 { } ``` -`if` 语句中条件的结果必须符合 `BooleanType` 协议。另外,条件语句也可以使用可选绑定,请参阅 [可选绑定](../chapter2/01_The_Basics.md#optional_binding)。 +`if` 语句中条件的结果必须是Bool类型或者Bool的桥接类型。另外,条件语句也可以使用可选绑定,请参阅 [可选绑定](../chapter2/01_The_Basics.md#optional_binding)。 > if 语句语法 @@ -234,18 +232,18 @@ guard 条件 else { } ``` -`guard` 语句中条件的结果必须符合 `BooleanType` 协议。另外,条件也可以是一条可选绑定,请参阅 [可选绑定](../chapter2/01_The_Basics.html#optional_binding)。 +`guard` 语句中条件的结果必须是Bool类型或者Bool的桥接类型。另外,条件也可以是一条可选绑定,请参阅 [可选绑定](../chapter2/01_The_Basics.html#optional_binding)。 在 `guard` 语句中进行可选绑定的常量或者变量,其可用范围从声明开始直到作用域结束。 -`guard` 语句必须有 `else` 子句,而且必须在该子句中调用标记 `noreturn` 特性的函数,或者使用下面的语句退出当前作用域: +`guard` 语句必须有 `else` 子句,而且必须在该子句中调用 `Never` 返回类型的函数,或者使用下面的语句退出当前作用域: * `return` * `break` * `continue` * `throw` -关于控制转移语句,请参阅 [控制转移语句](#control_transfer_statements)。 +关于控制转移语句,请参阅 [控制转移语句](#control_transfer_statements)。关于`Never`返回类型的函数,请参阅 [永不返回的函数](05_Declarations.md#rethrowing_functions_and_methods)。 > guard 语句语法 @@ -281,7 +279,7 @@ default: case let (x, y) where x == y: ``` -正如上面这个例子,也可以在模式中使用 `let`(或 `var`)语句来绑定常量(或变量)。这些常量(或变量)可以在对应的 `where` 子句以及 `case` 中的代码中使用。但是,如果一个 `case` 中含有多个模式,那么这些模式都不能绑定常量(或变量)。 +正如上面这个例子,也可以在模式中使用 `let`(或 `var`)语句来绑定常量(或变量)。这些常量(或变量)可以在对应的 `where` 子句以及 `case` 中的代码中使用。但是,如果一个 `case` 中含有多个模式,所有的模式必须包含相同的常量(或变量)绑定,并且每一个绑定的常量(或变量)必须在所有的条件模式中都有相同的类型。 `switch` 语句也可以包含默认分支,使用 `default` 关键字表示。只有所有 `case` 都无法匹配控制表达式时,默认分支中的代码才会被执行。一个 `switch` 语句只能有一个默认分支,而且必须在 `switch` 语句的最后面。 @@ -606,19 +604,19 @@ do { 行控制语句形式如下: -> \#line `行号` `文件名` +> \#sourceLocation(file: `文件名` , line:`行号`) -行控制语句会改变该语句之后的代码中的字面量表达式 `#line` 和 `#file` 所表示的值。`行号` 是一个大于 0 的整形字面量,会改变 `#line` 表达式的值。`文件名` 是一个字符串字面量,会改变 `#file` 表达式的值。 +> \#sourceLocation() -你可以只写 `#line`,而不指定行号和文件名,从而将源代码的定位信息重置回默认的行号和文件名。 +第一种的行控制语句会改变该语句之后的代码中的字面量表达式 `#line` 和 `#file` 所表示的值。`行号` 是一个大于 0 的整形字面量,会改变 `#line` 表达式的值。`文件名` 是一个字符串字面量,会改变 `#file` 表达式的值。 -`#line` 标记具有两种含义,作为行控制语句使用时必须独占一行,而且不能是源代码文件的最后一行。否则,它将作为字面量表达式使用,详情请参阅 [字面量表达式](04_Expressions.md#literal_expression)。 +第二种的行控制语句, `#sourceLocation()`,会将源代码的定位信息重置回默认的行号和文件名。 > 行控制语句语法 -> *行控制语句* → **#line** -> *行控制语句* → **#line** [*行号*](#line-number) [*文件名*](#file-name) +> *行控制语句* → **#sourceLocation(file:[*文件名*](#file-name),line:[*行号*](#line-number))** +> *行控制语句* → **#sourceLocation()** > *行号* → 大于 0 的十进制整数 @@ -663,5 +661,5 @@ if #available(平台名称 版本, ..., *) { > *平台版本* → [十进制数字](02_Lexical_Structure.md#decimal-digits) > *平台版本* → [十进制数字](02_Lexical_Structure.md#decimal-digits) **.** [十进制数字](02_Lexical_Structure.md#decimal-digits) > *平台版本* → [十进制数字](02_Lexical_Structure.md#decimal-digits) **.** [十进制数字](02_Lexical_Structure.md#decimal-digits) **.** [十进制数字](02_Lexical_Structure.md#decimal-digits) - - + +