diff --git a/chapter1/01_swift.html b/chapter1/01_swift.html old mode 100755 new mode 100644 index 9f0a665c..6225bbd5 --- a/chapter1/01_swift.html +++ b/chapter1/01_swift.html @@ -46,7 +46,7 @@ -
-翻译:numbbbbb
-校对:yeahdongcn
+翻译:numbbbbb
校对:yeahdongcn
Swift 是一种新的编程语言,用于编写 iOS 和 OS X 应用。Swift 结合了 C 和 Objective-C 的优点并且不受C兼容性的限制。Swift 采用安全的编程模式并添加了很多新特性,这将使编程更简单,更灵活,也更有趣。Swift 是基于成熟而且倍受喜爱的 Cocoa 和 Cocoa Touch 框架,他的降临将重新定义软件开发。
-Swift 的开发从很久之前就开始了。为了给 Swift 打好基础,苹果公司改进了编译器,调试器和框架结构。我们使用自动引用计数(Automatic Reference Counting, ARC)来简化内存管理。我们在 Foundation 和 Cocoa的基础上构建框架栈并将其标准化。Objective-C 本身支持块、集合语法和模块,所以框架可以轻松支持现代编程语言技术。正是得益于这些基础工作,我们现在才能发布这样一个用于未来苹果软件开发的新语言。
+Swift 是一种新的编程语言,用于编写 iOS 和 OS X 应用。Swift 结合了 C 和 Objective-C 的优点并且不受 C 兼容性的限制。Swift 采用安全的编程模式并添加了很多新特性,这将使编程更简单,更灵活,也更有趣。Swift 是基于成熟而且倍受喜爱的 Cocoa 和 Cocoa Touch 框架,它的降临将重新定义软件开发。
+Swift 的开发从很久之前就开始了。为了给 Swift 打好基础,苹果公司改进了编译器,调试器和框架结构。我们使用自动引用计数(Automatic Reference Counting, ARC)来简化内存管理。我们在 Foundation 和 Cocoa 的基础上构建框架栈并将其标准化。Objective-C 本身支持块、集合语法和模块,所以框架可以轻松支持现代编程语言技术。正是得益于这些基础工作,我们现在才能发布这样一个用于未来苹果软件开发的新语言。
Objective-C 开发者对 Swift 并不会感到陌生。它采用了 Objective-C 的命名参数以及动态对象模型,可以无缝对接到现有的 Cocoa 框架,并且可以兼容 Objective-C 代码。在此基础之上,Swift 还有许多新特性并且支持过程式编程和面向对象编程。
Swift 对于初学者来说也很友好。它是第一个既满足工业标准又像脚本语言一样充满表现力和趣味的编程语言。它支持代码预览,这个革命性的特性可以允许程序员在不编译和运行应用程序的前提下运行 Swift 代码并实时查看结果。
Swift 将现代编程语言的精华和苹果工程师文化的智慧结合了起来。编译器对性能进行了优化,编程语言对开发进行了优化,两者互不干扰,鱼与熊掌兼得。Swift 既可以用于开发 “hello, world” 这样的小程序,也可以用于开发一套完整的操作系统。所有的这些特性让 Swift 对于开发者和苹果来说都是一项值得的投资。
-用 Swift 编写 iOS 和 OS X 应用将是一场美妙的体验,Swift 之后也会不断开发新特性和兼容性。我们对 Swift 充满信心,你还在等什么!
+Swift 是编写 iOS 和 OS X 应用的极佳手段,并将伴随着新的特性和功能持续演进。我们对 Swift 充满信心,你还在等什么!
-翻译:numbbbbb -校对:shinyzhu
+翻译:numbbbbb
校对:shinyzhu, stanzhai
通常来说,编程语言教程中的第一个程序应该在屏幕上打印“Hello, world”。在 Swift 中,可以用一行代码实现:
- println("Hello, world")
+println("Hello, world")
如果你写过 C 或者 Objective-C 代码,那你应该很熟悉这种形式——在 Swift 中,这行代码就是一个完整的程序。你不需要为了输入输出或者字符串处理导入一个单独的库。全局作用域中的代码会被自动当做程序的入口点,所以你也不需要main函数。你同样不需要在每个语句结尾写上分号。
这个教程会通过一系列编程例子来让你对 Swift 有初步了解,如果你有什么不理解的地方也不用担心——任何本章介绍的内容都会在后面的章节中详细讲解。
-注意:
-为了获得最好的体验,在 Xcode 当中使用代码预览功能。代码预览功能可以让你编辑代码并实时看到运行结果。
+注意:
为了获得最好的体验,在 Xcode 当中使用代码预览功能。代码预览功能可以让你编辑代码并实时看到运行结果。
简单值
-使用let来声明常量,使用var来声明变量。一个常量的值在编译时并不需要获取,但是你只能为它赋值一次。也就是说你可以用常量来表示这样一个值:你只需要决定一次,但是需要使用很多次。
- var myVariable = 42
- myVariable = 50
- let myConstant = 42
+使用let来声明常量,使用var来声明变量。一个常量的值,在编译的时候,并不需要有明确的值,但是你只能为它赋值一次。也就是说你可以用常量来表示这样一个值:你只需要决定一次,但是需要使用很多次。
+var myVariable = 42
+myVariable = 50
+let myConstant = 42
常量或者变量的类型必须和你赋给它们的值一样。然而,声明时类型是可选的,声明的同时赋值的话,编译器会自动推断类型。在上面的例子中,编译器推断出myVariable是一个整数(integer)因为它的初始值是整数。
如果初始值没有提供足够的信息(或者没有初始值),那你需要在变量后面声明类型,用冒号分割。
- let implicitInteger = 70
- let implicitDouble = 70.0
- let explicitDouble: Double = 70
+let implicitInteger = 70
+let implicitDouble = 70.0
+let explicitDouble: Double = 70
-练习:
-创建一个常量,显式指定类型为Float并指定初始值为4。
+练习:
创建一个常量,显式指定类型为Float并指定初始值为4。
值永远不会被隐式转换为其他类型。如果你需要把一个值转换成其他类型,请显式转换。
- let label = "The width is"
- let width = 94
- let widthLabel = label + String(width)
+let label = "The width is"
+let width = 94
+let widthLabel = label + String(width)
-练习:
-删除最后一行中的String,错误提示是什么?
+练习:
删除最后一行中的String,错误提示是什么?
有一种更简单的把值转换成字符串的方法:把值写到括号中,并且在括号之前写一个反斜杠。例如:
- let apples = 3
- let oranges = 5
- let appleSummary = "I have \(apples) apples."
- let fruitSummary = "I have \(apples + oranges) pieces of fruit."
+let apples = 3
+let oranges = 5
+let appleSummary = "I have \(apples) apples."
+let fruitSummary = "I have \(apples + oranges) pieces of fruit."
-练习:
-使用\()来把一个浮点计算转换成字符串,并加上某人的名字,和他打个招呼。
+练习:
使用\()来把一个浮点计算转换成字符串,并加上某人的名字,和他打个招呼。
使用方括号[]来创建数组和字典,并使用下标或者键(key)来访问元素。
- var shoppingList = ["catfish", "water", "tulips", "blue paint"]
- shoppingList[1] = "bottle of water"
+var shoppingList = ["catfish", "water", "tulips", "blue paint"]
+shoppingList[1] = "bottle of water"
- var occupations = [
- "Malcolm": "Captain",
- "Kaylee": "Mechanic",
- ]
- occupations["Jayne"] = "Public Relations"
+var occupations = [
+ "Malcolm": "Captain",
+ "Kaylee": "Mechanic",
+]
+occupations["Jayne"] = "Public Relations"
要创建一个空数组或者字典,使用初始化语法。
- let emptyArray = String[]()
- let emptyDictionary = Dictionary<String, Float>()
+let emptyArray = String[]()
+let emptyDictionary = Dictionary<String, Float>()
如果类型信息可以被推断出来,你可以用[]和[:]来创建空数组和空字典——就像你声明变量或者给函数传参数的时候一样。
- shoppingList = [] // 去逛街并买点东西
+shoppingList = [] // 去逛街并买点东西
控制流
使用if和switch来进行条件操作,使用for-in、for、while和do-while来进行循环。包裹条件和循环变量括号可以省略,但是语句体的大括号是必须的。
- let individualScores = [75, 43, 103, 87, 12]
- var teamScore = 0
- for score in individualScores {
- if score > 50 {
- teamScore += 3
- } else {
- teamScore += 1
- }
+let individualScores = [75, 43, 103, 87, 12]
+var teamScore = 0
+for score in individualScores {
+ if score > 50 {
+ teamScore += 3
+ } else {
+ teamScore += 1
}
- teamScore
+}
+teamScore
在if语句中,条件必须是一个布尔表达式——这意味着像if score { ... }这样的代码将报错,而不会隐形地与 0 做对比。
你可以一起使用if和let来处理值缺失的情况。有些变量的值是可选的。一个可选的值可能是一个具体的值或者是nil,表示值缺失。在类型后面加一个问号来标记这个变量的值是可选的。
- var optionalString: String? = "Hello"
- optionalString == nil
+var optionalString: String? = "Hello"
+optionalString == nil
- var optionalName: String? = "John Appleseed"
- var greeting = "Hello!"
- if let name = optionalName {
- greeting = "Hello, \(name)"
- }
+var optionalName: String? = "John Appleseed"
+var greeting = "Hello!"
+if let name = optionalName {
+ greeting = "Hello, \(name)"
+}
-练习:
-把optionalName改成nil,greeting会是什么?添加一个else语句,当optionalName是nil时给greeting赋一个不同的值。
+练习:
把optionalName改成nil,greeting会是什么?添加一个else语句,当optionalName是nil时给greeting赋一个不同的值。
如果变量的可选值是nil,条件会判断为false,大括号中的代码会被跳过。如果不是nil,会将值赋给let后面的常量,这样代码块中就可以使用这个值了。
switch支持任意类型的数据以及各种比较操作——不仅仅是整数以及测试相等。
- let vegetable = "red pepper"
- switch vegetable {
- case "celery":
- let vegetableComment = "Add some raisins and make ants on a log."
- case "cucumber", "watercress":
- let vegetableComment = "That would make a good tea sandwich."
- case let x where x.hasSuffix("pepper"):
- let vegetableComment = "Is it a spicy \(x)?"
- default:
- let vegetableComment = "Everything tastes good in soup."
- }
+let vegetable = "red pepper"
+switch vegetable {
+case "celery":
+ let vegetableComment = "Add some raisins and make ants on a log."
+case "cucumber", "watercress":
+ let vegetableComment = "That would make a good tea sandwich."
+case let x where x.hasSuffix("pepper"):
+ let vegetableComment = "Is it a spicy \(x)?"
+default:
+ let vegetableComment = "Everything tastes good in soup."
+}
-练习:
-删除default语句,看看会有什么错误?
+练习:
删除default语句,看看会有什么错误?
运行switch中匹配到的子句之后,程序会退出switch语句,并不会继续向下运行,所以不需要在每个子句结尾写break。
你可以使用for-in来遍历字典,需要两个变量来表示每个键值对。
- let interestingNumbers = [
- "Prime": [2, 3, 5, 7, 11, 13],
- "Fibonacci": [1, 1, 2, 3, 5, 8],
- "Square": [1, 4, 9, 16, 25],
- ]
- var largest = 0
- for (kind, numbers) in interestingNumbers {
- for number in numbers {
- if number > largest {
- largest = number
- }
+let interestingNumbers = [
+ "Prime": [2, 3, 5, 7, 11, 13],
+ "Fibonacci": [1, 1, 2, 3, 5, 8],
+ "Square": [1, 4, 9, 16, 25],
+]
+var largest = 0
+for (kind, numbers) in interestingNumbers {
+ for number in numbers {
+ if number > largest {
+ largest = number
}
}
- largest
+}
+largest
-练习:
-添加另一个变量来记录哪种类型的数字是最大的。
+练习:
添加另一个变量来记录哪种类型的数字是最大的。
使用while来重复运行一段代码直到不满足条件。循环条件可以在开头也可以在结尾。
- var n = 2
- while n < 100 {
- n = n * 2
- }
- n
+var n = 2
+while n < 100 {
+ n = n * 2
+}
+n
- var m = 2
- do {
- m = m * 2
- } while m < 100
- m
+var m = 2
+do {
+ m = m * 2
+} while m < 100
+m
你可以在循环中使用..来表示范围,也可以使用传统的写法,两者是等价的:
- var firstForLoop = 0
- for i in 0..3 {
- firstForLoop += i
- }
- firstForLoop
+var firstForLoop = 0
+for i in 0..3 {
+ firstForLoop += i
+}
+firstForLoop
- var secondForLoop = 0
- for var i = 0; i < 3; ++i {
- secondForLoop += 1
- }
- secondForLoop
+var secondForLoop = 0
+for var i = 0; i < 3; ++i {
+ secondForLoop += 1
+}
+secondForLoop
使用..创建的范围不包含上界,如果想包含的话需要使用...。
函数和闭包
使用func来声明一个函数,使用名字和参数来调用函数。使用->来指定函数返回值。
- func greet(name: String, day: String) -> String {
- return "Hello \(name), today is \(day)."
- }
- greet("Bob", "Tuesday")
+func greet(name: String, day: String) -> String {
+ return "Hello \(name), today is \(day)."
+}
+greet("Bob", "Tuesday")
-练习:
-删除day参数,添加一个参数来表示今天吃了什么午饭。
+练习:
删除day参数,添加一个参数来表示今天吃了什么午饭。
使用一个元组来返回多个值。
- func getGasPrices() -> (Double, Double, Double) {
- return (3.59, 3.69, 3.79)
- }
- getGasPrices()
+func getGasPrices() -> (Double, Double, Double) {
+ return (3.59, 3.69, 3.79)
+}
+getGasPrices()
函数可以带有可变个数的参数,这些参数在函数内表现为数组的形式:
- func sumOf(numbers: Int...) -> Int {
- var sum = 0
- for number in numbers {
- sum += number
- }
- return sum
+func sumOf(numbers: Int...) -> Int {
+ var sum = 0
+ for number in numbers {
+ sum += number
}
- sumOf()
- sumOf(42, 597, 12)
+ return sum
+}
+sumOf()
+sumOf(42, 597, 12)
-练习:
-写一个计算参数平均值的函数。
+练习:
写一个计算参数平均值的函数。
函数可以嵌套。被嵌套的函数可以访问外侧函数的变量,你可以使用嵌套函数来重构一个太长或者太复杂的函数。
- func returnFifteen() -> Int {
- var y = 10
- func add() {
- y += 5
- }
- add()
- return y
+func returnFifteen() -> Int {
+ var y = 10
+ func add() {
+ y += 5
}
- returnFifteen()
+ add()
+ return y
+}
+returnFifteen()
函数是第一等类型,这意味着函数可以作为另一个函数的返回值。
- func makeIncrementer() -> (Int -> Int) {
- func addOne(number: Int) -> Int {
- return 1 + number
- }
- return addOne
+func makeIncrementer() -> (Int -> Int) {
+ func addOne(number: Int) -> Int {
+ return 1 + number
}
- var increment = makeIncrementer()
- increment(7)
+ return addOne
+}
+var increment = makeIncrementer()
+increment(7)
函数也可以当做参数传入另一个函数。
- func hasAnyMatches(list: Int[], condition: Int -> Bool) -> Bool {
- for item in list {
- if condition(item) {
- return true
- }
+func hasAnyMatches(list: Int[], condition: Int -> Bool) -> Bool {
+ for item in list {
+ if condition(item) {
+ return true
}
- return false
}
- func lessThanTen(number: Int) -> Bool {
- return number < 10
- }
- var numbers = [20, 19, 7, 12]
- hasAnyMatches(numbers, lessThanTen)
+ return false
+}
+func lessThanTen(number: Int) -> Bool {
+ return number < 10
+}
+var numbers = [20, 19, 7, 12]
+hasAnyMatches(numbers, lessThanTen)
函数实际上是一种特殊的闭包,你可以使用{}来创建一个匿名闭包。使用in将参数和返回值类型声明与闭包函数体进行分离。
- numbers.map({
- (number: Int) -> Int in
- let result = 3 * number
- return result
- })
+numbers.map({
+ (number: Int) -> Int in
+ let result = 3 * number
+ return result
+})
-练习:
-重写闭包,对所有奇数返回 0.
+练习:
重写闭包,对所有奇数返回 0.
有很多种创建闭包的方法。如果一个闭包的类型已知,比如作为一个回调函数,你可以忽略参数的类型和返回值。单个语句闭包会把它语句的值当做结果返回。
- numbers.map({ number in 3 * number })
+numbers.map({ number in 3 * number })
你可以通过参数位置而不是参数名字来引用参数——这个方法在非常短的闭包中非常有用。当一个闭包作为最后一个参数传给一个函数的时候,它可以直接跟在括号后面。
- sort([1, 5, 3, 12, 2]) { $0 > $1 }
+sort([1, 5, 3, 12, 2]) { $0 > $1 }
对象和类
使用class和类名来创建一个类。类中属性的声明和常量、变量声明一样,唯一的区别就是它们的上下文是类。同样,方法和函数声明也一样。
- class Shape {
- var numberOfSides = 0
- func simpleDescription() -> String {
- return "A shape with \(numberOfSides) sides."
- }
+class Shape {
+ var numberOfSides = 0
+ func simpleDescription() -> String {
+ return "A shape with \(numberOfSides) sides."
}
+}
-练习:
-使用let添加一个常量属性,再添加一个接收一个参数的方法。
+练习:
使用let添加一个常量属性,再添加一个接收一个参数的方法。
要创建一个类的实例,在类名后面加上括号。使用点语法来访问实例的属性和方法。
- var shape = Shape()
- shape.numberOfSides = 7
- var shapeDescription = shape.simpleDescription()
+var shape = Shape()
+shape.numberOfSides = 7
+var shapeDescription = shape.simpleDescription()
这个版本的Shape类缺少了一些重要的东西:一个构造函数来初始化类实例。使用init来创建一个构造器。
- class NamedShape {
- var numberOfSides: Int = 0
- var name: String
+class NamedShape {
+ var numberOfSides: Int = 0
+ var name: String
- init(name: String) {
- self.name = name
- }
-
- func simpleDescription() -> String {
- return "A shape with \(numberOfSides) sides."
- }
+ init(name: String) {
+ self.name = name
}
+
+ func simpleDescription() -> String {
+ return "A shape with \(numberOfSides) sides."
+ }
+}
注意self被用来区别实例变量。当你创建实例的时候,像传入函数参数一样给类传入构造器的参数。每个属性都需要赋值——无论是通过声明(就像numberOfSides)还是通过构造器(就像name)。
如果你需要在删除对象之前进行一些清理工作,使用deinit创建一个析构函数。
子类的定义方法是在它们的类名后面加上父类的名字,用冒号分割。创建类的时候并不需要一个标准的根类,所以你可以忽略父类。
子类如果要重写父类的方法的话,需要用override标记——如果没有添加override就重写父类方法的话编译器会报错。编译器同样会检测override标记的方法是否确实在父类中。
- class Square: NamedShape {
- var sideLength: Double
+class Square: NamedShape {
+ var sideLength: Double
- init(sideLength: Double, name: String) {
- self.sideLength = sideLength
- super.init(name: name)
- numberOfSides = 4
- }
-
- func area() -> Double {
- return sideLength * sideLength
- }
-
- override func simpleDescription() -> String {
- return "A square with sides of length \(sideLength)."
- }
+ init(sideLength: Double, name: String) {
+ self.sideLength = sideLength
+ super.init(name: name)
+ numberOfSides = 4
}
- let test = Square(sideLength: 5.2, name: "my test square")
- test.area()
- test.simpleDescription()
+
+ func area() -> Double {
+ return sideLength * sideLength
+ }
+
+ override func simpleDescription() -> String {
+ return "A square with sides of length \(sideLength)."
+ }
+}
+let test = Square(sideLength: 5.2, name: "my test square")
+test.area()
+test.simpleDescription()
-练习:
-创建NamedShape的另一个子类Circle,构造器接收两个参数,一个是半径一个是名称,实现area和describe方法。
+练习:
创建NamedShape的另一个子类Circle,构造器接收两个参数,一个是半径一个是名称,实现area和describe方法。
属性可以有 getter 和 setter 。
- class EquilateralTriangle: NamedShape {
- var sideLength: Double = 0.0
+class EquilateralTriangle: NamedShape {
+ var sideLength: Double = 0.0
- init(sideLength: Double, name: String) {
- self.sideLength = sideLength
- super.init(name: name)
- numberOfSides = 3
- }
-
- var perimeter: Double {
- get {
- return 3.0 * sideLength
- }
- set {
- sideLength = newValue / 3.0
- }
- }
-
- override func simpleDescription() -> String {
- return "An equilateral triagle with sides of length \(sideLength)."
- }
+ init(sideLength: Double, name: String) {
+ self.sideLength = sideLength
+ super.init(name: name)
+ numberOfSides = 3
}
- var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
- triangle.perimeter
- triangle.perimeter = 9.9
- triangle.sideLength
+
+ var perimeter: Double {
+ get {
+ return 3.0 * sideLength
+ }
+ set {
+ sideLength = newValue / 3.0
+ }
+ }
+
+ override func simpleDescription() -> String {
+ return "An equilateral triagle with sides of length \(sideLength)."
+ }
+}
+var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
+triangle.perimeter
+triangle.perimeter = 9.9
+triangle.sideLength
在perimeter的 setter 中,新值的名字是newValue。你可以在set之后显式的设置一个名字。
注意EquilateralTriangle类的构造器执行了三步:
@@ -945,238 +932,233 @@
调用父类的构造器
改变父类定义的属性值。其他的工作比如调用方法、getters和setters也可以在这个阶段完成。
-如果你不需要计算属性但是需要在设置一个新值之前运行一些代码,使用willSet和didSet。
+如果你不需要计算属性,但是仍然需要在设置一个新值之前或者之后运行代码,使用willSet和didSet。
比如,下面的类确保三角形的边长总是和正方形的边长相同。
- class TriangleAndSquare {
- var triangle: EquilateralTriangle {
- willSet {
- square.sideLength = newValue.sideLength
- }
- }
- var square: Square {
- willSet {
- triangle.sideLength = newValue.sideLength
- }
- }
- init(size: Double, name: String) {
- square = Square(sideLength: size, name: name)
- triangle = EquilateralTriangle(sideLength: size, name: name)
- }
+class TriangleAndSquare {
+ var triangle: EquilateralTriangle {
+ willSet {
+ square.sideLength = newValue.sideLength
}
- var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
- triangleAndSquare.square.sideLength
- triangleAndSquare.triangle.sideLength
- triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
- triangleAndSquare.triangle.sideLength
+ }
+ var square: Square {
+ willSet {
+ triangle.sideLength = newValue.sideLength
+ }
+ }
+ init(size: Double, name: String) {
+ square = Square(sideLength: size, name: name)
+ triangle = EquilateralTriangle(sideLength: size, name: name)
+ }
+}
+var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
+triangleAndSquare.square.sideLength
+triangleAndSquare.triangle.sideLength
+triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
+triangleAndSquare.triangle.sideLength
类中的方法和一般的函数有一个重要的区别,函数的参数名只在函数内部使用,但是方法的参数名需要在调用的时候显式说明(除了第一个参数)。默认情况下,方法的参数名和它在方法内部的名字一样,不过你也可以定义第二个名字,这个名字被用在方法内部。
- class Counter {
- var count: Int = 0
- func incrementBy(amount: Int, numberOfTimes times: Int) {
- count += amount * times
- }
+class Counter {
+ var count: Int = 0
+ func incrementBy(amount: Int, numberOfTimes times: Int) {
+ count += amount * times
}
- var counter = Counter()
- counter.incrementBy(2, numberOfTimes: 7)
+}
+var counter = Counter()
+counter.incrementBy(2, numberOfTimes: 7)
处理变量的可选值时,你可以在操作(比如方法、属性和子脚本)之前加?。如果?之前的值是nil,?后面的东西都会被忽略,并且整个表达式返回nil。否则,?之后的东西都会被运行。在这两种情况下,整个表达式的值也是一个可选值。
- let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
- let sideLength = optionalSquare?.sideLength
+let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
+let sideLength = optionalSquare?.sideLength
枚举和结构体
使用enum来创建一个枚举。就像类和其他所有命名类型一样,枚举可以包含方法。
- enum Rank: Int {
- case Ace = 1
- case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
- case Jack, Queen, King
- func simpleDescription() -> String {
- switch self {
- case .Ace:
- return "ace"
- case .Jack:
- return "jack"
- case .Queen:
- return "queen"
- case .King:
- return "king"
- default:
- return String(self.toRaw())
- }
+enum Rank: Int {
+ case Ace = 1
+ case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
+ case Jack, Queen, King
+ func simpleDescription() -> String {
+ switch self {
+ case .Ace:
+ return "ace"
+ case .Jack:
+ return "jack"
+ case .Queen:
+ return "queen"
+ case .King:
+ return "king"
+ default:
+ return String(self.toRaw())
}
}
- let ace = Rank.Ace
- let aceRawValue = ace.toRaw()
+}
+let ace = Rank.Ace
+let aceRawValue = ace.toRaw()
-练习:
-写一个函数,通过比较它们的原始值来比较两个Rank值。
+练习:
写一个函数,通过比较它们的原始值来比较两个Rank值。
在上面的例子中,枚举原始值的类型是Int,所以你只需要设置第一个原始值。剩下的原始值会按照顺序赋值。你也可以使用字符串或者浮点数作为枚举的原始值。
使用toRaw和fromRaw函数来在原始值和枚举值之间进行转换。
- if let convertedRank = Rank.fromRaw(3) {
- let threeDescription = convertedRank.simpleDescription()
- }
+if let convertedRank = Rank.fromRaw(3) {
+ let threeDescription = convertedRank.simpleDescription()
+}
枚举的成员值是实际值,并不是原始值的另一种表达方法。实际上,如果原始值没有意义,你不需要设置。
- enum Suit {
- case Spades, Hearts, Diamonds, Clubs
- func simpleDescription() -> String {
- switch self {
- case .Spades:
- return "spades"
- case .Hearts:
- return "hearts"
- case .Diamonds:
- return "diamonds"
- case .Clubs:
- return "clubs"
- }
+enum Suit {
+ case Spades, Hearts, Diamonds, Clubs
+ func simpleDescription() -> String {
+ switch self {
+ case .Spades:
+ return "spades"
+ case .Hearts:
+ return "hearts"
+ case .Diamonds:
+ return "diamonds"
+ case .Clubs:
+ return "clubs"
}
-
}
- let hearts = Suit.Hearts
- let heartsDescription = hearts.simpleDescription()
+
+}
+let hearts = Suit.Hearts
+let heartsDescription = hearts.simpleDescription()
-练习:
-给Suit添加一个color方法,对spades和clubs返回“black”,对hearts和diamonds返回“red”。
+练习:
给Suit添加一个color方法,对spades和clubs返回“black”,对hearts和diamonds返回“red”。
注意,有两种方式可以引用Hearts成员:给hearts常量赋值时,枚举成员Suit.Hearts需要用全名来引用,因为常量没有显式指定类型。在switch里,枚举成员使用缩写.Hearts来引用,因为self的值已经知道是一个suit。已知变量类型的情况下你可以使用缩写。
使用struct来创建一个结构体。结构体和类有很多相同的地方,比如方法和构造器。它们之间最大的一个区别就是
结构体是传值,类是传引用。
- struct Card {
- var rank: Rank
- var suit: Suit
- func simpleDescription() -> String {
- return "The \(rank.simpleDescription()) of \
- (suit.simpleDescription())"
- }
+struct Card {
+ var rank: Rank
+ var suit: Suit
+ func simpleDescription() -> String {
+ return "The \(rank.simpleDescription()) of \
+ (suit.simpleDescription())"
}
- let threeOfSpades = Card(rank: .Three, suit: .Spades)
- let threeOfSpadesDescription = threeOfSpades.simpleDescription()
+}
+let threeOfSpades = Card(rank: .Three, suit: .Spades)
+let threeOfSpadesDescription = threeOfSpades.simpleDescription()
-练习:
-给Card添加一个方法,创建一副完整的扑克牌并把每张牌的 rank 和 suit 对应起来。
+练习:
给Card添加一个方法,创建一副完整的扑克牌并把每张牌的 rank 和 suit 对应起来。
一个枚举成员的实例可以有实例值。相同枚举成员的实例可以有不同的值。创建实例的时候传入值即可。实例值和原始值是不同的:枚举成员的原始值对于所有实例都是相同的,而且你是在定义枚举的时候设置原始值。
例如,考虑从服务器获取日出和日落的时间。服务器会返回正常结果或者错误信息。
- enum ServerResponse {
- case Result(String, String)
- case Error(String)
- }
+enum ServerResponse {
+ case Result(String, String)
+ case Error(String)
+}
- let success = ServerResponse.Result("6:00 am", "8:09 pm")
- let failure = ServerResponse.Error("Out of cheese.")
+let success = ServerResponse.Result("6:00 am", "8:09 pm")
+let failure = ServerResponse.Error("Out of cheese.")
- switch success {
- case let .Result(sunrise, sunset):
- let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
- case let .Error(error):
- let serverResponse = "Failure... \(error)"
- }
+switch success {
+case let .Result(sunrise, sunset):
+ let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
+case let .Error(error):
+ let serverResponse = "Failure... \(error)"
+}
-练习:
-给ServerResponse和switch添加第三种情况。
+练习:
给ServerResponse和switch添加第三种情况。
注意如何从ServerResponse中提取日升和日落时间。
-接口和扩展
-使用protocol来声明一个接口。
- protocol ExampleProtocol {
- var simpleDescription: String { get }
- mutating func adjust()
- }
+协议和扩展
+使用protocol来声明一个协议。
+protocol ExampleProtocol {
+ var simpleDescription: String { get }
+ mutating func adjust()
+}
-类、枚举和结构体都可以实现接口。
- class SimpleClass: ExampleProtocol {
- var simpleDescription: String = "A very simple class."
- var anotherProperty: Int = 69105
- func adjust() {
- simpleDescription += " Now 100% adjusted."
- }
+类、枚举和结构体都可以实现协议。
+class SimpleClass: ExampleProtocol {
+ var simpleDescription: String = "A very simple class."
+ var anotherProperty: Int = 69105
+ func adjust() {
+ simpleDescription += " Now 100% adjusted."
}
- var a = SimpleClass()
- a.adjust()
- let aDescription = a.simpleDescription
+}
+var a = SimpleClass()
+a.adjust()
+let aDescription = a.simpleDescription
- struct SimpleStructure: ExampleProtocol {
- var simpleDescription: String = "A simple structure"
- mutating func adjust() {
- simpleDescription += " (adjusted)"
- }
+struct SimpleStructure: ExampleProtocol {
+ var simpleDescription: String = "A simple structure"
+ mutating func adjust() {
+ simpleDescription += " (adjusted)"
}
- var b = SimpleStructure()
- b.adjust()
- let bDescription = b.simpleDescription
+}
+var b = SimpleStructure()
+b.adjust()
+let bDescription = b.simpleDescription
练习:
-写一个实现这个接口的枚举。
+写一个实现这个协议的枚举。
注意声明SimpleStructure时候mutating关键字用来标记一个会修改结构体的方法。SimpleClass的声明不需要标记任何方法因为类中的方法经常会修改类。
-使用extension来为现有的类型添加功能,比如添加一个计算属性的方法。你可以使用扩展来给任意类型添加协议,甚至是你从外部库或者框架中导入的类型。
- extension Int: ExampleProtocol {
- var simpleDescription: String {
- return "The number \(self)"
- }
- mutating func adjust() {
- self += 42
- }
+使用extension来为现有的类型添加功能,比如新的方法和参数。你可以使用扩展来改造定义在别处,甚至是从外部库或者框架引入的一个类型,使得这个类型遵循某个协议。
+extension Int: ExampleProtocol {
+ var simpleDescription: String {
+ return "The number \(self)"
}
- 7.simpleDescription
+ mutating func adjust() {
+ self += 42
+ }
+}
+7.simpleDescription
练习:
给Double类型写一个扩展,添加absoluteValue功能。
-你可以像使用其他命名类型一样使用接口名——例如,创建一个有不同类型但是都实现一个接口的对象集合。当你处理类型是接口的值时,接口外定义的方法不可用。
- let protocolValue: ExampleProtocol = a
- protocolValue.simpleDescription
- // protocolValue.anotherProperty // Uncomment to see the error
+你可以像使用其他命名类型一样使用协议名——例如,创建一个有不同类型但是都实现一个协议的对象集合。当你处理类型是协议的值时,协议外定义的方法不可用。
+let protocolValue: ExampleProtocol = a
+protocolValue.simpleDescription
+// protocolValue.anotherProperty // Uncomment to see the error
-即使protocolValue变量运行时的类型是simpleClass,编译器会把它的类型当做ExampleProtocol。这表示你不能调用类在它实现的接口之外实现的方法或者属性。
+即使protocolValue变量运行时的类型是simpleClass,编译器会把它的类型当做ExampleProtocol。这表示你不能调用类在它实现的协议之外实现的方法或者属性。
泛型
在尖括号里写一个名字来创建一个泛型函数或者类型。
- func repeat<ItemType>(item: ItemType, times: Int) -> ItemType[] {
- var result = ItemType[]()
- for i in 0..times {
- result += item
- }
- return result
+func repeat<ItemType>(item: ItemType, times: Int) -> ItemType[] {
+ var result = ItemType[]()
+ for i in 0..times {
+ result += item
}
- repeat("knock", 4)
+ return result
+}
+repeat("knock", 4)
你也可以创建泛型类、枚举和结构体。
- // Reimplement the Swift standard library's optional type
- enum OptionalValue<T> {
- case None
- case Some(T)
- }
- var possibleInteger: OptionalValue<Int> = .None
- possibleInteger = .Some(100)
+// Reimplement the Swift standard library's optional type
+enum OptionalValue<T> {
+ case None
+ case Some(T)
+}
+var possibleInteger: OptionalValue<Int> = .None
+possibleInteger = .Some(100)
-在类型名后面使用where来指定一个需求列表——例如,要限定实现一个协议的类型,需要限定两个类型要相同,或者限定一个类必须有一个特定的父类。
- func anyCommonElements <T, U where T: Sequence, U: Sequence, T.GeneratorType.Element: Equatable, T.GeneratorType.Element == U.GeneratorType.Element> (lhs: T, rhs: U) -> Bool {
- for lhsItem in lhs {
- for rhsItem in rhs {
- if lhsItem == rhsItem {
- return true
- }
+在类型名后面使用where来指定对类型的需求,比如,限定类型实现某一个协议,限定两个类型是相同的,或者限定某个类必须有一个特定的父类
+func anyCommonElements <T, U where T: Sequence, U: Sequence, T.GeneratorType.Element: Equatable, T.GeneratorType.Element == U.GeneratorType.Element> (lhs: T, rhs: U) -> Bool {
+ for lhsItem in lhs {
+ for rhsItem in rhs {
+ if lhsItem == rhsItem {
+ return true
}
}
- return false
}
- anyCommonElements([1, 2, 3], [3])
+ return false
+}
+anyCommonElements([1, 2, 3], [3])
-练习:
-修改anyCommonElements函数来创建一个函数,返回一个数组,内容是两个序列的共有元素。
+练习:
修改anyCommonElements函数来创建一个函数,返回一个数组,内容是两个序列的共有元素。
-简单起见,你可以忽略where,只在冒号后面写接口或者类名。<T: Equatable>和<T where T: Equatable>是等价的。
+简单起见,你可以忽略where,只在冒号后面写协议或者类名。<T: Equatable>和<T where T: Equatable>是等价的。
在本章中您将了解 Swift 的特性和开发历史,并对 Swift 有一个初步的了解。
diff --git a/chapter2/01_The_Basics.html b/chapter2/01_The_Basics.html old mode 100755 new mode 100644 index 59fb5eac..8b04d051 --- a/chapter2/01_The_Basics.html +++ b/chapter2/01_The_Basics.html @@ -46,7 +46,7 @@ --翻译:numbbbbb, lyuka, JaySurplus
-校对:lslxdx
+翻译:numbbbbb, lyuka, JaySurplus
校对:lslxdx
常量和变量把一个名字(比如maximumNumberOfLoginAttempts或者welcomeMessage)和一个指定类型的值(比如数字10或者字符串"Hello")关联起来。常量的值一旦设定就不能改变,而变量的值可以随意更改。
常量和变量必须在使用前声明,用let来声明常量,用var来声明变量。下面的例子展示了如何用常量和变量来记录用户尝试登录的次数:
let maximumNumberOfLoginAttempts = 10
+let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0
-
这两行代码可以被理解为:
+
+这两行代码可以被理解为:
“声明一个名字是maximumNumberOfLoginAttempts的新常量,并给它一个值10。然后,声明一个名字是currentLoginAttempt的变量并将它的值初始化为0.”
在这个例子中,允许的最大尝试登录次数被声明为一个常量,因为这个值不会改变。当前尝试登录次数被声明为一个变量,因为每次尝试登录失败的时候都需要增加这个值。
你可以在一行中声明多个常量或者多个变量,用逗号隔开:
-var x = 0.0, y = 0.0, z = 0.0
--注意:
-如果你的代码中有不需要改变的值,请使用
+let关键字将它声明为常量。只将需要改变的值声明为变量。+var x = 0.0, y = 0.0, z = 0.0 ++注意:
如果你的代码中有不需要改变的值,请使用let关键字将它声明为常量。只将需要改变的值声明为变量。类型标注
当你声明常量或者变量的时候可以加上类型标注(type annotation),说明常量或者变量中要存储的值的类型。如果要添加类型标注,需要在常量或者变量名后面加上一个冒号和空格,然后加上类型名称。
这个例子给
-welcomeMessage变量添加了类型标注,表示这个变量可以存储String类型的值:var welcomeMessage: String -声明中的冒号代表着“是...类型”,所以这行代码可以被理解为:
++var welcomeMessage: String +声明中的冒号代表着“是...类型”,所以这行代码可以被理解为:
“声明一个类型为
String,名字为welcomeMessage的变量。”“类型为
String”的意思是“可以存储任意String类型的值。”-
welcomeMessage变量现在可以被设置成任意字符串:welcomeMessage = "Hello" --注意:
-一般来说你很少需要写类型标注。如果你在声明常量或者变量的时候赋了一个初始值,Swift可以推断出这个常量或者变量的类型,请参考类型安全和类型推断。在上面的例子中,没有给
+welcomeMessage赋初始值,所以变量welcomeMessage的类型是通过一个类型标注指定的,而不是通过初始值推断的。+welcomeMessage = "Hello" ++注意:
一般来说你很少需要写类型标注。如果你在声明常量或者变量的时候赋了一个初始值,Swift可以推断出这个常量或者变量的类型,请参考类型安全和类型推断。在上面的例子中,没有给welcomeMessage赋初始值,所以变量welcomeMessage的类型是通过一个类型标注指定的,而不是通过初始值推断的。常量和变量的命名
你可以用任何你喜欢的字符作为常量和变量名,包括 Unicode 字符:
-+let π = 3.14159 +let π = 3.14159 let 你好 = "你好世界" let 🐶🐮 = "dogcow" -常量与变量名不能包含数学符号,箭头,保留的(或者非法的)Unicode 码位,连线与制表符。也不能以数字开头,但是可以在常量与变量名的其他地方包含数字。
+常量与变量名不能包含数学符号,箭头,保留的(或者非法的)Unicode 码位,连线与制表符。也不能以数字开头,但是可以在常量与变量名的其他地方包含数字。
一旦你将常量或者变量声明为确定的类型,你就不能使用相同的名字再次进行声明,或者改变其存储的值的类型。同时,你也不能将常量与变量进行互转。
-注意:
-如果你需要使用与Swift保留关键字相同的名称作为常量或者变量名,你可以使用反引号(`)将关键字包围的方式将其作为名字使用。无论如何,你应当避免使用关键字作为常量或变量名,除非你别无选择。
+注意:
如果你需要使用与Swift保留关键字相同的名称作为常量或者变量名,你可以使用反引号(`)将关键字包围的方式将其作为名字使用。无论如何,你应当避免使用关键字作为常量或变量名,除非你别无选择。你可以更改现有的变量值为其他同类型的值,在下面的例子中,
-friendlyWelcome的值从"Hello!"改为了"Bonjour!":+var friendlyWelcome = "Hello!" +var friendlyWelcome = "Hello!" friendlyWelcome = "Bonjour!" // friendlyWelcome 现在是 "Bonjour!" -与变量不同,常量的值一旦被确定就不能更改了。尝试这样做会导致编译时报错:
-+let languageName = "Swift" +与变量不同,常量的值一旦被确定就不能更改了。尝试这样做会导致编译时报错:
+let languageName = "Swift" languageName = "Swift++" // 这会报编译时错误 - languageName 不可改变 -输出常量和变量
+输出常量和变量
你可以用
-println函数来输出当前常量或变量的值:+println(friendlyWelcome) +println(friendlyWelcome) // 输出 "Bonjour!" -+
println是一个用来输出的全局函数,输出的内容会在最后换行。如果你用 Xcode,println将会输出内容到“console”面板上。(另一种函数叫
println是一个用来输出的全局函数,输出的内容会在最后换行。如果你用 Xcode,println将会输出内容到“console”面板上。(另一种函数叫-
println函数输出传入的String值:+println("This is a string") +println("This is a string") // 输出 "This is a string" -与 Cocoa 里的
+NSLog函数类似的是,println函数可以输出更复杂的信息。这些信息可以包含当前常量和变量的值。与 Cocoa 里的
NSLog函数类似的是,println函数可以输出更复杂的信息。这些信息可以包含当前常量和变量的值。Swift 用字符串插值(string interpolation)的方式把常量名或者变量名当做占位符加入到长字符串中,Swift 会用当前常量或变量的值替换这些占位符。将常量或变量名放入圆括号中,并在开括号前使用反斜杠将其转义:
-+println("The current value of friendlyWelcome is \(friendlyWelcome)") +println("The current value of friendlyWelcome is \(friendlyWelcome)") // 输出 "The current value of friendlyWelcome is Bonjour! --注意:
-字符串插值所有可用的选项,请参考字符串插值。
++注意:
字符串插值所有可用的选项,请参考字符串插值。注释
请将你的代码中的非执行文本注释成提示或者笔记以方便你将来阅读。Swift 的编译器将会在编译代码时自动忽略掉注释部分。
Swift 中的注释与C 语言的注释非常相似。单行注释以双正斜杠(
-//)作为起始标记:// 这是一个注释 -你也可以进行多行注释,其起始标记为单个正斜杠后跟随一个星号(
-/*),终止标记为一个星号后跟随单个正斜杠(*/):+/* 这是一个, ++// 这是一个注释 +你也可以进行多行注释,其起始标记为单个正斜杠后跟随一个星号(
+/*),终止标记为一个星号后跟随单个正斜杠(*/):/* 这是一个, 多行注释 */ -与 C 语言多行注释不同,Swift 的多行注释可以嵌套在其它的多行注释之中。你可以先生成一个多行注释块,然后在这个注释块之中再嵌套成第二个多行注释。终止注释时先插入第二个注释块的终止标记,然后再插入第一个注释块的终止标记:
-+/* 这是第一个多行注释的开头 +与 C 语言多行注释不同,Swift 的多行注释可以嵌套在其它的多行注释之中。你可以先生成一个多行注释块,然后在这个注释块之中再嵌套成第二个多行注释。终止注释时先插入第二个注释块的终止标记,然后再插入第一个注释块的终止标记:
+/* 这是第一个多行注释的开头 /* 这是第二个被嵌套的多行注释 */ 这是第一个多行注释的结尾 */ -通过运用嵌套多行注释,你可以快速方便的注释掉一大段代码,即使这段代码之中已经含有了多行注释块。
+通过运用嵌套多行注释,你可以快速方便的注释掉一大段代码,即使这段代码之中已经含有了多行注释块。
分号
与其他大部分编程语言不同,Swift 并不强制要求你在每条语句的结尾处使用分号(
-;),当然,你也可以按照你自己的习惯添加分号。有一种情况下必须要用分号,即你打算在同一行内写多条独立的语句:+let cat = "🐱"; println(cat) ++let cat = "🐱"; println(cat) // 输出 "🐱" -整数
整数就是没有小数部分的数字,比如
42和-23。整数可以是有符号(正、负、零)或者无符号(正、零)。Swift 提供了8,16,32和64位的有符号和无符号整数类型。这些整数类型和 C 语言的命名方式很像,比如8位无符号整数类型是
UInt8,32位有符号整数类型是Int32。就像 Swift 的其他类型一样,整数类型采用大写命名法。整数范围
你可以访问不同整数类型的
-min和max属性来获取对应类型的最大值和最小值:+let minValue = UInt8.min // minValue 为 0,是 UInt8 类型的最小值 +let minValue = UInt8.min // minValue 为 0,是 UInt8 类型的最小值 let maxValue = UInt8.max // maxValue 为 255,是 UInt8 类型的最大值 -Int
+Int
一般来说,你不需要专门指定整数的长度。Swift 提供了一个特殊的整数类型
Int,长度与当前平台的原生字长相同:
- 在32位平台上,
@@ -722,7 +732,7 @@ let maxValue = UInt8.max // maxValue 为 255,是 UInt8 类型的最大值Int和Int32长度相同。注意:
-尽量不要使用
+UInt,除非你真的需要存储一个和当前平台原生字长相同的无符号整数。除了这种情况,最好使用Int,即使你要存储的值已知是非负的。统一使用Int可以提高代码的可复用性,避免不同类型数字之间的转换,并且匹配数字的类型推测,请参考类型安全和类型推测。尽量不要使用
UInt,除非你真的需要存储一个和当前平台原生字长相同的无符号整数。除了这种情况,最好使用Int,即使你要存储的值已知是非负的。统一使用Int可以提高代码的可复用性,避免不同类型数字之间的转换,并且匹配数字的类型推断,请参考类型安全和类型推断。浮点数
@@ -733,27 +743,29 @@ let maxValue = UInt8.max // maxValue 为 255,是 UInt8 类型的最大值Float表示32位浮点数。精度要求不高的话可以使用此类型。--注意:
-+
Double精确度很高,至少有15位数字,而Float最少只有6位数字。选择哪个类型取决于你的代码需要处理的值的范围。注意:
Double精确度很高,至少有15位数字,而Float最少只有6位数字。选择哪个类型取决于你的代码需要处理的值的范围。类型安全和类型推测
+类型安全和类型推断
Swift 是一个类型安全(type safe)的语言。类型安全的语言可以让你清楚地知道代码要处理的值的类型。如果你的代码需要一个
String,你绝对不可能不小心传进去一个Int。由于 Swift 是类型安全的,所以它会在编译你的代码时进行类型检查(type checks),并把不匹配的类型标记为错误。这可以让你在开发的时候尽早发现并修复错误。
-当你要处理不同类型的值时,类型检查可以帮你避免错误。然而,这并不是说你每次声明常量和变量的时候都需要显式指定类型。如果你没有显式指定类型,Swift 会使用类型推测(type inference)来选择合适的类型。有了类型推测,编译器可以在编译代码的时候自动推测出表达式的类型。原理很简单,只要检查你赋的值即可。
-因为有类型推测,和 C 或者 Objective-C 比起来 Swift 很少需要声明类型。常量和变量虽然需要明确类型,但是大部分工作并不需要你自己来完成。
-当你声明常量或者变量并赋初值的时候类型推测非常有用。当你在声明常量或者变量的时候赋给它们一个字面量(literal value 或 literal)即可触发类型推测。(字面量就是会直接出现在你代码中的值,比如
-42和3.14159。)例如,如果你给一个新常量赋值
-42并且没有标明类型,Swift 可以推测出常量类型是Int,因为你给它赋的初始值看起来像一个整数:+let meaningOfLife = 42 +当你要处理不同类型的值时,类型检查可以帮你避免错误。然而,这并不是说你每次声明常量和变量的时候都需要显式指定类型。如果你没有显式指定类型,Swift 会使用类型推断(type inference)来选择合适的类型。有了类型推断,编译器可以在编译代码的时候自动推断出表达式的类型。原理很简单,只要检查你赋的值即可。
+因为有类型推断,和 C 或者 Objective-C 比起来 Swift 很少需要声明类型。常量和变量虽然需要明确类型,但是大部分工作并不需要你自己来完成。
+当你声明常量或者变量并赋初值的时候类型推断非常有用。当你在声明常量或者变量的时候赋给它们一个字面量(literal value 或 literal)即可触发类型推断。(字面量就是会直接出现在你代码中的值,比如
+42和3.14159。)例如,如果你给一个新常量赋值
+42并且没有标明类型,Swift 可以推断出常量类型是Int,因为你给它赋的初始值看起来像一个整数:let meaningOfLife = 42 // meaningOfLife 会被推测为 Int 类型 -同理,如果你没有给浮点字面量标明类型,Swift 会推测你想要的是
-Double:+let pi = 3.14159 +同理,如果你没有给浮点字面量标明类型,Swift 会推断你想要的是
+Double:let pi = 3.14159 // pi 会被推测为 Double 类型 -当推测浮点数的类型时,Swift 总是会选择
-Double而不是Float。如果表达式中同时出现了整数和浮点数,会被推测为
-Double类型:+let anotherPi = 3 + 0.14159 +当推断浮点数的类型时,Swift 总是会选择
+Double而不是Float。如果表达式中同时出现了整数和浮点数,会被推断为
+Double类型:let anotherPi = 3 + 0.14159 // anotherPi 会被推测为 Double 类型 -原始值
+3没有显式声明类型,而表达式中出现了一个浮点字面量,所以表达式会被推测为Double类型。原始值
3没有显式声明类型,而表达式中出现了一个浮点字面量,所以表达式会被推断为Double类型。数值型字面量
整数字面量可以被写作:
@@ -764,11 +776,12 @@ let maxValue = UInt8.max // maxValue 为 255,是 UInt8 类型的最大值一个十六进制数,前缀是 0x下面的所有整数字面量的十进制值都是
-17:+let decimalInteger = 17 +let decimalInteger = 17 let binaryInteger = 0b10001 // 二进制的17 let octalInteger = 0o21 // 八进制的17 let hexadecimalInteger = 0x11 // 十六进制的17 -浮点字面量可以是十进制(没有前缀)或者是十六进制(前缀是
+0x)。小数点两边必须有至少一个十进制数字(或者是十六进制的数字)。浮点字面量还有一个可选的指数(exponent),在十进制浮点数中通过大写或者小写的e来指定,在十六进制浮点数中通过大写或者小写的p来指定。浮点字面量可以是十进制(没有前缀)或者是十六进制(前缀是
0x)。小数点两边必须有至少一个十进制数字(或者是十六进制的数字)。浮点字面量还有一个可选的指数(exponent),在十进制浮点数中通过大写或者小写的e来指定,在十六进制浮点数中通过大写或者小写的p来指定。如果一个十进制数的指数为
exp,那这个数相当于基数和10^exp的乘积:
- @@ -780,118 +793,134 @@ let hexadecimalInteger = 0x11 // 十六进制的17
1.25e2表示 1.25 × 10^2,等于125.0。0xFp-2表示 15 × 2^-2,等于3.75。下面的这些浮点字面量都等于十进制的
-12.1875:+let decimalDouble = 12.1875 +let decimalDouble = 12.1875 let exponentDouble = 1.21875e1 let hexadecimalDouble = 0xC.3p0 -数值类字面量可以包括额外的格式来增强可读性。整数和浮点数都可以添加额外的零并且包含下划线,并不会影响字面量:
-+let paddedDouble = 000123.456 +数值类字面量可以包括额外的格式来增强可读性。整数和浮点数都可以添加额外的零并且包含下划线,并不会影响字面量:
++let paddedDouble = 000123.456 let oneMillion = 1_000_000 let justOverOneMillion = 1_000_000.000_000_1 -数值型类型转换
-通常来讲,即使代码中的整数常量和变量已知非负,也请使用
Int类型。总是使用默认的整数类型可以保证你的整数常量和变量可以直接被复用并且可以匹配整数类字面量的类型推测。 +通常来讲,即使代码中的整数常量和变量已知非负,也请使用
Int类型。总是使用默认的整数类型可以保证你的整数常量和变量可以直接被复用并且可以匹配整数类字面量的类型推断。 只有在必要的时候才使用其他整数类型,比如要处理外部的长度明确的数据或者为了优化性能、内存占用等等。使用显式指定长度的类型可以及时发现值溢出并且可以暗示正在处理特殊数据。整数转换
不同整数类型的变量和常量可以存储不同范围的数字。
-Int8类型的常量或者变量可以存储的数字范围是-128~127,而UInt8类型的常量或者变量能存储的数字范围是0~255。如果数字超出了常量或者变量可存储的范围,编译的时候会报错:+let cannotBeNegative: UInt8 = -1 +let cannotBeNegative: UInt8 = -1 // UInt8 类型不能存储负数,所以会报错 let tooBig: Int8 = Int8.max + 1 // Int8 类型不能存储超过最大值的数,所以会报错 -由于每种整数类型都可以存储不同范围的值,所以你必须根据不同情况选择性使用数值型类型转换。这种选择性使用的方式,可以预防隐式转换的错误并让你的代码中的类型转换意图变得清晰。
+由于每种整数类型都可以存储不同范围的值,所以你必须根据不同情况选择性使用数值型类型转换。这种选择性使用的方式,可以预防隐式转换的错误并让你的代码中的类型转换意图变得清晰。
要将一种数字类型转换成另一种,你要用当前值来初始化一个期望类型的新数字,这个数字的类型就是你的目标类型。在下面的例子中,常量
-twoThousand是UInt16类型,然而常量one是UInt8类型。它们不能直接相加,因为它们类型不同。所以要调用UInt16(one)来创建一个新的UInt16数字并用one的值来初始化,然后使用这个新数字来计算:+let twoThousand: UInt16 = 2_000 +let twoThousand: UInt16 = 2_000 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内部有对应构造器的值。不过你可以扩展现有的类型来让它可以接收其他类型的值(包括自定义类型),请参考扩展。整数和浮点数转换
整数和浮点数的转换必须显式指定类型:
-+let three = 3 +let three = 3 let pointOneFourOneFiveNine = 0.14159 let pi = Double(three) + pointOneFourOneFiveNine // pi 等于 3.14159,所以被推测为 Double 类型 -这个例子中,常量
+three的值被用来创建一个Double类型的值,所以加号两边的数类型相同。如果不进行转换,两者无法相加。这个例子中,常量
three的值被用来创建一个Double类型的值,所以加号两边的数类型须相同。如果不进行转换,两者无法相加。浮点数到整数的反向转换同样行,整数类型可以用
-Double或者Float类型来初始化:+let integerPi = Int(pi) +let integerPi = Int(pi) // integerPi 等于 3,所以被推测为 Int 类型 -当用这种方式来初始化一个新的整数值时,浮点值会被截断。也就是说
+4.75会变成4,-3.9会变成-3。当用这种方式来初始化一个新的整数值时,浮点值会被截断。也就是说
4.75会变成4,-3.9会变成-3。-注意:
-结合数字类常量和变量不同于结合数字类字面量。字面量
+3可以直接和字面量0.14159相加,因为数字字面量本身没有明确的类型。它们的类型只在编译器需要求值的时候被推测。注意:
结合数字类常量和变量不同于结合数字类字面量。字面量3可以直接和字面量0.14159相加,因为数字字面量本身没有明确的类型。它们的类型只在编译器需要求值的时候被推测。类型别名
类型别名(type aliases)就是给现有类型定义另一个名字。你可以使用
typealias关键字来定义类型别名。当你想要给现有类型起一个更有意义的名字时,类型别名非常有用。假设你正在处理特定长度的外部资源的数据:
-typealias AudioSample = UInt16 -定义了一个类型别名之后,你可以在任何使用原始名的地方使用别名:
-+var maxAmplitudeFound = AudioSample.min ++typealias AudioSample = UInt16 +定义了一个类型别名之后,你可以在任何使用原始名的地方使用别名:
+var maxAmplitudeFound = AudioSample.min // maxAmplitudeFound 现在是 0 -本例中,
+AudioSample被定义为UInt16的一个别名。因为它是别名,AudioSample.min实际上是UInt16.min,所以会给maxAmplitudeFound赋一个初值0。本例中,
AudioSample被定义为UInt16的一个别名。因为它是别名,AudioSample.min实际上是UInt16.min,所以会给maxAmplitudeFound赋一个初值0。布尔值
Swift 有一个基本的布尔(Boolean)类型,叫做
-Bool。布尔值指逻辑上的(logical),因为它们只能是真或者假。Swift 有两个布尔常量,true和false:+let orangesAreOrange = true +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 turnipsAreDelicious { +if turnipsAreDelicious { println("Mmm, tasty turnips!") } else { println("Eww, turnips are horrible.") } // 输出 "Eww, turnips are horrible." -条件语句,例如
+if,请参考控制流。条件语句,例如
if,请参考控制流。如果你在需要使用
-Bool类型的地方使用了非布尔值,Swift 的类型安全机制会报错。下面的例子会报告一个编译时错误:+let i = 1 +let i = 1 if i { // 这个例子不会通过编译,会报错 } -然而,下面的例子是合法的:
-+let i = 1 +然而,下面的例子是合法的:
+let i = 1 if i == 1 { // 这个例子会编译成功 } -+
i == 1的比较结果是Bool类型,所以第二个例子可以通过类型检查。类似i == 1这样的比较,请参考基本操作符。
i == 1的比较结果是Bool类型,所以第二个例子可以通过类型检查。类似i == 1这样的比较,请参考基本操作符。和 Swift 中的其他类型安全的例子一样,这个方法可以避免错误并保证这块代码的意图总是清晰的。
元组
元组(tuples)把多个值组合成一个复合值。元组内的值可以使任意类型,并不要求是相同类型。
下面这个例子中,
-(404, "Not Found")是一个描述 HTTP 状态码(HTTP status code)的元组。HTTP 状态码是当你请求网页的时候 web 服务器返回的一个特殊值。如果你请求的网页不存在就会返回一个404 Not Found状态码。+let http404Error = (404, "Not Found") +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)或者其他任何你想要的组合的元组。你可以将一个元组的内容分解(decompose)成单独的常量和变量,然后你就可以正常使用它们了:
-+let (statusCode, statusMessage) = http404Error +let (statusCode, statusMessage) = http404Error println("The status code is \(statusCode)") // 输出 "The status code is 404" println("The status message is \(statusMessage)") // 输出 "The status message is Not Found" -如果你只需要一部分元组值,分解的时候可以把要忽略的部分用下划线(
-_)标记:+let (justTheStatusCode, _) = http404Error +如果你只需要一部分元组值,分解的时候可以把要忽略的部分用下划线(
+_)标记:let (justTheStatusCode, _) = http404Error println("The status code is \(justTheStatusCode)") // 输出 "The status code is 404" -此外,你还可以通过下标来访问元组中的单个元素,下标从零开始:
-+println("The status code is \(http404Error.0)") +此外,你还可以通过下标来访问元组中的单个元素,下标从零开始:
+println("The status code is \(http404Error.0)") // 输出 "The status code is 404" println("The status message is \(http404Error.1)") // 输出 "The status message is Not Found" -你可以在定义元组的时候给单个元素命名:
-let http200Status = (statusCode: 200, description: "OK") -给元组中的元素命名后,你可以通过名字来获取这些元素的值:
-+println("The status code is \(http200Status.statusCode)") +你可以在定义元组的时候给单个元素命名:
++let http200Status = (statusCode: 200, description: "OK") +给元组中的元素命名后,你可以通过名字来获取这些元素的值:
+println("The status code is \(http200Status.statusCode)") // 输出 "The status code is 200" println("The status message is \(http200Status.description)") // 输出 "The status message is OK" -作为函数返回值时,元组非常有用。一个用来获取网页的函数可能会返回一个
+(Int, String)元组来描述是否获取成功。和只能返回一个类型的值比较起来,一个包含两个不同类型值的元组可以让函数的返回信息更有用。请参考函数参数与返回值。作为函数返回值时,元组非常有用。一个用来获取网页的函数可能会返回一个
(Int, String)元组来描述是否获取成功。和只能返回一个类型的值比较起来,一个包含两个不同类型值的元组可以让函数的返回信息更有用。请参考函数参数与返回值。--注意:
-元组在临时组织值的时候很有用,但是并不适合创建复杂的数据结构。如果你的数据结构并不是临时使用,请使用类或者结构体而不是元组。请参考类和结构体。
+注意:
元组在临时组织值的时候很有用,但是并不适合创建复杂的数据结构。如果你的数据结构并不是临时使用,请使用类或者结构体而不是元组。请参考类和结构体。可选
-使用可选(optionals)来处理值可能缺失的情况。可选表示:
+可选类型
+使用可选类型(optionals)来处理值可能缺失的情况。可选类型表示:
@@ -901,119 +930,127 @@ println("The status message is \(http200Status.description)")
- 有值,等于 x
注意:
-C 和 Objective-C 中并没有可选这个概念。最接近的是 Objective-C 中的一个特性,一个方法要不返回一个对象要不返回
+nil,nil表示“缺少一个合法的对象”。然而,这只对对象起作用——对于结构体,基本的 C 类型或者枚举类型不起作用。对于这些类型,Objective-C 方法一般会返回一个特殊值(比如NSNotFound)来暗示值缺失。这种方法假设方法的调用者知道并记得对特殊值进行判断。然而,Swift 的可选可以让你暗示任意类型的值缺失,并不需要一个特殊值。C 和 Objective-C 中并没有可选类型这个概念。最接近的是 Objective-C 中的一个特性,一个方法要不返回一个对象要不返回
nil,nil表示“缺少一个合法的对象”。然而,这只对对象起作用——对于结构体,基本的 C 类型或者枚举类型不起作用。对于这些类型,Objective-C 方法一般会返回一个特殊值(比如NSNotFound)来暗示值缺失。这种方法假设方法的调用者知道并记得对特殊值进行判断。然而,Swift 的可选类型可以让你暗示任意类型的值缺失,并不需要一个特殊值。来看一个例子。Swift 的
String类型有一个叫做toInt的方法,作用是将一个String值转换成一个Int值。然而,并不是所有的字符串都可以转换成一个整数。字符串"123"可以被转换成数字123,但是字符串"hello, world"不行。下面的例子使用
-toInt方法来尝试将一个String转换成Int:+let possibleNumber = "123" +let possibleNumber = "123" let convertedNumber = possibleNumber.toInt() // convertedNumber 被推测为类型 "Int?", 或者类型 "optional Int" -因为
+toInt方法可能会失败,所以它返回一个可选的(optional)Int,而不是一个Int。一个可选的Int被写作Int?而不是Int。问号暗示包含的值是可选,也就是说可能包含Int值也可能不包含值。(不能包含其他任何值比如Bool值或者String值。只能是Int或者什么都没有。)因为
toInt方法可能会失败,所以它返回一个可选类型(optional)Int,而不是一个Int。一个可选的Int被写作Int?而不是Int。问号暗示包含的值是可选类型,也就是说可能包含Int值也可能不包含值。(不能包含其他任何值比如Bool值或者String值。只能是Int或者什么都没有。)if 语句以及强制解析
-你可以使用
-if语句来判断一个可选是否包含值。如果可选有值,结果是true;如果没有值,结果是false。当你确定可选确实包含值之后,你可以在可选的名字后面加一个感叹号(
-!)来获取值。这个惊叹号表示“我知道这个可选有值,请使用它。”这被称为可选值的强制解析(forced unwrapping):+if convertedNumber { +你可以使用
+if语句来判断一个可选是否包含值。如果可选类型有值,结果是true;如果没有值,结果是false。当你确定可选类型确实包含值之后,你可以在可选的名字后面加一个感叹号(
+!)来获取值。这个惊叹号表示“我知道这个可选有值,请使用它。”这被称为可选值的强制解析(forced unwrapping):if convertedNumber { println("\(possibleNumber) has an integer value of \(convertedNumber!)") } else { println("\(possibleNumber) could not be converted to an integer") } // 输出 "123 has an integer value of 123" -更多关于
+if语句的内容,请参考控制流。更多关于
if语句的内容,请参考控制流。-注意:
-使用
+!来获取一个不存在的可选值会导致运行时错误。使用!来强制解析值之前,一定要确定可选包含一个非nil的值。注意:
使用!来获取一个不存在的可选值会导致运行时错误。使用!来强制解析值之前,一定要确定可选包含一个非nil的值。可选绑定
-使用可选绑定(optional binding)来判断可选是否包含值,如果包含就把值赋给一个临时常量或者变量。可选绑定可以用在
+if和while语句中来对可选的值进行判断并把值赋给一个常量或者变量。if和while语句,请参考控制流。使用可选绑定(optional binding)来判断可选类型是否包含值,如果包含就把值赋给一个临时常量或者变量。可选绑定可以用在
if和while语句中来对可选类型的值进行判断并把值赋给一个常量或者变量。if和while语句,请参考控制流。像下面这样在
-if语句中写一个可选绑定:+if let constantName = someOptional { +if let constantName = someOptional { statements } -你可以像上面这样使用可选绑定来重写
-possibleNumber这个例子:+if let actualNumber = possibleNumber.toInt() { +你可以像上面这样使用可选绑定来重写
+possibleNumber这个例子:if let actualNumber = possibleNumber.toInt() { println("\(possibleNumber) has an integer value of \(actualNumber)") } else { println("\(possibleNumber) could not be converted to an integer") } // 输出 "123 has an integer value of 123" -这段代码可以被理解为:
+这段代码可以被理解为:
“如果
-possibleNumber.toInt返回的可选Int包含一个值,创建一个叫做actualNumber的新常量并将可选包含的值赋给它。”如果转换成功,
-actualNumber常量可以在if语句的第一个分支中使用。它已经被可选包含的值初始化过,所以不需要再使用!后缀来获取它的值。在这个例子中,actualNumber只被用来输出转换结果。你可以在可选绑定中使用常量和变量。如果你想在
+if语句的第一个分支中操作actualNumber的值,你可以改成if var actualNumber,这样可选包含的值就会被赋给一个变量而非常量。如果转换成功,
+actualNumber常量可以在if语句的第一个分支中使用。它已经被可选类型包含的值初始化过,所以不需要再使用!后缀来获取它的值。在这个例子中,actualNumber只被用来输出转换结果。你可以在可选绑定中使用常量和变量。如果你想在
if语句的第一个分支中操作actualNumber的值,你可以改成if var actualNumber,这样可选类型包含的值就会被赋给一个变量而非常量。nil
你可以给可选变量赋值为
-nil来表示它没有值:+var serverResponseCode: Int? = 404 +var serverResponseCode: Int? = 404 // serverResponseCode 包含一个可选的 Int 值 404 serverResponseCode = nil // serverResponseCode 现在不包含值 --注意:
-+
nil不能用于非可选的常量和变量。如果你的代码中有常量或者变量需要处理值缺失的情况,请把它们声明成对应的可选类型。+注意:
nil不能用于非可选的常量和变量。如果你的代码中有常量或者变量需要处理值缺失的情况,请把它们声明成对应的可选类型。如果你声明一个可选常量或者变量但是没有赋值,它们会自动被设置为
-nil:var surveyAnswer: String? +var surveyAnswer: String? // surveyAnswer 被自动设置为 nil ---注意:
-Swift 的
-nil和 Objective-C 中的nil并不一样。在 Objective-C 中,nil是一个指向不存在对象的指针。在 Swift 中,nil不是指针——它是一个确定的值,用来表示值缺失。任何类型的可选都可以被设置为nil,不只是对象类型。隐式解析可选
-如上所述,可选暗示了常量或者变量可以“没有值”。可选可以通过
-if语句来判断是否有值,如果有值的话可以通过可选绑定来解析值。有时候在程序架构中,第一次被赋值之后,可以确定一个可选总会有值。在这种情况下,每次都要判断和解析可选值是非常低效的,因为可以确定它总会有值。
-这种类型的可选被定义为隐式解析可选(implicitly unwrapped optionals)。把想要用作可选的类型的后面的问号(
-String?)改成感叹号(String!)来声明一个隐式解析可选。当可选被第一次赋值之后就可以确定之后一直有值的时候,隐式解析可选非常有用。隐式解析可选主要被用在 Swift 中类的构造过程中,请参考类实例之间的循环强引用。
-一个隐式解析可选其实就是一个普通的可选,但是可以被当做非可选来使用,并不需要每次都使用解析来获取可选值。下面的例子展示了可选
-String和隐式解析可选String之间的区别:let possibleString: String? = "An optional string." -println(possibleString!) // 需要惊叹号来获取值 -// 输出 "An optional string." - -let assumedString: String! = "An implicitly unwrapped optional string." -println(assumedString) // 不需要感叹号 -// 输出 "An implicitly unwrapped optional string." -你可以把隐式解析可选当做一个可以自动解析的可选。你要做的只是声明的时候把感叹号放到类型的结尾,而不是每次取值的可选名字的结尾。
+-注意:
-如果你在隐式解析可选没有值的时候尝试取值,会触发运行时错误。和你在没有值的普通可选后面加一个惊叹号一样。
+Swift 的
nil和 Objective-C 中的nil并不一样。在 Objective-C 中,nil是一个指向不存在对象的指针。在 Swift 中,nil不是指针——它是一个确定的值,用来表示值缺失。任何类型的可选状态都可以被设置为nil,不只是对象类型。你仍然可以把隐式解析可选当做普通可选来判断它是否包含值:
-+if assumedString { +隐式解析可选类型
+如上所述,可选类型暗示了常量或者变量可以“没有值”。可选可以通过
+if语句来判断是否有值,如果有值的话可以通过可选绑定来解析值。有时候在程序架构中,第一次被赋值之后,可以确定一个可选类型总会有值。在这种情况下,每次都要判断和解析可选值是非常低效的,因为可以确定它总会有值。
+这种类型的可选状态被定义为隐式解析可选类型(implicitly unwrapped optionals)。把想要用作可选的类型的后面的问号(
+String?)改成感叹号(String!)来声明一个隐式解析可选类型。当可选类型被第一次赋值之后就可以确定之后一直有值的时候,隐式解析可选类型非常有用。隐式解析可选类型主要被用在 Swift 中类的构造过程中,请参考类实例之间的循环强引用。
+一个隐式解析可选类型其实就是一个普通的可选类型,但是可以被当做非可选类型来使用,并不需要每次都使用解析来获取可选值。下面的例子展示了可选类型
+String和隐式解析可选类型String之间的区别:+let possibleString: String? = "An optional string." +println(possibleString!) // 需要惊叹号来获取值 +// 输出 "An optional string." ++let assumedString: String! = "An implicitly unwrapped optional string." +println(assumedString) // 不需要感叹号 +// 输出 "An implicitly unwrapped optional string." +你可以把隐式解析可选类型当做一个可以自动解析的可选类型。你要做的只是声明的时候把感叹号放到类型的结尾,而不是每次取值的可选名字的结尾。
+++注意:
+如果你在隐式解析可选类型没有值的时候尝试取值,会触发运行时错误。和你在没有值的普通可选类型后面加一个惊叹号一样。
+你仍然可以把隐式解析可选类型当做普通可选类型来判断它是否包含值:
+if assumedString { println(assumedString) } // 输出 "An implicitly unwrapped optional string." -你也可以在可选绑定中使用隐式解析可选来检查并解析它的值:
-+if let definiteString = assumedString { +你也可以在可选绑定中使用隐式解析可选类型来检查并解析它的值:
+if let definiteString = assumedString { println(definiteString) } // 输出 "An implicitly unwrapped optional string." -+注意:
-如果一个变量之后可能变成
+nil的话请不要使用隐式解析可选。如果你需要在变量的生命周期中判断是否是nil的话,请使用普通可选类型。如果一个变量之后可能变成
nil的话请不要使用隐式解析可选类型。如果你需要在变量的生命周期中判断是否是nil的话,请使用普通可选类型。断言
-可选可以让你判断值是否存在,你可以在代码中优雅地处理值缺失的情况。然而,在某些情况下,如果值缺失或者值并不满足特定的条件,你的代码可能并不需要继续执行。这时,你可以在你的代码中触发一个断言(assertion)来结束代码运行并通过调试来找到值缺失的原因。
+可选类型可以让你判断值是否存在,你可以在代码中优雅地处理值缺失的情况。然而,在某些情况下,如果值缺失或者值并不满足特定的条件,你的代码可能并不需要继续执行。这时,你可以在你的代码中触发一个断言(assertion)来结束代码运行并通过调试来找到值缺失的原因。
使用断言进行调试
断言会在运行时判断一个逻辑条件是否为
true。从字面意思来说,断言“断言”一个条件是否为真。你可以使用断言来保证在运行其他代码之前,某些重要的条件已经被满足。如果条件判断为true,代码运行会继续进行;如果条件判断为false,代码运行停止,你的应用被终止。如果你的代码在调试环境下触发了一个断言,比如你在 Xcode 中构建并运行一个应用,你可以清楚地看到不合法的状态发生在哪里并检查断言被触发时你的应用的状态。此外,断言允许你附加一条调试信息。
你可以使用全局
-assert函数来写一个断言。向assert函数传入一个结果为true或者false的表达式以及一条信息,当表达式为false的时候这条信息会被显示:+let age = -3 +let age = -3 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,断言被触发,结束应用。断言信息不能使用字符串插值。断言信息可以省略,就像这样:
-assert(age >= 0) -何时使用断言
++assert(age >= 0) +何时使用断言
当条件可能为假时使用断言,但是最终一定要保证条件为真,这样你的代码才能继续运行。断言的适用情景:
-
- +- 整数的附属脚本索引被传入一个自定义附属脚本实现,但是下标索引值可能太小或者太大。
+- 整数类型的下标索引被传入一个自定义下标脚本实现,但是下标索引值可能太小或者太大。
- 需要给函数传入一个值,但是非法的值可能导致函数不能正常执行。
- 一个可选值现在是
nil,但是后面的代码运行需要一个非nil值。-diff --git a/chapter2/02_Basic_Operators.html b/chapter2/02_Basic_Operators.html old mode 100755 new mode 100644 index 79128345..b3313542 --- a/chapter2/02_Basic_Operators.html +++ b/chapter2/02_Basic_Operators.html @@ -46,7 +46,7 @@ -注意:
-断言可能导致你的应用终止运行,所以你应当仔细设计你的代码来让非法条件不会出现。然而,在你的应用发布之前,有时候非法条件可能出现,这时使用断言可以快速发现问题。
+注意:
断言可能导致你的应用终止运行,所以你应当仔细设计你的代码来让非法条件不会出现。然而,在你的应用发布之前,有时候非法条件可能出现,这时使用断言可以快速发现问题。+@@ -268,7 +268,7 @@- 2.12. 附属脚本 + 2.12. 下标脚本 @@ -520,7 +520,7 @@ - + @@ -587,11 +587,10 @@ -+ -翻译:xielingwang
-校对:Evilcome
+翻译:xielingwang
校对:Evilcome基本运算符
@@ -628,9 +627,10 @@ a = b // a 现在等于 10如果赋值的右边是一个多元组,它的元素可以马上被分解多个变量或变量:
-+let (x, y) = (1, 2) +let (x, y) = (1, 2) // 现在 x 等于 1, y 等于 2 -与 C 语言和 Objective-C 不同,Swift 的赋值操作并不返回任何值。所以以下代码是错误的:
+与 C 语言和 Objective-C 不同,Swift 的赋值操作并不返回任何值。所以以下代码是错误的:
if x = y { // 此句错误, 因为 x = y 并不返回任何值 } @@ -665,8 +665,7 @@ let dogCow = dog + cow求余运算
求余运算(
a % b)是计算b的多少倍刚刚好可以容入a,返回多出来的那部分(余数)。-注意:
-求余运算(
+%)在其他语言也叫取模运算。然而严格说来,我们看该运算符对负数的操作结果,"求余"比"取模"更合适些。注意:
求余运算(%)在其他语言也叫取模运算。然而严格说来,我们看该运算符对负数的操作结果,"求余"比"取模"更合适些。我们来谈谈取余是怎么回事,计算
9 % 4,你先计算出4的多少倍会刚好可以容入9中:@@ -738,8 +737,7 @@ a += 2 // a 现在是 3
表达式
a += 2是a = a + 2的简写,一个加赋运算就把加法和赋值两件事完成了。-注意:
-复合赋值运算没有返回值,
+let b = a += 2这类代码是错误。这不同于上面提到的自增和自减运算符。注意:
复合赋值运算没有返回值,let b = a += 2这类代码是错误。这不同于上面提到的自增和自减运算符。在表达式章节里有复合运算符的完整列表。 @@ -755,8 +753,7 @@ a += 2 // a 现在是 3
小于等于( a <= b)-注意:
-Swift 也提供恒等
+===和不恒等!==这两个比较符来判断两个对象是否引用同一个对象实例。更多细节在类与结构。注意:
Swift 也提供恒等===和不恒等!==这两个比较符来判断两个对象是否引用同一个对象实例。更多细节在类与结构。每个比较运算都返回了一个标识表达式是否成立的布尔值:
1 == 1 // true, 因为 1 等于 1 @@ -781,10 +778,9 @@ if name == "world" {三元条件运算的特殊在于它是有三个操作数的运算符,它的原型是
问题 ? 答案1 : 答案2。它简洁地表达根据问题成立与否作出二选一的操作。如果问题成立,返回答案1的结果; 如果不成立,返回答案2的结果。使用三元条件运算简化了以下代码:
if question: { - answer1 -} -else { - answer2 + answer1 +} else { + answer2 }这里有个计算表格行高的例子。如果有表头,那行高应比内容高度要高出50像素; 如果没有表头,只需高出20像素。
@@ -814,7 +810,7 @@ if hasHeader { 闭区间运算符在迭代一个区间的所有值时是非常有用的,如在for-in循环中:for index in 1...5 { - println("\(index) * 5 = \(index * 5)") + println("\(index) * 5 = \(index * 5)") } // 1 * 5 = 5 // 2 * 5 = 10 diff --git a/chapter2/03_Strings_and_Characters.html b/chapter2/03_Strings_and_Characters.html old mode 100755 new mode 100644 index d91e5d39..ff24be2a --- a/chapter2/03_Strings_and_Characters.html +++ b/chapter2/03_Strings_and_Characters.html @@ -46,7 +46,7 @@ -+@@ -268,7 +268,7 @@- 2.12. 附属脚本 + 2.12. 下标脚本 @@ -520,7 +520,7 @@ - + @@ -587,11 +587,10 @@ -+ diff --git a/chapter2/04_Collection_Types.html b/chapter2/04_Collection_Types.html old mode 100755 new mode 100644 index 8e099792..ada3bdf2 --- a/chapter2/04_Collection_Types.html +++ b/chapter2/04_Collection_Types.html @@ -46,7 +46,7 @@ - -翻译:wh1100717
-校对:Hawstein
+翻译:wh1100717
校对:Hawstein字符串和字符(Strings and Characters)
@@ -618,19 +617,18 @@ 每一个字符串都是由独立编码的 Unicode 字符组成,并提供了以不同 Unicode 表示(representations)来访问这些字符的支持。Swift 可以在常量、变量、字面量和表达式中进行字符串插值操作,可以轻松创建用于展示、存储和打印的自定义字符串。
-注意:
-Swift 的
-String类型与 FoundationNSString类进行了无缝桥接。如果您利用 Cocoa 或 Cocoa Touch 中的 Foundation 框架进行工作。所有NSStringAPI 都可以调用您创建的任意String类型的值。除此之外,还可以使用本章介绍的String特性。您也可以在任意要求传入NSString实例作为参数的 API 中使用String类型的值作为替代。更多关于在 Foundation 和 Cocoa 中使用
+String的信息请查看 Using Swift with Cocoa and Objective-C。注意:
Swift 的String类型与 FoundationNSString类进行了无缝桥接。如果您利用 Cocoa 或 Cocoa Touch 中的 Foundation 框架进行工作。所有NSStringAPI 都可以调用您创建的任意String类型的值。除此之外,还可以使用本章介绍的String特性。您也可以在任意要求传入NSString实例作为参数的 API 中使用String类型的值作为替代。 +更多关于在 Foundation 和 Cocoa 中使用String的信息请查看 Using Swift with Cocoa and Objective-C。字符串字面量(String Literals)
您可以在您的代码中包含一段预定义的字符串值作为字符串字面量。 字符串字面量是由双引号 ("") 包裹着的具有固定顺序的文本字符集。
字符串字面量可以用于为常量和变量提供初始值。
-let someString = "Some string literal value" --注意:
-+
someString变量通过字符串字面量进行初始化,Swift 因此推断该变量为String类型。+let someString = "Some string literal value" ++注意:
someString变量通过字符串字面量进行初始化,Swift 因此推断该变量为String类型。字符串字面量可以包含以下特殊字符:
@@ -642,35 +640,38 @@
下面的代码为各种特殊字符的使用示例。
-wiseWords常量包含了两个转移特殊字符 (双括号);dollarSign、blackHeart和sparklingHeart常量演示了三种不同格式的 Unicode 标量:+let wiseWords = "\"我是要成为海贼王的男人\" - 路飞" ++let dollarSign = "\x24" // $, Unicode 标量 U+0024 +let blackHeart = "\u2665" // ♥, Unicode 标量 U+2665 +let sparklingHeart = "\U0001F496" // 💖, Unicode 标量 U+1F496 +let wiseWords = "\"我是要成为海贼王的男人\" - 路飞" // "我是要成为海贼王的男人" - 路飞 -let dollarSign = "\x24" // $, Unicode 标量 U+0024 -let blackHeart = "\u2665" // ♥, Unicode 标量 U+2665 -let sparklingHeart = "\U0001F496" // 💖, Unicode 标量 U+1F496 -初始化空字符串 (Initializing an Empty String)
为了构造一个很长的字符串,可以创建一个空字符串作为初始值。 可以将空的字符串字面量赋值给变量,也可以初始化一个新的
-String实例:+var emptyString = "" // 空字符串字面量 +var emptyString = "" // 空字符串字面量 var anotherEmptyString = String() // 初始化 String 实例 // 两个字符串均为空并等价。 -您可以通过检查其
-Boolean类型的isEmpty属性来判断该字符串是否为空:+if emptyString.isEmpty { +您可以通过检查其
+Boolean类型的isEmpty属性来判断该字符串是否为空:+if emptyString.isEmpty { println("什么都没有") } // 打印输出:"什么都没有" -字符串可变性 (String Mutability)
您可以通过将一个特定字符串分配给一个变量来对其进行修改,或者分配给一个常量来保证其不会被修改:
-+var variableString = "Horse" +var variableString = "Horse" variableString += " and carriage" // variableString 现在为 "Horse and carriage" let constantString = "Highlander" constantString += " and another Highlander" // 这会报告一个编译错误 (compile-time error) - 常量不可以被修改。 --注意:
-在 Objective-C 和 Cocoa 中,您通过选择两个不同的类(
+NSString和NSMutableString)来指定该字符串是否可以被修改,Swift 中的字符串是否可以修改仅通过定义的是变量还是常量来决定,实现了多种类型可变性操作的统一。+注意:
在 Objective-C 和 Cocoa 中,您通过选择两个不同的类(NSString和NSMutableString)来指定该字符串是否可以被修改,Swift 中的字符串是否可以修改仅通过定义的是变量还是常量来决定,实现了多种类型可变性操作的统一。字符串是值类型(Strings Are Value Types)
@@ -679,8 +680,7 @@ constantString += " and another Highlander" 任何情况下,都会对已有字符串值创建新副本,并对该新副本进行传递或赋值操作。 值类型在 结构体和枚举是值类型 中进行了说明。-注意:
-与 Cocoa 中的
+NSString不同,当您在 Cocoa 中创建了一个NSString实例,并将其传递给一个函数/方法,或者赋值给一个变量,您传递或赋值的是该NSString实例的一个引用,除非您特别要求进行值拷贝,否则字符串不会生成新的副本来进行赋值操作。注意:
与 Cocoa 中的NSString不同,当您在 Cocoa 中创建了一个NSString实例,并将其传递给一个函数/方法,或者赋值给一个变量,您传递或赋值的是该NSString实例的一个引用,除非您特别要求进行值拷贝,否则字符串不会生成新的副本来进行赋值操作。Swift 默认字符串拷贝的方式保证了在函数/方法中传递的是字符串的值。 很明显无论该值来自于哪里,都是您独自拥有的。 @@ -691,7 +691,7 @@ constantString += " and another Highlander"
Swift 的
-String类型表示特定序列的Character(字符) 类型值的集合。 每一个字符值代表一个 Unicode 字符。 您可利用for-in循环来遍历字符串中的每一个字符:+for character in "Dog!🐶" { +for character in "Dog!🐶" { println(character) } // D @@ -699,24 +699,26 @@ constantString += " and another Highlander" // g // ! // 🐶 -for-in 循环在 For Loops 中进行了详细描述。
+for-in 循环在 For Loops 中进行了详细描述。
另外,通过标明一个
-Character类型注解并通过字符字面量进行赋值,可以建立一个独立的字符常量或变量:+let yenSign: Character = "¥" -+let yenSign: Character = "¥" +计算字符数量 (Counting Characters)
通过调用全局
-countElements函数,并将字符串作为参数进行传递,可以获取该字符串的字符数量。+let unusualMenagerie = "Koala 🐨, Snail 🐌, Penguin 🐧, Dromedary 🐪" +let unusualMenagerie = "Koala 🐨, Snail 🐌, Penguin 🐧, Dromedary 🐪" println("unusualMenagerie has \(countElements(unusualMenagerie)) characters") // 打印输出:"unusualMenagerie has 40 characters" --注意:
-不同的 Unicode 字符以及相同 Unicode 字符的不同表示方式可能需要不同数量的内存空间来存储。所以 Swift 中的字符在一个字符串中并不一定占用相同的内存空间。因此字符串的长度不得不通过迭代字符串中每一个字符的长度来进行计算。如果您正在处理一个长字符串,需要注意
-countElements函数必须遍历字符串中的字符以精准计算字符串的长度。另外需要注意的是通过
+countElements返回的字符数量并不总是与包含相同字符的NSString的length属性相同。NSString的length属性是基于利用 UTF-16 表示的十六位代码单元数字,而不是基于 Unicode 字符。为了解决这个问题,NSString的length属性在被 Swift 的String访问时会成为utf16count。+注意:
不同的 Unicode 字符以及相同 Unicode 字符的不同表示方式可能需要不同数量的内存空间来存储。所以 Swift 中的字符在一个字符串中并不一定占用相同的内存空间。因此字符串的长度不得不通过迭代字符串中每一个字符的长度来进行计算。如果您正在处理一个长字符串,需要注意countElements函数必须遍历字符串中的字符以精准计算字符串的长度。 +另外需要注意的是通过countElements返回的字符数量并不总是与包含相同字符的NSString的length属性相同。NSString的length属性是基于利用 UTF-16 表示的十六位代码单元数字,而不是基于 Unicode 字符。为了解决这个问题,NSString的length属性在被 Swift 的String访问时会成为utf16count。连接字符串和字符 (Concatenating Strings and Characters)
字符串和字符的值可以通过加法运算符(
-+)相加在一起并创建一个新的字符串值:+let string1 = "hello" +let string1 = "hello" let string2 = " there" let character1: Character = "!" let character2: Character = "?" @@ -725,33 +727,34 @@ let stringPlusCharacter = string1 + character1 // 等于 "hello!&quo let stringPlusString = string1 + string2 // 等于 "hello there" let characterPlusString = character1 + string1 // 等于 "!hello" let characterPlusCharacter = character1 + character2 // 等于 "!?" -您也可以通过加法赋值运算符 (
-+=) 将一个字符串或者字符添加到一个已经存在字符串变量上:+var instruction = "look over" +您也可以通过加法赋值运算符 (
++=) 将一个字符串或者字符添加到一个已经存在字符串变量上:var instruction = "look over" instruction += string2 // instruction 现在等于 "look over there" var welcome = "good morning" welcome += character1 // welcome 现在等于 "good morning!" --注意:
-您不能将一个字符串或者字符添加到一个已经存在的字符变量上,因为字符变量只能包含一个字符。
++注意:
您不能将一个字符串或者字符添加到一个已经存在的字符变量上,因为字符变量只能包含一个字符。字符串插值 (String Interpolation)
字符串插值是一种构建新字符串的方式,可以在其中包含常量、变量、字面量和表达式。 您插入的字符串字面量的每一项都被包裹在以反斜线为前缀的圆括号中:
-+let multiplier = 3 +let multiplier = 3 let message = "\(multiplier) 乘以 2.5 是 \(Double(multiplier) * 2.5)" // message 是 "3 乘以 2.5 是 7.5" -在上面的例子中,
multiplier作为\(multiplier)被插入到一个字符串字面量中。 +在上面的例子中,
multiplier作为\(multiplier)被插入到一个字符串字面量中。 当创建字符串执行插值计算时此占位符会被替换为multiplier实际的值。
multiplier的值也作为字符串中后面表达式的一部分。 该表达式计算Double(multiplier) * 2.5的值并将结果 (7.5) 插入到字符串中。 在这个例子中,表达式写为\(Double(multiplier) * 2.5)并包含在字符串字面量中。-注意:
-插值字符串中写在括号中的表达式不能包含非转义双引号 (
+") 和反斜杠 (\),并且不能包含回车或换行符。注意:
插值字符串中写在括号中的表达式不能包含非转义双引号 (") 和反斜杠 (\),并且不能包含回车或换行符。比较字符串 (Comparing Strings)
@@ -759,19 +762,20 @@ let message = "\(multiplier) 乘以 2.5 是 \(Double(multiplier) * 2.5)&quo字符串相等 (String Equality)
如果两个字符串以同一顺序包含完全相同的字符,则认为两者字符串相等:
-+let quotation = "我们是一样一样滴." ++let quotation = "我们是一样一样滴." let sameQuotation = "我们是一样一样滴." if quotation == sameQuotation { println("这两个字符串被认为是相同的") } // 打印输出:"这两个字符串被认为是相同的" -前缀/后缀相等 (Prefix and Suffix Equality)
通过调用字符串的
hasPrefix/hasSuffix方法来检查字符串是否拥有特定前缀/后缀。 两个方法均需要以字符串作为参数传入并传出Boolean值。 两个方法均执行基本字符串和前缀/后缀字符串之间逐个字符的比较操作。下面的例子以一个字符串数组表示莎士比亚话剧《罗密欧与朱丽叶》中前两场的场景位置:
-+let romeoAndJuliet = [ +let romeoAndJuliet = [ "Act 1 Scene 1: Verona, A public place", "Act 1 Scene 2: Capulet's mansion", "Act 1 Scene 3: A room in Capulet's mansion", @@ -784,8 +788,9 @@ if quotation == sameQuotation { "Act 2 Scene 5: Capulet's mansion", "Act 2 Scene 6: Friar Lawrence's cell" ] -您可以利用
-hasPrefix方法来计算话剧中第一幕的场景数:+var act1SceneCount = 0 +您可以利用
+hasPrefix方法来计算话剧中第一幕的场景数:var act1SceneCount = 0 for scene in romeoAndJuliet { if scene.hasPrefix("Act 1 ") { ++act1SceneCount @@ -793,8 +798,9 @@ for scene in romeoAndJuliet { } println("There are \(act1SceneCount) scenes in Act 1") // 打印输出:"There are 5 scenes in Act 1" -相似地,您可以用
-hasSuffix方法来计算发生在不同地方的场景数:+var mansionCount = 0 +相似地,您可以用
+hasSuffix方法来计算发生在不同地方的场景数:+var mansionCount = 0 var cellCount = 0 for scene in romeoAndJuliet { if scene.hasSuffix("Capulet's mansion") { @@ -805,15 +811,17 @@ for scene in romeoAndJuliet { } println("\(mansionCount) mansion scenes; \(cellCount) cell scenes") // 打印输出:"6 mansion scenes; 2 cell scenes” -大写和小写字符串(Uppercase and Lowercase Strings)
您可以通过字符串的
-uppercaseString和lowercaseString属性来访问大写/小写版本的字符串。+let normal = "Could you help me, please?" ++let normal = "Could you help me, please?" let shouty = normal.uppercaseString // shouty 值为 "COULD YOU HELP ME, PLEASE?" let whispered = normal.lowercaseString // whispered 值为 "could you help me, please?" -Unicode
Unicode 是一个国际标准,用于文本的编码和表示。 它使您可以用标准格式表示来自任意语言几乎所有的字符,并能够对文本文件或网页这样的外部资源中的字符进行读写操作。
@@ -835,28 +843,31 @@ let whispered = normal.lowercaseString21位的 Unicode 标量值集合 (利用字符串的 unicodeScalars属性进行访问)下面由
-D``o``g``!和🐶(DOG FACE,Unicode 标量为U+1F436)组成的字符串中的每一个字符代表着一种不同的表示:+let dogString = "Dog!🐶" -+let dogString = "Dog!🐶" +UTF-8
您可以通过遍历字符串的
-utf8属性来访问它的UTF-8表示。 其为UTF8View类型的属性,UTF8View是无符号8位 (UInt8) 值的集合,每一个UInt8值都是一个字符的 UTF-8 表示:+for codeUnit in dogString.utf8 { +for codeUnit in dogString.utf8 { print("\(codeUnit) ") } print("\n") // 68 111 103 33 240 159 144 182 -上面的例子中,前四个10进制代码单元值 (68, 111, 103, 33) 代表了字符
Dog和!,他们的 UTF-8 表示与 ASCII 表示相同。 +上面的例子中,前四个10进制代码单元值 (68, 111, 103, 33) 代表了字符
Dog和!,它们的 UTF-8 表示与 ASCII 表示相同。 后四个代码单元值 (240, 159, 144, 182) 是DOG FACE的4字节 UTF-8 表示。UTF-16
您可以通过遍历字符串的
-utf16属性来访问它的UTF-16表示。 其为UTF16View类型的属性,UTF16View是无符号16位 (UInt16) 值的集合,每一个UInt16都是一个字符的 UTF-16 表示:+for codeUnit in dogString.utf16 { +for codeUnit in dogString.utf16 { print("\(codeUnit) ") } print("\n") // 68 111 103 33 55357 56374 -同样,前四个代码单元值 (68, 111, 103, 33) 代表了字符
+Dog和!,他们的 UTF-16 代码单元和 UTF-8 完全相同。同样,前四个代码单元值 (68, 111, 103, 33) 代表了字符
Dog和!,它们的 UTF-16 代码单元和 UTF-8 完全相同。第五和第六个代码单元值 (55357 和 56374) 是
@@ -865,16 +876,17 @@ print("\n") 其为DOG FACE字符的UTF-16 表示。 第一个值为U+D83D(十进制值为 55357),第二个值为U+DC36(十进制值为 56374)。UnicodeScalarView类型的属性,UnicodeScalarView是UnicodeScalar的集合。UnicodeScalar是21位的 Unicode 代码点。每一个
-UnicodeScalar拥有一个值属性,可以返回对应的21位数值,用UInt32来表示。+for scalar in dogString.unicodeScalars { +for scalar in dogString.unicodeScalars { print("\(scalar.value) ") } print("\n") // 68 111 103 33 128054 -同样,前四个代码单元值 (68, 111, 103, 33) 代表了字符
Dog和!。 +同样,前四个代码单元值 (68, 111, 103, 33) 代表了字符
Dog和!。 第五位数值,128054,是一个十六进制1F436的十进制表示。 其等同于DOG FACE的Unicode 标量 U+1F436。作为查询字符值属性的一种替代方法,每个
-UnicodeScalar值也可以用来构建一个新的字符串值,比如在字符串插值中使用:for scalar in dogString.unicodeScalars { ++for scalar in dogString.unicodeScalars { println("\(scalar) ") } // D @@ -883,6 +895,7 @@ print("\n") // ! // 🐶+@@ -268,7 +268,7 @@- 2.12. 附属脚本 + 2.12. 下标脚本 @@ -520,7 +520,7 @@ - + @@ -587,11 +587,11 @@ -+ -翻译:zqp
-校对:shinyzhu
+翻译:zqp +校对:shinyzhu
集合类型 (Collection Types)
@@ -604,90 +604,120 @@Swift 语言提供经典的数组和字典两种集合类型来存储集合数据。数组用来按顺序存储相同类型的数据。字典虽然无序存储相同类型数据值但是需要由独有的标识符引用和寻址(就是键值对)。
Swift 语言里的数组和字典中存储的数据值类型必须明确。 这意味着我们不能把不正确的数据类型插入其中。 同时这也说明我们完全可以对获取出的值类型非常自信。 Swift 对显式类型集合的使用确保了我们的代码对工作所需要的类型非常清楚,也让我们在开发中可以早早地找到任何的类型不匹配错误。
-注意:
-Swift 的数组结构在被声明成常量和变量或者被传入函数与方法中时会相对于其他类型展现出不同的特性。 获取更多信息请参见集合的可变性与集合在赋值和复制中的行为章节。
+注意: +Swift 的数组结构在被声明成常量和变量或者被传入函数与方法中时会相对于其他类型展现出不同的特性。 获取更多信息请参见集合的可变性与集合在赋值和复制中的行为章节。
数组
-数组使用有序列表存储相同类型的多重数据。相同的值可以多次出现在一个数组的不同位置中。
-Swift 数组对存储数据有具体要求。 不同于 Objective-C 的
+NSArray和NSMutableArray类,他们可以存储任何类型的实例而且不提供他们返回对象的任何本质信息。 在 Swift 中,数据值在被存储进入某个数组之前类型必须明确,方法是通过显式的类型标注或类型推断,而且不是必须是class类型。例如: 如果我们创建了一个Int值类型的数组,我们不能往其中插入任何不是Int类型的数据。 Swift 中的数组是类型安全的,并且它们中包含的类型必须明确。数组使用有序列表存储同一类型的多个值。相同的值可以多次出现在一个数组的不同位置中。
+Swift 数组特定于它所存储元素的类型。这与 Objective-C 的 NSArray 和 NSMutableArray 不同,这两个类可以存储任意类型的对象,并且不提供所返回对象的任何特别信息。在 Swift 中,数据值在被存储进入某个数组之前类型必须明确,方法是通过显式的类型标注或类型推断,而且不是必须是
class类型。例如: 如果我们创建了一个Int值类型的数组,我们不能往其中插入任何不是Int类型的数据。 Swift 中的数组是类型安全的,并且它们中包含的类型必须明确。数组的简单语法
写 Swift 数组应该遵循像
Array<SomeType>这样的形式,其中SomeType是这个数组中唯一允许存在的数据类型。 我们也可以使用像SomeType[]这样的简单语法。 尽管两种形式在功能上是一样的,但是推荐较短的那种,而且在本文中都会使用这种形式来使用数组。数组构造语句
-我们可以使用字面语句来进行数组构造,这是一种用一个或者多个数值构造数组的简单方法。字面语句是一系列由逗号分割并由方括号包含的数值。 +
我们可以使用字面量来进行数组构造,这是一种用一个或者多个数值构造数组的简单方法。字面量是一系列由逗号分割并由方括号包含的数值。
[value 1, value 2, value 3]。下面这个例子创建了一个叫做
-shoppingList并且存储字符串的数组:+var shoppingList: String[] = ["Eggs", "Milk"] +var shoppingList: String[] = ["Eggs", "Milk"] // shoppingList 已经被构造并且拥有两个初始项。 -+
shoppingList变量被声明为“字符串值类型的数组“,记作String[]。 因为这个数组被规定只有String一种数据结构,所以只有String类型可以在其中被存取。 在这里,shoppinglist数组由两个String值("Eggs"和"Milk")构造,并且由字面语句定义。
shoppingList变量被声明为“字符串值类型的数组“,记作String[]。 因为这个数组被规定只有String一种数据结构,所以只有String类型可以在其中被存取。 在这里,shoppinglist数组由两个String值("Eggs"和"Milk")构造,并且由字面量定义。--注意:
-+
Shoppinglist数组被声明为变量(var关键字创建)而不是常量(let创建)是因为以后可能会有更多的数据项被插入其中。注意: +
Shoppinglist数组被声明为变量(var关键字创建)而不是常量(let创建)是因为以后可能会有更多的数据项被插入其中。在这个例子中,字面语句仅仅包含两个
-String值。匹配了该数组的变量声明(只能包含String的数组),所以这个字面语句的分配过程就是允许用两个初始项来构造shoppinglist。由于 Swift 的类型推断机制,当我们用字面语句构造只拥有相同类型值数组的时候,我们不必把数组的类型定义清楚。
-shoppinglist的构造也可以这样写:var shoppingList = ["Eggs", "Milk"] -因为所有字面语句中的值都是相同的类型,Swift 可以推断出
+String[]是shoppinglist中变量的正确类型。在这个例子中,字面量仅仅包含两个
+String值。匹配了该数组的变量声明(只能包含String的数组),所以这个字面量的分配过程就是允许用两个初始项来构造shoppinglist。由于 Swift 的类型推断机制,当我们用字面量构造只拥有相同类型值数组的时候,我们不必把数组的类型定义清楚。
+shoppinglist的构造也可以这样写:+var shoppingList = ["Eggs", "Milk"] +因为所有字面量中的值都是相同的类型,Swift 可以推断出
String[]是shoppinglist中变量的正确类型。访问和修改数组
我们可以通过数组的方法和属性来访问和修改数组,或者下标语法。 还可以使用数组的只读属性
-count来获取数组中的数据项数量。+println("The shopping list contains \(shoppingList.count) items.") +println("The shopping list contains \(shoppingList.count) items.") // 输出"The shopping list contains 2 items."(这个数组有2个项) -使用布尔项
-isEmpty来作为检查count属性的值是否为 0 的捷径。+if shoppingList.isEmpty { +使用布尔项
+isEmpty来作为检查count属性的值是否为 0 的捷径。if shoppingList.isEmpty { println("The shopping list is empty.") } else { println("The shopping list is not empty.") } // 打印 "The shopping list is not empty."(shoppinglist不是空的) -也可以使用
-append方法在数组后面添加新的数据项:+shoppingList.append("Flour") +也可以使用
+append方法在数组后面添加新的数据项:shoppingList.append("Flour") // shoppingList 现在有3个数据项,有人在摊煎饼 -除此之外,使用加法赋值运算符(
-+=)也可以直接在数组后面添加数据项:+shoppingList += "Baking Powder" +除此之外,使用加法赋值运算符(
++=)也可以直接在数组后面添加数据项:shoppingList += "Baking Powder" // shoppingList 现在有四项了 -我们也可以使用加法赋值运算符(
-+=)直接添加拥有相同类型数据的数组。+shoppingList += ["Chocolate Spread", "Cheese", "Butter"] +我们也可以使用加法赋值运算符(
++=)直接添加拥有相同类型数据的数组。shoppingList += ["Chocolate Spread", "Cheese", "Butter"] // shoppingList 现在有7项了 -可以直接使用下标语法来获取数组中的数据项,把我们需要的数据项的索引值放在直接放在数组名称的方括号中:
-+var firstItem = shoppingList[0] +可以直接使用下标语法来获取数组中的数据项,把我们需要的数据项的索引值放在直接放在数组名称的方括号中:
+var firstItem = shoppingList[0] // 第一项是 "Eggs" -注意第一项在数组中的索引值是
+0而不是1。 Swift 中的数组索引总是从零开始。注意第一项在数组中的索引值是
0而不是1。 Swift 中的数组索引总是从零开始。我们也可以用下标来改变某个已有索引值对应的数据值:
-+shoppingList[0] = "Six eggs" +shoppingList[0] = "Six eggs" // 其中的第一项现在是 "Six eggs" 而不是 "Eggs" -还可以利用下标来一次改变一系列数据值,即使新数据和原有数据的数量是不一样的。下面的例子把
-"Chocolate Spread","Cheese",和"Butter"替换为"Bananas"和"Apples":+shoppingList[4...6] = ["Bananas", "Apples"] +还可以利用下标来一次改变一系列数据值,即使新数据和原有数据的数量是不一样的。下面的例子把
+"Chocolate Spread","Cheese",和"Butter"替换为"Bananas"和"Apples":shoppingList[4...6] = ["Bananas", "Apples"] // shoppingList 现在有六项 -+<<<<<<< HEAD
+注意:
-我们不能使用下标语法在数组尾部添加新项。如果我们试着用这种方法对索引越界的数据进行检索或者设置新值的操作,我们会引发一个运行期错误。我们可以使用索引值和数组的
+count属性进行比较来在使用某个索引之前先检验是否有效。除了当count等于 0 时(说明这是个空数组),最大索引值一直是count - 1,因为数组都是零起索引。我们不能使用下标语法在数组尾部添加新项。如果我们试着用这种方法对索引越界的数据进行检索或者设置新值的操作,我们会引发一个运行期错误。我们可以使用索引值和数组的
+count属性进行比较来在使用某个索引之前先检验是否有效。除了当count等于 0 时(说明这是个空数组),最大索引值一直是count - 1,因为数组都是零起索引。注意:
+我们不能使用下标语法在数组尾部添加新项。如果我们试着用这种方法对索引越界的数据进行检索或者设置新值的操作,我们会引发一个运行时错误。我们可以使用索引值和数组的
+count属性进行比较来在使用某个索引之前先检验是否有效。除了当count等于 0 时(说明这是个空数组),最大索引值一直是count - 1,因为数组都是零起索引。+++++++++++a516af6a531a104ec88da0d236ecf389a5ec72af
+调用数组的
-insert(atIndex:)方法来在某个具体索引值之前添加数据项:+shoppingList.insert("Maple Syrup", atIndex: 0) +shoppingList.insert("Maple Syrup", atIndex: 0) // shoppingList 现在有7项 // "Maple Syrup" 现在是这个列表中的第一项 -这次
+insert函数调用把值为"Maple Syrup"的新数据项插入列表的最开始位置,并且使用0作为索引值。这次
insert函数调用把值为"Maple Syrup"的新数据项插入列表的最开始位置,并且使用0作为索引值。类似的我们可以使用
-removeAtIndex方法来移除数组中的某一项。这个方法把数组在特定索引值中存储的数据项移除并且返回这个被移除的数据项(我们不需要的时候就可以无视它):+let mapleSyrup = shoppingList.removeAtIndex(0) -//索引值为0的数据项被移除 +let mapleSyrup = shoppingList.removeAtIndex(0) +// 索引值为0的数据项被移除 // shoppingList 现在只有6项,而且不包括Maple Syrup // mapleSyrup常量的值等于被移除数据项的值 "Maple Syrup" -数据项被移除后数组中的空出项会被自动填补,所以现在索引值为
-0的数据项的值再次等于"Six eggs":+firstItem = shoppingList[0] +数据项被移除后数组中的空出项会被自动填补,所以现在索引值为
+0的数据项的值再次等于"Six eggs":firstItem = shoppingList[0] // firstItem 现在等于 "Six eggs" -如果我们只想把数组中的最后一项移除,可以使用
-removeLast方法而不是removeAtIndex方法来避免我们需要获取数组的count属性。就像后者一样,前者也会返回被移除的数据项:+let apples = shoppingList.removeLast() +如果我们只想把数组中的最后一项移除,可以使用
+removeLast方法而不是removeAtIndex方法来避免我们需要获取数组的count属性。就像后者一样,前者也会返回被移除的数据项:+// apples 常量的值现在等于"Apples" 字符串 +let apples = shoppingList.removeLast() // 数组的最后一项被移除了 // shoppingList现在只有5项,不包括cheese -// apples 常量的值现在等于"Apples" 字符串 -数组的遍历
我们可以使用
-for-in循环来遍历所有数组中的数据项:+for item in shoppingList { +for item in shoppingList { println(item) } // Six eggs @@ -695,8 +725,9 @@ // Flour // Baking Powder // Bananas -如果我们同时需要每个数据项的值和索引值,可以使用全局
-enumerate函数来进行数组遍历。enumerate返回一个由每一个数据项索引值和数据值组成的键值对组。我们可以把这个键值对组分解成临时常量或者变量来进行遍历:+for (index, value) in enumerate(shoppingList) { +如果我们同时需要每个数据项的值和索引值,可以使用全局
+enumerate函数来进行数组遍历。enumerate返回一个由每一个数据项索引值和数据值组成的元组。我们可以把这个元组分解成临时常量或者变量来进行遍历:for (index, value) in enumerate(shoppingList) { println("Item \(index + 1): \(value)") } // Item 1: Six eggs @@ -704,142 +735,163 @@ // Item 3: Flour // Item 4: Baking Powder // Item 5: Bananas -更多关于
+for-in循环的介绍请参见for 循环。更多关于
for-in循环的介绍请参见for 循环。创建并且构造一个数组
我们可以使用构造语法来创建一个由特定数据类型构成的空数组:
-+var someInts = Int[]() +var someInts = Int[]() println("someInts is of type Int[] with \(someInts.count) items。") // 打印 "someInts is of type Int[] with 0 items。"(someInts是0数据项的Int[]数组) -注意
+someInts被设置为一个Int[]构造函数的输出所以它的变量类型被定义为Int[]。注意
someInts被设置为一个Int[]构造函数的输出所以它的变量类型被定义为Int[]。除此之外,如果代码上下文中提供了类型信息, 例如一个函数参数或者一个已经定义好类型的常量或者变量,我们可以使用空数组语句创建一个空数组,它的写法很简单:
-[](一对空方括号):+someInts.append(3) +someInts.append(3) // someInts 现在包含一个INT值 someInts = [] // someInts 现在是空数组,但是仍然是Int[]类型的。 -Swift 中的
-Array类型还提供一个可以创建特定大小并且所有数据都被默认的构造方法。我们可以把准备加入新数组的数据项数量(count)和适当类型的初始值(repeatedValue)传入数组构造函数:+var threeDoubles = Double[](count: 3, repeatedValue:0.0) +Swift 中的
+Array类型还提供一个可以创建特定大小并且所有数据都被默认的构造方法。我们可以把准备加入新数组的数据项数量(count)和适当类型的初始值(repeatedValue)传入数组构造函数:var threeDoubles = Double[](count: 3, repeatedValue:0.0) // threeDoubles 是一种 Double[]数组, 等于 [0.0, 0.0, 0.0] -因为类型推断的存在,我们使用这种构造方法的时候不需要特别指定数组中存储的数据类型,因为类型可以从默认值推断出来:
-+var anotherThreeDoubles = Array(count: 3, repeatedValue: 2.5) +因为类型推断的存在,我们使用这种构造方法的时候不需要特别指定数组中存储的数据类型,因为类型可以从默认值推断出来:
+var anotherThreeDoubles = Array(count: 3, repeatedValue: 2.5) // anotherThreeDoubles is inferred as Double[], and equals [2.5, 2.5, 2.5] -最后,我们可以使用加法操作符(
-+)来组合两种已存在的相同类型数组。新数组的数据类型会被从两个数组的数据类型中推断出来:+var sixDoubles = threeDoubles + anotherThreeDoubles +最后,我们可以使用加法操作符(
++)来组合两种已存在的相同类型数组。新数组的数据类型会被从两个数组的数据类型中推断出来:+var sixDoubles = threeDoubles + anotherThreeDoubles // sixDoubles 被推断为 Double[], 等于 [0.0, 0.0, 0.0, 2.5, 2.5, 2.5] -字典
-字典是一种存储相同类型多重数据的存储器。每个值(value)都关联独特的键(key),键作为字典中的这个值数据的标识符。和数组中的数据项不同,字典中的数据项并没有具体顺序。我们在需要通过标识符(键)访问数据的时候使用字典,这种方法很大程度上和我们在现实世界中使用字典查字义的方法一样。
+字典是一种存储多个相同类型的值的容器。每个值(value)都关联唯一的键(key),键作为字典中的这个值数据的标识符。和数组中的数据项不同,字典中的数据项并没有具体顺序。我们在需要通过标识符(键)访问数据的时候使用字典,这种方法很大程度上和我们在现实世界中使用字典查字义的方法一样。
Swift 的字典使用时需要具体规定可以存储键和值类型。不同于 Objective-C 的
NSDictionary和NSMutableDictionary类可以使用任何类型的对象来作键和值并且不提供任何关于这些对象的本质信息。在 Swift 中,在某个特定字典中可以存储的键和值必须提前定义清楚,方法是通过显性类型标注或者类型推断。Swift 的字典使用
Dictionary<KeyType, ValueType>定义,其中KeyType是字典中键的数据类型,ValueType是字典中对应于这些键所存储值的数据类型。-
KeyType的唯一限制就是可哈希的,这样可以保证它是独一无二的,所有的 Swift 基本类型(例如String,Int,Double和Bool)都是默认可哈希的,并且所有这些类型都可以在字典中当做键使用。未关联值的枚举成员(参见枚举)也是默认可哈希的。字典字面语句
-我们可以使用字典字面语句来构造字典,他们和我们刚才介绍过的数组字面语句拥有相似语法。一个字典字面语句是一个定义拥有一个或者多个键值对的字典集合的简单语句。
-一个键值对是一个
-key和一个value的结合体。在字典字面语句中,每一个键值对的键和值都由冒号分割。这些键值对构成一个列表,其中这些键值对由方括号包含并且由逗号分割:[key 1: value 1, key 2: value 2, key 3: value 3] -下面的例子创建了一个存储国际机场名称的字典。在这个字典中键是三个字母的国际航空运输相关代码,值是机场名称:
-var airports: Dictionary<String, String> = ["TYO": "Tokyo", "DUB": "Dublin"] -+
airports字典被定义为一种Dictionary<String, String>,它意味着这个字典的键和值都是String类型。字典字面量
+我们可以使用字典字面量来构造字典,它们和我们刚才介绍过的数组字面量拥有相似语法。一个字典字面量是一个定义拥有一个或者多个键值对的字典集合的简单语句。
+一个键值对是一个
+key和一个value的结合体。在字典字面量中,每一个键值对的键和值都由冒号分割。这些键值对构成一个列表,其中这些键值对由方括号包含并且由逗号分割:+[key 1: value 1, key 2: value 2, key 3: value 3] +下面的例子创建了一个存储国际机场名称的字典。在这个字典中键是三个字母的国际航空运输相关代码,值是机场名称:
++var airports: Dictionary<String, String> = ["TYO": "Tokyo", "DUB": "Dublin"] +
airports字典被定义为一种Dictionary<String, String>,它意味着这个字典的键和值都是String类型。--注意:
-+
airports字典被声明为变量(用var关键字)而不是常量(let关键字)因为后来更多的机场信息会被添加到这个示例字典中。注意: +
airports字典被声明为变量(用var关键字)而不是常量(let关键字)因为后来更多的机场信息会被添加到这个示例字典中。-
airports字典使用字典字面语句初始化,包含两个键值对。第一对的键是TYO,值是Tokyo。第二对的键是DUB,值是Dublin。这个字典语句包含了两个
-String: String类型的键值对。他们对应airports变量声明的类型(一个只有String键和String值的字典)所以这个字典字面语句是构造两个初始数据项的airport字典。和数组一样,如果我们使用字面语句构造字典就不用把类型定义清楚。
-airports的也可以用这种方法简短定义:var airports = ["TYO": "Tokyo", "DUB": "Dublin"] -因为这个语句中所有的键和值都分别是相同的数据类型,Swift 可以推断出
+Dictionary<String, String>是airports字典的正确类型。+
airports字典使用字典字面量初始化,包含两个键值对。第一对的键是TYO,值是Tokyo。第二对的键是DUB,值是Dublin。这个字典语句包含了两个
+String: String类型的键值对。它们对应airports变量声明的类型(一个只有String键和String值的字典)所以这个字典字面量是构造两个初始数据项的airport字典。和数组一样,如果我们使用字面量构造字典就不用把类型定义清楚。
+airports的也可以用这种方法简短定义:+var airports = ["TYO": "Tokyo", "DUB": "Dublin"] +因为这个语句中所有的键和值都分别是相同的数据类型,Swift 可以推断出
Dictionary<String, String>是airports字典的正确类型。读取和修改字典
我们可以通过字典的方法和属性来读取和修改字典,或者使用下标语法。和数组一样,我们可以通过字典的只读属性
-count来获取某个字典的数据项数量:+println("The dictionary of airports contains \(airports.count) items.") +println("The dictionary of airports contains \(airports.count) items.") // 打印 "The dictionary of airports contains 2 items."(这个字典有两个数据项) -我们也可以在字典中使用下标语法来添加新的数据项。可以使用一个合适类型的 key 作为下标索引,并且分配新的合适类型的值:
-+airports["LHR"] = "London" +我们也可以在字典中使用下标语法来添加新的数据项。可以使用一个合适类型的 key 作为下标索引,并且分配新的合适类型的值:
+airports["LHR"] = "London" // airports 字典现在有三个数据项 -我们也可以使用下标语法来改变特定键对应的值:
-+airports["LHR"] = "London Heathrow" +我们也可以使用下标语法来改变特定键对应的值:
+airports["LHR"] = "London Heathrow" // "LHR"对应的值 被改为 "London Heathrow -作为另一种下标方法,字典的
+updateValue(forKey:)方法可以设置或者更新特定键对应的值。就像上面所示的示例,updateValue(forKey:)方法在这个键不存在对应值的时候设置值或者在存在时更新已存在的值。和上面的下标方法不一样,这个方法返回更新值之前的原值。这样方便我们检查更新是否成功。作为另一种下标方法,字典的
updateValue(forKey:)方法可以设置或者更新特定键对应的值。就像上面所示的示例,updateValue(forKey:)方法在这个键不存在对应值的时候设置值或者在存在时更新已存在的值。和上面的下标方法不一样,这个方法返回更新值之前的原值。这样方便我们检查更新是否成功。-
updateValue(forKey:)函数会返回包含一个字典值类型的可选值。举例来说:对于存储String值的字典,这个函数会返回一个String?或者“可选String”类型的值。如果值存在,则这个可选值值等于被替换的值,否则将会是nil。+if let oldValue = airports.updateValue("Dublin Internation", forKey: "DUB") { +if let oldValue = airports.updateValue("Dublin Internation", forKey: "DUB") { println("The old value for DUB was \(oldValue).") } // 输出 "The old value for DUB was Dublin."(DUB原值是dublin) -我们也可以使用下标语法来在字典中检索特定键对应的值。由于使用一个没有值的键这种情况是有可能发生的,可选类型返回这个键存在的相关值,否则就返回
-nil:+if let airportName = airports["DUB"] { +我们也可以使用下标语法来在字典中检索特定键对应的值。由于使用一个没有值的键这种情况是有可能发生的,可选类型返回这个键存在的相关值,否则就返回
+nil:if let airportName = airports["DUB"] { println("The name of the airport is \(airportName).") } else { println("That airport is not in the airports dictionary.") } // 打印 "The name of the airport is Dublin Internation."(机场的名字是都柏林国际) -我们还可以使用下标语法来通过给某个键的对应值赋值为
-nil来从字典里移除一个键值对:+airports["APL"] = "Apple Internation" +我们还可以使用下标语法来通过给某个键的对应值赋值为
+nil来从字典里移除一个键值对:airports["APL"] = "Apple Internation" // "Apple Internation"不是真的 APL机场, 删除它 airports["APL"] = nil // APL现在被移除了 -另外,
-removeValueForKey方法也可以用来在字典中移除键值对。这个方法在键值对存在的情况下会移除该键值对并且返回被移除的value或者在没有值的情况下返回nil:+if let removedValue = airports.removeValueForKey("DUB") { +另外,
+removeValueForKey方法也可以用来在字典中移除键值对。这个方法在键值对存在的情况下会移除该键值对并且返回被移除的value或者在没有值的情况下返回nil:+if let removedValue = airports.removeValueForKey("DUB") { println("The removed airport's name is \(removedValue).") } else { println("The airports dictionary does not contain a value for DUB.") } // prints "The removed airport's name is Dublin International." -字典遍历
-我们可以使用
-for-in循环来遍历某个字典中的键值对。每一个字典中的数据项都由(key, value)元组形式返回,并且我们可以使用暂时性常量或者变量来分解这些元组:+for (airportCode, airportName) in airports { +我们可以使用
+for-in循环来遍历某个字典中的键值对。每一个字典中的数据项都由(key, value)元组形式返回,并且我们可以使用临时常量或者变量来分解这些元组:for (airportCode, airportName) in airports { println("\(airportCode): \(airportName)") } // TYO: Tokyo // LHR: London Heathrow --
for-in循环请参见For 循环。我们也可以通过访问他的
-keys或者values属性(都是可遍历集合)检索一个字典的键或者值:+for airportCode in airports.keys { ++
for-in循环请参见For 循环。我们也可以通过访问它的
+keys或者values属性(都是可遍历集合)检索一个字典的键或者值:+for airportCode in airports.keys { println("Airport code: \(airportCode)") } // Airport code: TYO // Airport code: LHR - -for airportName in airports.values { +for airportName in airports.values { println("Airport name: \(airportName)") } // Airport name: Tokyo // Airport name: London Heathrow -如果我们只是需要使用某个字典的键集合或者值集合来作为某个接受
-Array实例 API 的参数,可以直接使用keys或者values属性直接构造一个新数组:+let airportCodes = Array(airports.keys) +如果我们只是需要使用某个字典的键集合或者值集合来作为某个接受
+Array实例 API 的参数,可以直接使用keys或者values属性直接构造一个新数组:+let airportCodes = Array(airports.keys) // airportCodes is ["TYO", "LHR"] - -let airportNames = Array(airports.values) +let airportNames = Array(airports.values) // airportNames is ["Tokyo", "London Heathrow"] --注意:
-Swift 的字典类型是无序集合类型。其中字典键,值,键值对在遍历的时候会重新排列,而且其中顺序是不固定的。
++注意: +Swift 的字典类型是无序集合类型。其中字典键,值,键值对在遍历的时候会重新排列,而且其中顺序是不固定的。
创建一个空字典
我们可以像数组一样使用构造语法创建一个空字典:
-+var namesOfIntegers = Dictionary<Int, String>() +var namesOfIntegers = Dictionary<Int, String>() // namesOfIntegers 是一个空的 Dictionary<Int, String> -这个例子创建了一个
-Int, String类型的空字典来储存英语对整数的命名。它的键是Int型,值是String型。如果上下文已经提供了信息类型,我们可以使用空字典字面语句来创建一个空字典,记作
-[:](中括号中放一个冒号):+namesOfIntegers[16] = "sixteen" +这个例子创建了一个
+Int, String类型的空字典来储存英语对整数的命名。它的键是Int型,值是String型。如果上下文已经提供了信息类型,我们可以使用空字典字面量来创建一个空字典,记作
+[:](中括号中放一个冒号):namesOfIntegers[16] = "sixteen" // namesOfIntegers 现在包含一个键值对 namesOfIntegers = [:] // namesOfIntegers 又成为了一个 Int, String类型的空字典 --注意:
-在后台,Swift 的数组和字典都是由泛型集合来实现的,想了解更多泛型和集合信息请参见泛型。
++注意: +在后台,Swift 的数组和字典都是由泛型集合来实现的,想了解更多泛型和集合信息请参见泛型。
集合的可变性
-数组和字典都是在单个集合中存储可变值。如果我们创建一个数组或者字典并且把它分配成一个变量,这个集合将会是可变的。这意味着我们可以在创建之后添加更多或移除已存在的数据项来改变这个集合的大小。与此相反,如果我们把数组或字典分配成常量,那么他就是不可变的,它的大小不能被改变。
+数组和字典都是在单个集合中存储可变值。如果我们创建一个数组或者字典并且把它分配成一个变量,这个集合将会是可变的。这意味着我们可以在创建之后添加更多或移除已存在的数据项来改变这个集合的大小。与此相反,如果我们把数组或字典分配成常量,那么它就是不可变的,它的大小不能被改变。
对字典来说,不可变性也意味着我们不能替换其中任何现有键所对应的值。不可变字典的内容在被首次设定之后不能更改。 不可变性对数组来说有一点不同,当然我们不能试着改变任何不可变数组的大小,但是我们可以重新设定相对现存索引所对应的值。这使得 Swift 数组在大小被固定的时候依然可以做的很棒。
Swift 数组的可变性行为同时影响了数组实例如何被分配和修改,想获取更多信息,请参见集合在赋值和复制中的行为。
-diff --git a/chapter2/05_Control_Flow.html b/chapter2/05_Control_Flow.html old mode 100755 new mode 100644 index 99dac727..b4647dc3 --- a/chapter2/05_Control_Flow.html +++ b/chapter2/05_Control_Flow.html @@ -46,7 +46,7 @@ -注意:
-在我们不需要改变数组大小的时候创建不可变数组是很好的习惯。如此 Swift 编译器可以优化我们创建的集合。
+注意: +在我们不需要改变数组大小的时候创建不可变数组是很好的习惯。如此 Swift 编译器可以优化我们创建的集合。
+@@ -268,7 +268,7 @@- 2.12. 附属脚本 + 2.12. 下标脚本 @@ -520,7 +520,7 @@ - + @@ -587,11 +587,10 @@ -+ -翻译:vclwei, coverxit, NicePiao
-校对:coverxit
+翻译:vclwei, coverxit, NicePiao
校对:coverxit, stanzhai控制流
@@ -603,16 +602,14 @@控制转移语句(Control Transfer Statements) Swift提供了类似 C 语言的流程控制结构,包括可以多次执行任务的
-for和while循环,基于特定条件选择执行不同代码分支的if和switch语句,还有控制流程跳转到其他代码的break和continue语句。除了 C 语言里面传统的for条件递增(
+for-condition-increment)循环,Swift 还增加了for-in循环,用来更简单地遍历数组(array),字典(dictionary),区间(range),字符串(string)和其他序列类型。除了 C 语言里面传统的 for 条件递增(
for-condition-increment)循环,Swift 还增加了for-in循环,用来更简单地遍历数组(array),字典(dictionary),区间(range),字符串(string)和其他序列类型。Swift 的
switch语句比 C 语言中更加强大。在 C 语言中,如果某个 case 不小心漏写了break,这个 case 就会贯穿(fallthrough)至下一个 case,Swift 无需写break,所以不会发生这种贯穿(fallthrough)的情况。case 还可以匹配更多的类型模式,包括区间匹配(range matching),元组(tuple)和特定类型的描述。switch的 case 语句中匹配的值可以是由 case 体内部临时的常量或者变量决定,也可以由where分句描述更复杂的匹配条件。For 循环
for循环用来按照指定的次数多次执行一系列语句。Swift 提供两种for循环形式:-
- -
-
for-in用来遍历一个区间(range),序列(sequence),集合(collection),系列(progression)里面所有的元素执行一系列语句。- +
for条件递增(
-for-condition-increment)语句,用来重复执行一系列语句直到达成特定条件达成,一般通过在每次循环完成后增加计数器的值来实现。- +
for-in用来遍历一个区间(range),序列(sequence),集合(collection),系列(progression)里面所有的元素执行一系列语句。- for条件递增(
for-condition-increment)语句,用来重复执行一系列语句直到达成特定条件达成,一般通过在每次循环完成后增加计数器的值来实现。For-In
@@ -630,8 +627,7 @@例子中用来进行遍历的元素是一组使用闭区间操作符(
...)表示的从1到5的数字。index被赋值为闭区间中的第一个数字(1),然后循环中的语句被执行一次。在本例中,这个循环只包含一个语句,用来输出当前index值所对应的乘 5 乘法表结果。该语句执行后,index的值被更新为闭区间中的第二个数字(2),之后println方法会再执行一次。整个过程会进行到闭区间结尾为止。上面的例子中,
index是一个每次循环遍历开始时被自动赋值的常量。这种情况下,index在使用前不需要声明,只需要将它包含在循环的声明中,就可以对其进行隐式声明,而无需使用let关键字声明。-注意:
-+
index常量只存在于循环的生命周期里。如果你想在循环完成后访问index的值,又或者想让index成为一个变量而不是常量,你必须在循环之前自己进行声明。注意:
index常量只存在于循环的生命周期里。如果你想在循环完成后访问index的值,又或者想让index成为一个变量而不是常量,你必须在循环之前自己进行声明。如果你不需要知道区间内每一项的值,你可以使用下划线(
_)替代变量名来忽略对值的访问:let base = 3 @@ -685,10 +681,9 @@ for (animalName, legCount) in numberOfLegs { // index is 2下面是一般情况下这种循环方式的格式:
-+for `initialization`; `condition`; `increment` { - `statements` -} -+for
+initialization;condition;increment{
statements
}和 C 语言中一样,分号将循环的定义分为 3 个部分,不同的是,Swift 不需要使用圆括号将“initialization; condition; increment”包括起来。
这个循环执行流程如下:
@@ -698,12 +693,9 @@ for (animalName, legCount) in numberOfLegs {
- 执行所有语句(statements)之后,执行递增表达式(increment expression)。通常会增加或减少计数器的值,或者根据语句(statements)输出来修改某一个初始化的变量。当递增表达式运行完成后,重复执行第 2 步,条件表达式会再次执行。
上述描述和循环格式等同于:
-+`initialization` -while `condition` { - `statements` - `increment` -} -++
initialization
whilecondition{
statements
increment
}在初始化表达式中声明的常量和变量(比如
var index = 0)只在for循环的生命周期里有效。如果想在循环结束后访问index的值,你必须要在循环生命周期开始前声明index。var index: Int for index = 0; index < 3; ++index { @@ -720,19 +712,16 @@ println("The loop statements were executed \(index) times")While 循环
while循环运行一系列语句直到条件变成false。这类循环适合使用在第一次迭代前迭代次数未知的情况下。Swift 提供两种while循环形式:-
- -
-
while循环,每次在循环开始时计算条件是否符合;- +
-
do-while循环,每次在循环结束时计算条件是否符合。- +
while循环,每次在循环开始时计算条件是否符合;do-while循环,每次在循环结束时计算条件是否符合。While
while循环从计算单一条件开始。如果条件为true,会重复运行一系列语句,直到条件变为false。下面是一般情况下
-while循环格式:+while `condition` { - `statements` -} -+while
+condition{
statements
}下面的例子来玩一个叫做蛇和梯子(Snakes and Ladders)的小游戏,也叫做滑道和梯子(Chutes and Ladders):
游戏的规则如下:
@@ -775,10 +764,9 @@ println("Game over!")Do-While
while循环的另外一种形式是do-while,它和while的区别是在判断循环条件之前,先执行一次循环的代码块,然后重复循环直到条件为false。下面是一般情况下
-do-while循环的格式:+do { - `statements` -} while `condition` -+do {
+
statements
} whilecondition还是蛇和梯子的游戏,使用
do-while循环来替代while循环。finalSquare、board、square和diceRoll的值初始化同while循环一样:let finalSquare = 25 var board = Int[](count: finalSquare + 1, repeatedValue: 0) @@ -850,16 +838,9 @@ if temperatureInFahrenheit <= 32 {Switch
switch语句会尝试把某个值与若干个模式(pattern)进行匹配。根据第一个匹配成功的模式,switch语句会执行对应的代码。当有可能的情况较多时,通常用switch语句替换if语句。-
switch语句最简单的形式就是把某个值与一个或若干个相同类型的值作比较:+switch `some value to consider` { -case `value 1`: - `respond to value 1` -case `value 2`, -`value 3`: - `respond to value 2 or 3` -default: - `otherwise, do something else` -} -+switch
+some value to consider{
casevalue 1:
respond to value 1
casevalue 2,value 3:
respond to value 2 or 3
default:
otherwise, do something else
}
switch语句都由多个 case 构成。为了匹配某些更特定的值,Swift 提供了几种更复杂的匹配模式,这些模式将在本节的稍后部分提到。每一个 case 都是代码执行的一条分支,这与
if语句类似。与之不同的是,switch语句会决定哪一条分支应该被执行。@@ -882,8 +863,7 @@ default:
switch语句必须是完备的。这就是说,每一个可能的值都必须至少有一个 case 分支与之对应。在某些不可能涵盖所有值的情况下,你可以使用默认(default)分支满足该要求,这个默认分支必须在switch语句的最后面。不存在隐式的贯穿(No Implicit Fallthrough)
与 C 语言和 Objective-C 中的
switch语句不同,在 Swift 中,当匹配的 case 分支中的代码执行完毕后,程序会终止switch语句,而不会继续执行下一个 case 分支。这也就是说,不需要在 case 分支中显式地使用break语句。这使得switch语句更安全、更易用,也避免了因忘记写break语句而产生的错误。-注意:
-你依然可以在 case 分支中的代码执行完毕前跳出,详情请参考Switch 语句中的 break。
+注意:
你依然可以在 case 分支中的代码执行完毕前跳出,详情请参考Switch 语句中的 break。每一个 case 分支都必须包含至少一条语句。像下面这样书写代码是无效的,因为第一个 case 分支是空的:
let anotherCharacter: Character = "a" @@ -898,15 +878,9 @@ default:不像 C 语言里的
switch语句,在 Swift 中,switch语句不会同时匹配"a"和"A"。相反的,上面的代码会引起编译期错误:case "a": does not contain any executable statements——这就避免了意外地从一个 case 分支贯穿到另外一个,使得代码更安全、也更直观。一个 case 也可以包含多个模式,用逗号把它们分开(如果太长了也可以分行写):
-switch `some value to consider` { -case `value 1`, -`value 2`: - `statements` -} --注意: -如果想要贯穿至特定的 case 分支中,请使用
+fallthrough语句,详情请参考贯穿(Fallthrough)。switch
+some value to consider{
casevalue 1,value 2:
statements
}注意:
如果想要贯穿至特定的 case 分支中,请使用fallthrough语句,详情请参考贯穿(Fallthrough)。区间匹配(Range Matching)
@@ -1009,8 +983,7 @@ case let (x, y):Continue
continue语句告诉一个循环体立刻停止本次循环迭代,重新开始下次循环迭代。就好像在说“本次循环迭代我已经执行完了”,但是并不会离开整个循环体。-注意:
-在一个for条件递增(
+for-condition-increment)循环体中,在调用continue语句后,迭代增量仍然会被计算求值。循环体继续像往常一样工作,仅仅只是循环体中的执行代码会被跳过。注意:
在一个for条件递增(for-condition-increment)循环体中,在调用continue语句后,迭代增量仍然会被计算求值。循环体继续像往常一样工作,仅仅只是循环体中的执行代码会被跳过。下面的例子把一个小写字符串中的元音字母和空格字符移除,生成了一个含义模糊的短句:
let puzzleInput = "great minds think alike" @@ -1038,8 +1011,7 @@ println(puzzleOutput)当在一个
switch代码块中使用break时,会立即中断该switch代码块的执行,并且跳转到表示switch代码块结束的大括号(})后的第一行代码。这种特性可以被用来匹配或者忽略一个或多个分支。因为 Swift 的
switch需要包含所有的分支而且不允许有为空的分支,有时为了使你的意图更明显,需要特意匹配或者忽略某个分支。那么当你想忽略某个分支时,可以在该分支内写上break语句。当那个分支被匹配到时,分支内的break语句立即结束switch代码块。-注意:
-当一个
+switch分支仅仅包含注释时,会被报编译时错误。注释不是代码语句而且也不能让switch分支达到被忽略的效果。你总是可以使用break来忽略某个分支。注意:
当一个switch分支仅仅包含注释时,会被报编译时错误。注释不是代码语句而且也不能让switch分支达到被忽略的效果。你总是可以使用break来忽略某个分支。下面的例子通过
switch来判断一个Character值是否代表下面四种语言之一。为了简洁,多个值被包含在了同一个分支情况中。let numberSymbol: Character = "三" // 简体中文里的数字 3 @@ -1086,18 +1058,17 @@ println(description)如果
integerToDescribe的值不属于列表中的任何质数,那么它不会匹配到第一个switch分支。而这里没有其他特别的分支情况,所以integerToDescribe匹配到包含所有的default分支中。当
switch代码块执行完后,使用println函数打印该数字的描述。在这个例子中,数字5被准确的识别为了一个质数。-注意:
-+
fallthrough关键字不会检查它下一个将会落入执行的 case 中的匹配条件。fallthrough简单地使代码执行继续连接到下一个 case 中的执行代码,这和 C 语言标准中的switch语句特性是一样的。注意:
fallthrough关键字不会检查它下一个将会落入执行的 case 中的匹配条件。fallthrough简单地使代码执行继续连接到下一个 case 中的执行代码,这和 C 语言标准中的switch语句特性是一样的。带标签的语句(Labeled Statements)
在 Swift 中,你可以在循环体和
switch代码块中嵌套循环体和switch代码块来创造复杂的控制流结构。然而,循环体和switch代码块两者都可以使用break语句来提前结束整个方法体。因此,显示地指明break语句想要终止的是哪个循环体或者switch代码块,会很有用。类似地,如果你有许多嵌套的循环体,显示指明continue语句想要影响哪一个循环体也会非常有用。为了实现这个目的,你可以使用标签来标记一个循环体或者
switch代码块,当使用break或者continue时,带上这个标签,可以控制该标签代表对象的中断或者执行。产生一个带标签的语句是通过在该语句的关键词的同一行前面放置一个标签,并且该标签后面还需带着一个冒号。下面是一个
-while循环体的语法,同样的规则适用于所有的循环体和switch代码块。`label name`: while `condition` { - `statements` -} -下面的例子是在一个带有标签的
+while循环体中调用break和continue语句,该循环体是前面章节中蛇和梯子的改编版本。这次,游戏增加了一条额外的规则:+++
label name: whilecondition{
statements
}下面的例子是在一个带有标签的
while循环体中调用break和continue语句,该循环体是前面章节中蛇和梯子的改编版本。这次,游戏增加了一条额外的规则:@@ -1133,16 +1104,12 @@ println("Game over!")
- 为了获胜,你必须刚好落在第 25 个方块中。
每次循环迭代开始时掷骰子。与之前玩家掷完骰子就立即移动不同,这里使用了
switch来考虑每次移动可能产生的结果,从而决定玩家本次是否能够移动。-
- -
如果骰子数刚好使玩家移动到最终的方格里,游戏结束。
-break gameLoop语句跳转控制去执行while循环体后的第一行代码,游戏结束。- -
如果骰子数将会使玩家的移动超出最后的方格,那么这种移动是不合法的,玩家需要重新掷骰子。
-continue gameLoop语句结束本次while循环的迭代,开始下一次循环迭代。- +
在剩余的所有情况中,骰子数产生的都是合法的移动。玩家向前移动骰子数个方格,然后游戏逻辑再处理玩家当前是否处于蛇头或者梯子的底部。本次循环迭代结束,控制跳转到
-while循环体的条件判断语句处,再决定是否能够继续执行下次循环迭代。- 如果骰子数刚好使玩家移动到最终的方格里,游戏结束。
+break gameLoop语句跳转控制去执行while循环体后的第一行代码,游戏结束。- 如果骰子数将会使玩家的移动超出最后的方格,那么这种移动是不合法的,玩家需要重新掷骰子。
+continue gameLoop语句结束本次while循环的迭代,开始下一次循环迭代。- 在剩余的所有情况中,骰子数产生的都是合法的移动。玩家向前移动骰子数个方格,然后游戏逻辑再处理玩家当前是否处于蛇头或者梯子的底部。本次循环迭代结束,控制跳转到
while循环体的条件判断语句处,再决定是否能够继续执行下次循环迭代。-diff --git a/chapter2/06_Functions.html b/chapter2/06_Functions.html old mode 100755 new mode 100644 index 2d990ba9..dabea549 --- a/chapter2/06_Functions.html +++ b/chapter2/06_Functions.html @@ -46,7 +46,7 @@ -注意:
-如果上述的
break语句没有使用gameLoop标签,那么它将会中断switch代码块而不是while循环体。使用gameLoop标签清晰的表明了break想要中断的是哪个代码块。 +注意:
如果上述的break语句没有使用gameLoop标签,那么它将会中断switch代码块而不是while循环体。使用gameLoop标签清晰的表明了break想要中断的是哪个代码块。 同时请注意,当调用continue gameLoop去跳转到下一次循环迭代时,这里使用gameLoop标签并不是严格必须的。因为在这个游戏中,只有一个循环体,所以continue语句会影响到哪个循环体是没有歧义的。然而,continue语句使用gameLoop标签也是没有危害的。这样做符合标签的使用规则,同时参照旁边的break gameLoop,能够使游戏的逻辑更加清晰和易于理解。+@@ -268,7 +268,7 @@- 2.12. 附属脚本 + 2.12. 下标脚本 @@ -520,7 +520,7 @@ - + @@ -587,11 +587,10 @@ -+ -翻译:honghaoz
-校对:LunaticM
+翻译:honghaoz
校对:LunaticM函数(Functions)
@@ -605,8 +604,8 @@函数是用来完成特定任务的独立的代码块。你给一个函数起一个合适的名字,用来标示函数做什么,并且当函数需要执行的时候,这个名字会被“调用”。
Swift 统一的函数语法足够灵活,可以用来表示任何函数,包括从最简单的没有参数名字的 C 风格函数,到复杂的带局部和外部参数名的 Objective-C 风格函数。参数可以提供默认值,以简化函数调用。参数也可以即当做传入参数,也当做传出参数,也就是说,一旦函数执行结束,传入的参数值可以被修改。
-在 Swift 中,每个函数都有一种类型,包括函数的参数值类型和返回值类型。你可以把函数类型当做任何其他普通变量类型一样处理,这样就可以更简单地把函数当做别的函数的参数,也可以从其他函数中返回函数。函数的定义可以写在在其他函数定义中,这样可以在嵌套函数范围内实现功能封装。 -
+在 Swift 中,每个函数都有一种类型,包括函数的参数值类型和返回值类型。你可以把函数类型当做任何其他普通变量类型一样处理,这样就可以更简单地把函数当做别的函数的参数,也可以从其他函数中返回函数。函数的定义可以写在在其他函数定义中,这样可以在嵌套函数范围内实现功能封装。
+函数的定义与调用(Defining and Calling Functions)
当你定义一个函数时,你可以定义一个或多个有名字和类型的值,作为函数的输入(称为参数,parameters),也可以定义某种类型的值作为函数执行结束的输出(称为返回类型)。
每个函数有个函数名,用来描述函数执行的任务。要使用一个函数时,你用函数名“调用”,并传给它匹配的输入值(称作实参,arguments)。一个函数的实参必须与函数参数表里参数的顺序一致。
@@ -664,8 +663,7 @@ sayGoodbye("Dave")因为这个函数不需要返回值,所以这个函数的定义中没有返回箭头(->)和返回类型。
-注意:
-严格上来说,虽然没有返回值被定义,
+sayGoodbye函数依然返回了值。没有定义返回类型的函数会返回特殊的值,叫Void。它其实是一个空的元组(tuple),没有任何元素,可以写成()。注意:
严格上来说,虽然没有返回值被定义,sayGoodbye函数依然返回了值。没有定义返回类型的函数会返回特殊的值,叫Void。它其实是一个空的元组(tuple),没有任何元素,可以写成()。被调用时,一个函数的返回值可以被忽略:
func printAndCount(stringToPrint: String) -> Int { @@ -682,8 +680,7 @@ printWithoutCounting("hello, world")第一个函数
printAndCount,输出一个字符串并返回Int类型的字符数。第二个函数printWithoutCounting调用了第一个函数,但是忽略了它的返回值。当第二个函数被调用时,消息依然会由第一个函数输出,但是返回值不会被用到。-注意:
-返回值可以被忽略,但定义了有返回值的函数必须返回一个值,如果在函数定义底部没有返回任何值,这叫导致编译错误(compile-time error)。
+注意:
返回值可以被忽略,但定义了有返回值的函数必须返回一个值,如果在函数定义底部没有返回任何值,这叫导致编译错误(compile-time error)。多重返回值函数(Functions with Multiple Return Values)
你可以用元组(tuple)类型让多个值作为一个复合值从函数中返回。
@@ -705,11 +702,12 @@ printWithoutCounting("hello, world") }你可以用
-count函数来处理任何一个字符串,返回的值将是一个包含三个Int型值的元组(tuple):+let total = count("some arbitrary string!") ++let total = count("some arbitrary string!") println("\(total.vowels) vowels and \(total.consonants) consonants") // prints "6 vowels and 13 consonants -需要注意的是,元组的成员不需要在函数中返回时命名,因为它们的名字已经在函数返回类型有有了定义。
+函数参数名称(Function Parameter Names)
以上所有的函数都给它们的参数定义了
参数名(parameter name):func someFunction(parameterName: Int) { @@ -727,8 +725,7 @@ println("\(total.vowels) vowels and \(total.consonants) consonants") }-注意:
-如果你提供了外部参数名,那么函数在被调用时,必须使用外部参数名。
+注意:
如果你提供了外部参数名,那么函数在被调用时,必须使用外部参数名。以下是个例子,这个函数使用一个
结合者(joiner)把两个字符串联在一起:func join(s1: String, s2: String, joiner: String) -> String { @@ -751,8 +748,7 @@ println("\(total.vowels) vowels and \(total.consonants) consonants")使用外部参数名让第二个版本的
join函数的调用更为有表现力,更为通顺,同时还保持了函数体是可读的和有明确意图的。-注意:
-当其他人在第一次读你的代码,函数参数的意图显得不明显时,考虑使用外部参数名。如果函数参数名的意图是很明显的,那就不需要定义外部参数名了。
+注意:
当其他人在第一次读你的代码,函数参数的意图显得不明显时,考虑使用外部参数名。如果函数参数名的意图是很明显的,那就不需要定义外部参数名了。简写外部参数名(Shorthand External Parameter Names)
如果你需要提供外部参数名,但是局部参数名已经定义好了,那么你不需要写两次这些参数名。相反,只写一次参数名,并用
@@ -773,8 +769,7 @@ println("\(total.vowels) vowels and \(total.consonants) consonants")井号(#)作为前缀就可以了。这告诉 Swift 使用这个参数名作为局部和外部参数名。默认参数值(Default Parameter Values)
你可以在函数体中为每个参数定义
默认值。当默认值被定义后,调用这个函数时可以略去这个参数。-注意:
-将带有默认值的参数放在函数参数表的最后。这样可以保证在函数调用时,非默认参数的顺序是一致的,同时使得相同的函数在不同情况下调用时显得更为清晰。
+注意:
将带有默认值的参数放在函数参数表的最后。这样可以保证在函数调用时,非默认参数的顺序是一致的,同时使得相同的函数在不同情况下调用时显得更为清晰。以下是另一个版本的
join函数,其中joiner有了默认参数值:func join(string s1: String, toString s2: String, withJoiner joiner: String = " ") -> String { @@ -802,8 +797,7 @@ println("\(total.vowels) vowels and \(total.consonants) consonants") // returns "hello-world"-注意:
-你可以使用
+下划线(_)作为默认值参数的外部参数名,这样可以在调用时不用提供外部参数名。但是给带默认值的参数命名总是更加合适的。注意:
你可以使用下划线(_)作为默认值参数的外部参数名,这样可以在调用时不用提供外部参数名。但是给带默认值的参数命名总是更加合适的。可变参数(Variadic Parameters)
一个
@@ -812,7 +806,7 @@ println("\(total.vowels) vowels and \(total.consonants) consonants")可变参数(variadic parameter)可以接受一个或多个值。函数调用时,你可以用可变参数来传入不确定数量的输入参数。通过在变量类型名后面加入(...)的方式来定义可变参数。func arithmeticMean(numbers: Double...) -> Double { var total: Double = 0 for number in numbers { - total += number + total += number } return total / Double(numbers.count) } @@ -822,8 +816,7 @@ arithmeticMean(3, 8, 19) // returns 10.0, which is the arithmetic mean of these three numbers-注意:
-一个函数至多能有一个可变参数,而且它必须是参数表中最后的一个。这样做是为了避免函数调用时出现歧义。
+注意:
一个函数至多能有一个可变参数,而且它必须是参数表中最后的一个。这样做是为了避免函数调用时出现歧义。如果函数有一个或多个带默认值的参数,而且还有一个可变参数,那么把可变参数放在参数表的最后。
常量参数和变量参数(Constant and Variable Parameters)
@@ -846,16 +839,14 @@ let paddedString = alignRight(originalString, 10, "-")
alignRight函数将参数string定义为变量参数。这意味着string现在可以作为一个局部变量,用传入的字符串值初始化,并且可以在函数体中进行操作。该函数首先计算出多少个字符需要被添加到
string的左边,以右对齐到总的字符串中。这个值存在局部常量amountToPad中。这个函数然后将amountToPad多的填充(pad)字符填充到string左边,并返回结果。它使用了string这个变量参数来进行所有字符串操作。-注意:
-对变量参数所进行的修改在函数调用结束后便消失了,并且对于函数体外是不可见的。变量参数仅仅存在于函数调用的生命周期中。
+注意:
对变量参数所进行的修改在函数调用结束后便消失了,并且对于函数体外是不可见的。变量参数仅仅存在于函数调用的生命周期中。输入输出参数(In-Out Parameters)
变量参数,正如上面所述,仅仅能在函数体内被更改。如果你想要一个函数可以修改参数的值,并且想要在这些修改在函数调用结束后仍然存在,那么就应该把这个参数定义为输入输出参数(In-Out Parameters)。
定义一个输入输出参数时,在参数定义前加
inout关键字。一个输入输出参数有传入函数的值,这个值被函数修改,然后被传出函数,替换原来的值。你只能传入一个变量作为输入输出参数。你不能传入常量或者字面量(literal value),因为这些量是不能被修改的。当传入的参数作为输入输出参数时,需要在参数前加
&符,表示这个值可以被函数修改。-注意:
-输入输出参数不能有默认值,而且可变参数不能用
+inout标记。如果你用inout标记一个参数,这个参数不能被var或者let标记。注意:
输入输出参数不能有默认值,而且可变参数不能用inout标记。如果你用inout标记一个参数,这个参数不能被var或者let标记。下面是例子,
swapTwoInts函数,有两个分别叫做a和b的输出输出参数:func swapTwoInts(inout a: Int, inout b: Int) { @@ -874,8 +865,7 @@ println("someInt is now \(someInt), and anotherInt is now \(anotherInt)&quo从上面这个例子中,我们可以看到
someInt和anotherInt的原始值在swapTwoInts函数中被修改,尽管它们的定义在函数体外。-注意:
-输出输出参数和返回值是不一样的。上面的
+swapTwoInts函数并没有定义任何返回值,但仍然修改了someInt和anotherInt的值。输入输出参数是函数对函数体外产生影响的另一种方式。注意:
输出输出参数和返回值是不一样的。上面的swapTwoInts函数并没有定义任何返回值,但仍然修改了someInt和anotherInt的值。输入输出参数是函数对函数体外产生影响的另一种方式。函数类型(Function Types)
@@ -912,7 +902,7 @@ func multiplyTwoInts(a: Int, b: Int) -> Int { println("Result: \(mathFunction(2, 3))") // prints "Result: 6" -就像其他类型一样,当赋值一个函数给常量或变量时,你可以让 Swift 来推测其函数类型:
+就像其他类型一样,当赋值一个函数给常量或变量时,你可以让 Swift 来推断其函数类型:
diff --git a/chapter2/07_Closures.html b/chapter2/07_Closures.html old mode 100755 new mode 100644 index 709901e9..51790d29 --- a/chapter2/07_Closures.html +++ b/chapter2/07_Closures.html @@ -46,7 +46,7 @@ -let anotherMathFunction = addTwoInts // anotherMathFunction is inferred to be of type (Int, Int) -> Int+@@ -268,7 +268,7 @@- 2.12. 附属脚本 + 2.12. 下标脚本 @@ -520,7 +520,7 @@ - + @@ -587,11 +587,10 @@ -+ diff --git a/chapter2/08_Enumerations.html b/chapter2/08_Enumerations.html old mode 100755 new mode 100644 index 2da2075f..54e9bc3e --- a/chapter2/08_Enumerations.html +++ b/chapter2/08_Enumerations.html @@ -46,7 +46,7 @@ - -翻译:wh1100717
-校对:lyuka
+翻译:wh1100717
校对:lyuka闭包(Closures)
@@ -607,8 +606,7 @@ Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他闭包可以捕获和存储其所在上下文中任意常量和变量的引用。 这就是所谓的闭合并包裹着这些常量和变量,俗称闭包。Swift 会为您管理在捕获过程中涉及到的所有内存操作。
-注意:
-如果您不熟悉捕获(capturing)这个概念也不用担心,您可以在 值捕获 章节对其进行详细了解。
+注意:
如果您不熟悉捕获(capturing)这个概念也不用担心,您可以在 值捕获 章节对其进行详细了解。在函数 章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之一:
@@ -635,20 +633,22 @@ Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他
Swift 标准库提供了
sort函数,会根据您提供的基于输出类型排序的闭包函数将已知类型数组中的值进行排序。 一旦排序完成,函数会返回一个与原数组大小相同的新数组,该数组中包含已经正确排序的同类型元素。下面的闭包表达式示例使用
-sort函数对一个String类型的数组进行字母逆序排序,以下是初始数组值:let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"] -+
sort函数需要传入两个参数:+let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"] +
sort函数需要传入两个参数:
- 已知类型的数组
- 闭包函数,该闭包函数需要传入与数组类型相同的两个值,并返回一个布尔类型值来告诉
sort函数当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值前面,排序闭包函数需要返回true,反之返回false。该例子对一个
String类型的数组进行排序,因此排序闭包函数类型需为(String, String) -> Bool。提供排序闭包函数的一种方式是撰写一个符合其类型要求的普通函数,并将其作为
-sort函数的第二个参数传入:+func backwards(s1: String, s2: String) -> Bool { +func backwards(s1: String, s2: String) -> Bool { return s1 > s2 } var reversed = sort(names, backwards) // reversed 为 ["Ewa", "Daniella", "Chris", "Barry", "Alex"] -如果第一个字符串 (
s1) 大于第二个字符串 (s2),backwards函数返回true,表示在新的数组中s1应该出现在s2前。 +如果第一个字符串 (
@@ -657,93 +657,100 @@ var reversed = sort(names, backwards)s1) 大于第二个字符串 (s2),backwards函数返回true,表示在新的数组中s1应该出现在s2前。 对于字符串中的字符来说,“大于” 表示 “按照字母顺序较晚出现”。 这意味着字母"B"大于字母"A",字符串"Tom"大于字符串"Tim"。 其将进行字母逆序排序,"Barry"将会排在"Alex"之后。闭包表达式语法(Closure Expression Syntax)
闭包表达式语法有如下一般形式:
-+{ (parameters) -> returnType in +{ (parameters) -> returnType in statements } -闭包表达式语法可以使用常量、变量和
inout类型作为参数,不提供默认值。 +闭包表达式语法可以使用常量、变量和
inout类型作为参数,不提供默认值。 也可以在参数列表的最后使用可变参数。 元组也可以作为参数和返回值。下面的例子展示了之前
-backwards函数对应的闭包表达式版本的代码:+reversed = sort(names, { (s1: String, s2: String) -> Bool in +reversed = sort(names, { (s1: String, s2: String) -> Bool in return s1 > s2 - }) -需要注意的是内联闭包参数和返回值类型声明与
backwards函数类型声明相同。 +}) +需要注意的是内联闭包参数和返回值类型声明与
backwards函数类型声明相同。 在这两种方式中,都写成了(s1: String, s2: String) -> Bool。 然而在内联闭包表达式中,函数和返回值类型都写在大括号内,而不是大括号外。闭包的函数体部分由关键字
in引入。 该关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始。因为这个闭包的函数体部分如此短以至于可以将其改写成一行代码:
-reversed = sort(names, { (s1: String, s2: String) -> Bool in return s1 > s2 } ) -这说明
+sort函数的整体调用保持不变,一对圆括号仍然包裹住了函数中整个参数集合。而其中一个参数现在变成了内联闭包(相比于backwards版本的代码)。+reversed = sort(names, { (s1: String, s2: String) -> Bool in return s1 > s2 } ) +这说明
sort函数的整体调用保持不变,一对圆括号仍然包裹住了函数中整个参数集合。而其中一个参数现在变成了内联闭包(相比于backwards版本的代码)。根据上下文推断类型(Inferring Type From Context)
因为排序闭包函数是作为
-sort函数的参数进行传入的,Swift可以推断其参数和返回值的类型。sort期望第二个参数是类型为(String, String) -> Bool的函数,因此实际上String,String和Bool类型并不需要作为闭包表达式定义中的一部分。 因为所有的类型都可以被正确推断,返回箭头 (->) 和围绕在参数周围的括号也可以被省略:reversed = sort(names, { s1, s2 in return s1 > s2 } ) -实际上任何情况下,通过内联闭包表达式构造的闭包作为参数传递给函数时,都可以推断出闭包的参数和返回值类型,这意味着您几乎不需要利用完整格式构造任何内联闭包。
++reversed = sort(names, { s1, s2 in return s1 > s2 } ) +实际上任何情况下,通过内联闭包表达式构造的闭包作为参数传递给函数时,都可以推断出闭包的参数和返回值类型,这意味着您几乎不需要利用完整格式构造任何内联闭包。
单表达式闭包隐式返回(Implicit Return From Single-Expression Clossures)
单行表达式闭包可以通过隐藏
-return关键字来隐式返回单行表达式的结果,如上版本的例子可以改写为:reversed = sort(names, { s1, s2 in s1 > s2 } ) -在这个例子中,
sort函数的第二个参数函数类型明确了闭包必须返回一个Bool类型值。 ++reversed = sort(names, { s1, s2 in s1 > s2 } ) +在这个例子中,
sort函数的第二个参数函数类型明确了闭包必须返回一个Bool类型值。 因为闭包函数体只包含了一个单一表达式 (s1 > s2),该表达式返回Bool类型值,因此这里没有歧义,return关键字可以省略。参数名称缩写(Shorthand Argument Names)
Swift 自动为内联函数提供了参数名称缩写功能,您可以直接通过
$0,$1,$2来顺序调用闭包的参数。如果您在闭包表达式中使用参数名称缩写,您可以在闭包参数列表中省略对其的定义,并且对应参数名称缩写的类型会通过函数类型进行推断。
-in关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成:reversed = sort(names, { $0 > $1 } ) -在这个例子中,
+$0和$1表示闭包中第一个和第二个String类型的参数。+reversed = sort(names, { $0 > $1 } ) +在这个例子中,
$0和$1表示闭包中第一个和第二个String类型的参数。运算符函数(Operator Functions)
实际上还有一种更简短的方式来撰写上面例子中的闭包表达式。 Swift 的
-String类型定义了关于大于号 (>) 的字符串实现,其作为一个函数接受两个String类型的参数并返回Bool类型的值。 而这正好与sort函数的第二个参数需要的函数类型相符合。 因此,您可以简单地传递一个大于号,Swift可以自动推断出您想使用大于号的字符串函数实现:reversed = sort(names, >) -更多关于运算符表达式的内容请查看 运算符函数。
++reversed = sort(names, >) +更多关于运算符表达式的内容请查看 运算符函数。
尾随闭包(Trailing Closures)
如果您需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用尾随闭包来增强函数的可读性。 尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。
-+func someFunctionThatTakesAClosure(closure: () -> ()) { +func someFunctionThatTakesAClosure(closure: () -> ()) { // 函数体部分 } // 以下是不使用尾随闭包进行函数调用 - someFunctionThatTakesAClosure({ // 闭包主体部分 - }) +}) // 以下是使用尾随闭包进行函数调用 - someFunctionThatTakesAClosure() { - // 闭包主体部分 + // 闭包主体部分 } --注意:
-如果函数只需要闭包表达式一个参数,当您使用尾随闭包时,您甚至可以把
+()省略掉。+注意:
如果函数只需要闭包表达式一个参数,当您使用尾随闭包时,您甚至可以把()省略掉。在上例中作为
-sort函数参数的字符串排序闭包可以改写为:reversed = sort(names) { $0 > $1 } -当闭包非常长以至于不能在一行中进行书写时,尾随闭包变得非常有用。 +
+reversed = sort(names) { $0 > $1 } +当闭包非常长以至于不能在一行中进行书写时,尾随闭包变得非常有用。 举例来说,Swift 的
Array类型有一个map方法,其获取一个闭包表达式作为其唯一参数。 数组中的每一个元素调用一次该闭包函数,并返回该元素所映射的值(也可以是不同类型的值)。 具体的映射方式和返回值类型由闭包来指定。当提供给数组闭包函数后,
map方法将返回一个新的数组,数组中包含了与原数组一一对应的映射后的值。下例介绍了如何在
-map方法中使用尾随闭包将Int类型数组[16,58,510]转换为包含对应String类型的数组["OneSix", "FiveEight", "FiveOneZero"]:+let digitNames = [ +let digitNames = [ 0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four", 5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine" ] let numbers = [16, 58, 510] -如上代码创建了一个数字位和他们名字映射的英文版本字典。 +
如上代码创建了一个数字位和它们名字映射的英文版本字典。 同时定义了一个准备转换为字符串的整型数组。
您现在可以通过传递一个尾随闭包给
-numbers的map方法来创建对应的字符串版本数组。 需要注意的时调用numbers.map不需要在map后面包含任何括号,因为其只需要传递闭包表达式这一个参数,并且该闭包表达式参数通过尾随方式进行撰写:+let strings = numbers.map { +let strings = numbers.map { (var number) -> String in var output = "" while number > 0 { @@ -754,16 +761,14 @@ let numbers = [16, 58, 510] } // strings 常量被推断为字符串类型数组,即 String[] // 其值为 ["OneSix", "FiveEight", "FiveOneZero"] -
map在数组中为每一个元素调用了闭包表达式。 +
map在数组中为每一个元素调用了闭包表达式。 您不需要指定闭包的输入参数number的类型,因为可以通过要映射的数组类型进行推断。闭包
number参数被声明为一个变量参数(变量的具体描述请参看常量参数和变量参数),因此可以在闭包函数体内对其进行修改。闭包表达式制定了返回类型为String,以表明存储映射值的新数组类型为String。闭包表达式在每次被调用的时候创建了一个字符串并返回。 其使用求余运算符 (number % 10) 计算最后一位数字并利用
digitNames字典获取所映射的字符串。-注意:
-字典
+digitNames下标后跟着一个叹号 (!),因为字典下标返回一个可选值 (optional value),表明即使该 key 不存在也不会查找失败。 -在上例中,它保证了number % 10可以总是作为一个digitNames字典的有效下标 key。 -因此叹号可以用于强制解析 (force-unwrap) 存储在可选下标项中的String类型值。注意:
字典digitNames下标后跟着一个叹号 (!),因为字典下标返回一个可选值 (optional value),表明即使该 key 不存在也不会查找失败。
在上例中,它保证了number % 10可以总是作为一个digitNames字典的有效下标 key。
因此叹号可以用于强制解析 (force-unwrap) 存储在可选下标项中的String类型值。从
@@ -782,7 +787,7 @@ let numbers = [16, 58, 510] 嵌套函数digitNames字典中获取的字符串被添加到输出的前部,逆序建立了一个字符串版本的数字。 (在表达式number % 10中,如果number为16,则返回6,58返回8,510返回0)。incrementor从上下文中捕获了两个值,runningTotal和amount。 之后makeIncrementor将incrementor作为闭包返回。 每次调用incrementor时,其会以amount作为增量增加runningTotal的值。 -+func makeIncrementor(forIncrement amount: Int) -> () -> Int { +func makeIncrementor(forIncrement amount: Int) -> () -> Int { var runningTotal = 0 func incrementor() -> Int { runningTotal += amount @@ -790,7 +795,8 @@ let numbers = [16, 58, 510] } return incrementor } -
makeIncrementor返回类型为() -> Int。 +@@ -800,40 +806,39 @@ let numbers = [16, 58, 510]
makeIncrementor返回类型为() -> Int。 这意味着其返回的是一个函数,而不是一个简单类型值。 该函数在每次调用时不接受参数只返回一个Int类型的值。 关于函数返回其他函数的内容,请查看函数类型作为返回类型。
incrementor函数用来执行实际的增加操作。 该函数简单地使runningTotal增加amount,并将其返回。如果我们单独看这个函数,会发现看上去不同寻常:
-+func incrementor() -> Int { +func incrementor() -> Int { runningTotal += amount return runningTotal } -+
incrementor函数并没有获取任何参数,但是在函数体内访问了runningTotal和amount变量。这是因为其通过捕获在包含它的函数体内已经存在的runningTotal和amount变量而实现。
incrementor函数并没有获取任何参数,但是在函数体内访问了runningTotal和amount变量。这是因为其通过捕获在包含它的函数体内已经存在的runningTotal和amount变量而实现。由于没有修改
amount变量,incrementor实际上捕获并存储了该变量的一个副本,而该副本随着incrementor一同被存储。然而,因为每次调用该函数的时候都会修改
runningTotal的值,incrementor捕获了当前runningTotal变量的引用,而不是仅仅复制该变量的初始值。捕获一个引用保证了当makeIncrementor结束时候并不会消失,也保证了当下一次执行incrementor函数时,runningTotal可以继续增加。-注意:
-Swift 会决定捕获引用还是拷贝值。 -您不需要标注
+amount或者runningTotal来声明在嵌入的incrementor函数中的使用方式。 -Swift 同时也处理runingTotal变量的内存管理操作,如果不再被incrementor函数使用,则会被清除。注意:
Swift 会决定捕获引用还是拷贝值。
您不需要标注amount或者runningTotal来声明在嵌入的incrementor函数中的使用方式。
Swift 同时也处理runingTotal变量的内存管理操作,如果不再被incrementor函数使用,则会被清除。下面代码为一个使用
-makeIncrementor的例子:let incrementByTen = makeIncrementor(forIncrement: 10) -该例子定义了一个叫做
incrementByTen的常量,该常量指向一个每次调用会加10的incrementor函数。 ++let incrementByTen = makeIncrementor(forIncrement: 10) +该例子定义了一个叫做
-incrementByTen的常量,该常量指向一个每次调用会加10的incrementor函数。 调用这个函数多次可以得到以下结果:+incrementByTen() +incrementByTen() // 返回的值为10 incrementByTen() // 返回的值为20 incrementByTen() // 返回的值为30 -如果您创建了另一个
incrementor,其会有一个属于自己的独立的runningTotal变量的引用。 +如果您创建了另一个
-incrementor,其会有一个属于自己的独立的runningTotal变量的引用。 下面的例子中,incrementBySevne捕获了一个新的runningTotal变量,该变量和incrementByTen中捕获的变量没有任何联系:+let incrementBySeven = makeIncrementor(forIncrement: 7) +let incrementBySeven = makeIncrementor(forIncrement: 7) incrementBySeven() // 返回的值为7 incrementByTen() // 返回的值为40 --注意:
-如果您闭包分配给一个类实例的属性,并且该闭包通过指向该实例或其成员来捕获了该实例,您将创建一个在闭包和实例间的强引用环。 -Swift 使用捕获列表来打破这种强引用环。更多信息,请参考 闭包引起的循环强引用。
++注意:
如果您将闭包赋值给一个类实例的属性,并且该闭包通过指向该实例或其成员来捕获了该实例,您将创建一个在闭包和实例间的强引用环。
Swift 使用捕获列表来打破这种强引用环。更多信息,请参考 闭包引起的循环强引用。闭包是引用类型(Closures Are Reference Types)
@@ -842,10 +847,11 @@ Swift 使用捕获列表来打破这种强引用环。更多信息,请参考 <无论您将函数/闭包赋值给一个常量还是变量,您实际上都是将常量/变量的值设置为对应函数/闭包的引用。 上面的例子中,
incrementByTen指向闭包的引用是一个常量,而并非闭包内容本身。这也意味着如果您将闭包赋值给了两个不同的常量/变量,两个值都会指向同一个闭包:
-let alsoIncrementByTen = incrementByTen ++let alsoIncrementByTen = incrementByTen alsoIncrementByTen() // 返回的值为50+@@ -268,7 +268,7 @@- 2.12. 附属脚本 + 2.12. 下标脚本 @@ -520,7 +520,7 @@ - + @@ -587,11 +587,10 @@ -+ diff --git a/chapter2/09_Classes_and_Structures.html b/chapter2/09_Classes_and_Structures.html old mode 100755 new mode 100644 index 97ca093f..36f49dcc --- a/chapter2/09_Classes_and_Structures.html +++ b/chapter2/09_Classes_and_Structures.html @@ -46,7 +46,7 @@ - -翻译:yankuangshi
-校对:shinyzhu
+翻译:yankuangshi
校对:shinyzhu枚举(Enumerations)
@@ -610,34 +609,38 @@枚举语法
使用
-enum关键词并且把它们的整个定义放在一对大括号内:+enum SomeEumeration { - // enumeration definition goes here +enum SomeEumeration { + // enumeration definition goes here } -以下是指南针四个方向的一个例子:
-+enum CompassPoint { - case North - case South - case East - case West +以下是指南针四个方向的一个例子:
+enum CompassPoint { + case North + case South + case East + case West } -一个枚举中被定义的值(例如
+North,South,East和West)是枚举的成员值(或者成员)。case关键词表明新的一行成员值将被定义。一个枚举中被定义的值(例如
North,South,East和West)是枚举的成员值(或者成员)。case关键词表明新的一行成员值将被定义。-注意:
-不像 C 和 Objective-C 一样,Swift 的枚举成员在被创建时不会被赋予一个默认的整数值。在上面的
+CompassPoints例子中,North,South,East和West不是隐式的等于0,1,2和3。相反的,这些不同的枚举成员在CompassPoint的一种显示定义中拥有各自不同的值。注意:
不像 C 和 Objective-C 一样,Swift 的枚举成员在被创建时不会被赋予一个默认的整数值。在上面的CompassPoints例子中,North,South,East和West不是隐式的等于0,1,2和3。相反的,这些不同的枚举成员在CompassPoint的一种显示定义中拥有各自不同的值。多个成员值可以出现在同一行上,用逗号隔开:
-+enum Planet { - case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Nepturn +enum Planet { + case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Nepturn } -每个枚举定义了一个全新的类型。像 Swift 中其他类型一样,它们的名字(例如
-CompassPoint和Planet)必须以一个大写字母开头。给枚举类型起一个单数名字而不是复数名字,以便于读起来更加容易理解:var directionToHead = CompassPoint.West --
directionToHead的类型被推断当它被CompassPoint的一个可能值初始化。一旦directionToHead被声明为一个CompassPoint,你可以使用更短的点(.)语法将其设置为另一个CompassPoint的值:directionToHead = .East -+
directionToHead的类型已知时,当设定它的值时,你可以不再写类型名。使用显示类型的枚举值可以让代码具有更好的可读性。每个枚举定义了一个全新的类型。像 Swift 中其他类型一样,它们的名字(例如
+CompassPoint和Planet)必须以一个大写字母开头。给枚举类型起一个单数名字而不是复数名字,以便于读起来更加容易理解:+var directionToHead = CompassPoint.West ++
directionToHead的类型被推断当它被CompassPoint的一个可能值初始化。一旦directionToHead被声明为一个CompassPoint,你可以使用更短的点(.)语法将其设置为另一个CompassPoint的值:+directionToHead = .East +
directionToHead的类型已知时,当设定它的值时,你可以不再写类型名。使用显示类型的枚举值可以让代码具有更好的可读性。匹配枚举值和
Switch语句你可以匹配单个枚举值和
-switch语句:+directionToHead = .South +directionToHead = .South switch directionToHead { case .North: println("Lots of planets have a north") @@ -649,12 +652,13 @@ case .West: println("Where the skies are blue") } // 输出 "Watch out for penguins” -你可以如此理解这段代码:
+你可以如此理解这段代码:
“考虑
directionToHead的值。当它等于.North,打印“Lots of planets have a north”。当它等于.South,打印“Watch out for penguins”。”等等依次类推。
正如在控制流(Control Flow)中介绍,当考虑一个枚举的成员们时,一个
switch语句必须全面。如果忽略了.West这种情况,上面那段代码将无法通过编译,因为它没有考虑到CompassPoint的全部成员。全面性的要求确保了枚举成员不会被意外遗漏。当不需要匹配每个枚举成员的时候,你可以提供一个默认
-default分支来涵盖所有未明确被提出的任何成员:+let somePlanet = Planet.Earth ++let somePlanet = Planet.Earth switch somePlanet { case .Earth: println("Mostly harmless") @@ -662,7 +666,8 @@ default: println("Not a safe place for humans") } // 输出 "Mostly harmless” -实例值(Associated Values)
上一小节的例子演示了一个枚举的成员是如何被定义(分类)的。你可以为
Planet.Earth设置一个常量或则变量,并且在之后查看这个值。然而,有时候会很有用如果能够把其他类型的实例值和成员值一起存储起来。这能让你随着成员值存储额外的自定义信息,并且当每次你在代码中利用该成员时允许这个信息产生变化。你可以定义 Swift 的枚举存储任何类型的实例值,如果需要的话,每个成员的数据类型可以是各不相同的。枚举的这种特性跟其他语言中的可辨识联合(discriminated unions),标签联合(tagged unions),或者变体(variants)相似。
@@ -672,61 +677,70 @@ default:
对于库存跟踪系统来说,能够把 UPC-A 码作为三个整型值的元组,和把 QR 码作为一个任何长度的字符串存储起来是方便的。
在 Swift 中,用来定义两种商品条码的枚举是这样子的:
-+enum Barcode { - case UPCA(Int, Int, Int) - case QRCode(String) +enum Barcode { + case UPCA(Int, Int, Int) + case QRCode(String) } -以上代码可以这么理解:
+以上代码可以这么理解:
“定义一个名为
Barcode的枚举类型,它可以是UPCA的一个实例值(Int,Int,Int),或者QRCode的一个字符串类型(String)实例值。”这个定义不提供任何
Int或String的实际值,它只是定义了,当Barcode常量和变量等于Barcode.UPCA或Barcode.QRCode时,实例值的类型。然后可以使用任何一种条码类型创建新的条码,如:
-var productBarcode = Barcode.UPCA(8, 85909_51226, 3) -以上例子创建了一个名为
+productBarcode的新变量,并且赋给它一个Barcode.UPCA的实例元组值(8, 8590951226, 3)。提供的“标识符”值在整数字中有一个下划线,使其便于阅读条形码。+var productBarcode = Barcode.UPCA(8, 85909_51226, 3) +以上例子创建了一个名为
productBarcode的新变量,并且赋给它一个Barcode.UPCA的实例元组值(8, 8590951226, 3)。提供的“标识符”值在整数字中有一个下划线,使其便于阅读条形码。同一个商品可以被分配给一个不同类型的条形码,如:
-productBarcode = .QRCode("ABCDEFGHIJKLMNOP") -这时,原始的
+Barcode.UPCA和其整数值被新的Barcode.QRCode和其字符串值所替代。条形码的常量和变量可以存储一个.UPCA或者一个.QRCode(连同它的实例值),但是在任何指定时间只能存储其中之一。+productBarcode = .QRCode("ABCDEFGHIJKLMNOP") +这时,原始的
Barcode.UPCA和其整数值被新的Barcode.QRCode和其字符串值所替代。条形码的常量和变量可以存储一个.UPCA或者一个.QRCode(连同它的实例值),但是在任何指定时间只能存储其中之一。像以前那样,不同的条形码类型可以使用一个 switch 语句来检查,然而这次实例值可以被提取作为 switch 语句的一部分。你可以在
-switch的 case 分支代码中提取每个实例值作为一个常量(用let前缀)或者作为一个变量(用var前缀)来使用:+switch productBarcode { +switch productBarcode { case .UPCA(let numberSystem, let identifier, let check): println("UPC-A with value of \(numberSystem), \(identifier), \(check).") case .QRCode(let productCode): println("QR code with value of \(productCode).") } // 输出 "QR code with value of ABCDEFGHIJKLMNOP.” -如果一个枚举成员的所有实例值被提取为常量,或者它们全部被提取为变量,为了简洁,你可以只放置一个
-var或者let标注在成员名称前:+switch productBarcode { +如果一个枚举成员的所有实例值被提取为常量,或者它们全部被提取为变量,为了简洁,你可以只放置一个
+var或者let标注在成员名称前:+switch productBarcode { case let .UPCA(numberSystem, identifier, check): println("UPC-A with value of \(numberSystem), \(identifier), \(check).") case let .QRCode(productCode): println("QR code with value of \(productCode).") } // 输出 "QR code with value of ABCDEFGHIJKLMNOP." -原始值(Raw Values)
在实例值小节的条形码例子中演示了一个枚举的成员如何声明它们存储不同类型的实例值。作为实例值的替代,枚举成员可以被默认值(称为原始值)预先填充,其中这些原始值具有相同的类型。
这里是一个枚举成员存储原始 ASCII 值的例子:
-+enum ASCIIControlCharacter: Character { +enum ASCIIControlCharacter: Character { case Tab = "\t" case LineFeed = "\n" case CarriageReturn = "\r" } -在这里,称为
+ASCIIControlCharacter的枚举的原始值类型被定义为字符型Character,并被设置了一些比较常见的 ASCII 控制字符。字符值的描述请详见字符串和字符Strings and Characters部分。在这里,称为
ASCIIControlCharacter的枚举的原始值类型被定义为字符型Character,并被设置了一些比较常见的 ASCII 控制字符。字符值的描述请详见字符串和字符Strings and Characters部分。注意,原始值和实例值是不相同的。当你开始在你的代码中定义枚举的时候原始值是被预先填充的值,像上述三个 ASCII 码。对于一个特定的枚举成员,它的原始值始终是相同的。实例值是当你在创建一个基于枚举成员的新常量或变量时才会被设置,并且每次当你这么做得时候,它的值可以是不同的。
原始值可以是字符串,字符,或者任何整型值或浮点型值。每个原始值在它的枚举声明中必须是唯一的。当整型值被用于原始值,如果其他枚举成员没有值时,它们会自动递增。
下面的枚举是对之前
-Planet这个枚举的一个细化,利用原始整型值来表示每个 planet 在太阳系中的顺序:+enum Planet: Int { +enum Planet: Int { case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune } -自动递增意味着
+Planet.Venus的原始值是2,依次类推。自动递增意味着
Planet.Venus的原始值是2,依次类推。使用枚举成员的
-toRaw方法可以访问该枚举成员的原始值:+let earthsOrder = Planet.Earth.toRaw() +let earthsOrder = Planet.Earth.toRaw() // earthsOrder is 3 -使用枚举的
-fromRaw方法来试图找到具有特定原始值的枚举成员。这个例子通过原始值7识别Uranus:+let possiblePlanet = Planet.fromRaw(7) +使用枚举的
+fromRaw方法来试图找到具有特定原始值的枚举成员。这个例子通过原始值7识别Uranus:let possiblePlanet = Planet.fromRaw(7) // possiblePlanet is of type Planet? and equals Planet.Uranus -然而,并非所有可能的
+Int值都可以找到一个匹配的行星。正因为如此,fromRaw方法可以返回一个可选的枚举成员。在上面的例子中,possiblePlanet是Planet?类型,或“可选的Planet”。然而,并非所有可能的
Int值都可以找到一个匹配的行星。正因为如此,fromRaw方法可以返回一个可选的枚举成员。在上面的例子中,possiblePlanet是Planet?类型,或“可选的Planet”。如果你试图寻找一个位置为9的行星,通过
-fromRaw返回的可选Planet值将是nil:+let positionToFind = 9 +let positionToFind = 9 if let somePlanet = Planet.fromRaw(positionToFind) { switch somePlanet { case .Earth: @@ -738,7 +752,8 @@ if let somePlanet = Planet.fromRaw(positionToFind) { println("There isn't a planet at position \(positionToFind)") } // 输出 "There isn't a planet at position 9 -这个范例使用可选绑定(optional binding),通过原始值
+9试图访问一个行星。if let somePlanet = Planet.fromRaw(9)语句获得一个可选Planet,如果可选Planet可以被获得,把somePlanet设置成该可选Planet的内容。在这个范例中,无法检索到位置为9的行星,所以else分支被执行。这个范例使用可选绑定(optional binding),通过原始值
9试图访问一个行星。if let somePlanet = Planet.fromRaw(9)语句获得一个可选Planet,如果可选Planet可以被获得,把somePlanet设置成该可选Planet的内容。在这个范例中,无法检索到位置为9的行星,所以else分支被执行。+@@ -268,7 +268,7 @@- 2.12. 附属脚本 + 2.12. 下标脚本 @@ -520,7 +520,7 @@ - + @@ -587,11 +587,10 @@ -+ -翻译:JaySurplus
-校对:sg552
+翻译:JaySurplus
校对:sg552类和结构体
本页包含内容:
@@ -605,21 +604,43 @@类和结构体是人们构建代码所用的一种通用且灵活的构造体。为了在类和结构体中实现各种功能,我们必须要严格按照对于常量,变量以及函数所规定的语法规则来定义属性和添加方法。
与其他编程语言所不同的是,Swift 并不要求你为自定义类和结构去创建独立的接口和实现文件。你所要做的是在一个单一文件中定义一个类或者结构体,系统将会自动生成面向其它代码的外部接口。
-注意:
-通常一个
+类的实例被称为对象。然而在Swift 中,类和结构体的关系要比在其他语言中更加的密切,本章中所讨论的大部分功能都可以用在类和结构体上。因此,我们会主要使用实例而不是对象。注意:
通常一个类的实例被称为对象。然而在Swift 中,类和结构体的关系要比在其他语言中更加的密切,本章中所讨论的大部分功能都可以用在类和结构体上。因此,我们会主要使用实例而不是对象。类和结构体对比
Swift 中类和结构体有很多共同点。共同处在于:
+<<<<<<< HEAD
-
- 定义属性用于储存值
- 定义方法用于提供功能
- 定义附属脚本用于访问值
- 定义构造器用于生成初始化值
- 通过扩展以增加默认实现的功能
-- 符合协议以对某类提供标准功能
+- +
符合协议以对某类提供标准功能
+- 定义属性用于储存值
+- 定义方法用于提供功能
+- 定义下标脚本用于访问值
+- 定义构造器用于生成初始化值
+- 通过扩展以增加默认实现的功能
+- 符合协议以对某类提供标准功能
++++++++++++++a516af6a531a104ec88da0d236ecf389a5ec72af
+更多信息请参见 属性,方法,附属脚本,初始过程,扩展,和协议。
+更多信息请参见 属性,方法,下标脚本,初始过程,扩展,和协议。
与结构体相比,类还有如下的附加功能:
- 继承允许一个类继承另一个类的特征
@@ -629,23 +650,22 @@-注意:
-结构体总是通过被复制的方式在代码中传递,因此请不要使用引用计数。
+注意:
结构体总是通过被复制的方式在代码中传递,因此请不要使用引用计数。定义
类和结构体有着类似的定义方式。我们通过关键字
-class和struct来分别表示类和结构体,并在一对大括号中定义它们的具体内容:+class SomeClass { +class SomeClass { // class definition goes here } struct SomeStructure { // structure definition goes here } --注意:
-在你每次定义一个新类或者结构体的时候,实际上你是有效地定义了一个新的 Swift 类型。因此请使用
+UpperCamelCase这种方式来命名(如SomeClass和SomeStructure等),以便符合标准Swift 类型的大写命名风格(如String,Int和Bool)。相反的,请使用lowerCamelCase这种方式为属性和方法命名(如framerate和incrementCount),以便和类区分。+注意:
在你每次定义一个新类或者结构体的时候,实际上你是有效地定义了一个新的 Swift 类型。因此请使用UpperCamelCase这种方式来命名(如SomeClass和SomeStructure等),以便符合标准Swift 类型的大写命名风格(如String,Int和Bool)。相反的,请使用lowerCamelCase这种方式为属性和方法命名(如framerate和incrementCount),以便和类区分。以下是定义结构体和定义类的示例:
-+struct Resolution { +struct Resolution { var width = 0 var heigth = 0 } @@ -655,56 +675,64 @@ class VideoMode { var frameRate = 0.0 var name: String? } -在上面的示例中我们定义了一个名为
+Resolution的结构体,用来描述一个显示器的像素分辨率。这个结构体包含了两个名为width和height的储存属性。储存属性是捆绑和储存在类或结构体中的常量或变量。当这两个属性被初始化为整数0的时候,它们会被推断为Int类型。在上面的示例中我们定义了一个名为
Resolution的结构体,用来描述一个显示器的像素分辨率。这个结构体包含了两个名为width和height的储存属性。储存属性是捆绑和储存在类或结构体中的常量或变量。当这两个属性被初始化为整数0的时候,它们会被推断为Int类型。在上面的示例中我们还定义了一个名为
VideoMode的类,用来描述一个视频显示器的特定模式。这个类包含了四个储存属性变量。第一个是分辨率,它被初始化为一个新的Resolution结构体的实例,具有Resolution的属性类型。新VideoMode实例同时还会初始化其它三个属性,它们分别是,初始值为false(意为“non-interlaced video”)的inteflaced,回放帧率初始值为0.0的frameRate和值为可选String的name。name属性会被自动赋予一个默认值nil,意为“没有name值”,因它是一个可选类型。类和结构体实例
Resolution结构体和VideoMode类的定义仅描述了什么是Resolution和VideoMode。它们并没有描述一个特定的分辨率(resolution)或者视频模式(video mode)。为了描述一个特定的分辨率或者视频模式,我们需要生成一个它们的实例。生成结构体和类实例的语法非常相似:
-+let someResolution = Resolution() +let someResolution = Resolution() let someVideoMode = VideoMode() -结构体和类都使用构造器语法来生成新的实例。构造器语法的最简单形式是在结构体或者类的类型名称后跟随一个空括弧,如
+Resolution()或VideoMode()。通过这种方式所创建的类或者结构体实例,其属均会被初始化为默认值。构造过程章节会对类和结构体的初始化进行更详细的讨论。结构体和类都使用构造器语法来生成新的实例。构造器语法的最简单形式是在结构体或者类的类型名称后跟随一个空括弧,如
Resolution()或VideoMode()。通过这种方式所创建的类或者结构体实例,其属均会被初始化为默认值。构造过程章节会对类和结构体的初始化进行更详细的讨论。属性访问
通过使用点语法(dot syntax),你可以访问实例中所含有的属性。其语法规则是,实例名后面紧跟属性名,两者通过点号(.)连接:
-+println("The width of someResolution is \(someResolution.width)") +println("The width of someResolution is \(someResolution.width)") // 输出 "The width of someResolution is 0" -在上面的例子中,
+someResolution.width引用someResolution的width属性,返回width的初始值0。在上面的例子中,
someResolution.width引用someResolution的width属性,返回width的初始值0。你也可以访问子属性,如何
-VideoMode中Resolution属性的width属性:+println("The width of someVideoMode is \(someVideoMode.resolution.width)") +println("The width of someVideoMode is \(someVideoMode.resolution.width)") // 输出 "The width of someVideoMode is 0" -你也可以使用点语法为属性变量赋值:
-+someVideoMode.resolution.width = 12880 +你也可以使用点语法为属性变量赋值:
+someVideoMode.resolution.width = 12880 println("The width of someVideoMode is now \(someVideoMode.resolution.width)") // 输出 "The width of someVideoMode is now 1280" --注意:
-与 Objective-C 语言不同的是,Swift 允许直接设置结构体属性的子属性。上面的最后一个例子,就是直接设置了
+someVideoMode中resolution属性的width这个子属性,以上操作并不需要从新设置resolution属性。+-注意:
与 Objective-C 语言不同的是,Swift 允许直接设置结构体属性的子属性。上面的最后一个例子,就是直接设置了someVideoMode中resolution属性的width这个子属性,以上操作并不需要从新设置resolution属性。结构体类型的成员逐一构造器
-//Memberwise Initializers for structure Types
+结构体类型的成员逐一构造器(Memberwise Initializers for structure Types)
所有结构体都有一个自动生成的成员逐一构造器,用于初始化新结构体实例中成员的属性。新实例中各个属性的初始值可以通过属性的名称传递到成员逐一构造器之中:
-let vga = resolution(width:640, heigth: 480) -与结构体不同,类实例没有默认的成员逐一构造器。构造过程章节会对构造器进行更详细的讨论。
++let vga = resolution(width:640, heigth: 480) +与结构体不同,类实例没有默认的成员逐一构造器。构造过程章节会对构造器进行更详细的讨论。
结构体和枚举是值类型
值类型被赋予给一个变量,常数或者本身被传递给一个函数的时候,实际上操作的是其的拷贝。
在之前的章节中,我们已经大量使用了值类型。实际上,在 Swift 中,所有的基本类型:整数(Integer)、浮点数(floating-point)、布尔值(Booleans)、字符串(string)、数组(array)和字典(dictionaries),都是值类型,并且都是以结构体的形式在后台所实现。
在 Swift 中,所有的结构体和枚举都是值类型。这意味着它们的实例,以及实例中所包含的任何值类型属性,在代码中传递的时候都会被复制。
请看下面这个示例,其使用了前一个示例中
-Resolution结构体:+let hd = Resolution(width: 1920, height: 1080) +let hd = Resolution(width: 1920, height: 1080) var cinema = hd -在以上示例中,声明了一个名为
+hd的常量,其值为一个初始化为全高清视频分辨率(1920 像素宽,1080 像素高)的Resolution实例。在以上示例中,声明了一个名为
hd的常量,其值为一个初始化为全高清视频分辨率(1920 像素宽,1080 像素高)的Resolution实例。然后示例中又声明了一个名为
cinema的变量,其值为之前声明的hd。因为Resolution是一个结构体,所以cinema的值其实是hd的一个拷贝副本,而不是hd本身。尽管hd和cinema有着相同的宽(width)和高(height)属性,但是在后台中,它们是两个完全不同的实例。下面,为了符合数码影院放映的需求(2048 像素宽,1080 像素高),
-cinema的width属性需要作如下修改:cinema.width = 2048 -这里,将会显示
-cinema的width属性确已改为了2048:+println("cinema is now \(cinema.width) pixels wide") ++cinema.width = 2048 +这里,将会显示
+cinema的width属性确已改为了2048:println("cinema is now \(cinema.width) pixels wide") // 输出 "cinema is now 2048 pixels wide" -然而,初始的
-hd实例中width属性还是1920:+println("hd is still \(hd.width ) pixels wide") +然而,初始的
+hd实例中width属性还是1920:println("hd is still \(hd.width ) pixels wide") // 输出 "hd is still 1920 pixels wide" -在将
+hd赋予给cinema的时候,实际上是将hd中所储存的值(values)进行拷贝,然后将拷贝的数据储存到新的cinema实例中。结果就是两个完全独立的实例碰巧包含有相同的数值。由于两者相互独立,因此将cinema的width修改为2048并不会影响hd中的宽(width)。在将
hd赋予给cinema的时候,实际上是将hd中所储存的值(values)进行拷贝,然后将拷贝的数据储存到新的cinema实例中。结果就是两个完全独立的实例碰巧包含有相同的数值。由于两者相互独立,因此将cinema的width修改为2048并不会影响hd中的宽(width)。枚举也遵循相同的行为准则:
-+enum CompassPoint { +enum CompassPoint { case North, South, East, West } var currentDirection = CompassPoint.West @@ -714,25 +742,29 @@ if rememberDirection == .West { println("The remembered direction is still .West") } // 输出 "The remembered direction is still .West" -上例中
+rememberedDirection被赋予了currentDirection的值(value),实际上它被赋予的是值(value)的一个拷贝。赋值过程结束后再修改currentDirection的值并不影响rememberedDirection所储存的原始值(value)的拷贝。上例中
rememberedDirection被赋予了currentDirection的值(value),实际上它被赋予的是值(value)的一个拷贝。赋值过程结束后再修改currentDirection的值并不影响rememberedDirection所储存的原始值(value)的拷贝。类是引用类型
与值类型不同,引用类型在被赋予到一个变量,常量或者被传递到一个函数时,操作的并不是其拷贝。因此,引用的是已存在的实例本身而不是其拷贝。
请看下面这个示例,其使用了之前定义的
-VideoMode类:+let tenEighty = VideoMode() +let tenEighty = VideoMode() tenEighty.resolution = hd tenEighty.interlaced = true tenEighty.name = "1080i" tenEighty.frameRate = 25.0 -以上示例中,声明了一个名为
+tenEighty的常量,其引用了一个VideoMode类的新实例。在之前的示例中,这个视频模式(video mode)被赋予了HD分辨率(1920*1080)的一个拷贝(hd)。同时设置为交错(interlaced),命名为“1080i”。最后,其帧率是25.0帧每秒。以上示例中,声明了一个名为
tenEighty的常量,其引用了一个VideoMode类的新实例。在之前的示例中,这个视频模式(video mode)被赋予了HD分辨率(1920*1080)的一个拷贝(hd)。同时设置为交错(interlaced),命名为“1080i”。最后,其帧率是25.0帧每秒。然后,
-tenEighty被赋予名为alsoTenEighty的新常量,同时对alsoTenEighty的帧率进行修改:+let alsoTenEighty = tenEighty +let alsoTenEighty = tenEighty alsoTenEighty.frameRate = 30.0 -因为类是引用类型,所以
+tenEight和alsoTenEight实际上引用的是相同的VideoMode实例。换句话说,它们只是同一个实例的两种叫法。因为类是引用类型,所以
tenEight和alsoTenEight实际上引用的是相同的VideoMode实例。换句话说,它们只是同一个实例的两种叫法。下面,通过查看
-tenEighty的frameRate属性,我们会发现它正确的显示了基本VideoMode实例的新帧率,其值为30.0:+println("The frameRate property of tenEighty is now \(tenEighty.frameRate)") +println("The frameRate property of tenEighty is now \(tenEighty.frameRate)") // 输出 "The frameRate property of theEighty is now 30.0" -需要注意的是
+tenEighty和alsoTenEighty被声明为常量((constants)而不是变量。然而你依然可以改变tenEighty.frameRate和alsoTenEighty.frameRate,因为这两个常量本身不会改变。它们并不储存这个VideoMode实例,在后台仅仅是对VideoMode实例的引用。所以,改变的是被引用的基础VideoMode的frameRate参数,而不改变常量的值。需要注意的是
tenEighty和alsoTenEighty被声明为常量((constants)而不是变量。然而你依然可以改变tenEighty.frameRate和alsoTenEighty.frameRate,因为这两个常量本身不会改变。它们并不储存这个VideoMode实例,在后台仅仅是对VideoMode实例的引用。所以,改变的是被引用的基础VideoMode的frameRate参数,而不改变常量的值。恒等运算符
因为类是引用类型,有可能有多个常量和变量在后台同时引用某一个类实例。(对于结构体和枚举来说,这并不成立。因为它们作值类型,在被赋予到常量,变量或者传递到函数时,总是会被拷贝。)
如果能够判定两个常量或者变量是否引用同一个类实例将会很有帮助。为了达到这个目的,Swift 内建了两个恒等运算符:
@@ -741,11 +773,12 @@ alsoTenEighty.frameRate = 30.0不等价于 ( !== ) 以下是运用这两个运算符检测两个常量或者变量是否引用同一个实例:
-+if tenEighty === alsoTenTighty { +if tenEighty === alsoTenTighty { println("tenTighty and alsoTenEighty refer to the same Resolution instance.") } //输出 "tenEighty and alsoTenEighty refer to the same Resolution instance." -请注意“等价于”(用三个等号表示,===) 与“等于”(用两个等号表示,==)的不同:
+请注意“等价于”(用三个等号表示,===) 与“等于”(用两个等号表示,==)的不同:
- “等价于”表示两个类类型(class type)的常量或者变量引用同一个类实例。
- “等于”表示两个实例的值“相等”或“相同”,判定时要遵照类设计者定义定义的评判标准,因此相比于“相等”,这是一种更加合适的叫法。
@@ -776,48 +809,52 @@ alsoTenEighty.frameRate = 30.0Swift 中
数组(Array)和字典(Dictionary)类型均以结构体的形式实现。然而当数组被赋予一个常量或变量,或被传递给一个函数或方法时,其拷贝行为与字典和其它结构体有些许不同。以下对
数组和结构体的行为描述与对NSArray和NSDictionary的行为描述在本质上不同,后者是以类的形式实现,前者是以结构体的形式实现。NSArray和NSDictionary实例总是以对已有实例引用,而不是拷贝的方式被赋值和传递。-注意:
-以下是对于数组,字典,字符串和其它值的
拷贝的描述。 +注意:
以下是对于数组,字典,字符串和其它值的拷贝的描述。 在你的代码中,拷贝好像是确实是在有拷贝行为的地方产生过。然而,在 Swift 的后台中,只有确有必要,实际(actual)拷贝才会被执行。Swift 管理所有的值拷贝以确保性能最优化的性能,所以你也没有必要去避免赋值以保证最优性能。(实际赋值由系统管理优化)字典类型的赋值和拷贝行为
无论何时将一个
字典实例赋给一个常量或变量,或者传递给一个函数或方法,这个字典会即会在赋值或调用发生时被拷贝。在章节结构体和枚举是值类型中将会对此过程进行详细介绍。如果
字典实例中所储存的键(keys)和/或值(values)是值类型(结构体或枚举),当赋值或调用发生时,它们都会被拷贝。相反,如果键(keys)和/或值(values)是引用类型,被拷贝的将会是引用,而不是被它们引用的类实例或函数。字典的键和值的拷贝行为与结构体所储存的属性的拷贝行为相同。下面的示例定义了一个名为
-ages的字典,其中储存了四个人的名字和年龄。ages字典被赋予了一个名为copiedAges的新变量,同时ages在赋值的过程中被拷贝。赋值结束后,ages和copiedAges成为两个相互独立的字典。+var ages = ["Peter": 23, "Wei": 35, "Anish": 65, "Katya": 19] +var ages = ["Peter": 23, "Wei": 35, "Anish": 65, "Katya": 19] var copiedAges = ages -这个字典的键(keys)是
+字符串(String)类型,值(values)是整(Int)类型。这两种类型在Swift 中都是值类型(value types),所以当字典被拷贝时,两者都会被拷贝。这个字典的键(keys)是
字符串(String)类型,值(values)是整(Int)类型。这两种类型在Swift 中都是值类型(value types),所以当字典被拷贝时,两者都会被拷贝。我们可以通过改变一个字典中的年龄值(age value),检查另一个字典中所对应的值,来证明
-ages字典确实是被拷贝了。如果在copiedAges字典中将Peter的值设为24,那么ages字典仍然会返回修改前的值23:+copiedAges["Peter"] = 24 +copiedAges["Peter"] = 24 println(ages["Peter"]) // 输出 "23" -数组的赋值和拷贝行为
+数组的赋值和拷贝行为
在Swift 中,
数组(Arrays)类型的赋值和拷贝行为要比字典(Dictionary)类型的复杂的多。当操作数组内容时,数组(Array)能提供接近C语言的的性能,并且拷贝行为只有在必要时才会发生。如果你将一个
数组(Array)实例赋给一个变量或常量,或者将其作为参数传递给函数或方法调用,在事件发生时数组的内容不会被拷贝。相反,数组公用相同的元素序列。当你在一个数组内修改某一元素,修改结果也会在另一数组显示。对数组来说,拷贝行为仅仅当操作有可能修改数组
长度时才会发生。这种行为包括了附加(appending),插入(inserting),删除(removing)或者使用范围下标(ranged subscript)去替换这一范围内的元素。只有当数组拷贝确要发生时,数组内容的行为规则与字典中键值的相同,参见章节[集合(collection)类型的赋值与复制行为](#assignment_and_copy_behavior_for_collection_types。下面的示例将一个
-整数(Int)数组赋给了一个名为a的变量,继而又被赋给了变量b和c:+var a = [1, 2, 3] +var a = [1, 2, 3] var b = a var c = a -我们可以在
-a,b,c上使用下标语法以得到数组的第一个元素:+println(a[0]) +我们可以在
+a,b,c上使用下标语法以得到数组的第一个元素:println(a[0]) // 1 println(b[0]) // 1 println(c[0]) // 1 -如果通过下标语法修改数组中某一元素的值,那么
-a,b,c中的相应值都会发生改变。请注意当你用下标语法修改某一值时,并没有拷贝行为伴随发生,因为下表语法修改值时没有改变数组长度的可能:+a[0] = 42 +如果通过下标语法修改数组中某一元素的值,那么
+a,b,c中的相应值都会发生改变。请注意当你用下标语法修改某一值时,并没有拷贝行为伴随发生,因为下表语法修改值时没有改变数组长度的可能:a[0] = 42 println(a[0]) // 42 println(b[0]) // 42 println(c[0]) // 42 -然而,当你给
a附加新元素时,数组的长度会改变。 +然而,当你给
a附加新元素时,数组的长度会改变。 当附加元素这一事件发生时,Swift 会创建这个数组的一个拷贝。从此以后,a将会是原数组的一个独立拷贝。拷贝发生后,如果再修改
-a中元素值的话,a将会返回与b,c不同的结果,因为后两者引用的是原来的数组:+a.append(4) +a.append(4) a[0] = 777 println(a[0]) // 777 @@ -825,48 +862,54 @@ println(b[0]) // 42 println(c[0]) // 42 -确保数组的唯一性
+确保数组的唯一性
在操作一个数组,或将其传递给函数以及方法调用之前是很有必要先确定这个数组是有一个唯一拷贝的。通过在数组变量上调用
unshare方法来确定数组引用的唯一性。(当数组赋给常量时,不能调用unshare方法)如果一个数组被多个变量引用,在其中的一个变量上调用
unshare方法,则会拷贝此数组,此时这个变量将会有属于它自己的独立数组拷贝。当数组仅被一个变量引用时,则不会有拷贝发生。在上一个示例的最后,
-b和c都引用了同一个数组。此时在b上调用unshare方法则会将b变成一个唯一个拷贝:b.unshare() -在
-unshare方法调用后再修改b中第一个元素的值,这三个数组(a,b,c)会返回不同的三个值:+b[0] = -105 ++b.unshare() +在
+unshare方法调用后再修改b中第一个元素的值,这三个数组(a,b,c)会返回不同的三个值:b[0] = -105 println(a[0]) // 77 println(b[0]) // -105 println(c[0]) // 42 -判定两个数组是否共用相同元素
+判定两个数组是否共用相同元素
我们通过使用恒等运算符(identity operators)( === 和 !==)来判定两个数组或子数组共用相同的储存空间或元素。
下面这个示例使用了“等同(identical to)” 运算符(===) 来判定
-b和c是否共用相同的数组元素:+if b === c { +if b === c { println("b and c still share the same array elements.") } else { println("b and c now refer to two independent sets of array elements.") } - -// 输出 "b and c now refer totwo independent sets of array elements." -此外,我们还可以使用恒等运算符来判定两个子数组是否共用相同的元素。下面这个示例中,比较了
-b的两个相等的子数组,并且确定了这两个子数组都引用相同的元素:+if b[0...1] === b[0...1] { ++// 输出 "b and c now refer totwo independent sets of array elements." +此外,我们还可以使用恒等运算符来判定两个子数组是否共用相同的元素。下面这个示例中,比较了
+b的两个相等的子数组,并且确定了这两个子数组都引用相同的元素:if b[0...1] === b[0...1] { println("These two subarrays share the same elements.") } else { println("These two subarrays do not share the same elements.") } // 输出 "These two subarrays share the same elements." -强制复制数组
+强制复制数组
我们通过调用数组的
copy方法进行强制显性复制。这个方法对数组进行了浅拷贝(shallow copy),并且返回一个包含此拷贝的新数组。下面这个示例中定义了一个
-names数组,其包含了七个人名。还定义了一个copiedNames变量,用以储存在names上调用copy方法所返回的结果:+var names = ["Mohsen", "Hilary", "Justyn", "Amy", "Rich", "Graham", "Vic"] +var names = ["Mohsen", "Hilary", "Justyn", "Amy", "Rich", "Graham", "Vic"] var copiedNames = names.copy() -我们可以通过修改一个数组中某元素,并且检查另一个数组中对应元素的方法来判定
-names数组确已被复制。如果你将copiedNames中第一个元素从"Mohsen"修改为"Mo",则names数组返回的仍是拷贝发生前的"Mohsen":+copiedName[0] = "Mo" +我们可以通过修改一个数组中某元素,并且检查另一个数组中对应元素的方法来判定
+names数组确已被复制。如果你将copiedNames中第一个元素从"Mohsen"修改为"Mo",则names数组返回的仍是拷贝发生前的"Mohsen":copiedName[0] = "Mo" println(name[0]) // 输出 "Mohsen" --注意:
-如果你仅需要确保你对数组的引用是唯一引用,请调用
+unshare方法,而不是copy方法。unshare方法仅会在确有必要时才会创建数组拷贝。copy方法会在任何时候都创建一个新的拷贝,即使引用已经是唯一引用。+diff --git a/chapter2/10_Properties.html b/chapter2/10_Properties.html old mode 100755 new mode 100644 index d02aabda..03e90964 --- a/chapter2/10_Properties.html +++ b/chapter2/10_Properties.html @@ -46,7 +46,7 @@ -注意:
如果你仅需要确保你对数组的引用是唯一引用,请调用unshare方法,而不是copy方法。unshare方法仅会在确有必要时才会创建数组拷贝。copy方法会在任何时候都创建一个新的拷贝,即使引用已经是唯一引用。+@@ -268,7 +268,7 @@- 2.12. 附属脚本 + 2.12. 下标脚本 @@ -520,7 +520,7 @@ - + @@ -587,11 +587,10 @@ -+ diff --git a/chapter2/11_Methods.html b/chapter2/11_Methods.html old mode 100755 new mode 100644 index 0e514a1a..fd8c6d32 --- a/chapter2/11_Methods.html +++ b/chapter2/11_Methods.html @@ -46,7 +46,7 @@ - -翻译:shinyzhu
-校对:pp-prog
+翻译:shinyzhu
校对:pp-prog属性 (Properties)
@@ -610,8 +609,8 @@存储属性
简单来说,一个存储属性就是存储在特定类或结构体的实例里的一个常量或变量,存储属性可以是变量存储属性(用关键字
var定义),也可以是常量存储属性(用关键字let定义)。可以在定义存储属性的时候指定默认值,请参考构造过程一章的默认属性值一节。也可以在构造过程中设置或修改存储属性的值,甚至修改常量存储属性的值,请参考构造过程一章的在初始化阶段修改常量存储属性一节。
-下面的例子定义了一个名为
-FixedLengthRange的结构体,他描述了一个在创建后无法修改值域宽度的区间:+struct FixedLengthRange { +下面的例子定义了一个名为
+FixedLengthRange的结构体,它描述了一个在创建后无法修改值域宽度的区间:struct FixedLengthRange { var firstValue: Int let length: Int } @@ -619,27 +618,28 @@ var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3) // 该区间表示整数0,1,2 rangeOfThreeItems.firstValue = 6 // 该区间现在表示整数6,7,8 -+
FixedLengthRange的实例包含一个名为firstValue的变量存储属性和一个名为length的常量存储属性。在上面的例子中,length在创建实例的时候被赋值,因为它是一个常量存储属性,所以之后无法修改它的值。
FixedLengthRange的实例包含一个名为firstValue的变量存储属性和一个名为length的常量存储属性。在上面的例子中,length在创建实例的时候被赋值,因为它是一个常量存储属性,所以之后无法修改它的值。常量和存储属性
如果创建了一个结构体的实例并赋值给一个常量,则无法修改实例的任何属性,即使定义了变量存储属性:
-+let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4) +let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4) // 该区间表示整数0,1,2,3 rangeOfFourItems.firstValue = 6 // 尽管 firstValue 是个变量属性,这里还是会报错 -因为
+rangeOfFourItems声明成了常量(用let关键字),即使firstValue是一个变量属性,也无法再修改它了。因为
rangeOfFourItems声明成了常量(用let关键字),即使firstValue是一个变量属性,也无法再修改它了。这种行为是由于结构体(struct)属于值类型。当值类型的实例被声明为常量的时候,它的所有属性也就成了常量。
属于引用类型的类(class)则不一样,把一个引用类型的实例赋给一个常量后,仍然可以修改实例的变量属性。
延迟存储属性
延迟存储属性是指当第一次被调用的时候才会计算其初始值的属性。在属性声明前使用
@lazy来标示一个延迟存储属性。-注意:
-必须将延迟存储属性声明成变量(使用
+var关键字),因为属性的值在实例构造完成之前可能无法得到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟属性。注意:
必须将延迟存储属性声明成变量(使用var关键字),因为属性的值在实例构造完成之前可能无法得到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟属性。延迟属性很有用,当属性的值依赖于在实例的构造过程结束前无法知道具体值的外部因素时,或者当属性的值需要复杂或大量计算时,可以只在需要的时候来计算它。
下面的例子使用了延迟存储属性来避免复杂类的不必要的初始化。例子中定义了
-DataImporter和DataManager两个类,下面是部分代码:+class DataImporter { +class DataImporter { /* DataImporter 是一个将外部文件中的数据导入的类。 这个类的初始化会消耗不少时间。 @@ -658,14 +658,16 @@ let manager = DataManager() manager.data += "Some data" manager.data += "Some more data" // DataImporter 实例的 importer 属性还没有被创建 -+
DataManager类包含一个名为data的存储属性,初始值是一个空的字符串(String)数组。虽然没有写出全部代码,DataManager类的目的是管理和提供对这个字符串数组的访问。
DataManager类包含一个名为data的存储属性,初始值是一个空的字符串(String)数组。虽然没有写出全部代码,DataManager类的目的是管理和提供对这个字符串数组的访问。
DataManager的一个功能是从文件导入数据,该功能由DataImporter类提供,DataImporter需要消耗不少时间完成初始化:因为它的实例在初始化时可能要打开文件,还要读取文件内容到内存。
DataManager也可能不从文件中导入数据。所以当DataManager的实例被创建时,没必要创建一个DataImporter的实例,更明智的是当用到DataImporter的时候才去创建它。由于使用了
-@lazy,importer属性只有在第一次被访问的时候才被创建。比如访问它的属性fileName时:+println(manager.importer.fileName) ++println(manager.importer.fileName) // DataImporter 实例的 importer 属性现在被创建了 // 输出 "data.txt” -存储属性和实例变量
如果您有过 Objective-C 经验,应该知道有两种方式在类实例存储值和引用。对于属性来说,也可以使用实例变量作为属性值的后端存储。
Swift 编程语言中把这些理论统一用属性来实现。Swift 中的属性没有对应的实例变量,属性的后端存储也无法直接访问。这就避免了不同场景下访问方式的困扰,同时也将属性的定义简化成一个语句。 @@ -673,7 +675,7 @@ manager.data += "Some more data"
计算属性
除存储属性外,类、结构体和枚举可以定义计算属性,计算属性不直接存储值,而是提供一个 getter 来获取值,一个可选的 setter 来间接设置其他属性或变量的值。
-+struct Point { +struct Point { var x = 0.0, y = 0.0 } struct Size { @@ -700,7 +702,8 @@ let initialSquareCenter = square.center square.center = Point(x: 15.0, y: 15.0) println("square.origin is now at (\(square.origin.x), \(square.origin.y))") // 输出 "square.origin is now at (10.0, 10.0)” -这个例子定义了 3 个几何形状的结构体:
+这个例子定义了 3 个几何形状的结构体:
Point封装了一个(x, y)的坐标- @@ -714,7 +717,7 @@ println("square.origin is now at (\(square.origin.x), \(square.origin.y))&q
Size封装了一个width和height便捷 setter 声明
如果计算属性的 setter 没有定义表示新值的参数名,则可以使用默认名称
-newValue。下面是使用了便捷 setter 声明的Rect结构体代码:+struct AlternativeRect { ++struct AlternativeRect { var origin = Point() var size = Size() var center: Point { @@ -729,15 +732,32 @@ println("square.origin is now at (\(square.origin.x), \(square.origin.y))&q } } } -只读计算属性
只有 getter 没有 setter 的计算属性就是只读计算属性。只读计算属性总是返回一个值,可以通过点运算符访问,但不能设置新的值。
+<<<<<<< HEAD
+注意:
+必须使用
var关键字定义计算属性,包括只读计算属性,因为他们的值不是固定的。let关键字只用来声明常量属性,表示初始化后再也无法修改的值。注意:
-必须使用
+var关键字定义计算属性,包括只读计算属性,因为他们的值不是固定的。let关键字只用来声明常量属性,表示初始化后再也无法修改的值。必须使用
+var关键字定义计算属性,包括只读计算属性,因为它们的值不是固定的。let关键字只用来声明常量属性,表示初始化后再也无法修改的值。+++++++++++a516af6a531a104ec88da0d236ecf389a5ec72af
+只读计算属性的声明可以去掉
-get关键字和花括号:+struct Cuboid { +struct Cuboid { var width = 0.0, height = 0.0, depth = 0.0 var volume: Double { return width * height * depth @@ -746,14 +766,14 @@ println("square.origin is now at (\(square.origin.x), \(square.origin.y))&q let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0) println("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)") // 输出 "the volume of fourByFiveByTwo is 40.0" -这个例子定义了一个名为
+Cuboid的结构体,表示三维空间的立方体,包含width、height和depth属性,还有一个名为volume的只读计算属性用来返回立方体的体积。设置volume的值毫无意义,因为通过width、height和depth就能算出volume。然而,Cuboid提供一个只读计算属性来让外部用户直接获取体积是很有用的。这个例子定义了一个名为
Cuboid的结构体,表示三维空间的立方体,包含width、height和depth属性,还有一个名为volume的只读计算属性用来返回立方体的体积。设置volume的值毫无意义,因为通过width、height和depth就能算出volume。然而,Cuboid提供一个只读计算属性来让外部用户直接获取体积是很有用的。属性监视器
属性监视器监控和响应属性值的变化,每次属性被设置值的时候都会调用属性监视器,甚至新的值和现在的值相同的时候也不例外。
可以为除了延迟存储属性之外的其他存储属性添加属性监视器,也可以通过重载属性的方式为继承的属性(包括存储属性和计算属性)添加属性监视器。属性重载请参考继承一章的重载。
-注意:
-不需要为无法重载的计算属性添加属性监视器,因为可以通过 setter 直接监控和响应值的变化。
+注意:
不需要为无法重载的计算属性添加属性监视器,因为可以通过 setter 直接监控和响应值的变化。可以为属性添加如下的一个或全部监视器:
@@ -762,12 +782,28 @@ println("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
willSet监视器会将新的属性值作为固定参数传入,在willSet的实现代码中可以为这个参数指定一个名称,如果不指定则参数仍然可用,这时使用默认名称newValue表示。类似地,
+didSet监视器会将旧的属性值作为参数传入,可以为该参数命名或者使用默认参数名oldValue。<<<<<<< HEAD
+注意:
+
willSet和didSet监视器在属性初始化过程中不会被调用,他们只会当属性的值在初始化之外的地方被设置时被调用。注意:
-+
willSet和didSet监视器在属性初始化过程中不会被调用,他们只会当属性的值在初始化之外的地方被设置时被调用。+
willSet和didSet监视器在属性初始化过程中不会被调用,它们只会当属性的值在初始化之外的地方被设置时被调用。+++++++++++a516af6a531a104ec88da0d236ecf389a5ec72af
+这里是一个
-willSet和didSet的实际例子,其中定义了一个名为StepCounter的类,用来统计当人步行时的总步数,可以跟计步器或其他日常锻炼的统计装置的输入数据配合使用。+class StepCounter { +class StepCounter { var totalSteps: Int = 0 { willSet(newTotalSteps) { println("About to set totalSteps to \(newTotalSteps)") @@ -789,13 +825,13 @@ stepCounter.totalSteps = 360 stepCounter.totalSteps = 896 // About to set totalSteps to 896 // Added 536 steps -+
StepCounter类定义了一个Int类型的属性totalSteps,它是一个存储属性,包含willSet和didSet监视器。
StepCounter类定义了一个Int类型的属性totalSteps,它是一个存储属性,包含willSet和didSet监视器。当
totalSteps设置新值的时候,它的willSet和didSet监视器都会被调用,甚至当新的值和现在的值完全相同也会调用。例子中的
willSet监视器将表示新值的参数自定义为newTotalSteps,这个监视器只是简单的将新的值输出。
didSet监视器在totalSteps的值改变后被调用,它把新的值和旧的值进行对比,如果总的步数增加了,就输出一个消息表示增加了多少步。didSet没有提供自定义名称,所以默认值oldValue表示旧值的参数名。-注意:
-如果在
+didSet监视器里为属性赋值,这个值会替换监视器之前设置的值。注意:
如果在didSet监视器里为属性赋值,这个值会替换监视器之前设置的值。全局变量和局部变量
@@ -803,9 +839,7 @@ stepCounter.totalSteps = 896前面章节提到的全局或局部变量都属于存储型变量,跟存储属性类似,它提供特定类型的存储空间,并允许读取和写入。
另外,在全局或局部范围都可以定义计算型变量和为存储型变量定义监视器,计算型变量跟计算属性一样,返回一个计算的值而不是存储值,声明格式也完全一样。
-注意:
-全局的常量或变量都是延迟计算的,跟延迟存储属性相似,不同的地方在于,全局的常量或变量不需要标记
-@lazy特性。局部范围的常量或变量不会延迟计算。
+注意:
全局的常量或变量都是延迟计算的,跟延迟存储属性相似,不同的地方在于,全局的常量或变量不需要标记@lazy特性。
局部范围的常量或变量不会延迟计算。类型属性
@@ -815,14 +849,13 @@ stepCounter.totalSteps = 896对于值类型(指结构体和枚举)可以定义存储型和计算型类型属性,对于类(class)则只能定义计算型类型属性。
值类型的存储型类型属性可以是变量或常量,计算型类型属性跟实例的计算属性一样定义成变量属性。
-注意:
-跟实例的存储属性不同,必须给存储型类型属性指定默认值,因为类型本身无法在初始化过程中使用构造器给类型属性赋值。
+注意:
跟实例的存储属性不同,必须给存储型类型属性指定默认值,因为类型本身无法在初始化过程中使用构造器给类型属性赋值。类型属性语法
在 C 或 Objective-C 中,静态常量和静态变量的定义是通过特定类型加上
global关键字。在 Swift 编程语言中,类型属性是作为类型定义的一部分写在类型最外层的花括号内,因此它的作用范围也就在类型支持的范围内。使用关键字
-static来定义值类型的类型属性,关键字class来为类(class)定义类型属性。下面的例子演示了存储型和计算型类型属性的语法:+struct SomeStructure { +struct SomeStructure { static var storedTypeProperty = "Some value." static var computedTypeProperty: Int { // 这里返回一个 Int 值 @@ -839,14 +872,14 @@ class SomeClass { // 这里返回一个 Int 值 } } --注意:
-例子中的计算型类型属性是只读的,但也可以定义可读可写的计算型类型属性,跟实例计算属性的语法类似。
++注意:
例子中的计算型类型属性是只读的,但也可以定义可读可写的计算型类型属性,跟实例计算属性的语法类似。获取和设置类型属性的值
跟实例的属性一样,类型属性的访问也是通过点运算符来进行,但是,类型属性是通过类型本身来获取和设置,而不是通过实例。比如:
-+println(SomeClass.computedTypeProperty) +println(SomeClass.computedTypeProperty) // 输出 "42" println(SomeStructure.storedTypeProperty) @@ -854,11 +887,12 @@ println(SomeStructure.storedTypeProperty) SomeStructure.storedTypeProperty = "Another value." println(SomeStructure.storedTypeProperty) // 输出 "Another value.” -下面的例子定义了一个结构体,使用两个存储型类型属性来表示多个声道的声音电平值,每个声道有一个 0 到 10 之间的整数表示声音电平值。
+下面的例子定义了一个结构体,使用两个存储型类型属性来表示多个声道的声音电平值,每个声道有一个 0 到 10 之间的整数表示声音电平值。
后面的图表展示了如何联合使用两个声道来表示一个立体声的声音电平值。当声道的电平值是 0,没有一个灯会亮;当声道的电平值是 10,所有灯点亮。本图中,左声道的电平是 9,右声道的电平是 7。
上面所描述的声道模型使用
-AudioChannel结构体来表示:+struct AudioChannel { +struct AudioChannel { static let thresholdLevel = 10 static var maxInputLevelForAllChannels = 0 var currentLevel: Int = 0 { @@ -874,7 +908,8 @@ println(SomeStructure.storedTypeProperty) } } } -结构
+AudioChannel定义了 2 个存储型类型属性来实现上述功能。第一个是thresholdLevel,表示声音电平的最大上限阈值,它是一个取值为 10 的常量,对所有实例都可见,如果声音电平高于 10,则取最大上限值 10(见后面描述)。结构
AudioChannel定义了 2 个存储型类型属性来实现上述功能。第一个是thresholdLevel,表示声音电平的最大上限阈值,它是一个取值为 10 的常量,对所有实例都可见,如果声音电平高于 10,则取最大上限值 10(见后面描述)。第二个类型属性是变量存储型属性
maxInputLevelForAllChannels,它用来表示所有AudioChannel实例的电平值的最大值,初始值是 0。
AudioChannel也定义了一个名为currentLevel的实例存储属性,表示当前声道现在的电平值,取值为 0 到 10。属性
@@ -883,25 +918,27 @@ println(SomeStructure.storedTypeProperty)currentLevel包含didSet属性监视器来检查每次新设置后的属性值,有如下两个检查:如果修正后的 currentLevel值大于任何之前任意AudioChannel实例中的值,属性监视器将新值保存在静态属性maxInputLevelForAllChannels中。-注意:
-在第一个检查过程中,
+didSet属性监视器将currentLevel设置成了不同的值,但这时不会再次调用属性监视器。注意:
在第一个检查过程中,didSet属性监视器将currentLevel设置成了不同的值,但这时不会再次调用属性监视器。可以使用结构体
-AudioChannel来创建表示立体声系统的两个声道leftChannel和rightChannel:var leftChannel = AudioChannel() +var leftChannel = AudioChannel() var rightChannel = AudioChannel() -如果将左声道的电平设置成 7,类型属性
-maxInputLevelForAllChannels也会更新成 7:+leftChannel.currentLevel = 7 +如果将左声道的电平设置成 7,类型属性
+maxInputLevelForAllChannels也会更新成 7:leftChannel.currentLevel = 7 println(leftChannel.currentLevel) // 输出 "7" println(AudioChannel.maxInputLevelForAllChannels) // 输出 "7" -如果试图将右声道的电平设置成 11,则会将右声道的
-currentLevel修正到最大值 10,同时maxInputLevelForAllChannels的值也会更新到 10:+rightChannel.currentLevel = 11 +如果试图将右声道的电平设置成 11,则会将右声道的
+currentLevel修正到最大值 10,同时maxInputLevelForAllChannels的值也会更新到 10:+rightChannel.currentLevel = 11 println(rightChannel.currentLevel) // 输出 "10" println(AudioChannel.maxInputLevelForAllChannels) // 输出 "10"+@@ -268,7 +268,7 @@diff --git a/chapter2/12_Subscripts.html b/chapter2/12_Subscripts.html old mode 100755 new mode 100644 index 12dfce3f..59da1110 --- a/chapter2/12_Subscripts.html +++ b/chapter2/12_Subscripts.html @@ -21,7 +21,7 @@ - + @@ -46,7 +46,7 @@ -- 2.12. 附属脚本 + 2.12. 下标脚本 @@ -520,7 +520,7 @@ - + @@ -587,11 +587,10 @@ -+ @@ -813,7 +826,7 @@ println("level 6 has not yet been unlocked") - + -翻译:pp-prog
-校对:zqp
+翻译:pp-prog
校对:zqp方法(Methods)
@@ -607,7 +606,7 @@实例方法是属于某个特定类、结构体或者枚举类型实例的方法。实例方法提供访问和修改实例属性的方法或提供与实例目的相关的功能,并以此来支撑实例的功能。实例方法的语法与函数完全一致,详情参见函数。
实例方法要写在它所属的类型的前后大括号之间。实例方法能够隐式访问它所属类型的所有的其他实例方法和属性。实例方法只能被它所属的类的某个特定实例调用。实例方法不能脱离于现存的实例而被调用。
下面的例子,定义一个很简单的类
-Counter,Counter能被用来对一个动作发生的次数进行计数:+class Counter { +class Counter { var count = 0 func increment() { count++ @@ -619,7 +618,8 @@ count = 0 } } -+
Counter类定义了三个实例方法:
Counter类定义了三个实例方法:
increment让计数器按一递增;- @@ -627,7 +627,7 @@
incrementBy(amount: Int)让计数器按一个指定的整数值递增;
Counter这个类还声明了一个可变属性count,用它来保持对当前计数器值的追踪。和调用属性一样,用点语法(dot syntax)调用实例方法:
-+let counter = Counter() ++let counter = Counter() // 初始计数值是0 counter.increment() // 计数值现在是1 @@ -635,28 +635,32 @@ // 计数值现在是6 counter.reset() // 计数值现在是0 -方法的局部参数名称和外部参数名称(Local and External Parameter Names for Methods)
函数参数可以同时有一个局部名称(在函数体内部使用)和一个外部名称(在调用函数时使用),详情参见函数的外部参数名。方法参数也一样(因为方法就是函数,只是这个函数与某个类型相关联了)。但是,方法和函数的局部名称和外部名称的默认行为是不一样的。
Swift 中的方法和 Objective-C 中的方法极其相似。像在 Objective-C 中一样,Swift 中方法的名称通常用一个介词指向方法的第一个参数,比如:
with,for,by等等。前面的Counter类的例子中incrementBy方法就是这样的。介词的使用让方法在被调用时能像一个句子一样被解读。和函数参数不同,对于方法的参数,Swift 使用不同的默认处理方式,这可以让方法命名规范更容易写。具体来说,Swift 默认仅给方法的第一个参数名称一个局部参数名称;默认同时给第二个和后续的参数名称局部参数名称和外部参数名称。这个约定与典型的命名和调用约定相适应,与你在写 Objective-C 的方法时很相似。这个约定还让表达式方法在调用时不需要再限定参数名称。
看看下面这个
-Counter的另一个版本(它定义了一个更复杂的incrementBy方法):+class Counter { +class Counter { var count: Int = 0 func incrementBy(amount: Int, numberOfTimes: Int) { count += amount * numberOfTimes } } --
incrementBy方法有两个参数:amount和numberOfTimes。默认情况下,Swift 只把amount当作一个局部名称,但是把numberOfTimes即看作局部名称又看作外部名称。下面调用这个方法:+let counter = Counter() ++
incrementBy方法有两个参数:amount和numberOfTimes。默认情况下,Swift 只把amount当作一个局部名称,但是把numberOfTimes即看作局部名称又看作外部名称。下面调用这个方法:let counter = Counter() counter.incrementBy(5, numberOfTimes: 3) // counter value is now 15 -你不必为第一个参数值再定义一个外部变量名:因为从函数名
+incrementBy已经能很清楚地看出它的作用。但是第二个参数,就要被一个外部参数名称所限定,以便在方法被调用时明确它的作用。你不必为第一个参数值再定义一个外部变量名:因为从函数名
incrementBy已经能很清楚地看出它的作用。但是第二个参数,就要被一个外部参数名称所限定,以便在方法被调用时明确它的作用。这种默认的行为能够有效的处理方法(method),类似于在参数
-numberOfTimes前写一个井号(#):+func incrementBy(amount: Int, #numberOfTimes: Int) { - count += amount * numberOfTimes +func incrementBy(amount: Int, #numberOfTimes: Int) { + count += amount * numberOfTimes } -这种默认行为使上面代码意味着:在 Swift 中定义方法使用了与 Objective-C 同样的语法风格,并且方法将以自然表达式的方式被调用。
+这种默认行为使上面代码意味着:在 Swift 中定义方法使用了与 Objective-C 同样的语法风格,并且方法将以自然表达式的方式被调用。
修改方法的外部参数名称(Modifying External Parameter Name Behavior for Methods)
有时为方法的第一个参数提供一个外部参数名称是非常有用的,尽管这不是默认的行为。你可以自己添加一个显式的外部名称或者用一个井号(
@@ -665,13 +669,14 @@ counter.incrementBy(5, numberOfTimes: 3)#)作为第一个参数的前缀来把这个局部名称当作外部名称使用。
self属性(The self Property)类型的每一个实例都有一个隐含属性叫做
self,self完全等同于该实例本身。你可以在一个实例的实例方法中使用这个隐含的self属性来引用当前实例。上面例子中的
-increment方法还可以这样写:+func increment() { +func increment() { self.count++ } -实际上,你不必在你的代码里面经常写
+self。不论何时,只要在一个方法中使用一个已知的属性或者方法名称,如果你没有明确的写self,Swift 假定你是指当前实例的属性或者方法。这种假定在上面的Counter中已经示范了:Counter中的三个实例方法中都使用的是count(而不是self.count)。实际上,你不必在你的代码里面经常写
self。不论何时,只要在一个方法中使用一个已知的属性或者方法名称,如果你没有明确的写self,Swift 假定你是指当前实例的属性或者方法。这种假定在上面的Counter中已经示范了:Counter中的三个实例方法中都使用的是count(而不是self.count)。使用这条规则的主要场景是实例方法的某个参数名称与实例的某个属性名称相同的时候。在这种情况下,参数名称享有优先权,并且在引用属性时必须使用一种更严格的方式。这时你可以使用
self属性来区分参数名称和属性名称。下面的例子中,
-self消除方法参数x和实例属性x之间的歧义:+struct Point { +struct Point { var x = 0.0, y = 0.0 func isToTheRightOfX(x: Double) -> Bool { return self.x > x @@ -682,13 +687,14 @@ if somePoint.isToTheRightOfX(1.0) { println("This point is to the right of the line where x == 1.0") } // 输出 "This point is to the right of the line where x == 1.0"(这个点在x等于1.0这条线的右边) -如果不使用
+self前缀,Swift 就认为两次使用的x都指的是名称为x的函数参数。如果不使用
self前缀,Swift 就认为两次使用的x都指的是名称为x的函数参数。在实例方法中修改值类型(Modifying Value Types from Within Instance Methods)
结构体和枚举是值类型。一般情况下,值类型的属性不能在它的实例方法中被修改。
但是,如果你确实需要在某个具体的方法中修改结构体或者枚举的属性,你可以选择
变异(mutating)这个方法,然后方法就可以从方法内部改变它的属性;并且它做的任何改变在方法结束时还会保留在原始结构中。方法还可以给它隐含的self属性赋值一个全新的实例,这个新实例在方法结束后将替换原来的实例。要使用
-变异方法, 将关键字mutating放到方法的func关键字之前就可以了:+struct Point { +struct Point { var x = 0.0, y = 0.0 mutating func moveByX(deltaX: Double, y deltaY: Double) { x += deltaX @@ -699,23 +705,26 @@ var somePoint = Point(x: 1.0, y: 1.0) somePoint.moveByX(2.0, y: 3.0) println("The point is now at (\(somePoint.x), \(somePoint.y))") // 输出 "The point is now at (3.0, 4.0)" -上面的
+Point结构体定义了一个变异方法(mutating method)moveByX,moveByX用来移动点。moveByX方法在被调用时修改了这个点,而不是返回一个新的点。方法定义时加上mutating关键字,这才让方法可以修改值类型的属性。上面的
Point结构体定义了一个变异方法(mutating method)moveByX,moveByX用来移动点。moveByX方法在被调用时修改了这个点,而不是返回一个新的点。方法定义时加上mutating关键字,这才让方法可以修改值类型的属性。注意:不能在结构体类型常量上调用变异方法,因为常量的属性不能被改变,即使想改变的是常量的变量属性也不行,详情参见存储属性和实例变量
-+let fixedPoint = Point(x: 3.0, y: 3.0) ++let fixedPoint = Point(x: 3.0, y: 3.0) fixedPoint.moveByX(2.0, y: 3.0) // this will report an error -在变异方法中给self赋值(Assigning to self Within a Mutating Method)
变异方法能够赋给隐含属性
-self一个全新的实例。上面Point的例子可以用下面的方式改写:+struct Point { +struct Point { var x = 0.0, y = 0.0 mutating func moveByX(deltaX: Double, y deltaY: Double) { self = Point(x: x + deltaX, y: y + deltaY) } } -新版的变异方法
+moveByX创建了一个新的结构(它的 x 和 y 的值都被设定为目标值)。调用这个版本的方法和调用上个版本的最终结果是一样的。新版的变异方法
moveByX创建了一个新的结构(它的 x 和 y 的值都被设定为目标值)。调用这个版本的方法和调用上个版本的最终结果是一样的。枚举的变异方法可以把
-self设置为相同的枚举类型中不同的成员:+enum TriStateSwitch { +enum TriStateSwitch { case Off, Low, High mutating func next() { switch self { @@ -733,26 +742,27 @@ ovenLight.next() // ovenLight 现在等于 .High ovenLight.next() // ovenLight 现在等于 .Off -上面的例子中定义了一个三态开关的枚举。每次调用
+next方法时,开关在不同的电源状态(Off,Low,High)之前循环切换。上面的例子中定义了一个三态开关的枚举。每次调用
next方法时,开关在不同的电源状态(Off,Low,High)之前循环切换。类型方法(Type Methods)
实例方法是被类型的某个实例调用的方法。你也可以定义类型本身调用的方法,这种方法就叫做类型方法。声明类的类型方法,在方法的
func关键字之前加上关键字class;声明结构体和枚举的类型方法,在方法的func关键字之前加上关键字static。-注意:
-在 Objective-C 里面,你只能为 Objective-C 的类定义类型方法(type-level methods)。在 Swift 中,你可以为所有的类、结构体和枚举定义类型方法:每一个类型方法都被它所支持的类型显式包含。
+注意:
在 Objective-C 里面,你只能为 Objective-C 的类定义类型方法(type-level methods)。在 Swift 中,你可以为所有的类、结构体和枚举定义类型方法:每一个类型方法都被它所支持的类型显式包含。类型方法和实例方法一样用点语法调用。但是,你是在类型层面上调用这个方法,而不是在实例层面上调用。下面是如何在
-SomeClass类上调用类型方法的例子:+class SomeClass { +class SomeClass { class func someTypeMethod() { // type method implementation goes here } } SomeClass.someTypeMethod() -在类型方法的方法体(body)中,
+self指向这个类型本身,而不是类型的某个实例。对于结构体和枚举来说,这意味着你可以用self来消除静态属性和静态方法参数之间的歧义(类似于我们在前面处理实例属性和实例方法参数时做的那样)。在类型方法的方法体(body)中,
self指向这个类型本身,而不是类型的某个实例。对于结构体和枚举来说,这意味着你可以用self来消除静态属性和静态方法参数之间的歧义(类似于我们在前面处理实例属性和实例方法参数时做的那样)。一般来说,任何未限定的方法和属性名称,将会来自于本类中另外的类型级别的方法和属性。一个类型方法可以调用本类中另一个类型方法的名称,而无需在方法名称前面加上类型名称的前缀。同样,结构体和枚举的类型方法也能够直接通过静态属性的名称访问静态属性,而不需要类型名称前缀。
下面的例子定义了一个名为
LevelTracker结构体。它监测玩家的游戏发展情况(游戏的不同层次或阶段)。这是一个单人游戏,但也可以存储多个玩家在同一设备上的游戏信息。游戏初始时,所有的游戏等级(除了等级 1)都被锁定。每次有玩家完成一个等级,这个等级就对这个设备上的所有玩家解锁。
-LevelTracker结构体用静态属性和方法监测游戏的哪个等级已经被解锁。它还监测每个玩家的当前等级。+struct LevelTracker { +struct LevelTracker { static var highestUnlockedLevel = 1 static func unlockLevel(level: Int) { if level > highestUnlockedLevel { highestUnlockedLevel = level } @@ -770,12 +780,13 @@ SomeClass.someTypeMethod() } } } -+
LevelTracker监测玩家的已解锁的最高等级。这个值被存储在静态属性highestUnlockedLevel中。
LevelTracker监测玩家的已解锁的最高等级。这个值被存储在静态属性highestUnlockedLevel中。
LevelTracker还定义了两个类型方法与highestUnlockedLevel配合工作。第一个类型方法是unlockLevel:一旦新等级被解锁,它会更新highestUnlockedLevel的值。第二个类型方法是levelIsUnlocked:如果某个给定的等级已经被解锁,它将返回true。(注意:尽管我们没有使用类似LevelTracker.highestUnlockedLevel的写法,这个类型方法还是能够访问静态属性highestUnlockedLevel)除了静态属性和类型方法,
LevelTracker还监测每个玩家的进度。它用实例属性currentLevel来监测玩家当前的等级。为了便于管理
currentLevel属性,LevelTracker定义了实例方法advanceToLevel。这个方法会在更新currentLevel之前检查所请求的新等级是否已经解锁。advanceToLevel方法返回布尔值以指示是否能够设置currentLevel。下面,
-Player类使用LevelTracker来监测和更新每个玩家的发展进度:+class Player { +class Player { var tracker = LevelTracker() let playerName: String func completedLevel(level: Int) { @@ -786,22 +797,24 @@ SomeClass.someTypeMethod() playerName = name } } -+
Player类创建一个新的LevelTracker实例来监测这个用户的发展进度。他提供了completedLevel方法:一旦玩家完成某个指定等级就调用它。这个方法为所有玩家解锁下一等级,并且将当前玩家的进度更新为下一等级。(我们忽略了advanceToLevel返回的布尔值,因为之前调用LevelTracker.unlockLevel时就知道了这个等级已经被解锁了)。
Player类创建一个新的LevelTracker实例来监测这个用户的发展进度。它提供了completedLevel方法:一旦玩家完成某个指定等级就调用它。这个方法为所有玩家解锁下一等级,并且将当前玩家的进度更新为下一等级。(我们忽略了advanceToLevel返回的布尔值,因为之前调用LevelTracker.unlockLevel时就知道了这个等级已经被解锁了)。你还可以为一个新的玩家创建一个
-Player的实例,然后看这个玩家完成等级一时发生了什么:var player = Player(name: "Argyrios") +var player = Player(name: "Argyrios") player.completedLevel(1) println("highest unlocked level is now \(LevelTracker.highestUnlockedLevel)") -// 输出 "highest unlocked level is now 2"(最高等级现在是2 -) -如果你创建了第二个玩家,并尝试让他开始一个没有被任何玩家解锁的等级,那么这次设置玩家当前等级的尝试将会失败:
-+player = Player(name: "Beto") +// 输出 "highest unlocked level is now 2"(最高等级现在是2) +如果你创建了第二个玩家,并尝试让它开始一个没有被任何玩家解锁的等级,那么这次设置玩家当前等级的尝试将会失败:
++player = Player(name: "Beto") if player.tracker.advanceToLevel(6) { -println("player is now on level 6") + println("player is now on level 6") } else { -println("level 6 has not yet been unlocked") + println("level 6 has not yet been unlocked") } // 输出 "level 6 has not yet been unlocked"(等级6还没被解锁)+@@ -268,7 +268,7 @@- 2.12. 附属脚本 + 2.12. 下标脚本 @@ -520,7 +520,7 @@ - + @@ -587,82 +587,132 @@ -+ diff --git a/chapter2/13_Inheritance.html b/chapter2/13_Inheritance.html old mode 100755 new mode 100644 index 825a2c5d..84997154 --- a/chapter2/13_Inheritance.html +++ b/chapter2/13_Inheritance.html @@ -46,7 +46,7 @@ - --翻译:siemenliu
-校对:zq54zquan
+翻译:siemenliu
校对:zq54zquan附属脚本(Subscripts)
+下标脚本(Subscripts)
本页包含内容:
-附属脚本 可以定义在类(Class)、结构体(structure)和枚举(enumeration)这些目标中,可以认为是访问对象、集合或序列的快捷方式,不需要再调用实例的特定的赋值和访问方法。举例来说,用附属脚本访问一个数组(Array)实例中的元素可以这样写
-someArray[index],访问字典(Dictionary)实例中的元素可以这样写someDictionary[key]。对于同一个目标可以定义多个附属脚本,通过索引值类型的不同来进行重载,而且索引值的个数可以是多个。
+下标脚本 可以定义在类(Class)、结构体(structure)和枚举(enumeration)这些目标中,可以认为是访问对象、集合或序列的快捷方式,不需要再调用实例的特定的赋值和访问方法。举例来说,用下标脚本访问一个数组(Array)实例中的元素可以这样写
+someArray[index],访问字典(Dictionary)实例中的元素可以这样写someDictionary[key]。对于同一个目标可以定义多个下标脚本,通过索引值类型的不同来进行重载,而且索引值的个数可以是多个。
+<<<<<<< HEAD
+> 译者:这里附属脚本重载在本小节中原文并没有任何演示
--译者:这里附属脚本重载在本小节中原文并没有任何演示
+译者:这里下标脚本重载在本小节中原文并没有任何演示
++++++++++++a516af6a531a104ec88da0d236ecf389a5ec72af
+附属脚本语法
-附属脚本允许你通过在实例后面的方括号中传入一个或者多个的索引值来对实例进行访问和赋值。语法类似于实例方法和计算型属性的混合。与定义实例方法类似,定义附属脚本使用
-subscript关键字,显式声明入参(一个或多个)和返回类型。与实例方法不同的是附属脚本可以设定为读写或只读。这种方式又有点像计算型属性的getter和setter:+subscript(index: Int) -> Int { +下标脚本语法
+下标脚本允许你通过在实例后面的方括号中传入一个或者多个的索引值来对实例进行访问和赋值。语法类似于实例方法和计算型属性的混合。与定义实例方法类似,定义下标脚本使用
+subscript关键字,显式声明入参(一个或多个)和返回类型。与实例方法不同的是下标脚本可以设定为读写或只读。这种方式又有点像计算型属性的getter和setter:subscript(index: Int) -> Int { get { - // 返回与入参匹配的Int类型的值 + // 返回与入参匹配的Int类型的值 } set(newValue) { - // 执行赋值操作 + // 执行赋值操作 } } -+
newValue的类型必须和附属脚本定义的返回类型相同。与计算型属性相同的是set的入参声明newValue就算不写,在set代码块中依然可以使用默认的newValue这个变量来访问新赋的值。
newValue的类型必须和下标脚本定义的返回类型相同。与计算型属性相同的是set的入参声明newValue就算不写,在set代码块中依然可以使用默认的newValue这个变量来访问新赋的值。与只读计算型属性一样,可以直接将原本应该写在
-get代码块中的代码写在subscript中:+subscript(index: Int) -> Int { +subscript(index: Int) -> Int { // 返回与入参匹配的Int类型的值 } -下面代码演示了一个在
-TimesTable结构体中使用只读附属脚本的用法,该结构体用来展示传入整数的n倍。+struct TimesTable { +下面代码演示了一个在
+TimesTable结构体中使用只读下标脚本的用法,该结构体用来展示传入整数的n倍。struct TimesTable { let multiplier: Int subscript(index: Int) -> Int { - return multiplier * index + return multiplier * index } } let threeTimesTable = TimesTable(multiplier: 3) println("3的6倍是\(threeTimesTable[6])") // 输出 "3的6倍是18" -在上例中,通过
-TimesTable结构体创建了一个用来表示索引值三倍的实例。数值3作为结构体构造函数入参初始化实例成员multiplier。你可以通过附属脚本来得到结果,比如
+threeTimesTable[6]。这条语句访问了threeTimesTable的第六个元素,返回6的3倍即18。在上例中,通过
+TimesTable结构体创建了一个用来表示索引值三倍的实例。数值3作为结构体构造函数入参初始化实例成员multiplier。你可以通过下标脚本来得到结果,比如
+threeTimesTable[6]。这条语句访问了threeTimesTable的第六个元素,返回6的3倍即18。<<<<<<< HEAD
+-注意:
+
TimesTable例子是基于一个固定的数学公式。它并不适合开放写权限来对threeTimesTable[someIndex]进行赋值操作,这也是为什么附属脚本只定义为只读的原因。注意:
-+
TimesTable例子是基于一个固定的数学公式。它并不适合开放写权限来对threeTimesTable[someIndex]进行赋值操作,这也是为什么附属脚本只定义为只读的原因。+
TimesTable例子是基于一个固定的数学公式。它并不适合开放写权限来对threeTimesTable[someIndex]进行赋值操作,这也是为什么下标脚本只定义为只读的原因。+++++++++++a516af6a531a104ec88da0d236ecf389a5ec72af
+附属脚本用法
-根据使用场景不同附属脚本也具有不同的含义。通常附属脚本是用来访问集合(collection),列表(list)或序列(sequence)中元素的快捷方式。你可以在你自己特定的类或结构体中自由的实现附属脚本来提供合适的功能。
-例如,Swift 的字典(Dictionary)实现了通过附属脚本来对其实例中存放的值进行存取操作。在附属脚本中使用和字典索引相同类型的值,并且把一个字典值类型的值赋值给这个附属脚本来为字典设值:
-+var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4] +下标脚本用法
+根据使用场景不同下标脚本也具有不同的含义。通常下标脚本是用来访问集合(collection),列表(list)或序列(sequence)中元素的快捷方式。你可以在你自己特定的类或结构体中自由的实现下标脚本来提供合适的功能。
+例如,Swift 的字典(Dictionary)实现了通过下标脚本来对其实例中存放的值进行存取操作。在下标脚本中使用和字典索引相同类型的值,并且把一个字典值类型的值赋值给这个下标脚本来为字典设值:
+var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4] numberOfLegs["bird"] = 2 -上例定义一个名为
-numberOfLegs的变量并用一个字典字面量初始化出了包含三对键值的字典实例。numberOfLegs的字典存放值类型推断为Dictionary<String, Int>。字典实例创建完成之后通过附属脚本的方式将整型值2赋值到字典实例的索引为bird的位置中。更多关于字典(Dictionary)附属脚本的信息请参考读取和修改字典
+上例定义一个名为
+numberOfLegs的变量并用一个字典字面量初始化出了包含三对键值的字典实例。numberOfLegs的字典存放值类型推断为Dictionary<String, Int>。字典实例创建完成之后通过下标脚本的方式将整型值2赋值到字典实例的索引为bird的位置中。更多关于字典(Dictionary)下标脚本的信息请参考读取和修改字典
+<<<<<<< HEAD
+-注意:
+Swift 中字典的附属脚本实现中,在
get部分返回值是Int?,上例中的numberOfLegs字典通过附属脚本返回的是一个Int?或者说“可选的int”,不是每个字典的索引都能得到一个整型值,对于没有设过值的索引的访问返回的结果就是nil;同样想要从字典实例中删除某个索引下的值也只需要给这个索引赋值为nil即可。注意:
-Swift 中字典的附属脚本实现中,在
+get部分返回值是Int?,上例中的numberOfLegs字典通过附属脚本返回的是一个Int?或者说“可选的int”,不是每个字典的索引都能得到一个整型值,对于没有设过值的索引的访问返回的结果就是nil;同样想要从字典实例中删除某个索引下的值也只需要给这个索引赋值为nil即可。Swift 中字典的下标脚本实现中,在
+get部分返回值是Int?,上例中的numberOfLegs字典通过下标脚本返回的是一个Int?或者说“可选的int”,不是每个字典的索引都能得到一个整型值,对于没有设过值的索引的访问返回的结果就是nil;同样想要从字典实例中删除某个索引下的值也只需要给这个索引赋值为nil即可。+++++++++++a516af6a531a104ec88da0d236ecf389a5ec72af
+附属脚本选项
-附属脚本允许任意数量的入参索引,并且每个入参类型也没有限制。附属脚本的返回值也可以是任何类型。附属脚本可以使用变量参数和可变参数,但使用写入读出(in-out)参数或给参数设置默认值都是不允许的。
-一个类或结构体可以根据自身需要提供多个附属脚本实现,在定义附属脚本时通过入参个类型进行区分,使用附属脚本时会自动匹配合适的附属脚本实现运行,这就是附属脚本的重载。
-一个附属脚本入参是最常见的情况,但只要有合适的场景也可以定义多个附属脚本入参。如下例定义了一个
-Matrix结构体,将呈现一个Double类型的二维矩阵。Matrix结构体的附属脚本需要两个整型参数:+struct Matrix { +下标脚本选项
+下标脚本允许任意数量的入参索引,并且每个入参类型也没有限制。下标脚本的返回值也可以是任何类型。下标脚本可以使用变量参数和可变参数,但使用写入读出(in-out)参数或给参数设置默认值都是不允许的。
+一个类或结构体可以根据自身需要提供多个下标脚本实现,在定义下标脚本时通过入参个类型进行区分,使用下标脚本时会自动匹配合适的下标脚本实现运行,这就是下标脚本的重载。
+一个下标脚本入参是最常见的情况,但只要有合适的场景也可以定义多个下标脚本入参。如下例定义了一个
+Matrix结构体,将呈现一个Double类型的二维矩阵。Matrix结构体的下标脚本需要两个整型参数:struct Matrix { let rows: Int, columns: Int var grid: Double[] init(rows: Int, columns: Int) { - self.rows = rows - self.columns = columns - grid = Array(count: rows * columns, repeatedValue: 0.0) + self.rows = rows + self.columns = columns + grid = Array(count: rows * columns, repeatedValue: 0.0) } func indexIsValidForRow(row: Int, column: Int) -> Bool { return row >= 0 && row < rows && column >= 0 && column < columns @@ -678,30 +728,37 @@ numberOfLegs["bird"] = 2 } } } -+
Matrix提供了一个两个入参的构造方法,入参分别是rows和columns,创建了一个足够容纳rows * columns个数的Double类型数组。为了存储,将数组的大小和数组每个元素初始值0.0,都传入数组的构造方法中来创建一个正确大小的新数组。关于数组的构造方法和析构方法请参考创建并且构造一个数组。
Matrix提供了一个两个入参的构造方法,入参分别是rows和columns,创建了一个足够容纳rows * columns个数的Double类型数组。为了存储,将数组的大小和数组每个元素初始值0.0,都传入数组的构造方法中来创建一个正确大小的新数组。关于数组的构造方法和析构方法请参考创建并且构造一个数组。你可以通过传入合适的
-row和column的数量来构造一个新的Matrix实例:var matrix = Matrix(rows: 2, columns: 2) -上例中创建了一个新的两行两列的
-Matrix实例。在阅读顺序从左上到右下的Matrix实例中的数组实例grid是矩阵二维数组的扁平化存储:// 示意图 ++var matrix = Matrix(rows: 2, columns: 2) +上例中创建了一个新的两行两列的
+Matrix实例。在阅读顺序从左上到右下的Matrix实例中的数组实例grid是矩阵二维数组的扁平化存储:// 示意图 grid = [0.0, 0.0, 0.0, 0.0] - col0 col1 + col0 col1 row0 [0.0, 0.0, -row1 0.0, 0.0] -将值赋给带有
-row和column附属脚本的matrix实例表达式可以完成赋值操作,附属脚本入参使用逗号分割+matrix[0, 1] = 1.5 +row1 0.0, 0.0] +将值赋给带有
+row和column下标脚本的matrix实例表达式可以完成赋值操作,下标脚本入参使用逗号分割matrix[0, 1] = 1.5 matrix[1, 0] = 3.2 -上面两条语句分别
-让matrix的右上值为 1.5,坐下值为 3.2:+[0.0, 1.5, +上面两条语句分别
+让matrix的右上值为 1.5,坐下值为 3.2:[0.0, 1.5, 3.2, 0.0] --
Matrix附属脚本的getter和setter中同时调用了附属脚本入参的row和column是否有效的判断。为了方便进行断言,Matrix包含了一个名为indexIsValid的成员方法,用来确认入参的row或column值是否会造成数组越界:+func indexIsValidForRow(row: Int, column: Int) -> Bool { ++
Matrix下标脚本的getter和setter中同时调用了下标脚本入参的row和column是否有效的判断。为了方便进行断言,Matrix包含了一个名为indexIsValid的成员方法,用来确认入参的row或column值是否会造成数组越界:func indexIsValidForRow(row: Int, column: Int) -> Bool { return row >= 0 && row < rows && column >= 0 && column < columns } -断言在附属脚本越界时触发:
-+let someValue = matrix[2, 2] +断言在下标脚本越界时触发:
++let someValue = matrix[2, 2] // 断言将会触发,因为 [2, 2] 已经超过了matrix的最大长度+@@ -268,7 +268,7 @@- 2.12. 附属脚本 + 2.12. 下标脚本 @@ -520,7 +520,7 @@ - + @@ -587,11 +587,10 @@ -- + diff --git a/chapter2/14_Initialization.html b/chapter2/14_Initialization.html old mode 100755 new mode 100644 index b718c296..26a7fd41 --- a/chapter2/14_Initialization.html +++ b/chapter2/14_Initialization.html @@ -46,7 +46,7 @@ -+ -翻译:Hawstein
-校对:menlongsheng
+翻译:Hawstein
校对:menlongsheng继承(Inheritance)
@@ -603,17 +602,16 @@防止重写 一个类可以继承(inherit)另一个类的方法(methods),属性(property)和其它特性。当一个类继承其它类时,继承类叫子类(subclass),被继承类叫超类(或父类,superclass)。在 Swift 中,继承是区分「类」与其它类型的一个基本特征。
-在 Swift 中,类可以调用和访问超类的方法,属性和附属脚本(subscripts),并且可以重写(override)这些方法,属性和附属脚本来优化或修改它们的行为。Swift 会检查你的重写定义在超类中是否有匹配的定义,以此确保你的重写行为是正确的。
+在 Swift 中,类可以调用和访问超类的方法,属性和下标脚本(subscripts),并且可以重写(override)这些方法,属性和下标脚本来优化或修改它们的行为。Swift 会检查你的重写定义在超类中是否有匹配的定义,以此确保你的重写行为是正确的。
可以为类中继承来的属性添加属性观察器(property observer),这样一来,当属性值改变时,类就会被通知到。可以为任何属性添加属性观察器,无论它原本被定义为存储型属性(stored property)还是计算型属性(computed property)。
定义一个基类(Base class)
不继承于其它类的类,称之为基类(base calss)。
-注意:
-Swift 中的类并不是从一个通用的基类继承而来。如果你不为你定义的类指定一个超类的话,这个类就自动成为基类。
+注意:
Swift 中的类并不是从一个通用的基类继承而来。如果你不为你定义的类指定一个超类的话,这个类就自动成为基类。下面的例子定义了一个叫
-Vehicle的基类。这个基类声明了两个对所有车辆都通用的属性(numberOfWheels和maxPassengers)。这些属性在description方法中使用,这个方法返回一个String类型的,对车辆特征的描述:+class Vehicle { +class Vehicle { var numberOfWheels: Int var maxPassengers: Int func description() -> String { @@ -624,77 +622,84 @@ maxPassengers = 1 } } -+
Vehicle类定义了构造器(initializer)来设置属性的值。构造器会在构造过程一节中详细介绍,这里我们做一下简单介绍,以便于讲解子类中继承来的属性如何被修改。
Vehicle类定义了构造器(initializer)来设置属性的值。构造器会在构造过程一节中详细介绍,这里我们做一下简单介绍,以便于讲解子类中继承来的属性如何被修改。构造器用于创建某个类型的一个新实例。尽管构造器并不是方法,但在语法上,两者很相似。构造器的工作是准备新实例以供使用,并确保实例中的所有属性都拥有有效的初始化值。
构造器的最简单形式就像一个没有参数的实例方法,使用
-init关键字:+init() { +init() { // 执行构造过程 } -如果要创建一个
-Vehicle类的新实例,使用构造器语法调用上面的初始化器,即类名后面跟一个空的小括号:let someVehicle = Vehicle() -这个
+Vehicle类的构造器为任意的一辆车设置一些初始化属性值(numberOfWheels = 0和maxPassengers = 1)。如果要创建一个
+Vehicle类的新实例,使用构造器语法调用上面的初始化器,即类名后面跟一个空的小括号:+let someVehicle = Vehicle() +这个
Vehicle类的构造器为任意的一辆车设置一些初始化属性值(numberOfWheels = 0和maxPassengers = 1)。
Vehicle类定义了车辆的共同特性,但这个类本身并没太大用处。为了使它更为实用,你需要进一步细化它来描述更具体的车辆。子类生成(Subclassing)
子类生成(Subclassing)指的是在一个已有类的基础上创建一个新的类。子类继承超类的特性,并且可以优化或改变它。你还可以为子类添加新的特性。
为了指明某个类的超类,将超类名写在子类名的后面,用冒号分隔:
-+class SomeClass: SomeSuperclass { +class SomeClass: SomeSuperclass { // 类的定义 } -下一个例子,定义一个更具体的车辆类叫
+Bicycle。这个新类是在Vehicle类的基础上创建起来。因此你需要将Vehicle类放在Bicycle类后面,用冒号分隔。下一个例子,定义一个更具体的车辆类叫
Bicycle。这个新类是在Vehicle类的基础上创建起来。因此你需要将Vehicle类放在Bicycle类后面,用冒号分隔。我们可以将这读作:
“定义一个新的类叫
-Bicycle,它继承了Vehicle的特性”;+class Bicycle: Vehicle { +class Bicycle: Vehicle { init() { super.init() numberOfWheels = 2 } } -+
Bicycle是Vehicle的子类,Vehicle是Bicycle的超类。新的Bicycle类自动获得Vehicle类的特性,比如maxPassengers和numberOfWheels属性。你可以在子类中定制这些特性,或添加新的特性来更好地描述Bicycle类。preview +
Bicycle是Vehicle的子类,Vehicle是Bicycle的超类。新的Bicycle类自动获得Vehicle类的特性,比如maxPassengers和numberOfWheels属性。你可以在子类中定制这些特性,或添加新的特性来更好地描述Bicycle类。
Bicycle类定义了一个构造器来设置它定制的特性(自行车只有2个轮子)。Bicycle的构造器调用了它父类Vehicle的构造器super.init(),以此确保在Bicycle类试图修改那些继承来的属性前Vehicle类已经初始化过它们了。-注意:
-不像 Objective-C,在 Swift 中,初始化器默认是不继承的,见初始化器的继承与重写
+注意:
不像 Objective-C,在 Swift 中,初始化器默认是不继承的,见初始化器的继承与重写
Vehicle类中maxPassengers的默认值对自行车来说已经是正确的,因此在Bicycle的构造器中并没有改变它。而numberOfWheels原来的值对自行车来说是不正确的,因此在初始化器中将它更改为 2。-
Bicycle不仅可以继承Vehicle的属性,还可以继承它的方法。如果你创建了一个Bicycle类的实例,你就可以调用它继承来的description方法,并且可以看到,它输出的属性值已经发生了变化:+let bicycle = Bicycle() +let bicycle = Bicycle() println("Bicycle: \(bicycle.description())") // Bicycle: 2 wheels; up to 1 passengers -子类还可以继续被其它类继承:
-+class Tandem: Bicycle { +子类还可以继续被其它类继承:
+class Tandem: Bicycle { init() { super.init() maxPassengers = 2 } } -上面的例子创建了
+Bicycle的一个子类:双人自行车(tandem)。Tandem从Bicycle继承了两个属性,而这两个属性是Bicycle从Vehicle继承而来的。Tandem并不修改轮子的数量,因为它仍是一辆自行车,有 2 个轮子。但它需要修改maxPassengers的值,因为双人自行车可以坐两个人。上面的例子创建了
Bicycle的一个子类:双人自行车(tandem)。Tandem从Bicycle继承了两个属性,而这两个属性是Bicycle从Vehicle继承而来的。Tandem并不修改轮子的数量,因为它仍是一辆自行车,有 2 个轮子。但它需要修改maxPassengers的值,因为双人自行车可以坐两个人。-注意:
-子类只允许修改从超类继承来的变量属性,而不能修改继承来的常量属性。
+注意:
子类只允许修改从超类继承来的变量属性,而不能修改继承来的常量属性。创建一个
-Tandem类的实例,打印它的描述,即可看到它的属性已被更新:+let tandem = Tandem() +let tandem = Tandem() println("Tandem: \(tandem.description())") // Tandem: 2 wheels; up to 2 passengers -注意,
+Tandem类也继承了description方法。一个类的实例方法会被这个类的所有子类继承。注意,
Tandem类也继承了description方法。一个类的实例方法会被这个类的所有子类继承。重写(Overriding)
-子类可以为继承来的实例方法(instance method),类方法(class method),实例属性(instance property),或附属脚本(subscript)提供自己定制的实现(implementation)。我们把这种行为叫重写(overriding)。
+子类可以为继承来的实例方法(instance method),类方法(class method),实例属性(instance property),或下标脚本(subscript)提供自己定制的实现(implementation)。我们把这种行为叫重写(overriding)。
如果要重写某个特性,你需要在重写定义的前面加上
override关键字。这么做,你就表明了你是想提供一个重写版本,而非错误地提供了一个相同的定义。意外的重写行为可能会导致不可预知的错误,任何缺少override关键字的重写都会在编译时被诊断为错误。-
override关键字会提醒 Swift 编译器去检查该类的超类(或其中一个父类)是否有匹配重写版本的声明。这个检查可以确保你的重写定义是正确的。访问超类的方法,属性及附属脚本
-当你在子类中重写超类的方法,属性或附属脚本时,有时在你的重写版本中使用已经存在的超类实现会大有裨益。比如,你可以优化已有实现的行为,或在一个继承来的变量中存储一个修改过的值。
-在合适的地方,你可以通过使用
+super前缀来访问超类版本的方法,属性或附属脚本:访问超类的方法,属性及下标脚本
+当你在子类中重写超类的方法,属性或下标脚本时,有时在你的重写版本中使用已经存在的超类实现会大有裨益。比如,你可以优化已有实现的行为,或在一个继承来的变量中存储一个修改过的值。
+在合适的地方,你可以通过使用
super前缀来访问超类版本的方法,属性或下标脚本:
- 在方法
someMethod的重写实现中,可以通过super.someMethod()来调用超类版本的someMethod方法。- 在属性
-someProperty的 getter 或 setter 的重写实现中,可以通过super.someProperty来访问超类版本的someProperty属性。- 在附属脚本的重写实现中,可以通过
+super[someIndex]来访问超类版本中的相同附属脚本。- 在下标脚本的重写实现中,可以通过
super[someIndex]来访问超类版本中的相同下标脚本。重写方法
在子类中,你可以重写继承来的实例方法或类方法,提供一个定制或替代的方法实现。
下面的例子定义了
-Vehicle的一个新的子类,叫Car,它重写了从Vehicle类继承来的description方法:+class Car: Vehicle { +class Car: Vehicle { var speed: Double = 0.0 init() { super.init() @@ -706,24 +711,25 @@ println("Tandem: \(tandem.description())") + "traveling at \(speed) mph" } } -+
Car声明了一个新的存储型属性speed,它是Double类型的,默认值是0.0,表示“时速是0英里”。Car有自己的初始化器,它将乘客的最大数量设为5,轮子数量设为4。
Car声明了一个新的存储型属性speed,它是Double类型的,默认值是0.0,表示“时速是0英里”。Car有自己的初始化器,它将乘客的最大数量设为5,轮子数量设为4。
Car重写了继承来的description方法,它的声明与Vehicle中的description方法一致,声明前面加上了override关键字。
Car中的description方法并非完全自定义,而是通过super.description使用了超类Vehicle中的description方法,然后再追加一些额外的信息,比如汽车的当前速度。如果你创建一个
-Car的新实例,并打印description方法的输出,你就会发现描述信息已经发生了改变:+let car = Car() +let car = Car() println("Car: \(car.description())") // Car: 4 wheels; up to 5 passengers; traveling at 0.0 mph -重写属性
+重写属性
你可以重写继承来的实例属性或类属性,提供自己定制的getter和setter,或添加属性观察器使重写的属性观察属性值什么时候发生改变。
重写属性的Getters和Setters
你可以提供定制的 getter(或 setter)来重写任意继承来的属性,无论继承来的属性是存储型的还是计算型的属性。子类并不知道继承来的属性是存储型的还是计算型的,它只知道继承来的属性会有一个名字和类型。你在重写一个属性时,必需将它的名字和类型都写出来。这样才能使编译器去检查你重写的属性是与超类中同名同类型的属性相匹配的。
你可以将一个继承来的只读属性重写为一个读写属性,只需要你在重写版本的属性里提供 getter 和 setter 即可。但是,你不可以将一个继承来的读写属性重写为一个只读属性。
-注意:
-如果你在重写属性中提供了 setter,那么你也一定要提供 getter。如果你不想在重写版本中的 getter 里修改继承来的属性值,你可以直接返回
+super.someProperty来返回继承来的值。正如下面的SpeedLimitedCar的例子所示。注意:
如果你在重写属性中提供了 setter,那么你也一定要提供 getter。如果你不想在重写版本中的 getter 里修改继承来的属性值,你可以直接返回super.someProperty来返回继承来的值。正如下面的SpeedLimitedCar的例子所示。以下的例子定义了一个新类,叫
-SpeedLimitedCar,它是Car的子类。类SpeedLimitedCar表示安装了限速装置的车,它的最高速度只能达到40mph。你可以通过重写继承来的speed属性来实现这个速度限制:+class SpeedLimitedCar: Car { +class SpeedLimitedCar: Car { override var speed: Double { get { return super.speed @@ -733,20 +739,21 @@ println("Car: \(car.description())") } } } -当你设置一个
+SpeedLimitedCar实例的speed属性时,属性setter的实现会去检查新值与限制值40mph的大小,它会将超类的speed设置为newValue和40.0中较小的那个。这两个值哪个较小由min函数决定,它是Swift标准库中的一个全局函数。min函数接收两个或更多的数,返回其中最小的那个。当你设置一个
SpeedLimitedCar实例的speed属性时,属性setter的实现会去检查新值与限制值40mph的大小,它会将超类的speed设置为newValue和40.0中较小的那个。这两个值哪个较小由min函数决定,它是Swift标准库中的一个全局函数。min函数接收两个或更多的数,返回其中最小的那个。如果你尝试将
-SpeedLimitedCar实例的speed属性设置为一个大于40mph的数,然后打印description函数的输出,你会发现速度被限制在40mph:+let limitedCar = SpeedLimitedCar() +let limitedCar = SpeedLimitedCar() limitedCar.speed = 60.0 println("SpeedLimitedCar: \(limitedCar.description())") // SpeedLimitedCar: 4 wheels; up to 5 passengers; traveling at 40.0 mph -重写属性观察器(Property Observer)
+重写属性观察器(Property Observer)
你可以在属性重写中为一个继承来的属性添加属性观察器。这样一来,当继承来的属性值发生改变时,你就会被通知到,无论那个属性原本是如何实现的。关于属性观察器的更多内容,请看属性观察器。
-注意:
-你不可以为继承来的常量存储型属性或继承来的只读计算型属性添加属性观察器。这些属性的值是不可以被设置的,所以,为它们提供
+willSet或didSet实现是不恰当。此外还要注意,你不可以同时提供重写的 setter 和重写的属性观察器。如果你想观察属性值的变化,并且你已经为那个属性提供了定制的 setter,那么你在 setter 中就可以观察到任何值变化了。注意:
你不可以为继承来的常量存储型属性或继承来的只读计算型属性添加属性观察器。这些属性的值是不可以被设置的,所以,为它们提供willSet或didSet实现是不恰当。此外还要注意,你不可以同时提供重写的 setter 和重写的属性观察器。如果你想观察属性值的变化,并且你已经为那个属性提供了定制的 setter,那么你在 setter 中就可以观察到任何值变化了。下面的例子定义了一个新类叫
-AutomaticCar,它是Car的子类。AutomaticCar表示自动挡汽车,它可以根据当前的速度自动选择合适的挡位。AutomaticCar也提供了定制的description方法,可以输出当前挡位。+class AutomaticCar: Car { +class AutomaticCar: Car { var gear = 1 override var speed: Double { didSet { @@ -757,15 +764,17 @@ println("SpeedLimitedCar: \(limitedCar.description())") return super.description() + " in gear \(gear)" } } -当你设置
-AutomaticCar的speed属性,属性的didSet观察器就会自动地设置gear属性,为新的速度选择一个合适的挡位。具体来说就是,属性观察器将新的速度值除以10,然后向下取得最接近的整数值,最后加1来得到档位gear的值。例如,速度为10.0时,挡位为1;速度为35.0时,挡位为4:+let automatic = AutomaticCar() +当你设置
+AutomaticCar的speed属性,属性的didSet观察器就会自动地设置gear属性,为新的速度选择一个合适的挡位。具体来说就是,属性观察器将新的速度值除以10,然后向下取得最接近的整数值,最后加1来得到档位gear的值。例如,速度为10.0时,挡位为1;速度为35.0时,挡位为4:+let automatic = AutomaticCar() automatic.speed = 35.0 println("AutomaticCar: \(automatic.description())") // AutomaticCar: 4 wheels; up to 5 passengers; traveling at 35.0 mph in gear 4 -防止重写
-你可以通过把方法,属性或附属脚本标记为
-final来防止它们被重写,只需要在声明关键字前加上@final特性即可。(例如:@final var,@final func,@final class func, 以及@final subscript)如果你重写了
+final方法,属性或附属脚本,在编译时会报错。在扩展中,你添加到类里的方法,属性或附属脚本也可以在扩展的定义里标记为 final。你可以通过把方法,属性或下标脚本标记为
+final来防止它们被重写,只需要在声明关键字前加上@final特性即可。(例如:@final var,@final func,@final class func, 以及@final subscript)如果你重写了
final方法,属性或下标脚本,在编译时会报错。在扩展中,你添加到类里的方法,属性或下标脚本也可以在扩展的定义里标记为 final。你可以通过在关键字
@@ -776,7 +785,7 @@ println("AutomaticCar: \(automatic.description())")class前添加@final特性(@final class)来将整个类标记为 final 的,这样的类是不可被继承的,否则会报编译错误。+@@ -268,7 +268,7 @@- 2.12. 附属脚本 + 2.12. 下标脚本 @@ -520,7 +520,7 @@ - + @@ -587,11 +587,10 @@ -+ diff --git a/chapter2/15_Deinitialization.html b/chapter2/15_Deinitialization.html old mode 100755 new mode 100644 index 70a0339d..db3f201b --- a/chapter2/15_Deinitialization.html +++ b/chapter2/15_Deinitialization.html @@ -46,7 +46,7 @@ - -翻译:lifedim
-校对:lifedim
+翻译:lifedim
校对:lifedim构造过程(Initialization)
@@ -612,40 +611,40 @@类和结构体在实例创建时,必须为所有存储型属性设置合适的初始值。存储型属性的值不能处于一个未知的状态。
你可以在构造器中为存储型属性赋初值,也可以在定义属性时为其设置默认值。以下章节将详细介绍这两种方法。
-注意:
-当你为存储型属性设置默认值或者在构造器中为其赋值时,它们的值是被直接设置的,不会触发任何属性观测器(
+property observers)。注意:
当你为存储型属性设置默认值或者在构造器中为其赋值时,它们的值是被直接设置的,不会触发任何属性观测器(property observers)。构造器
构造器在创建某特定类型的新实例时调用。它的最简形式类似于一个不带任何参数的实例方法,以关键字
init命名。下面例子中定义了一个用来保存华氏温度的结构体
-Fahrenheit,它拥有一个Double类型的存储型属性temperature:+struct Fahrenheit { ++struct Fahrenheit { var temperature: Double init() { temperature = 32.0 } } - -var f = Fahrenheit() +var f = Fahrenheit() println("The default temperature is \(f.temperature)° Fahrenheit") // 输出 "The default temperature is 32.0° Fahrenheit” -这个结构体定义了一个不带参数的构造器
+init,并在里面将存储型属性temperature的值初始化为32.0(华摄氏度下水的冰点)。这个结构体定义了一个不带参数的构造器
init,并在里面将存储型属性temperature的值初始化为32.0(华摄氏度下水的冰点)。默认属性值
如前所述,你可以在构造器中为存储型属性设置初始值;同样,你也可以在属性声明时为其设置默认值。
-注意:
-如果一个属性总是使用同一个初始值,可以为其设置一个默认值。无论定义默认值还是在构造器中赋值,最终它们实现的效果是一样的,只不过默认值跟属性构造过程结合的更紧密。使用默认值能让你的构造器更简洁、更清晰,且能通过默认值自动推导出属性的类型;同时,它也能让你充分利用默认构造器、构造器继承(后续章节将讲到)等特性。
+注意:
如果一个属性总是使用同一个初始值,可以为其设置一个默认值。无论定义默认值还是在构造器中赋值,最终它们实现的效果是一样的,只不过默认值跟属性构造过程结合的更紧密。使用默认值能让你的构造器更简洁、更清晰,且能通过默认值自动推导出属性的类型;同时,它也能让你充分利用默认构造器、构造器继承(后续章节将讲到)等特性。你可以使用更简单的方式在定义结构体
-Fahrenheit时为属性temperature设置默认值:+struct Fahrenheit { ++struct Fahrenheit { var temperature = 32.0 } -定制化构造过程
你可以通过输入参数和可选属性类型来定制构造过程,也可以在构造过程中修改常量属性。这些都将在后面章节中提到。
构造参数
你可以在定义构造器时提供构造参数,为其提供定制化构造所需值的类型和名字。构造器参数的功能和语法跟函数和方法参数相同。
下面例子中定义了一个包含摄氏度温度的结构体
-Celsius。它定义了两个不同的构造器:init(fromFahrenheit:)和init(fromKelvin:),二者分别通过接受不同刻度表示的温度值来创建新的实例:+struct Celsius { ++struct Celsius { var temperatureInCelsius: Double = 0.0 init(fromFahrenheit fahrenheit: Double) { temperatureInCelsius = (fahrenheit - 32.0) / 1.8 @@ -654,22 +653,22 @@ println("The default temperature is \(f.temperature)° Fahrenheit") temperatureInCelsius = kelvin - 273.15 } } - -let boilingPointOfWater = Celsius(fromFahrenheit: 212.0) +let boilingPointOfWater = Celsius(fromFahrenheit: 212.0) // boilingPointOfWater.temperatureInCelsius 是 100.0 let freezingPointOfWater = Celsius(fromKelvin: 273.15) // freezingPointOfWater.temperatureInCelsius 是 0.0” -第一个构造器拥有一个构造参数,其外部名字为
+fromFahrenheit,内部名字为fahrenheit;第二个构造器也拥有一个构造参数,其外部名字为fromKelvin,内部名字为kelvin。这两个构造器都将唯一的参数值转换成摄氏温度值,并保存在属性temperatureInCelsius中。第一个构造器拥有一个构造参数,其外部名字为
fromFahrenheit,内部名字为fahrenheit;第二个构造器也拥有一个构造参数,其外部名字为fromKelvin,内部名字为kelvin。这两个构造器都将唯一的参数值转换成摄氏温度值,并保存在属性temperatureInCelsius中。内部和外部参数名
跟函数和方法参数相同,构造参数也存在一个在构造器内部使用的参数名字和一个在调用构造器时使用的外部参数名字。
然而,构造器并不像函数和方法那样在括号前有一个可辨别的名字。所以在调用构造器时,主要通过构造器中的参数名和类型来确定需要调用的构造器。正因为参数如此重要,如果你在定义构造器时没有提供参数的外部名字,Swift 会为每个构造器的参数自动生成一个跟内部名字相同的外部名,就相当于在每个构造参数之前加了一个哈希符号。
-注意:
-如果你不希望为构造器的某个参数提供外部名字,你可以使用下划线
+_来显示描述它的外部名,以此覆盖上面所说的默认行为。注意:
如果你不希望为构造器的某个参数提供外部名字,你可以使用下划线_来显示描述它的外部名,以此覆盖上面所说的默认行为。以下例子中定义了一个结构体
Color,它包含了三个常量:red、green和blue。这些属性可以存储0.0到1.0之间的值,用来指示颜色中红、绿、蓝成分的含量。-
Color提供了一个构造器,其中包含三个Double类型的构造参数:+struct Color { +struct Color { let red = 0.0, green = 0.0, blue = 0.0 init(red: Double, green: Double, blue: Double) { self.red = red @@ -677,15 +676,18 @@ let freezingPointOfWater = Celsius(fromKelvin: 273.15) self.blue = blue } } -每当你创建一个新的
-Color实例,你都需要通过三种颜色的外部参数名来传值,并调用构造器。let magenta = Color(red: 1.0, green: 0.0, blue: 1.0) -注意,如果不通过外部参数名字传值,你是没法调用这个构造器的。只要构造器定义了某个外部参数名,你就必须使用它,忽略它将导致编译错误:
-+let veryGreen = Color(0.0, 1.0, 0.0) +每当你创建一个新的
+Color实例,你都需要通过三种颜色的外部参数名来传值,并调用构造器。+let magenta = Color(red: 1.0, green: 0.0, blue: 1.0) +注意,如果不通过外部参数名字传值,你是没法调用这个构造器的。只要构造器定义了某个外部参数名,你就必须使用它,忽略它将导致编译错误:
+let veryGreen = Color(0.0, 1.0, 0.0) // 报编译时错误,需要外部名称 -可选属性类型
+可选属性类型
如果你定制的类型包含一个逻辑上允许取值为空的存储型属性--不管是因为它无法在初始化时赋值,还是因为它可以在之后某个时间点可以赋值为空--你都需要将它定义为可选类型
optional type。可选类型的属性将自动初始化为空nil,表示这个属性是故意在初始化时设置为空的。下面例子中定义了类
-SurveyQuestion,它包含一个可选字符串属性response:+class SurveyQuestion { +class SurveyQuestion { var text: String var response: String? init(text: String) { @@ -699,15 +701,15 @@ let cheeseQuestion = SurveyQuestion(text: "Do you like cheese?") cheeseQuestion.ask() // 输出 "Do you like cheese?" cheeseQuestion.response = "Yes, I do like cheese. -调查问题在问题提出之后,我们才能得到回答。所以我们将属性回答
+response声明为String?类型,或者说是可选字符串类型optional String。当SurveyQuestion实例化时,它将自动赋值为空nil,表明暂时还不存在此字符串。调查问题在问题提出之后,我们才能得到回答。所以我们将属性回答
response声明为String?类型,或者说是可选字符串类型optional String。当SurveyQuestion实例化时,它将自动赋值为空nil,表明暂时还不存在此字符串。构造过程中常量属性的修改
只要在构造过程结束前常量的值能确定,你可以在构造过程中的任意时间点修改常量属性的值。
-注意:
-对某个类实例来说,它的常量属性只能在定义它的类的构造过程中修改;不能在子类中修改。
+注意:
对某个类实例来说,它的常量属性只能在定义它的类的构造过程中修改;不能在子类中修改。你可以修改上面的
-SurveyQuestion示例,用常量属性替代变量属性text,指明问题内容text在其创建之后不会再被修改。尽管text属性现在是常量,我们仍然可以在其类的构造器中修改它的值:+class SurveyQuestion { ++class SurveyQuestion { let text: String var response: String? init(text: String) { @@ -721,45 +723,48 @@ let beetsQuestion = SurveyQuestion(text: "How about beets?") beetsQuestion.ask() // 输出 "How about beets?" beetsQuestion.response = "I also like beets. (But not with cheese.) -默认构造器
Swift 将为所有属性已提供默认值的且自身没有定义任何构造器的结构体或基类,提供一个默认的构造器。这个默认构造器将简单的创建一个所有属性值都设置为默认值的实例。
下面例子中创建了一个类
-ShoppingListItem,它封装了购物清单中的某一项的属性:名字(name)、数量(quantity)和购买状态purchase state。+class ShoppingListItem { +class ShoppingListItem { var name: String? var quantity = 1 var purchased = false } var item = ShoppingListItem() -由于
+ShoppingListItem类中的所有属性都有默认值,且它是没有父类的基类,它将自动获得一个可以为所有属性设置默认值的默认构造器(尽管代码中没有显式为name属性设置默认值,但由于name是可选字符串类型,它将默认设置为nil)。上面例子中使用默认构造器创造了一个ShoppingListItem类的实例(使用ShoppingListItem()形式的构造器语法),并将其赋值给变量item。由于
ShoppingListItem类中的所有属性都有默认值,且它是没有父类的基类,它将自动获得一个可以为所有属性设置默认值的默认构造器(尽管代码中没有显式为name属性设置默认值,但由于name是可选字符串类型,它将默认设置为nil)。上面例子中使用默认构造器创造了一个ShoppingListItem类的实例(使用ShoppingListItem()形式的构造器语法),并将其赋值给变量item。结构体的逐一成员构造器
除上面提到的默认构造器,如果结构体对所有存储型属性提供了默认值且自身没有提供定制的构造器,它们能自动获得一个逐一成员构造器。
逐一成员构造器是用来初始化结构体新实例里成员属性的快捷方法。我们在调用逐一成员构造器时,通过与成员属性名相同的参数名进行传值来完成对成员属性的初始赋值。
下面例子中定义了一个结构体
Size,它包含两个属性width和height。Swift 可以根据这两个属性的初始赋值0.0自动推导出它们的类型Double。由于这两个存储型属性都有默认值,结构体
-Size自动获得了一个逐一成员构造器init(width:height:)。 你可以用它来为Size创建新的实例:+struct Size { ++struct Size { var width = 0.0, height = 0.0 } let twoByTwo = Size(width: 2.0, height: 2.0) -值类型的构造器代理
构造器可以通过调用其它构造器来完成实例的部分构造过程。这一过程称为构造器代理,它能减少多个构造器间的代码重复。
构造器代理的实现规则和形式在值类型和类类型中有所不同。值类型(结构体和枚举类型)不支持继承,所以构造器代理的过程相对简单,因为它们只能代理任务给本身提供的其它构造器。类则不同,它可以继承自其它类(请参考继承),这意味着类有责任保证其所有继承的存储型属性在构造时也能正确的初始化。这些责任将在后续章节类的继承和构造过程中介绍。
对于值类型,你可以使用
self.init在自定义的构造器中引用其它的属于相同值类型的构造器。并且你只能在构造器内部调用self.init。注意,如果你为某个值类型定义了一个定制的构造器,你将无法访问到默认构造器(如果是结构体,则无法访问逐一对象构造器)。这个限制可以防止你在为值类型定义了一个更复杂的,完成了重要准备构造器之后,别人还是错误的使用了那个自动生成的构造器。
-注意:
-假如你想通过默认构造器、逐一对象构造器以及你自己定制的构造器为值类型创建实例,我们建议你将自己定制的构造器写到扩展(
+extension)中,而不是跟值类型定义混在一起。想查看更多内容,请查看扩展章节。注意:
假如你想通过默认构造器、逐一对象构造器以及你自己定制的构造器为值类型创建实例,我们建议你将自己定制的构造器写到扩展(extension)中,而不是跟值类型定义混在一起。想查看更多内容,请查看扩展章节。下面例子将定义一个结构体
-Rect,用来展现几何矩形。这个例子需要两个辅助的结构体Size和Point,它们各自为其所有的属性提供了初始值0.0。+struct Size { +struct Size { var width = 0.0, height = 0.0 } struct Point { var x = 0.0, y = 0.0 } -你可以通过以下三种方式为
-Rect创建实例--使用默认的0值来初始化origin和size属性;使用特定的origin和size实例来初始化;使用特定的center和size来初始化。在下面Rect结构体定义中,我们为着三种方式提供了三个自定义的构造器:+struct Rect { +你可以通过以下三种方式为
+Rect创建实例--使用默认的0值来初始化origin和size属性;使用特定的origin和size实例来初始化;使用特定的center和size来初始化。在下面Rect结构体定义中,我们为着三种方式提供了三个自定义的构造器:struct Rect { var origin = Point() var size = Size() init() {} @@ -773,21 +778,24 @@ struct Point { self.init(origin: Point(x: originX, y: originY), size: size) } } -第一个
-Rect构造器init(),在功能上跟没有自定义构造器时自动获得的默认构造器是一样的。这个构造器是一个空函数,使用一对大括号{}来描述,它没有执行任何定制的构造过程。调用这个构造器将返回一个Rect实例,它的origin和size属性都使用定义时的默认值Point(x: 0.0, y: 0.0)和Size(width: 0.0, height: 0.0):+let basicRect = Rect() +第一个
+Rect构造器init(),在功能上跟没有自定义构造器时自动获得的默认构造器是一样的。这个构造器是一个空函数,使用一对大括号{}来描述,它没有执行任何定制的构造过程。调用这个构造器将返回一个Rect实例,它的origin和size属性都使用定义时的默认值Point(x: 0.0, y: 0.0)和Size(width: 0.0, height: 0.0):let basicRect = Rect() // basicRect 的原点是 (0.0, 0.0),尺寸是 (0.0, 0.0) -第二个
-Rect构造器init(origin:size:),在功能上跟结构体在没有自定义构造器时获得的逐一成员构造器是一样的。这个构造器只是简单的将origin和size的参数值赋给对应的存储型属性:+let originRect = Rect(origin: Point(x: 2.0, y: 2.0), +第二个
+Rect构造器init(origin:size:),在功能上跟结构体在没有自定义构造器时获得的逐一成员构造器是一样的。这个构造器只是简单的将origin和size的参数值赋给对应的存储型属性:let originRect = Rect(origin: Point(x: 2.0, y: 2.0), size: Size(width: 5.0, height: 5.0)) // originRect 的原点是 (2.0, 2.0),尺寸是 (5.0, 5.0) -第三个
-Rect构造器init(center:size:)稍微复杂一点。它先通过center和size的值计算出origin的坐标。然后再调用(或代理给)init(origin:size:)构造器来将新的origin和size值赋值到对应的属性中:let centerRect = Rect(center: Point(x: 4.0, y: 4.0), - size: Size(width: 3.0, height: 3.0)) -// centerRect 的原点是 (2.5, 2.5),尺寸是 (3.0, 3.0)
+第三个
+Rect构造器init(center:size:)稍微复杂一点。它先通过center和size的值计算出origin的坐标。然后再调用(或代理给)init(origin:size:)构造器来将新的origin和size值赋值到对应的属性中:let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
++size: Size(width: 3.0, height: 3.0)) +// centerRect 的原点是 (2.5, 2.5),尺寸是 (3.0, 3.0)
构造器
init(center:size:)可以自己将origin和size的新值赋值到对应的属性中。然而尽量利用现有的构造器和它所提供的功能来实现init(center:size:)的功能,是更方便、更清晰和更直观的方法。-注意:
-如果你想用另外一种不需要自己定义
+init()和init(origin:size:)的方式来实现这个例子,请参考扩展。注意:
如果你想用另外一种不需要自己定义init()和init(origin:size:)的方式来实现这个例子,请参考扩展。类的继承和构造过程
@@ -817,8 +825,7 @@ struct Point {如图所示,父类中包含一个指定构造器和两个便利构造器。其中一个便利构造器调用了另外一个便利构造器,而后者又调用了唯一的指定构造器。这满足了上面提到的规则2和3。这个父类没有自己的父类,所以规则1没有用到。
子类中包含两个指定构造器和一个便利构造器。便利构造器必须调用两个指定构造器中的任意一个,因为它只能调用同一个类里的其他构造器。这满足了上面提到的规则2和3。而两个指定构造器必须调用父类中唯一的指定构造器,这满足了规则1。
-注意:
-这些规则不会影响使用时,如何用类去创建实例。任何上图中展示的构造器都可以用来完整创建对应类的实例。这些规则只在实现类的定义时有影响。
+注意:
这些规则不会影响使用时,如何用类去创建实例。任何上图中展示的构造器都可以用来完整创建对应类的实例。这些规则只在实现类的定义时有影响。下面图例中展示了一种更复杂的类层级结构。它演示了指定构造器是如果在类层级中充当“管道”的作用,在类的构造器链上简化了类之间的内部关系。
@@ -827,8 +834,7 @@ struct Point {
Swift 中类的构造过程包含两个阶段。第一个阶段,每个存储型属性通过引入它们的类的构造器来设置初始值。当每一个存储型属性值被确定后,第二阶段开始,它给每个类一次机会在新实例准备使用之前进一步定制它们的存储型属性。
两段式构造过程的使用让构造过程更安全,同时在整个类层级结构中给予了每个类完全的灵活性。两段式构造过程可以防止属性值在初始化之前被访问;也可以防止属性被另外一个构造器意外地赋予不同的值。
-注意:
-Swift的两段式构造过程跟 Objective-C 中的构造过程类似。最主要的区别在于阶段 1,Objective-C 给每一个属性赋值
+0或空值(比如说0或nil)。Swift 的构造流程则更加灵活,它允许你设置定制的初始值,并自如应对某些属性不能以0或nil作为合法默认值的情况。注意:
Swift的两段式构造过程跟 Objective-C 中的构造过程类似。最主要的区别在于阶段 1,Objective-C 给每一个属性赋值0或空值(比如说0或nil)。Swift 的构造流程则更加灵活,它允许你设置定制的初始值,并自如应对某些属性不能以0或nil作为合法默认值的情况。Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造过程能顺利完成:
安全检查 1
@@ -873,8 +879,7 @@ struct Point {如果你重载的构造器是一个指定构造器,你可以在子类里重载它的实现,并在自定义版本的构造器中调用父类版本的构造器。
如果你重载的构造器是一个便利构造器,你的重载过程必须通过调用同一类中提供的其它指定构造器来实现。这一规则的详细内容请参考构造器链。
-注意:
-与方法、属性和下标不同,在重载构造器时你没有必要使用关键字
+override。注意:
与方法、属性和下标不同,在重载构造器时你没有必要使用关键字override。自动构造器的继承
@@ -886,22 +891,23 @@ struct Point {如果子类提供了所有父类指定构造器的实现--不管是通过规则1继承过来的,还是通过自定义实现的--它将自动继承所有父类的便利构造器。
即使你在子类中添加了更多的便利构造器,这两条规则仍然适用。
-注意:
-子类可以通过部分满足规则2的方式,使用子类便利构造器来实现父类的指定构造器。
+注意:
子类可以通过部分满足规则2的方式,使用子类便利构造器来实现父类的指定构造器。指定构造器和便利构造器的语法
类的指定构造器的写法跟值类型简单构造器一样:
-+init(parameters) { +init(parameters) { statements } -便利构造器也采用相同样式的写法,但需要在
-init关键字之前放置convenience关键字,并使用空格将它们俩分开:+convenience init(parameters) { +便利构造器也采用相同样式的写法,但需要在
+init关键字之前放置convenience关键字,并使用空格将它们俩分开:convenience init(parameters) { statements } -指定构造器和便利构造器实战
+指定构造器和便利构造器实战
接下来的例子将在实战中展示指定构造器、便利构造器和自动构造器的继承。它定义了包含三个类
Food、RecipeIngredient以及ShoppingListItem的类层次结构,并将演示它们的构造器是如何相互作用的。类层次中的基类是
-Food,它是一个简单的用来封装食物名字的类。Food类引入了一个叫做name的String类型属性,并且提供了两个构造器来创建Food实例:+class Food { +class Food { var name: String init(name: String) { self.name = name @@ -910,17 +916,20 @@ struct Point { self.init(name: "[Unnamed]") } } -下图中展示了
+Food的构造器链:下图中展示了
Food的构造器链:
类没有提供一个默认的逐一成员构造器,所以
-Food类提供了一个接受单一参数name的指定构造器。这个构造器可以使用一个特定的名字来创建新的Food实例:+let namedMeat = Food(name: "Bacon") +let namedMeat = Food(name: "Bacon") // namedMeat 的名字是 "Bacon” -+
Food类中的构造器init(name: String)被定义为一个指定构造器,因为它能确保所有新Food实例的中存储型属性都被初始化。Food类没有父类,所以init(name: String)构造器不需要调用super.init()来完成构造。
Food类中的构造器init(name: String)被定义为一个指定构造器,因为它能确保所有新Food实例的中存储型属性都被初始化。Food类没有父类,所以init(name: String)构造器不需要调用super.init()来完成构造。-
Food类同样提供了一个没有参数的便利构造器init()。这个init()构造器为新食物提供了一个默认的占位名字,通过代理调用同一类中定义的指定构造器init(name: String)并给参数name传值[Unnamed]来实现:+let mysteryMeat = Food() +let mysteryMeat = Food() // mysteryMeat 的名字是 [Unnamed] -类层级中的第二个类是
-Food的子类RecipeIngredient。RecipeIngredient类构建了食谱中的一味调味剂。它引入了Int类型的数量属性quantity(以及从Food继承过来的name属性),并且定义了两个构造器来创建RecipeIngredient实例:+class RecipeIngredient: Food { +类层级中的第二个类是
+Food的子类RecipeIngredient。RecipeIngredient类构建了食谱中的一味调味剂。它引入了Int类型的数量属性quantity(以及从Food继承过来的name属性),并且定义了两个构造器来创建RecipeIngredient实例:class RecipeIngredient: Food { var quantity: Int init(name: String, quantity: Int) { self.quantity = quantity @@ -930,19 +939,21 @@ struct Point { self.init(name: name, quantity: 1) } } -下图中展示了
+RecipeIngredient类的构造器链:下图中展示了
RecipeIngredient类的构造器链:
RecipeIngredient类拥有一个指定构造器init(name: String, quantity: Int),它可以用来产生新RecipeIngredient实例的所有属性值。这个构造器一开始先将传入的quantity参数赋值给quantity属性,这个属性也是唯一在RecipeIngredient中新引入的属性。随后,构造器将任务向上代理给父类Food的init(name: String)。这个过程满足两段式构造过程中的安全检查1。
RecipeIngredient也定义了一个便利构造器init(name: String),它只通过name来创建RecipeIngredient的实例。这个便利构造器假设任意RecipeIngredient实例的quantity为1,所以不需要显示指明数量即可创建出实例。这个便利构造器的定义可以让创建实例更加方便和快捷,并且避免了使用重复的代码来创建多个quantity为 1 的RecipeIngredient实例。这个便利构造器只是简单的将任务代理给了同一类里提供的指定构造器。注意,
RecipeIngredient的便利构造器init(name: String)使用了跟Food中指定构造器init(name: String)相同的参数。尽管RecipeIngredient这个构造器是便利构造器,RecipeIngredient依然提供了对所有父类指定构造器的实现。因此,RecipeIngredient也能自动继承了所有父类的便利构造器。在这个例子中,
RecipeIngredient的父类是Food,它有一个便利构造器init()。这个构造器因此也被RecipeIngredient继承。这个继承的init()函数版本跟Food提供的版本是一样的,除了它是将任务代理给RecipeIngredient版本的init(name: String)而不是Food提供的版本。所有的这三种构造器都可以用来创建新的
-RecipeIngredient实例:+let oneMysteryItem = RecipeIngredient() +let oneMysteryItem = RecipeIngredient() let oneBacon = RecipeIngredient(name: "Bacon") let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6) -类层级中第三个也是最后一个类是
+RecipeIngredient的子类,叫做ShoppingListItem。这个类构建了购物单中出现的某一种调味料。类层级中第三个也是最后一个类是
RecipeIngredient的子类,叫做ShoppingListItem。这个类构建了购物单中出现的某一种调味料。购物单中的每一项总是从
-unpurchased未购买状态开始的。为了展现这一事实,ShoppingListItem引入了一个布尔类型的属性purchased,它的默认值是false。ShoppingListItem还添加了一个计算型属性description,它提供了关于ShoppingListItem实例的一些文字描述:+class ShoppingListItem: RecipeIngredient { +class ShoppingListItem: RecipeIngredient { var purchased = false var description: String { var output = "\(quantity) x \(name.lowercaseString)" @@ -950,15 +961,15 @@ let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6) return output } } --注意:
-+
ShoppingListItem没有定义构造器来为purchased提供初始化值,这是因为任何添加到购物单的项的初始状态总是未购买。+注意:
ShoppingListItem没有定义构造器来为purchased提供初始化值,这是因为任何添加到购物单的项的初始状态总是未购买。由于它为自己引入的所有属性都提供了默认值,并且自己没有定义任何构造器,
ShoppingListItem将自动继承所有父类中的指定构造器和便利构造器。下图种展示了所有三个类的构造器链:
你可以使用全部三个继承来的构造器来创建
-ShoppingListItem的新实例:+var breakfastList = [ +var breakfastList = [ ShoppingListItem(), ShoppingListItem(name: "Bacon"), ShoppingListItem(name: "Eggs", quantity: 6), @@ -971,29 +982,30 @@ for item in breakfastList { // 1 x orange juice ✔ // 1 x bacon ✘ // 6 x eggs ✘ -如上所述,例子中通过字面量方式创建了一个新数组
+breakfastList,它包含了三个新的ShoppingListItem实例,因此数组的类型也能自动推导为ShoppingListItem[]。在数组创建完之后,数组中第一个ShoppingListItem实例的名字从[Unnamed]修改为Orange juice,并标记为已购买。接下来通过遍历数组每个元素并打印它们的描述值,展示了所有项当前的默认状态都已按照预期完成了赋值。如上所述,例子中通过字面量方式创建了一个新数组
breakfastList,它包含了三个新的ShoppingListItem实例,因此数组的类型也能自动推导为ShoppingListItem[]。在数组创建完之后,数组中第一个ShoppingListItem实例的名字从[Unnamed]修改为Orange juice,并标记为已购买。接下来通过遍历数组每个元素并打印它们的描述值,展示了所有项当前的默认状态都已按照预期完成了赋值。通过闭包和函数来设置属性的默认值
如果某个存储型属性的默认值需要特别的定制或准备,你就可以使用闭包或全局函数来为其属性提供定制的默认值。每当某个属性所属的新类型实例创建时,对应的闭包或函数会被调用,而它们的返回值会当做默认值赋值给这个属性。
这种类型的闭包或函数一般会创建一个跟属性类型相同的临时变量,然后修改它的值以满足预期的初始状态,最后将这个临时变量的值作为属性的默认值进行返回。
下面列举了闭包如何提供默认值的代码概要:
-+class SomeClass { +class SomeClass { let someProperty: SomeType = { // 在这个闭包中给 someProperty 创建一个默认值 // someValue 必须和 SomeType 类型相同 return someValue }() } -注意闭包结尾的大括号后面接了一对空的小括号。这是用来告诉 Swift 需要立刻执行此闭包。如果你忽略了这对括号,相当于是将闭包本身作为值赋值给了属性,而不是将闭包的返回值赋值给属性。
+注意闭包结尾的大括号后面接了一对空的小括号。这是用来告诉 Swift 需要立刻执行此闭包。如果你忽略了这对括号,相当于是将闭包本身作为值赋值给了属性,而不是将闭包的返回值赋值给属性。
-注意:
-如果你使用闭包来初始化属性的值,请记住在闭包执行时,实例的其它部分都还没有初始化。这意味着你不能够在闭包里访问其它的属性,就算这个属性有默认值也不允许。同样,你也不能使用隐式的
+self属性,或者调用其它的实例方法。注意:
如果你使用闭包来初始化属性的值,请记住在闭包执行时,实例的其它部分都还没有初始化。这意味着你不能够在闭包里访问其它的属性,就算这个属性有默认值也不允许。同样,你也不能使用隐式的self属性,或者调用其它的实例方法。下面例子中定义了一个结构体
Checkerboard,它构建了西洋跳棋游戏的棋盘:
西洋跳棋游戏在一副黑白格交替的 10x10 的棋盘中进行。为了呈现这副游戏棋盘,
Checkerboard结构体定义了一个属性boardColors,它是一个包含 100 个布尔值的数组。数组中的某元素布尔值为true表示对应的是一个黑格,布尔值为false表示对应的是一个白格。数组中第一个元素代表棋盘上左上角的格子,最后一个元素代表棋盘上右下角的格子。-
boardColor数组是通过一个闭包来初始化和组装颜色值的:struct Checkerboard { +struct Checkerboard { let boardColors: Bool[] = { var temporaryBoard = Bool[]() var isBlack = false @@ -1010,13 +1022,15 @@ for item in breakfastList { return boardColors[(row * 10) + column] } } -每当一个新的
-Checkerboard实例创建时,对应的赋值闭包会执行,一系列颜色值会被计算出来作为默认值赋值给boardColors。上面例子中描述的闭包将计算出棋盘中每个格子合适的颜色,将这些颜色值保存到一个临时数组temporaryBoard中,并在构建完成时将此数组作为闭包返回值返回。这个返回的值将保存到boardColors中,并可以通squareIsBlackAtRow这个工具函数来查询。+let board = Checkerboard() +每当一个新的
+Checkerboard实例创建时,对应的赋值闭包会执行,一系列颜色值会被计算出来作为默认值赋值给boardColors。上面例子中描述的闭包将计算出棋盘中每个格子合适的颜色,将这些颜色值保存到一个临时数组temporaryBoard中,并在构建完成时将此数组作为闭包返回值返回。这个返回的值将保存到boardColors中,并可以通squareIsBlackAtRow这个工具函数来查询。+let board = Checkerboard() println(board.squareIsBlackAtRow(0, column: 1)) // 输出 "true" println(board.squareIsBlackAtRow(9, column: 9)) // 输出 "false"+@@ -268,7 +268,7 @@- 2.12. 附属脚本 + 2.12. 下标脚本 @@ -520,7 +520,7 @@ - + @@ -587,11 +587,10 @@ -+ diff --git a/chapter2/16_Automatic_Reference_Counting.html b/chapter2/16_Automatic_Reference_Counting.html old mode 100755 new mode 100644 index be4f7fa0..d264db13 --- a/chapter2/16_Automatic_Reference_Counting.html +++ b/chapter2/16_Automatic_Reference_Counting.html @@ -46,7 +46,7 @@ - -翻译:bruce0505
-校对:fd5788
+翻译:bruce0505
校对:fd5788析构过程(Deinitialization)
@@ -605,15 +604,16 @@析构过程原理
Swift 会自动释放不再需要的实例以释放资源。如自动引用计数那一章描述,Swift 通过自动引用计数(ARC)处理实例的内存管理。通常当你的实例被释放时不需要手动地去清理。但是,当使用自己的资源时,你可能需要进行一些额外的清理。例如,如果创建了一个自定义的类来打开一个文件,并写入一些数据,你可能需要在类实例被释放之前关闭该文件。
在类的定义中,每个类最多只能有一个析构函数。析构函数不带任何参数,在写法上不带括号:
-+deinit { +deinit { // 执行析构过程 } -析构函数是在实例释放发生前一步被自动调用。不允许主动调用自己的析构函数。子类继承了父类的析构函数,并且在子类析构函数实现的最后,父类的析构函数被自动调用。即使子类没有提供自己的析构函数,父类的析构函数也总是被调用。
+析构函数是在实例释放发生前一步被自动调用。不允许主动调用自己的析构函数。子类继承了父类的析构函数,并且在子类析构函数实现的最后,父类的析构函数被自动调用。即使子类没有提供自己的析构函数,父类的析构函数也总是被调用。
因为直到实例的析构函数被调用时,实例才会被释放,所以析构函数可以访问所有请求实例的属性,并且根据那些属性可以修改它的行为(比如查找一个需要被关闭的文件的名称)。
析构函数操作
这里是一个析构函数操作的例子。这个例子是一个简单的游戏,定义了两种新类型,
-Bank和Player。Bank结构体管理一个虚拟货币的流通,在这个流通中Bank永远不可能拥有超过 10,000 的硬币。在这个游戏中有且只能有一个Bank存在,因此Bank由带有静态属性和静态方法的结构体实现,从而存储和管理其当前的状态。+struct Bank { +struct Bank { static var coinsInBank = 10_000 static func vendCoins(var numberOfCoinsToVend: Int) -> Int { numberOfCoinsToVend = min(numberOfCoinsToVend, coinsInBank) @@ -621,46 +621,51 @@ return numberOfCoinsToVend } static func receiveCoins(coins: Int) { - coinsInBank += coins + coinsInBank += coins } } -+
Bank根据它的coinsInBank属性来跟踪当前它拥有的硬币数量。银行还提供两个方法——vendCoins和receiveCoins——用来处理硬币的分发和收集。
Bank根据它的coinsInBank属性来跟踪当前它拥有的硬币数量。银行还提供两个方法——vendCoins和receiveCoins——用来处理硬币的分发和收集。
vendCoins方法在 bank 分发硬币之前检查是否有足够的硬币。如果没有足够多的硬币,Bank返回一个比请求时小的数字(如果没有硬币留在 bank 中就返回 0)。vendCoins方法声明numberOfCoinsToVend为一个变量参数,这样就可以在方法体的内部修改数字,而不需要定义一个新的变量。vendCoins方法返回一个整型值,表明了提供的硬币的实际数目。
receiveCoins方法只是将 bank 的硬币存储和接收到的硬币数目相加,再保存回 bank。-
Player类描述了游戏中的一个玩家。每一个 player 在任何时刻都有一定数量的硬币存储在他们的钱包中。这通过 player 的coinsInPurse属性来体现:+class Player { - var coinsInPurse: Int - init(coins: Int) { - coinsInPurse = Bank.vendCoins(coins) +class Player { + var coinsInPurse: Int + init(coins: Int) { + coinsInPurse = Bank.vendCoins(coins) } func winCoins(coins: Int) { - coinsInPurse += Bank.vendCoins(coins) + coinsInPurse += Bank.vendCoins(coins) } deinit { - Bank.receiveCoins(coinsInPurse) + Bank.receiveCoins(coinsInPurse) } - } -每个
+} +Player实例都由一个指定数目硬币组成的启动额度初始化,这些硬币在 bank 初始化的过程中得到。如果没有足够的硬币可用,Player实例可能收到比指定数目少的硬币。每个
Player实例都由一个指定数目硬币组成的启动额度初始化,这些硬币在 bank 初始化的过程中得到。如果没有足够的硬币可用,Player实例可能收到比指定数目少的硬币。-
Player类定义了一个winCoins方法,该方法从银行获取一定数量的硬币,并把它们添加到玩家的钱包。Player类还实现了一个析构函数,这个析构函数在Player实例释放前一步被调用。这里析构函数只是将玩家的所有硬币都返回给银行:+var playerOne: Player? = Player(coins: 100) +var playerOne: Player? = Player(coins: 100) println("A new player has joined the game with \(playerOne!.coinsInPurse) coins") -// 输出 "A new player has joined the game with 100 coins" -println("There are now \(Bank.coinsInBank) coins left in the bank") +// 输出 "A new player has joined the game with 100 coins" +println("There are now \(Bank.coinsInBank) coins left in the bank") // 输出 "There are now 9900 coins left in the bank" -一个新的
+Player实例随着一个 100 个硬币(如果有)的请求而被创建。这个Player实例存储在一个名为playerOne的可选Player变量中。这里使用一个可选变量,是因为玩家可以随时离开游戏。设置为可选使得你可以跟踪当前是否有玩家在游戏中。一个新的
Player实例随着一个 100 个硬币(如果有)的请求而被创建。这个Player实例存储在一个名为playerOne的可选Player变量中。这里使用一个可选变量,是因为玩家可以随时离开游戏。设置为可选使得你可以跟踪当前是否有玩家在游戏中。因为
-playerOne是可选的,所以由一个感叹号(!)来修饰,每当其winCoins方法被调用时,coinsInPurse属性被访问并打印出它的默认硬币数目。+playerOne!.winCoins(2_000) -println("PlayerOne won 2000 coins & now has \ (playerOne!.coinsInPurse) coins") +playerOne!.winCoins(2_000) +println("PlayerOne won 2000 coins & now has \ (playerOne!.coinsInPurse) coins") // 输出 "PlayerOne won 2000 coins & now has 2100 coins" println("The bank now only has \(Bank.coinsInBank) coins left") // 输出 "The bank now only has 7900 coins left" -这里,player 已经赢得了 2,000 硬币。player 的钱包现在有 2,100 硬币,bank 只剩余 7,900 硬币。
-+playerOne = nil +这里,player 已经赢得了 2,000 硬币。player 的钱包现在有 2,100 硬币,bank 只剩余 7,900 硬币。
+playerOne = nil println("PlayerOne has left the game") // 输出 "PlayerOne has left the game" println("The bank now has \(Bank.coinsInBank) coins") // 输出 "The bank now has 10000 coins" -玩家现在已经离开了游戏。这表明是要将可选的
+playerOne变量设置为nil,意思是“没有Player实例”。当这种情况发生的时候,playerOne变量对Player实例的引用被破坏了。没有其它属性或者变量引用Player实例,因此为了清空它占用的内存从而释放它。在这发生前一步,其析构函数被自动调用,其硬币被返回到银行。玩家现在已经离开了游戏。这表明是要将可选的
playerOne变量设置为nil,意思是“没有Player实例”。当这种情况发生的时候,playerOne变量对Player实例的引用被破坏了。没有其它属性或者变量引用Player实例,因此为了清空它占用的内存从而释放它。在这发生前一步,其析构函数被自动调用,其硬币被返回到银行。+@@ -268,7 +268,7 @@- 2.12. 附属脚本 + 2.12. 下标脚本 @@ -520,7 +520,7 @@ - + @@ -587,7 +587,7 @@ -+ 翻译:TimothyYe
@@ -779,7 +779,7 @@ class CreditCard {现在你可以创建
Customer类的实例,用它初始化CreditCard实例,并将新创建的CreditCard实例赋值为客户的card属性。john = Customer(name: "John Appleseed") john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!) -在你关联两个实例后,他们的引用关系如下图所示:
+在你关联两个实例后,它们的引用关系如下图所示:
Customer实例持有对CreditCard实例的强引用,而CreditCard实例持有对Customer实例的无主引用。由于
@@ -815,7 +815,7 @@ class City { }customer的无主引用,当你断开john变量持有的强引用时,再也没有指向Customer实例的强引用了:为了建立两个类的依赖关系,
City的构造函数有一个Country实例的参数,并且将实例保存为country属性。-
Country的构造函数调用了City的构造函数。然而,只有Country的实例完全初始化完后,Country的构造函数才能把self传给City的构造函数。(在两段式构造过程中有具体描述)为了满足这种需求,通过在类型结尾处加上感叹号(City!)的方式,将
+Country的capitalCity属性声明为隐式解析可选类型的属性。这表示像其他可选类型一样,capitalCity属性的默认值为nil,但是不需要展开他的值就能访问它。(在隐式解析可选类型中有描述)为了满足这种需求,通过在类型结尾处加上感叹号(City!)的方式,将
Country的capitalCity属性声明为隐式解析可选类型的属性。这表示像其他可选类型一样,capitalCity属性的默认值为nil,但是不需要展开它的值就能访问它。(在隐式解析可选类型中有描述)由于
capitalCity默认值为nil,一旦Country的实例在构造函数中给name属性赋值后,整个初始化过程就完成了。这代表一旦name属性被赋值后,Country的构造函数就能引用并传递隐式的self。Country的构造函数在赋值capitalCity时,就能将self作为参数传递给City的构造函数。以上的意义在于你可以通过一条语句同时创建
Country和City的实例,而不产生循环强引用,并且capitalCity的属性能被直接访问,而不需要通过感叹号来展开它的可选值:var country = Country(name: "Canada", capitalName: "Ottawa") @@ -900,7 +900,7 @@ println(paragraph!.asHTML()) }弱引用和无主引用
当闭包和捕获的实例总是互相引用时并且总是同时销毁时,将闭包内的捕获定义为无主引用。
-相反的,当捕获引用有时可能会是
+nil时,将闭包内的捕获定义为弱引用。弱引用总是可选类型,并且当引用的实例被销毁后,弱引用的值会自动置为nil。这使我们可以在闭包内检查他们是否存在。相反的,当捕获引用有时可能会是
nil时,将闭包内的捕获定义为弱引用。弱引用总是可选类型,并且当引用的实例被销毁后,弱引用的值会自动置为nil。这使我们可以在闭包内检查它们是否存在。注意:
如果捕获的引用绝对不会置为
diff --git a/chapter2/17_Optional_Chaining.html b/chapter2/17_Optional_Chaining.html old mode 100755 new mode 100644 index 984f2103..c07da2a9 --- a/chapter2/17_Optional_Chaining.html +++ b/chapter2/17_Optional_Chaining.html @@ -46,7 +46,7 @@ -nil,应该用无主引用,而不是弱引用。+@@ -268,7 +268,7 @@- 2.12. 附属脚本 + 2.12. 下标脚本 @@ -520,7 +520,7 @@ - + @@ -587,7 +587,7 @@ -+ 翻译:Jasonbroker
@@ -612,7 +612,7 @@ Swift 的可选链和 Objective-C 中的消息为空有些相像,但是 Swift可选链可替代强制解析
-通过在想调用的属性、方法、或子脚本的可选值(
+optional value)(非空)后面放一个问号,可以定义一个可选链。这一点很像在可选值后面放一个叹号来强制拆得其封包内的值。他们的主要的区别在于当可选值为空时可选链即刻失败,然而一般的强制解析将会引发运行时错误。通过在想调用的属性、方法、或子脚本的可选值(
optional value)(非空)后面放一个问号,可以定义一个可选链。这一点很像在可选值后面放一个叹号来强制拆得其封包内的值。它们的主要的区别在于当可选值为空时可选链即刻失败,然而一般的强制解析将会引发运行时错误。为了反映可选链可以调用空(
nil),不论你调用的属性、方法、子脚本等返回的值是不是可选值,它的返回结果都是一个可选值。你可以利用这个返回值来检测你的可选链是否调用成功,有返回值即成功,返回nil则失败。调用可选链的返回结果与原本的返回结果具有相同的类型,但是原本的返回结果被包装成了一个可选值,当可选链调用成功时,一个应该返回
Int的属性将会返回Int?。下面几段代码将解释可选链和强制解析的不同。
@@ -760,7 +760,7 @@ if let firstRoomName = john.residence?[0].name {因此:
如果你试图通过可选链获得
-Int值,不论使用了多少层链接返回的总是Int?。 相似的,如果你试图通过可选链获得Int?值,不论使用了多少层链接返回的总是Int?。下面的例子试图获取
+john的residence属性里的address的street属性。这里使用了两层可选链来联系residence和address属性,他们两者都是可选类型:下面的例子试图获取
john的residence属性里的address的street属性。这里使用了两层可选链来联系residence和address属性,它们两者都是可选类型:if let johnsStreet = john.residence?.address?.street { println("John's street name is \(johnsStreet).") } else { diff --git a/chapter2/18_Type_Casting.html b/chapter2/18_Type_Casting.html old mode 100755 new mode 100644 index a1c451d6..71d8ef5e --- a/chapter2/18_Type_Casting.html +++ b/chapter2/18_Type_Casting.html @@ -46,7 +46,7 @@ -+@@ -268,7 +268,7 @@- 2.12. 附属脚本 + 2.12. 下标脚本 @@ -520,7 +520,7 @@ - + @@ -587,7 +587,7 @@ -+ 翻译:xiehurricane
diff --git a/chapter2/19_Nested_Types.html b/chapter2/19_Nested_Types.html old mode 100755 new mode 100644 index b37366bd..f4856881 --- a/chapter2/19_Nested_Types.html +++ b/chapter2/19_Nested_Types.html @@ -46,7 +46,7 @@ -+@@ -268,7 +268,7 @@- 2.12. 附属脚本 + 2.12. 下标脚本 @@ -520,7 +520,7 @@ - + @@ -587,7 +587,7 @@ -+ 翻译:Lin-H
diff --git a/chapter2/20_Extensions.html b/chapter2/20_Extensions.html old mode 100755 new mode 100644 index e3d0fda9..2e653209 --- a/chapter2/20_Extensions.html +++ b/chapter2/20_Extensions.html @@ -46,7 +46,7 @@ -+@@ -268,7 +268,7 @@- 2.12. 附属脚本 + 2.12. 下标脚本 @@ -520,7 +520,7 @@ - + @@ -587,7 +587,7 @@ -+ 翻译:lyuka
@@ -612,7 +612,7 @@提供新的构造器 定义下标 定义和使用新的嵌套类型 -使一个已有类型符合某个接口 +使一个已有类型符合某个协议 注意:
@@ -624,7 +624,7 @@extension SomeType { // 加到SomeType的新功能写到这里 } -一个扩展可以扩展一个已有类型,使其能够适配一个或多个协议(protocol)。当这种情况发生时,接口的名字应该完全按照类或结构体的名字的方式进行书写:
+一个扩展可以扩展一个已有类型,使其能够适配一个或多个协议(protocol)。当这种情况发生时,协议的名字应该完全按照类或结构体的名字的方式进行书写:
extension SomeType: SomeProtocol, AnotherProctocol { // 协议实现写到这里 } @@ -658,11 +658,11 @@ println("A marathon is \(aMarathon) meters long")构造器(Initializers)
-扩展可以向已有类型添加新的构造器。这可以让你扩展其它类型,将你自己的定制类型作为构造器参数,或者提供该类型的原始实现中没有包含的额外初始化选项。
+扩展可以向已有类型添加新的构造器。这可以让你扩展其它类型,将你自己的定制类型作为构造器参数,或者提供该类型的原始实现中没有包含的额外初始化选项。
+扩展能向类中添加新的便利构造器,但是它们不能向类中添加新的指定构造器或析构函数。指定构造器和析构函数必须总是由原始的类实现来提供。
注意:
-如果你使用扩展向一个值类型添加一个构造器,该构造器向所有的存储属性提供默认值,而且没有定义任何定制构造器(custom initializers),那么对于来自你的扩展构造器中的值类型,你可以调用默认构造器(default initializers)和成员级构造器(memberwise initializers)。 -正如在值类型的构造器授权中描述的,如果你已经把构造器写成值类型原始实现的一部分,上述规则不再适用。
+如果你使用扩展向一个值类型添加一个构造器,该构造器向所有的存储属性提供默认值,而且没有定义任何定制构造器(custom initializers),那么对于来自你的扩展构造器中的值类型,你可以调用默认构造器(default initializers)和逐一成员构造器(memberwise initializers)。
正如在值类型的构造器授权中描述的,如果你已经把构造器写成值类型原始实现的一部分,上述规则不再适用。下面的例子定义了一个用于描述几何矩形的定制结构体
Rect。这个例子同时定义了两个辅助结构体Size和Point,它们都把0.0作为所有属性的默认值:struct Size { diff --git a/chapter2/21_Protocols.html b/chapter2/21_Protocols.html old mode 100755 new mode 100644 index cba371d6..1455e659 --- a/chapter2/21_Protocols.html +++ b/chapter2/21_Protocols.html @@ -46,7 +46,7 @@ -+@@ -268,7 +268,7 @@- 2.12. 附属脚本 + 2.12. 下标脚本 @@ -520,7 +520,7 @@ - + @@ -587,7 +587,7 @@ -+ 翻译:geek5nan
@@ -863,7 +863,7 @@ game.play() }通过
扩展为上一节中提到的Dice类遵循TextRepresentable协议extension Dice: TextRepresentable { - cun asText() -> String { + func asText() -> String { return "A \(sides)-sided dice" } } @@ -1005,7 +1005,7 @@ class Country: HasArea { var legs: Int init(legs: Int) { self.legs = legs } } -+
Circle,Country,Animal并没有一个相同的基类,所以采用AnyObject类型的数组来装载在他们的实例,如下所示:
Circle,Country,Animal并没有一个相同的基类,所以采用AnyObject类型的数组来装载在它们的实例,如下所示:let objects: AnyObject[] = [ Circle(radius: 2.0), Country(area: 243_610), diff --git a/chapter2/22_Generics.html b/chapter2/22_Generics.html old mode 100755 new mode 100644 index 2fb4590d..df3f243a --- a/chapter2/22_Generics.html +++ b/chapter2/22_Generics.html @@ -46,7 +46,7 @@ -+@@ -268,7 +268,7 @@- 2.12. 附属脚本 + 2.12. 下标脚本 @@ -520,7 +520,7 @@ - + @@ -587,7 +587,7 @@ -+ diff --git a/chapter2/23_Advanced_Operators.html b/chapter2/23_Advanced_Operators.html old mode 100755 new mode 100644 index 9e11d2bc..fc2a9ced --- a/chapter2/23_Advanced_Operators.html +++ b/chapter2/23_Advanced_Operators.html @@ -46,7 +46,7 @@ - 翻译:takalard
@@ -709,7 +709,7 @@ swapTwoValues(&someString, &anotherString) return items.removeLast() } } -这个结构体在栈中使用一个
+Array性质的items存储值。Stack提供两个方法:push和pop,从栈中压进一个值和移除一个值。这些方法标记为可变的,因为他们需要修改(或转换)结构体的items数组。这个结构体在栈中使用一个
Array性质的items存储值。Stack提供两个方法:push和pop,从栈中压进一个值和移除一个值。这些方法标记为可变的,因为它们需要修改(或转换)结构体的items数组。上面所展现的
IntStack类型只能用于Int值,不过,其对于定义一个泛型Stack类(可以处理任何类型值的栈)是非常有用的。这里是一个相同代码的泛型版本:
struct Stack<T> { @@ -748,7 +748,7 @@ stackOfStrings.push("cuatro")
swapTwoValues函数和Stack类型可以作用于任何类型,不过,有的时候对使用在泛型函数和泛型类型上的类型强制约束为某种特定类型是非常有用的。类型约束指定了一个必须继承自指定类的类型参数,或者遵循一个特定的协议或协议构成。例如,Swift 的
Dictionary类型对作用于其键的类型做了些限制。在[字典][5]的描述中,字典的键类型必须是可哈希,也就是说,必须有一种方法可以使其是唯一的表示。Dictionary之所以需要其键是可哈希是为了以便于其检查其是否包含某个特定键的值。如无此需求,Dictionary即不会告诉是否插入或者替换了某个特定键的值,也不能查找到已经存储在字典里面的给定键值。这个需求强制加上一个类型约束作用于
-Dictionary的键上,当然其键类型必须遵循Hashable协议(Swift 标准库中定义的一个特定协议)。所有的 Swift 基本类型(如String,Int,Double和Bool)默认都是可哈希。当你创建自定义泛型类型时,你可以定义你自己的类型约束,当然,这些约束要支持泛型编程的强力特征中的多数。抽象概念如
+可哈希具有的类型特征是根据他们概念特征来界定的,而不是他们的直接类型特征。当你创建自定义泛型类型时,你可以定义你自己的类型约束,当然,这些约束要支持泛型编程的强力特征中的多数。抽象概念如
可哈希具有的类型特征是根据它们概念特征来界定的,而不是它们的直接类型特征。类型约束语法
你可以写一个在一个类型参数名后面的类型约束,通过冒号分割,来作为类型参数链的一部分。这种作用于泛型函数的类型约束的基础语法如下所示(和泛型类型的语法相同):
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) { @@ -876,7 +876,7 @@ let stringIndex = findIndex(["Mike", "Malcolm", "Andrea[类型约束][8]中描述的类型约束确保你定义关于类型参数的需求和一泛型函数或类型有关联。
对于关联类型的定义需求也是非常有用的。你可以通过这样去定义where语句作为一个类型参数队列的一部分。一个
where语句使你能够要求一个关联类型遵循一个特定的协议,以及(或)那个特定的类型参数和关联类型可以是相同的。你可写一个where语句,通过紧随放置where关键字在类型参数队列后面,其后跟着一个或者多个针对关联类型的约束,以及(或)一个或多个类型和关联类型的等于关系。下面的列子定义了一个名为
-allItemsMatch的泛型函数,用来检查是否两个Container单例包含具有相同顺序的相同元素。如果匹配到所有的元素,那么返回一个为true的Boolean值,反之,则相反。这两个容器可以被检查出是否是相同类型的容器(虽然它们可以是),但他们确实拥有相同类型的元素。这个需求通过一个类型约束和
+where语句结合来表示:这两个容器可以被检查出是否是相同类型的容器(虽然它们可以是),但它们确实拥有相同类型的元素。这个需求通过一个类型约束和
where语句结合来表示:func allItemsMatch< C1: Container, C2: Container where C1.ItemType == C2.ItemType, C1.ItemType: Equatable> @@ -912,9 +912,9 @@ let stringIndex = findIndex(["Mike", "Malcolm", "AndreaanotherContainer是一个C2类型的容器。someContainer和anotherContainer包含相同的元素类型。someContainer中的元素可以通过不等于操作(!=)来检查它们是否彼此不同。 -第三个和第四个要求结合起来的意思是
-anotherContainer中的元素也可以通过!=操作来检查,因为他们在someContainer中元素确实是相同的类型。这些要求能够使
-allItemsMatch函数比较两个容器,即便他们是不同的容器类型。+
allItemsMatch首先检查两个容器是否拥有同样数目的items,如果他们的元素数目不同,没有办法进行匹配,函数就会false。第三个和第四个要求结合起来的意思是
+anotherContainer中的元素也可以通过!=操作来检查,因为它们在someContainer中元素确实是相同的类型。这些要求能够使
+allItemsMatch函数比较两个容器,即便它们是不同的容器类型。
allItemsMatch首先检查两个容器是否拥有同样数目的items,如果它们的元素数目不同,没有办法进行匹配,函数就会false。检查完之后,函数通过
for-in循环和半闭区间操作(..)来迭代someContainer中的所有元素。对于每个元素,函数检查是否someContainer中的元素不等于对应的anotherContainer中的元素,如果这两个元素不等,则这两个容器不匹配,返回false。如果循环体结束后未发现没有任何的不匹配,那表明两个容器匹配,函数返回
true。这里演示了allItemsMatch函数运算的过程:
@@ -931,7 +931,7 @@ if allItemsMatch(stackOfStrings, arrayOfStrings) { println("Not all items match.") } // 输出 "All items match." -上面的例子创建一个
+Stack单例来存储String,然后压了三个字符串进栈。这个例子也创建了一个Array单例,并初始化包含三个同栈里一样的原始字符串。即便栈和数组否是不同的类型,但他们都遵循Container协议,而且他们都包含同样的类型值。你因此可以调用allItemsMatch函数,用这两个容器作为它的参数。在上面的例子中,allItemsMatch函数正确的显示了所有的这两个容器的items匹配。上面的例子创建一个
Stack单例来存储String,然后压了三个字符串进栈。这个例子也创建了一个Array单例,并初始化包含三个同栈里一样的原始字符串。即便栈和数组否是不同的类型,但它们都遵循Container协议,而且它们都包含同样的类型值。你因此可以调用allItemsMatch函数,用这两个容器作为它的参数。在上面的例子中,allItemsMatch函数正确的显示了所有的这两个容器的items匹配。+@@ -268,7 +268,7 @@- 2.12. 附属脚本 + 2.12. 下标脚本 @@ -520,7 +520,7 @@ - + @@ -587,7 +587,7 @@ -+ diff --git a/chapter2/chapter2.html b/chapter2/chapter2.html old mode 100755 new mode 100644 index 4dc01039..7810fbbb --- a/chapter2/chapter2.html +++ b/chapter2/chapter2.html @@ -46,7 +46,7 @@ - 翻译:xielingwang
@@ -855,7 +855,7 @@ let afterIncrement = ++toIncrement return !(left == right) } -上述代码实现了相等运算符
+==来判断两个Vector2D对象是否有相等的值,相等的概念就是他们有相同的x值和相同的y值,我们就用这个逻辑来实现。接着使用==的结果实现了不相等运算符!=。上述代码实现了相等运算符
==来判断两个Vector2D对象是否有相等的值,相等的概念就是它们有相同的x值和相同的y值,我们就用这个逻辑来实现。接着使用==的结果实现了不相等运算符!=。现在我们可以使用这两个运算符来判断两个
Vector2D对象是否相等。-let twoThree = Vector2D(x: 2.0, y: 3.0) let anotherTwoThree = Vector2D(x: 2.0, y: 3.0) @@ -895,7 +895,7 @@ let secondVector = Vector2D(x: 3.0, y: 4.0) let plusMinusVector = firstVector +- secondVector // plusMinusVector 此时的值为 (4.0, -2.0)这个运算符把两个向量的
+x相加,把向量的y相减。因为他实际是属于加减运算,所以让它保持了和加法一样的结合性和优先级(left和140)。查阅完整的Swift默认结合性和优先级的设置,请移步表达式;这个运算符把两个向量的
x相加,把向量的y相减。因为它实际是属于加减运算,所以让它保持了和加法一样的结合性和优先级(left和140)。查阅完整的Swift默认结合性和优先级的设置,请移步表达式;+@@ -268,7 +268,7 @@- 2.12. 附属脚本 + 2.12. 下标脚本 @@ -520,7 +520,7 @@ - + @@ -587,7 +587,7 @@ -+ Swift 教程
本章介绍了 Swift 的各种特性及其使用方法,是全书的核心部分。
diff --git a/chapter3/01_About_the_Language_Reference.html b/chapter3/01_About_the_Language_Reference.html old mode 100755 new mode 100644 index 30e4b2fb..341f3133 --- a/chapter3/01_About_the_Language_Reference.html +++ b/chapter3/01_About_the_Language_Reference.html @@ -46,7 +46,7 @@ -+@@ -268,7 +268,7 @@- 2.12. 附属脚本 + 2.12. 下标脚本 @@ -520,7 +520,7 @@ - + @@ -587,7 +587,7 @@ -+ 翻译:ChildhoodAndy
diff --git a/chapter3/02_Lexical_Structure.html b/chapter3/02_Lexical_Structure.html old mode 100755 new mode 100644 index cb25c822..be954633 --- a/chapter3/02_Lexical_Structure.html +++ b/chapter3/02_Lexical_Structure.html @@ -46,7 +46,7 @@ -+@@ -268,7 +268,7 @@- 2.12. 附属脚本 + 2.12. 下标脚本 @@ -520,7 +520,7 @@ - + @@ -587,7 +587,7 @@ -+ 翻译:superkam
diff --git a/chapter3/03_Types.html b/chapter3/03_Types.html old mode 100755 new mode 100644 index b0814ab4..a4bdce45 --- a/chapter3/03_Types.html +++ b/chapter3/03_Types.html @@ -46,7 +46,7 @@ -+@@ -268,7 +268,7 @@- 2.12. 附属脚本 + 2.12. 下标脚本 @@ -520,7 +520,7 @@ - + @@ -587,7 +587,7 @@ -+ 翻译:lyuka
diff --git a/chapter3/04_Expressions.html b/chapter3/04_Expressions.html old mode 100755 new mode 100644 index 3a89a79c..193b885f --- a/chapter3/04_Expressions.html +++ b/chapter3/04_Expressions.html @@ -46,7 +46,7 @@ -+@@ -268,7 +268,7 @@- 2.12. 附属脚本 + 2.12. 下标脚本 @@ -520,7 +520,7 @@ - + @@ -587,7 +587,7 @@ -+ -翻译:sg552
@@ -1125,13 +1125,13 @@ someInstance.dynamicType.printClassName()dynamic type 表达式
dynamic-type-expression → postfix-expression.dynamicType
附属脚本表达式(Subscript Expression)
-附属脚本表达式提供了通过附属脚本访问getter/setter 的方法。它的形式是:
+下标脚本表达式(Subscript Expression)
+下标脚本表达式提供了通过下标脚本访问getter/setter 的方法。它的形式是:
-
expression[index expressions]可以通过附属脚本表达式通过getter获取某个值,或者通过setter赋予某个值.
+可以通过下标脚本表达式通过getter获取某个值,或者通过setter赋予某个值.
关于subscript的声明,请参见: Protocol Subscript Declaration.
-附属脚本表达式的语法
+下标脚本表达式的语法
subscript-expression → postfix-expression[expression-list]
强制取值表达式(Forced-Value Expression)
diff --git a/chapter3/05_Declarations.html b/chapter3/05_Declarations.html old mode 100755 new mode 100644 index 6839b203..4554f9b6 --- a/chapter3/05_Declarations.html +++ b/chapter3/05_Declarations.html @@ -46,7 +46,7 @@ -+@@ -268,7 +268,7 @@- 2.12. 附属脚本 + 2.12. 下标脚本 @@ -520,7 +520,7 @@ - + @@ -587,11 +587,11 @@ -+ 翻译:marsprince
-校对:numbbbbb
+校对:numbbbbb, stanzhai
声明
@@ -611,41 +611,23 @@构造器声明 析构声明 扩展声明 -附属脚本声明 +下标脚本声明 运算符声明 一条声明可以在你的程序里引入新的名字和构造。举例来说,你可以使用声明来引入函数和方法,变量和常量,或者来定义 新的命名好的枚举,结构,类和协议类型。你也可以使用一条声明来延长一个已经存在的命名好的类型的行为。或者在你的 程序里引入在其他地方声明的符号。
-在swift中,大多数声明在某种意义上讲也是执行或同事声明它们的初始化定义。这意味着,因为协议和他们的成员不匹配, +
在swift中,大多数声明在某种意义上讲也是执行或同事声明它们的初始化定义。这意味着,因为协议和它们的成员不匹配, 大多数协议成员需要单独的声明。为了方便起见,也因为这些区别在swift里不是很重要,声明语句同时包含了声明和定义。
-GRAMMAR OF A DECLARATION
-declaration → import-declaration
-declaration → constant-declaration
-declaration → variable-declaration
-declaration → typealias-declaration
-declaration → function-declaration
-declaration → enum-declaration
-declaration → struct-declaration
-declaration → class-declaration
-declaration → protocol-declaration
-declaration → initializer-declaration
-declaration → deinitializer-declaration
-declaration → extension-declaration
-declaration → subscript-declaration
-declaration → operator-declaration
-declarations → declarationdeclarationsopt
-declaration-specifiers → declaration-specifierdeclaration-specifiersopt
-declaration-specifier → class | mutating | nonmutating | override | static | unowned |
+声明语法
声明 → 导入声明
声明 → 常量声明
声明 → 变量声明
声明 → 类型别名声明
声明 → 函数声明
声明 → 枚举声明
声明 → 结构体声明
声明 → 类声明
声明 → 协议声明
声明 → 构造器声明
声明 → 析构器声明
声明 → 扩展声明
声明 → 附属脚本声明
声明 → 运算符声明
声明(Declarations)列表 → 声明 声明(Declarations)列表 可选
声明描述符(Specifiers)列表 → 声明描述符(Specifier) 声明描述符(Specifiers)列表 可选
声明描述符(Specifier) → class | mutating | nonmutating | override | static | unowned | unowned(safe) | unowned(unsafe) | weak模块范围
模块范围定义了对模块中其他源文件可见的代码。(注:待改进)在swift的源文件中,最高级别的代码由零个或多个语句, -声明和表达组成。变量,常量和其他的声明语句在一个源文件的最顶级被声明,使得他们对同一模块中的每个源文件都是可见的。
+声明和表达组成。变量,常量和其他的声明语句在一个源文件的最顶级被声明,使得它们对同一模块中的每个源文件都是可见的。-GRAMMAR OF A TOP-LEVEL DECLARATION
-top-level-declaration → statements opt
+顶级(Top Level) 声明语法
顶级声明 → 多条语句(Statements) 可选代码块
@@ -655,8 +637,7 @@ }代码块中的语句包括声明,表达式和各种其他类型的语句,它们按照在源码中的出现顺序被依次执行。
-GRAMMAR OF A CODE BLOCK
-code-block → statements opt
+代码块语法
代码块 → { 多条语句(Statements) 可选 }引入声明
@@ -668,11 +649,7 @@import import kind module.symbol name import module.submodule-GRAMMAR OF AN IMPORT DECLARATION
-import-declaration → attributes opt import import-kind opt import-path -import-kind → typealias | struct | class | enum | protocol | var | func -import-path → import-path-identifier import-path-identifier.import-path -import-path-identifier → identifier operator
+导入(Import)声明语法
导入声明 → 特性(Attributes)列表 可选 import 导入类型 可选 导入路径
导入类型 → typealias | struct | class | enum | protocol | var | func
导入路径 → 导入路径标识符 | 导入路径标识符 . 导入路径
导入路径标识符 → 标识符 | 运算符常量声明
@@ -680,25 +657,21 @@ import-path-identifier → identifier operatorlet constant name: type = expression当常量的值被给定后,常量就将常量名称和表达式初始值不变的结合在了一起,而且不能更改。 这意味着如果常量以类的形式被初始化,类本身的内容是可以改变的,但是常量和类之间的结合关系是不能改变的。 -当一个常量被声明为全局变量,它必须被给定一个初始值。当一个常量在类或者结构体中被声明时,他被认为是一个常量 +当一个常量被声明为全局变量,它必须被给定一个初始值。当一个常量在类或者结构体中被声明时,它被认为是一个常量 属性。常量并不是可计算的属性,因此不包含getters和setters。(译者注:getters和setters不知道怎么翻译,待改进)
如果常量名是一个元祖形式,元祖中的每一项初始化表达式中都要有对应的值
let (firstNumber, secondNumber) = (10, 42)在上例中,firstNumber是一个值为10的常量,secnodeName是一个值为42的常量。所有常量都可以独立的使用:
-1 println("The first number is \(firstNumber).") -2 // prints "The first number is 10." -3 println("The second number is \(secondNumber).") -4 // prints "The second number is 42." -类型注释(:type)在常量声明中是一个可选项,它可以用来描述在类型接口(type inference)中找到的类型。
+println("The first number is \(firstNumber).") +// prints "The first number is 10." +println("The second number is \(secondNumber).") +// prints "The second number is 42." +类型注释(:type)在常量声明中是一个可选项,它可以用来描述在类型推断(type inference)中找到的类型。
声明一个静态常量要使用关键字static。静态属性在类型属性(type propetries)中有介绍。
如果还想获得更多关于常量的信息或者想在使用中获得帮助,请查看常量和变量(constants and variables), 存储属性(stored properties)等节。
-GRAMMAR OF A CONSTANT DECLARATION
-constant-declaration → attributes opt declaration-specifiers opt letpattern-initializer-list -pattern-initializer-list → pattern-initializer | pattern-initializer , pattern-initializer-list -pattern-initializer → pattern initializer opt -initializer → =expression
+常数声明语法
常量声明 → 特性(Attributes)列表 可选 声明描述符(Specifiers)列表 可选 let 模式构造器列表
模式构造器列表 → 模式构造器 | 模式构造器 , 模式构造器列表
模式构造器 → 模式 构造器 可选
构造器 → = 表达式变量声明
@@ -713,7 +686,7 @@ initializer → =expression下面的形式声明了一个存储型变量或存储型变量属性
var variable name: type = expression你可以在全局,函数内,或者在类和结构体的声明(context)中使用这种形式来声明一个变量。当变量以这种形式 -在全局或者一个函数内被声明时,它代表一个存储型变量。当他在类或者结构体中被声明时,他代表一个存储型变量属性。
+在全局或者一个函数内被声明时,它代表一个存储型变量。当它在类或者结构体中被声明时,它代表一个存储型变量属性。构造器表达式可以被
和常量声明相比,如果变量名是一个元祖类型,元祖的每一项的名字都要和初始化表达式一致。
正如名字一样,存储型变量的值或存储型变量属性存储在内存中。
@@ -728,8 +701,8 @@ set(setter name) { } }你可以在全局,函数体内或者类,结构体,枚举,扩展声明的上下文中使用这种形式的声明。 -当变量以这种形式在全局或者一个函数内被声明时,它代表一个计算型变量。当他在类,结构体,枚举,扩展声明的上下文 -中中被声明时,他代表一个计算型变量属性。
+当变量以这种形式在全局或者一个函数内被声明时,它代表一个计算型变量。当它在类,结构体,枚举,扩展声明的上下文 +中中被声明时,它代表一个计算型变量属性。getter用来读取变量值,setter用来写入变量值。setter子句是可选择的,只有getter是必需的,你可以将这些语句 都省略,只是简单的直接返回请求值,正如在只读计算属性(read-only computed properites)中描述的那样。 但是如果你提供了一个setter语句,你也必需提供一个getter语句。
@@ -749,13 +722,13 @@ didSet(setter name { }你可以在全局,函数体内或者类,结构体,枚举,扩展声明的上下文中使用这种形式的声明。 当变量以这种形式在全局或者一个函数内被声明时,监视器代表一个存储型变量监视器; -当他在类,结构体,枚举,扩展声明的上下文中被声明时,监视器代表属性监视器。
+当它在类,结构体,枚举,扩展声明的上下文中被声明时,监视器代表属性监视器。你可以为适合的监视器添加任何存储型属性。你也可以通过重写子类属性的方式为适合的监视器添加任何继承的属性 (无论是存储型还是计算型的),参见重写属性监视器(overriding properyt observers)。
初始化表达式在类或者结构体的声明中是可选的,但是在其他地方是必需的。无论在什么地方声明, 所有包含监视器的变量声明都必须有类型注释(type annotation)。
当变量或属性的值被改变时,willset和didset监视器提供了一个监视方法(适当的回应)。 -监视器不会在变量或属性第一次初始化时不会被运行,他们只有在值被外部初始化语句改变时才会被运行。
+监视器不会在变量或属性第一次初始化时不会被运行,它们只有在值被外部初始化语句改变时才会被运行。willset监视器只有在变量或属性值被改变之前运行。新的值作为一个常量经过过willset监视器,因此不可以在 willset语句中改变它。didset监视器在变量或属性值被改变后立即运行。和willset监视器相反,为了以防止你仍然 需要获得旧的数据,旧变量值或者属性会经过didset监视器。这意味着,如果你在变量或属性自身的didiset监视器语句 @@ -767,29 +740,7 @@ willset监视器初始名为newvalue,didset监视器初始名为oldvalue。
类和静态变量属性class关键字用来声明类的计算型属性。static关键字用来声明类的静态变量属性。类和静态变量在类型属性(type properties)中有详细讨论。
-GRAMMAR OF A VARIABLE DECLARATION
-variable-declaration → variable-declaration-headpattern-initializer-list
-variable-declaration → variable-declaration-head variable-name type-annotation code-block
-variable-declaration → variable-declaration-head variable-name type-annotation getter-setter-block
-variable-declaration → variable-declaration-head variable-name type-annotation getter-setter-keyword-block
-variable-declaration → variable-declaration-head variable-name type-annotationinitializer opt willSet-didSet-block
-variable-declaration-head → attributes opt declaration-specifiers opt var - -variable-name → identifier
-getter-setter-block → {getter-clause setter-clause opt}
-getter-setter-block → {setter-clause getter-clause}
-getter-clause → attributes optgetcode-block
-setter-clause → attributes opt set setter-name opt code-block
-setter-name → (identifier)
-getter-setter-keyword-block → {getter-keyword-clause setter-keyword-clause opt} - -getter-setter-keyword-block → {setter-keyword-clause getter-keyword-clause}
-getter-keyword-clause → attributes opt get
-setter-keyword-clause → attributes opt set
-willSet-didSet-block → {willSet-clause didSet-clause opt}
-willSet-didSet-block → {didSet-clause willSet-clause}
-willSet-clause → attributes opt willSet setter-name opt code-block
-didSet-clause → attributes opt didSet setter-name opt code-block
+变量声明语法
变量声明 → 变量声明头(Head) 模式构造器列表
变量声明 → 变量声明头(Head) 变量名 类型注解 代码块
变量声明 → 变量声明头(Head) 变量名 类型注解 getter-setter块
变量声明 → 变量声明头(Head) 变量名 类型注解 getter-setter关键字(Keyword)块
变量声明 → 变量声明头(Head) 变量名 类型注解 构造器 可选 willSet-didSet代码块
变量声明头(Head) → 特性(Attributes)列表 可选 声明描述符(Specifiers)列表 可选 var
变量名称 → 标识符
getter-setter块 → { getter子句 setter子句 可选 }
getter-setter块 → { setter子句 getter子句 }
getter子句 → 特性(Attributes)列表 可选 get 代码块
setter子句 → 特性(Attributes)列表 可选 set setter名称 可选 代码块
setter名称 → ( 标识符 )
getter-setter关键字(Keyword)块 → { getter关键字(Keyword)子句 setter关键字(Keyword)子句 可选 }
getter-setter关键字(Keyword)块 → { setter关键字(Keyword)子句 getter关键字(Keyword)子句 }
getter关键字(Keyword)子句 → 特性(Attributes)列表 可选 get
setter关键字(Keyword)子句 → 特性(Attributes)列表 可选 set
willSet-didSet代码块 → { willSet子句 didSet子句 可选 }
willSet-didSet代码块 → { didSet子句 willSet子句 }
willSet子句 → 特性(Attributes)列表 可选 willSet setter名称 可选 代码块
didSet子句 → 特性(Attributes)列表 可选 didSet setter名称 可选 代码块类型的别名声明
@@ -800,11 +751,7 @@ getter-setter-keyword-block → {setter-keyword-clause getter-keyword-clause 类型或者是混合类型。类型的别名不产生新的类型,它只是简单的和已存在的类型做名称替换。查看更多Protocol Associated Type Declaration.
-GRAMMAR OF A TYPE ALIAS DECLARATION
-typealias-declaration → typealias-head typealias-assignment -typealias-head → typealias typealias-name -typealias-name → identifier -typealias-assignment → =type
+类型别名声明语法
类型别名声明 → 类型别名头(Head) 类型别名赋值
类型别名头(Head) → typealias 类型别名名称
类型别名名称 → 标识符
类型别名赋值 → = 类型函数声明
@@ -885,22 +832,7 @@ func addTwoNumbers(a: Int) -> (Int -> Int) { addTwoNumbers(4)(5) // Returns 9多级柯里化应用如下
-GRAMMAR OF A FUNCTION DECLARATION
-function-declaration → function-head function-name generic-parameter-clause optfunction-signature function-body -function-head → attributes opt declaration-specifiers opt func -function-name → identifier operator -function-signature → parameter-clauses function-result opt -function-result → ->attributes opt type - function-body → code-block - parameter-clauses → parameter-clause parameter-clauses opt - parameter-clause → () (parameter-list...opt) - parameter-list → parameter parameter,parameter-list -parameter → inout opt let opt#optparameter-name local-parameter-name opt type-annotation default-argument-clause opt -parameter → inoutoptvar#optparameter-namelocal-parameter-name opt type-annotationdefault-argument-clause opt -parameter → attributes opt type -parameter-name → identifier - local-parameter-name → identifier - default-argument-clause → =expression:
+函数声明语法
函数声明 → 函数头 函数名 泛型参数子句 可选 函数签名(Signature) 函数体
函数头 → 特性(Attributes)列表 可选 声明描述符(Specifiers)列表 可选 func
函数名 → 标识符 | 运算符
函数签名(Signature) → parameter-clauses 函数结果 可选
函数结果 → -> 特性(Attributes)列表 可选 类型
函数体 → 代码块
parameter-clauses → 参数子句 parameter-clauses 可选
参数子句 → ( ) | ( 参数列表 ... 可选 )
参数列表 → 参数 | 参数 , 参数列表
参数 → inout 可选 let 可选 # 可选 参数名 本地参数名 可选 类型注解 默认参数子句 可选
参数 → inout 可选 var # 可选 参数名 本地参数名 可选 类型注解 默认参数子句 可选
参数 → 特性(Attributes)列表 可选 类型
参数名 → 标识符 | _
本地参数名 → 标识符 | _
默认参数子句 → = 表达式枚举声明
@@ -947,23 +879,7 @@ ExampleEnum.D的值会自动增长为6.使用switch语句来检验枚举事件的值,正如使用switch语句匹配枚举值(Matching Enumeration Values with a Switch Statement)一节描述的那样。
枚举类型是模式匹配(pattern-matched)的,和其相反的是switch语句case块中枚举事件匹配,在枚举事件类型(Enumeration Case Pattern)中有描述。
-GRAMMAR OF AN ENUMERATION DECLARATION
-enum-declaration → attributesoptunion-style-enum attributesoptraw-value-style-enum - union-style-enum → enum-namegeneric-parameter-clauseopt{union-style-enum-membersopt} - union-style-enum-members → union-style-enum-memberunion-style-enum-membersopt - union-style-enum-member → declaration union-style-enum-case-clause - union-style-enum-case-clause → attributesoptcaseunion-style-enum-case-list - union-style-enum-case-list → union-style-enum-case union-style-enum-case,union-style-enum-case-list - union-style-enum-case → enum-case-nametuple-typeopt - enum-name → identifier - enum-case-name → identifier - raw-value-style-enum → enum-namegeneric-parameter-clauseopt:type-identifier{raw-value-style-enum-membersopt} - raw-value-style-enum-members → raw-value-style-enum-memberraw-value-style-enum-membersopt - raw-value-style-enum-member → declaration raw-value-style-enum-case-clause - raw-value-style-enum-case-clause → attributesoptcaseraw-value-style-enum-case-list - raw-value-style-enum-case-list → raw-value-style-enum-case raw-value-style-enum-case,raw-value-style-enum-case-list - raw-value-style-enum-case → enum-case-nameraw-value-assignmentopt - raw-value-assignment → =literal
+枚举声明语法
枚举声明 → 特性(Attributes)列表 可选 联合式枚举 | 特性(Attributes)列表 可选 原始值式枚举
联合式枚举 → 枚举名 泛型参数子句 可选 { union-style-enum-members 可选 }
union-style-enum-members → union-style-enum-member union-style-enum-members 可选
union-style-enum-member → 声明 | 联合式(Union Style)的枚举case子句
联合式(Union Style)的枚举case子句 → 特性(Attributes)列表 可选 case 联合式(Union Style)的枚举case列表
联合式(Union Style)的枚举case列表 → 联合式(Union Style)的case | 联合式(Union Style)的case , 联合式(Union Style)的枚举case列表
联合式(Union Style)的case → 枚举的case名 元组类型 可选
枚举名 → 标识符
枚举的case名 → 标识符
原始值式枚举 → 枚举名 泛型参数子句 可选 : 类型标识 { 原始值式枚举成员列表 可选 }
原始值式枚举成员列表 → 原始值式枚举成员 原始值式枚举成员列表 可选
原始值式枚举成员 → 声明 | 原始值式枚举case子句
原始值式枚举case子句 → 特性(Attributes)列表 可选 case 原始值式枚举case列表
原始值式枚举case列表 → 原始值式枚举case | 原始值式枚举case , 原始值式枚举case列表
原始值式枚举case → 枚举的case名 原始值赋值 可选
原始值赋值 → = 字面量结构体声明
@@ -985,10 +901,7 @@ ExampleEnum.D的值会自动增长为6. 结构体和枚举都是值类型(Structures and Enumerations Are Value Types)一节。你可以使用扩展声明来扩展结构体类型的行为,参见扩展声明(Extension Declaration).
-GRAMMAR OF A STRUCTURE DECLARATION
-struct-declaration → attributesoptstructstruct-namegeneric-parameter-clauseopttype-inheritance-clauseoptstruct-body - struct-name → identifier - struct-body → {declarationsopt}
+结构体声明语法
结构体声明 → 特性(Attributes)列表 可选 struct 结构体名称 泛型参数子句 可选 类型继承子句 可选 结构体主体
结构体名称 → 标识符
结构体主体 → { 声明(Declarations)列表 可选 }类声明
@@ -1014,10 +927,7 @@ ExampleEnum.D的值会自动增长为6. 结构体和枚举都是值类型(Structures and Enumerations Are Value Types)一节。你可以使用扩展声明来扩展类的行为,参见扩展声明(Extension Declaration).
-GRAMMAR OF A CLASS DECLARATION
-class-declaration → attributesoptclassclass-namegeneric-parameter-clauseopttype-inheritance-clauseoptclass-body - class-name → identifier - class-body → {declarationsopt}
+类声明语法
类声明 → 特性(Attributes)列表 可选 class 类名 泛型参数子句 可选 类型继承子句 可选 类主体
类名 → 标识符
类主体 → { 声明(Declarations)列表 可选 }协议声明(translated by 小一)
@@ -1026,14 +936,14 @@ ExampleEnum.D的值会自动增长为6. protocol member declarations } -协议的主体包含零或多个协议成员声明,这些成员描述了任何采用该协议必须满足的一致性要求。特别的,一个协议可以声明必须实现某些属性、方法、初始化程序及附属脚本的一致性类型。协议也可以声明专用种类的类型别名,叫做关联类型,它可以指定协议的不同声明之间的关系。协议成员声明会在下面的详情里进行讨论。
+协议的主体包含零或多个协议成员声明,这些成员描述了任何采用该协议必须满足的一致性要求。特别的,一个协议可以声明必须实现某些属性、方法、初始化程序及下标脚本的一致性类型。协议也可以声明专用种类的类型别名,叫做关联类型,它可以指定协议的不同声明之间的关系。协议成员声明会在下面的详情里进行讨论。
协议类型可以从很多其它协议那继承。当一个协议类型从其它协议那继承的时候,来自其它协议的所有要求就集合了,而且从当前协议继承的任何类型必须符合所有的这些要求。对于如何使用协议继承的例子,查看协议继承
注意:
你可以通过采用在类型的扩展声明中的协议来为之前声明的类型添加协议一致性。在扩展中你必须实现所有采用协议的要求。如果该类型已经实现了所有的要求,你可以让这个扩展声明的主题留空。
-默认地,符合某一个协议的类型必须实现所有声明在协议中的属性、方法和附属脚本。也就是说,你可以用
+optional属性标注这些协议成员声明以指定它们的一致性类型实现是可选的。optional属性仅仅可以用于使用objc属性标记过的协议。这样的结果就是仅仅类类型可以采用并符合包含可选成员要求的协议。更多关于如何使用optional属性的信息及如何访问可选协议成员的指导——比如当你不能肯定是否一致性的类型实现了它们——参见可选协议要求默认地,符合某一个协议的类型必须实现所有声明在协议中的属性、方法和下标脚本。也就是说,你可以用
optional属性标注这些协议成员声明以指定它们的一致性类型实现是可选的。optional属性仅仅可以用于使用objc属性标记过的协议。这样的结果就是仅仅类类型可以采用并符合包含可选成员要求的协议。更多关于如何使用optional属性的信息及如何访问可选协议成员的指导——比如当你不能肯定是否一致性的类型实现了它们——参见可选协议要求为了限制协议的采用仅仅针对类类型,需要使用
class_protocol属性标记整个协议声明。任意继承自标记有class_protocol属性协议的协议都可以智能地仅能被类类型采用。+注意:
@@ -1042,70 +952,53 @@ ExampleEnum.D的值会自动增长为6.协议是命名的类型,因此它们可以以另一个命名类型出现在你代码的所有地方,就像协议类型里讨论的那样。然而你不能构造一个协议的实例,因为协议实际上不提供它们指定的要求的实现。
你可以使用协议来声明一个类的代理的方法或者应该实现的结构,就像委托(代理)模式描述的那样。
-协议声明的语法 -protocol-declaration → attributesoptprotocolprotocol-nametype-inheritance-clauseoptprotocol-body -protocol-name → identifier -protocol-body → {protocol-member-declarationsopt} -protocol-member-declaration → protocol-property-declaration -protocol-member-declaration → protocol-method-declaration -protocol-member-declaration → protocol-initializer-declaration -protocol-member-declaration → protocol-subscript-declaration -protocol-member-declaration → protocol-associated-type-declaration -protocol-member-declarations → protocol-member-declarationprotocol-member-declarationsopt
+协议(Protocol)声明语法
协议声明 → 特性(Attributes)列表 可选 protocol 协议名 类型继承子句 可选 协议主体
协议名 → 标识符
协议主体 → { 协议成员声明(Declarations)列表 可选 }
协议成员声明 → 协议属性声明
协议成员声明 → 协议方法声明
协议成员声明 → 协议构造器声明
协议成员声明 → 协议附属脚本声明
协议成员声明 → 协议关联类型声明
协议成员声明(Declarations)列表 → 协议成员声明 协议成员声明(Declarations)列表 可选协议属性声明
协议声明了一致性类型必须在协议声明的主体里通过引入一个协议属性声明来实现一个属性。协议属性声明有一种特殊的类型声明形式:
--var property name: type { get set } -同其它协议成员声明一样,这些属性声明仅仅针对符合该协议的类型声明了
+getter和setter要求。结果就是你不需要在协议里它被声明的地方实现getter和setter。var property name: type { get set } +同其它协议成员声明一样,这些属性声明仅仅针对符合该协议的类型声明了
getter和setter要求。结果就是你不需要在协议里它被声明的地方实现getter和setter。
getter和setter要求可以通过一致性类型以各种方式满足。如果属性声明包含get和set关键词,一致性类型就可以用可读写(实现了getter和setter)的存储型变量属性或计算型属性,但是属性不能以常量属性或只读计算型属性实现。如果属性声明仅仅包含get关键词的话,它可以作为任意类型的属性被实现。比如说实现了协议的属性要求的一致性类型,参见属性要求更多参见变量声明
-协议属性声明语法 -protocol-property-declaration → variable-declaration-headvariable-nametype-annotationgetter-setter-keyword-block
+协议属性声明语法
协议属性声明 → 变量声明头(Head) 变量名 类型注解 getter-setter关键字(Keyword)块协议方法声明
协议声明了一致性类型必须在协议声明的主体里通过引入一个协议方法声明来实现一个方法. -协议方法声明和函数方法声明有着相同的形式,包含如下两条规则:他们不包括函数体,你不能在类的声明内为他们的 +协议方法声明和函数方法声明有着相同的形式,包含如下两条规则:它们不包括函数体,你不能在类的声明内为它们的 参数提供初始值.举例来说,符合的类型执行协议必需的方法。参见必需方法一节。
使用关键字class可以在协议声明中声明一个类或必需的静态方法。执行这些方法的类也用关键字class声明。 相反的,执行这些方法的结构体必须以关键字static声明。如果你想使用扩展方法,在扩展类时使用class关键字, 在扩展结构体时使用static关键字。
更多请参阅函数声明。
-GRAMMAR OF A PROTOCOL METHOD DECLARATION
-protocol-method-declaration → function-headfunction-namegeneric-parameter-clauseoptfunction-signature
+协议方法声明语法
协议方法声明 → 函数头 函数名 泛型参数子句 可选 函数签名(Signature)协议构造器声明
协议声明了一致性类型必须在协议声明的主体里通过引入一个协议构造器声明来实现一个构造器。协议构造器声明 除了不包含构造器体外,和构造器声明有着相同的形式,
更多请参阅构造器声明。
--GRAMMAR OF A PROTOCOL INITIALIZER DECLARATION
-protocol-initializer-declaration → initializer-headgeneric-parameter-clauseoptparameter-clause
+协议构造器声明语法
协议构造器声明 → 构造器头(Head) 泛型参数子句 可选 参数子句协议附属脚本声明
-协议声明了一致性类型必须在协议声明的主体里通过引入一个协议附属脚本声明来实现一个附属脚本。协议属性声明 -对附属脚本声明有一个特殊的形式:
---subscript (parameters) -> return type { get set }
-附属脚本声明只为和协议一致的类型声明了必需的最小数量的的getter和setter。如果附属脚本申明包含get和set关键字, -一致的类型也必须有一个getter和setter语句。如果附属脚本声明值包含get关键字,一致的类型必须至少包含一个 +
协议下标脚本声明
+协议声明了一致性类型必须在协议声明的主体里通过引入一个协议下标脚本声明来实现一个下标脚本。协议属性声明 +对下标脚本声明有一个特殊的形式:
+subscript (parameters) -> return type { get set } +下标脚本声明只为和协议一致的类型声明了必需的最小数量的的getter和setter。如果下标脚本申明包含get和set关键字, +一致的类型也必须有一个getter和setter语句。如果下标脚本声明值包含get关键字,一致的类型必须至少包含一个 getter语句,可以选择是否包含setter语句。
-更多参阅附属脚本声明。
+更多参阅下标脚本声明。
-GRAMMAR OF A PROTOCOL SUBSCRIPT DECLARATION
-protocol-subscript-declaration → subscript-headsubscript-resultgetter-setter-keyword-block
+协议附属脚本声明语法
协议附属脚本声明 → 附属脚本头(Head) 附属脚本结果(Result) getter-setter关键字(Keyword)块协议相关类型声明
协议声明相关类型使用关键字typealias。相关类型为作为协议声明的一部分的类型提供了一个别名。相关类型和参数 语句中的类型参数很相似,但是它们在声明的协议中包含self关键字。在这些语句中,self指代和协议一致的可能的类型。 获得更多信息和例子,查看相关类型或类型别名声明。
-GRAMMAR OF A PROTOCOL ASSOCIATED TYPE DECLARATION
-protocol-associated-type-declaration → typealias-headtype-inheritance-clauseopttypealias-assignmentopt
+协议关联类型声明语法
协议关联类型声明 → 类型别名头(Head) 类型继承子句 可选 类型别名赋值 可选构造器声明
@@ -1128,15 +1021,12 @@ getter语句,可以选择是否包含setter语句。便利构造器可以将初始化过程委托给另一个便利构造器或类的一个指定构造器。这意味着,类的初始化过程必须 以一个将所有类属性完全初始化的指定构造器的调用作为结束。便利构造器不能调用超类的构造器。
你可以使用requierd关键字,将便利构造器和指定构造器标记为每个子类的构造器都必须拥有的。因为指定构造器 -不被子类继承,他们必须被立即执行。当子类直接执行所有超类的指定构造器(或使用便利构造器重写指定构造器)时, -必需的便利构造器可以被隐式的执行,亦可以被继承。不像方法,附属脚本那样,你不需要为这些重写的构造器标注 +不被子类继承,它们必须被立即执行。当子类直接执行所有超类的指定构造器(或使用便利构造器重写指定构造器)时, +必需的便利构造器可以被隐式的执行,亦可以被继承。不像方法,下标脚本那样,你不需要为这些重写的构造器标注 overrride关键字。
查看更多关于不同声明方法的构造器的例子,参阅构造过程一节。
-GRAMMAR OF AN INITIALIZER DECLARATION
-initializer-declaration → initializer-headgeneric-parameter-clauseoptparameter-clauseinitializer-body -initializer-head → attributesoptconvenienceoptinit -initializer-body → code-block
+构造器声明语法
构造器声明 → 构造器头(Head) 泛型参数子句 可选 参数子句 构造器主体
构造器头(Head) → 特性(Attributes)列表 可选 convenience 可选 init
构造器主体 → 代码块析构声明
@@ -1148,11 +1038,9 @@ initializer-body → code-block 类的扩展声明内,每个类最多只能有一个。子类继承了它的超类的析构器,在子类将要被释放时隐式的调用。子类在所有析构器被执行完毕前不会被释放。
析构器不会被直接调用。
-查看例子和如何在类的声明中使用析构器,参见析构过程一节 -。
+查看例子和如何在类的声明中使用析构器,参见析构过程一节。
-GRAMMAR OF A DEINITIALIZER DECLARATION
-deinitializer-declaration → attributesoptdeinitcode-block
+析构器声明语法
析构器声明 → 特性(Attributes)列表 可选 deinit 代码块扩展声明
@@ -1161,7 +1049,7 @@ initializer-body → code-block declarations }一个扩展声明体包括零个或多个声明。这些声明可以包括计算型属性,计算型静态属性,实例方法,静态和类方法,构造器, -附属脚本声明,甚至其他结构体,类,和枚举声明。扩展声明不能包含析构器,协议声明,存储型属性,属性监测器或其他 +下标脚本声明,甚至其他结构体,类,和枚举声明。扩展声明不能包含析构器,协议声明,存储型属性,属性监测器或其他 的扩展属性。详细讨论和查看包含多种扩展声明的实例,参见扩展一节。
扩展声明可以向现存的类,结构体,枚举内添加一致的协议。扩展声明不能向一个类中添加继承的类,因此 type-inheritance-clause是一个只包含协议列表的扩展声明。
@@ -1169,13 +1057,17 @@ type-inheritance-clause是一个只包含协议列表的扩展声明。扩展声明可以包含构造器声明,这意味着,如果你扩展的类型在其他模块中定义,构造器声明必须委托另一个在 那个模块里声明的构造器来恰当的初始化。
--GRAMMAR OF AN EXTENSION DECLARATION
-extension-declaration → extensiontype-identifiertype-inheritance-clauseoptextension-body -extension-body → {declarationsopt}
+扩展(Extension)声明语法
扩展声明 → extension 类型标识 类型继承子句 可选 extension-body
extension-body → { 声明(Declarations)列表 可选 }附属脚本声明(translated by 林)
-附属脚本用于向特定类型添加附属脚本支持,通常为访问集合,列表和序列的元素时提供语法便利。附属脚本声明使用关键字
+subscript,声明形式如下:下标脚本声明(translated by 林)
+<<<<<<< HEAD +附属脚本用于向特定类型添加附属脚本支持,通常为访问集合,列表和序列的元素时提供语法便利。附属脚本声明使用关键字
+subscript,声明形式如下:++subscript (
+parameter) -> (return type){
get{
statements
}
set(setter name){
statements
}
}附属脚本声明只能在类,结构体,枚举,扩展和协议声明的上下文进行声明。
+下标脚本用于向特定类型添加下标脚本支持,通常为访问集合,列表和序列的元素时提供语法便利。下标脚本声明使用关键字
subscript,声明形式如下:-subscript (
parameter) -> (return type){ get{ @@ -1185,21 +1077,29 @@ extension-body → {declarationsopt}statements} } -附属脚本声明只能在类,结构体,枚举,扩展和协议声明的上下文进行声明。 -变量(parameters)指定一个或多个用于在相关类型的附属脚本中访问元素的索引(例如,表达式
-object[i]中的i)。尽管用于元素访问的索引可以是任意类型的,但是每个变量必须包含一个用于指定每种索引类型的类型标注。返回类型(return type)指定被访问的元素的类型。和计算性属性一样,附属脚本声明支持对访问元素的读写操作。getter用于读取值,setter用于写入值。setter子句是可选的,当仅需要一个getter子句时,可以将二者都忽略且直接返回请求的值即可。也就是说,如果使用了setter子句,就必须使用getter子句。
-setter的名字和封闭的括号是可选的。如果使用了setter名称,它会被当做传给setter的变量的名称。如果不使用setter名称,那么传给setter的变量的名称默认是
-value。setter名称的类型必须与返回类型(return type)的类型相同。可以在附属脚本声明的类型中,可以重载附属脚本,只要变量(parameters)或返回类型(return type)与先前的不同即可。此时,必须使用
-override关键字声明那个被覆盖的附属脚本。(注:好乱啊!到底是重载还是覆盖?!)同样可以在协议声明的上下文中声明附属脚本,Protocol Subscript Declaration中有所描述。
-更多关于附属脚本和附属脚本声明的例子,请参考Subscripts。
+下标脚本声明只能在类,结构体,枚举,扩展和协议声明的上下文进行声明。-+GRAMMAR OF A SUBSCRIPT DECLARATION
-subscript-declaration → subscript-headsubscript-resultcode-block -subscript-declaration → subscript-headsubscript-resultgetter-setter-block -subscript-declaration → subscript-headsubscript-resultgetter-setter-keyword-block -subscript-head → attributesoptsubscriptparameter-clause -subscript-result → ->attributesopttype
+++++++++++a516af6a531a104ec88da0d236ecf389a5ec72af
+变量(parameters)指定一个或多个用于在相关类型的下标脚本中访问元素的索引(例如,表达式
+object[i]中的i)。尽管用于元素访问的索引可以是任意类型的,但是每个变量必须包含一个用于指定每种索引类型的类型标注。返回类型(return type)指定被访问的元素的类型。和计算性属性一样,下标脚本声明支持对访问元素的读写操作。getter用于读取值,setter用于写入值。setter子句是可选的,当仅需要一个getter子句时,可以将二者都忽略且直接返回请求的值即可。也就是说,如果使用了setter子句,就必须使用getter子句。
+setter的名字和封闭的括号是可选的。如果使用了setter名称,它会被当做传给setter的变量的名称。如果不使用setter名称,那么传给setter的变量的名称默认是
+value。setter名称的类型必须与返回类型(return type)的类型相同。可以在下标脚本声明的类型中,可以重载下标脚本,只要变量(parameters)或返回类型(return type)与先前的不同即可。此时,必须使用
+override关键字声明那个被覆盖的下标脚本。(注:好乱啊!到底是重载还是覆盖?!)同样可以在协议声明的上下文中声明下标脚本,Protocol Subscript Declaration中有所描述。
+更多关于下标脚本和下标脚本声明的例子,请参考Subscripts。
++附属脚本声明语法
附属脚本声明 → 附属脚本头(Head) 附属脚本结果(Result) 代码块
附属脚本声明 → 附属脚本头(Head) 附属脚本结果(Result) getter-setter块
附属脚本声明 → 附属脚本头(Head) 附属脚本结果(Result) getter-setter关键字(Keyword)块
附属脚本头(Head) → 特性(Attributes)列表 可选 subscript 参数子句
附属脚本结果(Result) → -> 特性(Attributes)列表 可选 类型运算符声明(translated by 林)
@@ -1233,16 +1133,7 @@ subscript-result → ->attributesopttype和前缀运算符一样,后缀运算符的声明中不指定优先级。后缀运算符是非结合的。
声明了一个新的运算符以后,需要声明一个跟这个运算符同名的函数来实现这个运算符。如何实现一个新的运算符,请参考Custom Operators。
-diff --git a/chapter3/06_Attributes.html b/chapter3/06_Attributes.html old mode 100755 new mode 100644 index ddf1cd09..7175af55 --- a/chapter3/06_Attributes.html +++ b/chapter3/06_Attributes.html @@ -46,7 +46,7 @@ -GRAMMAR OF AN OPERATOR DECLARATION
-operator-declaration → prefix-operator-declaration postfix-operator-declaration >infix-operator-declaration -prefix-operator-declaration → operator prefix operator{} -postfix-operator-declaration → operator postfix operator{} -infix-operator-declaration → operatorinfixoperator{infix-operator-attributesopt} -infix-operator-attributes → precedence-clauseoptassociativity-clauseopt -precedence-clause → precedenceprecedence-level -precedence-level → Digit 0 through 255 -associativity-clause → associativityassociativity -associativity → left right none
+运算符声明语法
运算符声明 → 前置运算符声明 | 后置运算符声明 | 中置运算符声明
前置运算符声明 → 运算符 prefix 运算符 { }
后置运算符声明 → 运算符 postfix 运算符 { }
中置运算符声明 → 运算符 infix 运算符 { 中置运算符属性 可选 }
中置运算符属性 → 优先级子句 可选 结和性子句 可选
优先级子句 → precedence 优先级水平
优先级水平 → 数值 0 到 255
结和性子句 → associativity 结和性
结和性 → left | right | none+@@ -268,7 +268,7 @@- 2.12. 附属脚本 + 2.12. 下标脚本 @@ -520,7 +520,7 @@ - + @@ -587,7 +587,7 @@ -+ 翻译:Hawstein
@@ -656,17 +656,7 @@ class ExampleClass {
noreturn该特性用于修饰函数或方法的类型,表明该函数或方法不会返回到它的调用者中去。你也可以用它标记函数或方法的声明,表示函数或方法的相应类型,
T,是@noreturn T。-diff --git a/chapter3/07_Patterns.html b/chapter3/07_Patterns.html old mode 100755 new mode 100644 index ac7586e9..38320e75 --- a/chapter3/07_Patterns.html +++ b/chapter3/07_Patterns.html @@ -46,7 +46,7 @@ -特性的语法 -attribute -> @ attribute-name attribute-argument-clauseopt -attribute-name -> identifier -attribute-argument-clause -> ( balanced-tokensopt ) -attributes -> attribute attributesopt -balanced-tokens -> balanced-token balanced-tokensopt -balanced-token -> ( balanced-tokensopt ) -balanced-token -> [ balanced-tokensopt ] -balanced-token -> { balanced-tokensopt } -balanced-token -> 任意标识符,关键字,字面量,或运算符 -balanced-token -> 任意标点符号,除了(, ), [, ], {, 或 }
+特性语法
特色 → @ 特性名 特性参数子句 可选
特性名 → 标识符
特性参数子句 → ( 平衡令牌列表 可选 )
特性(Attributes)列表 → 特色 特性(Attributes)列表 可选
平衡令牌列表 → 平衡令牌 平衡令牌列表 可选
平衡令牌 → ( 平衡令牌列表 可选 )
平衡令牌 → [ 平衡令牌列表 可选 ]
平衡令牌 → { 平衡令牌列表 可选 }
平衡令牌 → 任意标识符, 关键字, 字面量或运算符
平衡令牌 → 任意标点除了(, ), [, ], {, 或 }+@@ -268,7 +268,7 @@- 2.12. 附属脚本 + 2.12. 下标脚本 @@ -520,7 +520,7 @@ - + @@ -587,11 +587,11 @@ -+ 翻译:honghaoz
-校对:numbbbbb
+校对:numbbbbb, stanzhai
模式(Patterns)
@@ -601,132 +601,114 @@标识符模式(Identifier Pattern) 值绑定模式(Value-Binding Pattern) 元组模式(Tuple Pattern) -枚举案例模式(Enumeration Case Pattern) +枚举用例模式(Enumeration Case Pattern) 类型转换模式(Type-Casting Patterns) 表达式模式(Expression Pattern) 模式(pattern)代表了单个值或者复合值的结构。例如,元组
-(1, 2)的结构是逗号分隔的,包含两个元素的列表。因为模式代表一种值的结构,而不是特定的某个值,你可以把模式和各种同类型的值匹配起来。比如,(x, y)可以匹配元组(1, 2),以及任何含两个元素的元组。除了将模式与一个值匹配外,你可以从合成值中提取出部分或全部,然后分别把各个部分和一个常量或变量绑定起来。在Swift中,模式出现在变量和常量的声明(在它们的左侧),
+for-in语句和switch语句(在他们的case标签)中。尽管任何模式都可以出现在switch语句的case标签中,但在其他情况下,只有通配符模式(wildcard pattern),标识符模式(identifier pattern)和包含这两种模式的模式才能出现。在Swift中,模式出现在变量和常量的声明(在它们的左侧),
for-in语句和switch语句(在它们的case标签)中。尽管任何模式都可以出现在switch语句的case标签中,但在其他情况下,只有通配符模式(wildcard pattern),标识符模式(identifier pattern)和包含这两种模式的模式才能出现。你可以为通配符模式(wildcard pattern),标识符模式(identifier pattern)和元组模式(tuple pattern)指定类型注释,用来限制这种模式只匹配某种类型的值。
-模式的语法:
-pattern → wildcard-patterntype-annotationopt
-pattern → identifier-patterntype-annotationopt
-pattern → value-binding-pattern
-pattern → tuple-patterntype-annotationopt
-pattern → enum-case-pattern
-pattern → type-casting-pattern
-pattern → expression-pattern
+模式(Patterns) 语法
模式 → 通配符模式 类型注解 可选
模式 → 标识符模式 类型注解on) 可选
模式 → 值绑定模式
模式 → 元组模式 类型注解 可选
模式 → enum-case-pattern
模式 → type-casting-pattern
模式 → 表达式模式通配符模式(Wildcard Pattern)
通配符模式匹配并忽略任何值,包含一个下划线(_)。当你不关心被匹配的值时,可以使用此模式。例如,下面这段代码进行了
-1...3的循环,并忽略了每次循环的值:for _ in 1...3 { - // Do something three times. - } +for _ in 1...3 { + // Do something three times. +}-通配符模式的语法:
-wildcard-pattern → _
+通配符模式语法
通配符模式 → _标识符模式(Identifier Pattern)
标识符模式匹配任何值,并将匹配的值和一个变量或常量绑定起来。例如,在下面的常量申明中,
-someValue是一个标识符模式,匹配了类型是Int的42。let someValue = 42 +let someValue = 42当匹配成功时,
42被绑定(赋值)给常量someValue。当一个变量或常量申明的左边是标识符模式时,此时,标识符模式是隐式的值绑定模式(value-binding pattern)。
-标识符模式的语法:
-identifier-pattern → identifier
+标识符模式语法
标识符模式 → 标识符值绑定模式(Value-Binding Pattern)
值绑定模式绑定匹配的值到一个变量或常量。当绑定匹配值给常量时,用关键字
let,绑定给变量时,用关键之var。标识符模式包含在值绑定模式中,绑定新的变量或常量到匹配的值。例如,你可以分解一个元组的元素,并把每个元素绑定到相应的标识符模式中。
-let point = (3, 2) - switch point { - // Bind x and y to the elements of point. - case let (x, y): - println("The point is at (\(x), \(y)).") - } - // prints "The point is at (3, 2).” +let point = (3, 2) +switch point { + // Bind x and y to the elements of point. +case let (x, y): + println("The point is at (\(x), \(y)).") +} +// prints "The point is at (3, 2).”在上面这个例子中,
let将元组模式(x, y)分配到各个标识符模式。因为这种行为,switch语句中case let (x, y):和case (let x, let y):匹配的值是一样的。-值绑定模式的语法:
-value-binding-pattern → var pattern | let pattern
+值绑定(Value Binding)模式语法