diff --git a/source/chapter1/02_a_swift_tour.md b/source/chapter1/02_a_swift_tour.md index e9d4eb76..256a5fb3 100644 --- a/source/chapter1/02_a_swift_tour.md +++ b/source/chapter1/02_a_swift_tour.md @@ -17,7 +17,9 @@ 通常来说,编程语言教程中的第一个程序应该在屏幕上打印“Hello, world”。在 Swift 中,可以用一行代码实现: +```swift println("Hello, world") +``` 如果你写过 C 或者 Objective-C 代码,那你应该很熟悉这种形式——在 Swift 中,这行代码就是一个完整的程序。你不需要为了输入输出或者字符串处理导入一个单独的库。全局作用域中的代码会被自动当做程序的入口点,所以你也不需要`main`函数。你同样不需要在每个语句结尾写上分号。 @@ -32,17 +34,21 @@ 使用`let`来声明常量,使用`var`来声明变量。一个常量的值在编译时并不需要获取,但是你只能为它赋值一次。也就是说你可以用常量来表示这样一个值:你只需要决定一次,但是需要使用很多次。 +```swift var myVariable = 42 myVariable = 50 let myConstant = 42 +``` 常量或者变量的类型必须和你赋给它们的值一样。然而,声明时类型是可选的,声明的同时赋值的话,编译器会自动推断类型。在上面的例子中,编译器推断出`myVariable`是一个整数(integer)因为它的初始值是整数。 如果初始值没有提供足够的信息(或者没有初始值),那你需要在变量后面声明类型,用冒号分割。 +```swift let implicitInteger = 70 let implicitDouble = 70.0 let explicitDouble: Double = 70 +``` > 练习: > @@ -50,20 +56,23 @@ 值永远不会被隐式转换为其他类型。如果你需要把一个值转换成其他类型,请显式转换。 +```swift let label = "The width is" let width = 94 let widthLabel = label + String(width) - +``` > 练习: > > 删除最后一行中的`String`,错误提示是什么? 有一种更简单的把值转换成字符串的方法:把值写到括号中,并且在括号之前写一个反斜杠。例如: +```swift let apples = 3 let oranges = 5 let appleSummary = "I have \(apples) apples." let fruitSummary = "I have \(apples + oranges) pieces of fruit." +``` > 练习: > @@ -71,29 +80,38 @@ 使用方括号`[]`来创建数组和字典,并使用下标或者键(key)来访问元素。 +```swift var shoppingList = ["catfish", "water", "tulips", "blue paint"] shoppingList[1] = "bottle of water" +``` +```swift var occupations = [ "Malcolm": "Captain", "Kaylee": "Mechanic", ] occupations["Jayne"] = "Public Relations" +``` 要创建一个空数组或者字典,使用初始化语法。 +```swift let emptyArray = String[]() let emptyDictionary = Dictionary() +``` 如果类型信息可以被推断出来,你可以用`[]`和`[:]`来创建空数组和空字典——就像你声明变量或者给函数传参数的时候一样。 +```swift shoppingList = [] // 去逛街并买点东西 +``` ## 控制流 使用`if`和`switch`来进行条件操作,使用`for-in`、`for`、`while`和`do-while`来进行循环。包裹条件和循环变量括号可以省略,但是语句体的大括号是必须的。 +```swift let individualScores = [75, 43, 103, 87, 12] var teamScore = 0 for score in individualScores { @@ -104,11 +122,13 @@ } } teamScore +``` 在`if`语句中,条件必须是一个布尔表达式——这意味着像`if score { ... }`这样的代码将报错,而不会隐形地与 0 做对比。 你可以一起使用`if`和`let`来处理值缺失的情况。有些变量的值是可选的。一个可选的值可能是一个具体的值或者是`nil`,表示值缺失。在类型后面加一个问号来标记这个变量的值是可选的。 +```swift var optionalString: String? = "Hello" optionalString == nil @@ -117,6 +137,7 @@ if let name = optionalName { greeting = "Hello, \(name)" } +``` > 练习: > @@ -126,6 +147,7 @@ `switch`支持任意类型的数据以及各种比较操作——不仅仅是整数以及测试相等。 +```swift let vegetable = "red pepper" switch vegetable { case "celery": @@ -137,6 +159,7 @@ default: let vegetableComment = "Everything tastes good in soup." } +``` > 练习: > @@ -146,6 +169,7 @@ 你可以使用`for-in`来遍历字典,需要两个变量来表示每个键值对。 +```swift let interestingNumbers = [ "Prime": [2, 3, 5, 7, 11, 13], "Fibonacci": [1, 1, 2, 3, 5, 8], @@ -160,6 +184,7 @@ } } largest +``` > 练习: > @@ -167,6 +192,7 @@ 使用`while`来重复运行一段代码直到不满足条件。循环条件可以在开头也可以在结尾。 +```swift var n = 2 while n < 100 { n = n * 2 @@ -178,9 +204,11 @@ m = m * 2 } while m < 100 m +``` 你可以在循环中使用`..`来表示范围,也可以使用传统的写法,两者是等价的: +```swift var firstForLoop = 0 for i in 0..3 { firstForLoop += i @@ -192,6 +220,7 @@ secondForLoop += 1 } secondForLoop +``` 使用`..`创建的范围不包含上界,如果想包含的话需要使用`...`。 @@ -200,10 +229,12 @@ 使用`func`来声明一个函数,使用名字和参数来调用函数。使用`->`来指定函数返回值。 +```swift func greet(name: String, day: String) -> String { return "Hello \(name), today is \(day)." } greet("Bob", "Tuesday") +``` > 练习: > @@ -211,13 +242,16 @@ 使用一个元组来返回多个值。 +```swift func getGasPrices() -> (Double, Double, Double) { return (3.59, 3.69, 3.79) } getGasPrices() +``` 函数的参数数量是可变的,用一个数组来获取它们: +```swift func sumOf(numbers: Int...) -> Int { var sum = 0 for number in numbers { @@ -227,6 +261,7 @@ } sumOf() sumOf(42, 597, 12) +``` > 练习: > @@ -234,6 +269,7 @@ 函数可以嵌套。被嵌套的函数可以访问外侧函数的变量,你可以使用嵌套函数来重构一个太长或者太复杂的函数。 +```swift func returnFifteen() -> Int { var y = 10 func add() { @@ -243,9 +279,11 @@ return y } returnFifteen() +``` -函数是一等公民,这意味着函数可以作为另一个函数的返回值。 +函数是第一等类型,这意味着函数可以作为另一个函数的返回值。 +```swift func makeIncrementer() -> (Int -> Int) { func addOne(number: Int) -> Int { return 1 + number @@ -254,9 +292,11 @@ } var increment = makeIncrementer() increment(7) +``` 函数也可以当做参数传入另一个函数。 +```swift func hasAnyMatches(list: Int[], condition: Int -> Bool) -> Bool { for item in list { if condition(item) { @@ -270,14 +310,17 @@ } var numbers = [20, 19, 7, 12] hasAnyMatches(numbers, lessThanTen) +``` 函数实际上是一种特殊的闭包,你可以使用`{}`来创建一个匿名闭包。使用`in`将参数和返回值类型声明与闭包函数体进行分离。 +```swift numbers.map({ (number: Int) -> Int in let result = 3 * number return result }) +``` > 练习: > @@ -285,23 +328,29 @@ 有很多种创建闭包的方法。如果一个闭包的类型已知,比如作为一个回调函数,你可以忽略参数的类型和返回值。单个语句闭包会把它语句的值当做结果返回。 +```swift numbers.map({ number in 3 * number }) +``` 你可以通过参数位置而不是参数名字来引用参数——这个方法在非常短的闭包中非常有用。当一个闭包作为最后一个参数传给一个函数的时候,它可以直接跟在括号后面。 +```swift sort([1, 5, 3, 12, 2]) { $0 > $1 } +``` ## 对象和类 使用`class`和类名来创建一个类。类中属性的声明和常量、变量声明一样,唯一的区别就是它们的上下文是类。同样,方法和函数声明也一样。 +```swift class Shape { var numberOfSides = 0 func simpleDescription() -> String { return "A shape with \(numberOfSides) sides." } } +``` > 练习: > @@ -309,12 +358,15 @@ 要创建一个类的实例,在类名后面加上括号。使用点语法来访问实例的属性和方法。 +```swift var shape = Shape() shape.numberOfSides = 7 var shapeDescription = shape.simpleDescription() +``` 这个版本的`Shape`类缺少了一些重要的东西:一个构造函数来初始化类实例。使用`init`来创建一个构造器。 +```swift class NamedShape { var numberOfSides: Int = 0 var name: String @@ -327,6 +379,7 @@ return "A shape with \(numberOfSides) sides." } } +``` 注意`self`被用来区别实例变量。当你创建实例的时候,像传入函数参数一样给类传入构造器的参数。每个属性都需要赋值——无论是通过声明(就像`numberOfSides`)还是通过构造器(就像`name`)。 @@ -336,6 +389,7 @@ 子类如果要重写父类的方法的话,需要用`override`标记——如果没有添加`override`就重写父类方法的话编译器会报错。编译器同样会检测`override`标记的方法是否确实在父类中。 +```swift class Square: NamedShape { var sideLength: Double @@ -356,6 +410,7 @@ let test = Square(sideLength: 5.2, name: "my test square") test.area() test.simpleDescription() +``` > 练习: > @@ -363,6 +418,7 @@ 属性可以有 getter 和 setter 。 +```swift class EquilateralTriangle: NamedShape { var sideLength: Double = 0.0 @@ -389,6 +445,7 @@ triangle.perimeter triangle.perimeter = 9.9 triangle.sideLength +``` 在`perimeter`的 setter 中,新值的名字是`newValue`。你可以在`set`之后显式的设置一个名字。 @@ -402,6 +459,7 @@ 比如,下面的类确保三角形的边长总是和正方形的边长相同。 +```swift class TriangleAndSquare { var triangle: EquilateralTriangle { willSet { @@ -423,9 +481,11 @@ triangleAndSquare.triangle.sideLength triangleAndSquare.square = Square(sideLength: 50, name: "larger square") triangleAndSquare.triangle.sideLength +``` 类中的方法和一般的函数有一个重要的区别,函数的参数名只在函数内部使用,但是方法的参数名需要在调用的时候显式说明(除了第一个参数)。默认情况下,方法的参数名和它在方法内部的名字一样,不过你也可以定义第二个名字,这个名字被用在方法内部。 +```swift class Counter { var count: Int = 0 func incrementBy(amount: Int, numberOfTimes times: Int) { @@ -434,17 +494,21 @@ } var counter = Counter() counter.incrementBy(2, numberOfTimes: 7) +``` 处理变量的可选值时,你可以在操作(比如方法、属性和子脚本)之前加`?`。如果`?`之前的值是`nil`,`?`后面的东西都会被忽略,并且整个表达式返回`nil`。否则,`?`之后的东西都会被运行。在这两种情况下,整个表达式的值也是一个可选值。 +```swift let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square") let sideLength = optionalSquare?.sideLength +``` ## 枚举和结构体 使用`enum`来创建一个枚举。就像类和其他所有命名类型一样,枚举可以包含方法。 +```swift enum Rank: Int { case Ace = 1 case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten @@ -466,6 +530,7 @@ } let ace = Rank.Ace let aceRawValue = ace.toRaw() +``` > 练习: > @@ -475,12 +540,15 @@ 使用`toRaw`和`fromRaw`函数来在原始值和枚举值之间进行转换。 +```swift if let convertedRank = Rank.fromRaw(3) { let threeDescription = convertedRank.simpleDescription() } +``` 枚举的成员值是实际值,并不是原始值的另一种表达方法。实际上,如果原始值没有意义,你不需要设置。 +```swift enum Suit { case Spades, Hearts, Diamonds, Clubs func simpleDescription() -> String { @@ -499,6 +567,7 @@ } let hearts = Suit.Hearts let heartsDescription = hearts.simpleDescription() +``` > 练习: > @@ -509,6 +578,7 @@ 使用`struct`来创建一个结构体。结构体和类有很多相同的地方,比如方法和构造器。它们之间最大的一个区别就是 结构体是传值,类是传引用。 +```swift struct Card { var rank: Rank var suit: Suit @@ -519,6 +589,7 @@ } let threeOfSpades = Card(rank: .Three, suit: .Spades) let threeOfSpadesDescription = threeOfSpades.simpleDescription() +``` > 练习: > @@ -528,6 +599,7 @@ 例如,考虑从服务器获取日出和日落的时间。服务器会返回正常结果或者错误信息。 +```swift enum ServerResponse { case Result(String, String) case Error(String) @@ -542,6 +614,7 @@ case let .Error(error): let serverResponse = "Failure... \(error)" } +``` > 练习: > @@ -554,13 +627,16 @@ 使用`protocol`来声明一个接口。 +```swift protocol ExampleProtocol { var simpleDescription: String { get } mutating func adjust() } +``` 类、枚举和结构体都可以实现接口。 +```swift class SimpleClass: ExampleProtocol { var simpleDescription: String = "A very simple class." var anotherProperty: Int = 69105 @@ -581,6 +657,7 @@ var b = SimpleStructure() b.adjust() let bDescription = b.simpleDescription +``` > 练习: > @@ -590,6 +667,7 @@ 使用`extension`来为现有的类型添加功能,比如添加一个计算属性的方法。你可以使用扩展来给任意类型添加协议,甚至是你从外部库或者框架中导入的类型。 +```swift extension Int: ExampleProtocol { var simpleDescription: String { return "The number \(self)" @@ -599,6 +677,7 @@ } } 7.simpleDescription +``` > 练习: > @@ -606,9 +685,11 @@ 你可以像使用其他命名类型一样使用接口名——例如,创建一个有不同类型但是都实现一个接口的对象集合。当你处理类型是接口的值时,接口外定义的方法不可用。 +```swift let protocolValue: ExampleProtocol = a protocolValue.simpleDescription // protocolValue.anotherProperty // Uncomment to see the error +``` 即使`protocolValue`变量运行时的类型是`simpleClass`,编译器会把它的类型当做`ExampleProtocol`。这表示你不能调用类在它实现的接口之外实现的方法或者属性。 @@ -617,6 +698,7 @@ 在尖括号里写一个名字来创建一个泛型函数或者类型。 +```swift func repeat(item: ItemType, times: Int) -> ItemType[] { var result = ItemType[]() for i in 0..times { @@ -625,9 +707,11 @@ return result } repeat("knock", 4) +``` 你也可以创建泛型类、枚举和结构体。 +```swift // Reimplement the Swift standard library's optional type enum OptionalValue { case None @@ -635,9 +719,11 @@ } var possibleInteger: OptionalValue = .None possibleInteger = .Some(100) +``` 在类型名后面使用`where`来指定一个需求列表——例如,要限定实现一个协议的类型,需要限定两个类型要相同,或者限定一个类必须有一个特定的父类。 +```swift func anyCommonElements (lhs: T, rhs: U) -> Bool { for lhsItem in lhs { for rhsItem in rhs { @@ -649,6 +735,7 @@ return false } anyCommonElements([1, 2, 3], [3]) +``` > 练习: >