First Tour: About Swift (一)

First, You dont need import(导入) a separate library(单独的库) for functionality(功能) like input/output(输入输出) or string handling(处理字符串).

so

1
print("hellow world")

is a complete program.


Simple Value

Use let make a constant and use var make a variable.

The value of a constant doesnt need to be known at compile(编译) time, but you must assgin(赋值) it a value exactly(强调精准性) once.

You can use constants to name(声明) a value that you determine once but use in many places.

A constant or variable must have the same type as the value you want to assign(the value) to it.

You don’t always have to write the type explicitly(清楚地), just Providing a value when you create a constant or variable and lets the compiler infer(推断) its type.

If the initial value doesn’t provide enough information (or if there is no initial value), specify(指定) the type by writing it after the variable, separated by a colon(冒号).

1
2
3
let integerValue = 70
let doubleValue = 70.0
let doubleValue2: Double = 70

EXPERIMENT:

Create a constant with an explicit type of float and a value of 4.

1
let floatValue: Float = 4

Values are never implicitly(隐式地) converted to another type, if you want to convert it to another type plz explicitly make an instance(实例) of the desired(需求的) type like:

1
2
3
let label = "the width is "
let width = 94
let widthLabel = label + String(width) // "the width is 94"

EXPERIMENT:

Try removing the conversion to String from the last line. What error do you get?

1
// Binary(二元) operator(操作符) '+' cannt be applied(应用) to operands(操作) of type 'String' and 'int'

the easy way to include values in strings:Write the value in parentheses(括号), and write a backslash() before the parentheses.

1
2
3
4
5
let apples = 3
let oranges = 5
let appleSummary = "I have \(apples) apples."
let fruitSummary = "I have \(apples + oranges) pieces of fruit."
// "I have 8 pieces of fruit."

EXPERIMENT:

Use () to include(包含) a floating-point calculation(计算) in a string and to include someone’s name in a greeting.

1
2
3
let floatValue: Float = 3.5
let name: String = "Jack"
let stringValue = "\(name)'s height is \(floatValue)." // "Jack's height is 3.5."

Array and Dictionary

To create an empty array or dictionary, use the initializer syntax.

1
2
let emptyArray = [String]()
let emptyDictionary = [String: Float]()

If type information can be inferred, you can write an empty array as [] and an empty dictionary as [:]—for example, when you set a new value for a variable or pass(传递) an argument(参数) to a function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var shoppingList = ["catfish", "water", "tulips", "blue paint"]
shoppingList[1] = "bottle of water"

var occupations = [
"Malcolm": "Captain",
"Kaylee": "Mechanic",
]
occupations["Jayne"] = "Public Relations"

shoppingList = []

occupations = [:]

print(shoppingList)
print(occupations)

Control Flow

if and switch make conditionals.

for-in, for, while, repeat-while make loops.

Parentheses(括号) around the condition or loop variable are optional(可以省略). Braces(大括号) around the body are required.

1
2
3
4
5
6
7
8
9
10
let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in individualScores {
if score > 50 {
teamScore += 3
} else {
teamScore += 1
}
}
print(teamScore)

If

In an if statement, the conditional must be a Boolean expression—this means that code such as if score { ... } is an error, not an implicit(隐性) comparison(比较) to zero.

条件语句中的判断语句必须是 Boolean 类型的,要么是1(true)要么是0(flase),不能是其他值

You can use if and let together to work with values that might be missing. These values are represented(被作为) as optionals. An optional value either contains a value or contains nil to indicate(表示) that a value is missing. Write a question mark (?) after the type of a value to mark the value as optional.

1
2
3
4
5
6
7
8
9
var optionalString: String? = "Hello"
print(optionalString == nil)

var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
// 可选变量绑定if的意思是表示if中语句一定在可选类型变量有值时才运行,没值则跳过,语法比较严谨
greeting = "Hello, \(name)"
}

EXPERIMENT:

Change optionalName to nil. What greeting do you get? Add an else clause(从句) that sets a different greeting if optionalName is nil.

1
2
3
4
5
6
7
8
9
10
optionalName = nil

if let name = optionalName
{
greeting = "Hello, \(name)"
} else {
greeting = "hi"
}

print(greeting)

If the opertional value is nil, the conditional is flase and the code in braces is skipped(跳过).
Otherwise, the optional value is unwrapped(被打开) and assigned to the constant after let, which makes the unwrapped value available inside the block of code.

Another way to handle optional values is to provide(提供) a default value using ?? operator.If the optional value is missing, the default value is used instead.

1
2
3
let nickName: String? = nil
let fullName: String = "John"
let informalGreeting = "hi \(nickName ?? fullName)" // "hi John"

Switch

Switch support any kind of data and a wide variety(广泛的) of comparison(比较) operations(操作)——they arent limited to integers and tests(例子) for equality(相等)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let vegetable = "red pepper"

switch vegetable
{
case "celery":
print("add some raisins.")
case "cucumber", "watercress":
print("good.")
// where有点当的意思
case let x where x.hasSuffix("pepper"):
print("is it \(x)?")
default:
print("sss")
}

EXPERIMENT:

Try removing the default case. What error do you get?

1
the error is Switch must be exhaustive(全面), consider adding a default clause(从句)

Notice how let can be used in a pattern(模型) to assign the value that matched that part of a pattern to a constant. (match…to… 将…和…匹配)

翻译:注意如何将常量用作一个模型,把模型中的一部分和一个常量进行匹配,将匹配的结果作为值(true/false)

There is no need to explicitly(明确的) break out of the switch at end of each case’code.

for-in

You use for-in to iterate(重复) over items in a dictionary by providing a pair(一对) of names to use for each key-value pair. Dictionaries are an unordered(无序) collection(集合), so their keys and values are iterated over in an arbitrary(随意的) order.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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
}
}
}

print(largest)

EXPERIMENT:

Add another variable to keep track of(记录) which kind of number was the largest, as well as what that largest number was.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var largest = 0
var largestKind: String?

for (kind, numbers) in interestingNumbers
{
for number in numbers
{
if number > largest
{
largest = number
largestKind = kind
}
}
}

print(largest)
print(largestKind) // Optional("Square")\n

while

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var n = 2

while n < 100
{
n = n * 2
}

print(n)

var m = 2

repeat
{
m = m * 2
} while m < 100

print(m)

You can set an index in a loop, either by using ..< to make a range of indexes or writing a explicit initialization(初始化), condition(判断条件), and increment(自增). These two loops do the same thing:

1
2
3
4
5
6
7
8
9
10
11
var firstForLoop = 0
for i in 0..<4 {
firstForLoop += i
}
print(firstForLoop)

var secondForLoop = 0
for var i = 0; i < 4; ++i {
secondForLoop += i
}
print(secondForLoop)

Use ..< to make a range that omits(省略) its upper value(上界), and use … to make a range that includes both values.

Functions and Closures(闭包)

Use func to declare(声明) a function. The following is its name and argument is in parentheses. Use -> to separate(隔开) the parameter names and types from the function’s return type.

1
2
3
4
5
6
7
func greet(name: String, day: String) ->String
{
return "hellow \(name), today is \(day)."

}

greet("bob", day: "tuesday")

EXPERIMENT:

Remove the day parameter. Add a parameter to include today’s lunch special in the greeting.

1
2
3
4
5
6
7
func greet(name: String, lunch: String) ->String
{
return "hellow \(name), today we eat \(lunch)."

}

greet("bob", lunch: "meat")

Use a tuple(元祖) to make a compound(混合) value—for example, to return multiple values from a function. The elements of a tuple can be referred to(用…来表示) either by name or by number.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
func calculateStatistics(scores: [Int]) ->(min: Int, max: Int, sum: Int)
{
var min = scores[0]
var max = scores[0]
var sum = 0

for score in scores
{
if score > max
{
max = score
} else if score < min {
min = score
}

sum += score
}

return (min, max, sum)
}

let statistics = calculateStatistics([5, 3, 100, 3, 9])

// (.0 3, .1 100, .2 120)
print(statistics)
print(statistics.sum)
print(statistics.2)

Functions can also take a variable number(个数) of arguments, collecting them into an array.

1
2
3
4
5
6
7
8
9
10
11
12
func sumOf(numbers: Int...) -> Int
{
var sum = 0
for number in numbers
{
sum += number
}
return sum
}

sumOf()
sumOf(42, 597, 12)

EXPERIMENT:

Write a function that calculates the average of its arguments.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func averageOf(numbers: Int...) -> Double
{
var sum = 0
var result: Double
var count = 0

for number in numbers
{
sum += number
count++
}

result = (Double)(sum) / (Double)(count)

return result
}

averageOf(2, 1, 3, 5)

Functions can be nested(嵌套). Nested functions(被嵌套的) have access to(允许使用) variables that were declared(声明) in the outer function(声明在外部方法的). You can use nested functions to organize(组织) the code in a function that is long or complex(复杂).

1
2
3
4
5
6
7
8
9
func returnFifteen() -> Int {
var y = 10
func add() {
y += 5
}
add()
return y
}
returnFifteen()

A function can return another function as its value.

1
2
3
4
5
6
7
8
9
10
func makeIncrementer() -> ((Int) -> Int) {
func addOne(number: Int) -> Int {
return 1 + number
}
return addOne
}

// increment就是addOne方法
var increment = makeIncrementer()
increment(7)

A function can take another function as one of its arguments.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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, condition: lessThanTen)

Functions are actually a special case of closures: blocks of code that can be called later.The code in a closure has access to(接近) things like variables and functions that were available(有效的) in the scope(地方) where the closure was created, even if the closure is in a different scope when it is executed—you saw an example of this already with nested functions. You can write a closure without a name by surrounding code with braces ({}). Use in to separate the arguments and return type from the body.

翻译:方法实际上是一种特殊的闭包:代码块可以在后面被调用。当闭包中的代码执行的时候,即使闭包在别的地方被创建,闭包中的代码比如变量和方法也是有效的。你在方法嵌套中已经看到了这个例子。你可以用{}写一个没有名字的闭包。用in来将参数和返回值类型与闭包的内的代码分开。

EXPERIMENT:

Rewrite the closure to return zero for all odd numbers.

1
2
3
4
5
6
7
8
9
10
var numbers = [20, 19, 7, 12]

// numbers.map表示遍历numbers里每一个元素
let result = numbers.map({
(number: Int) -> Int in
let result = number % 2 == 1 ? 0 : number
return result
})

print(result) // [20, 0, 0, 12]\n

You have several options for writing closures more concisely(简明的). When a closure’s type is already known, such as the callback for a delegate, you can omit(省略) the type of its parameters, its return type, or both. Single statement(单个语句) closures implicitly return the value of their only statement.

当闭包的类型已经知道的时候(代理的回调函数),可以省略参数类型,返回值类型;闭包中只有一个语句,可以省略return,并且会把那个语句的值作为返回值

1
2
let mappedNumbers = numbers.map({ number in 3 * number })
print(mappedNumbers)

You can refer to parameters by number instead of by name—this approach(方法) is especially useful in very short closures. A closure passed(被传递) as the last argument to a function can appear immediately after the parentheses. When a closure is the only argument to a function, you can omit the parentheses entirely.

可以通过参数位置指定参数,如果闭包作为一个方法的最后一个参数,可以直接跟在括号后面,如果闭包是一个方法唯一的参数,可以省略括号

1
2
let sortedNumbers = numbers.sort { $0 < $1 }
print(sortedNumbers)

Objects and Classes

Use class followed by the class’s name to create a class. A property declaration in a class is written the same way as a constant or variable declaration, except that it is in the context of a class(区别property在类中). Likewise(同样的), method and function declarations are written the same way.

1
2
3
4
5
6
7
8
class Shape {
// 属性
var numberOfSides = 0
// 方法
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}

EXPERIMENT:

Add a constant property with let, and add another method that takes an argument.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Shape {
// 属性
var numberOfSides = 0
let numberOfShape = 1
// 方法
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides and the shape is \(numberOfShape)."
}

func shapeDescription(shape: String) -> String
{
return "\(numberOfShape)" + shape
}
}

Create an instance of a class by putting parentheses after the class name. Use dot(点) syntax(语法) to access the properties and methods of the instance.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Shape {
// 属性
var numberOfSides = 0
let numberOfShape = 1
// 方法
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides and the shape is \(numberOfShape)."
}

func shapeDescription(shape: String) -> String
{
return "\(numberOfShape)" + shape
}
}

var shape = Shape()

let str = shape.shapeDescription("1woka1")

Create an instance of a class by putting parentheses after the class name. Use dot syntax(点语法) to access(访问) the properties and methods of the instanc

1
2
3
var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()

This version of the Shape class is missing something important: an initializer(构造方法) to set up the class when an instance is created. Use init to create one.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let shape = Shape(name: "11")

class NamedShape {
var numberOfSides: Int = 0
var name: String

init(name: String) {
self.name = name
}

func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}

Notice how self is used to distinguish the name property from the name argument to the initializer. The arguments to the initializer are passed like a function call when you create an instance of the class. Every property needs a value assigned—either in its declaration (as with numberOfSides) or in the initializer (as with name).
在你创建一个类的实例的时候,不管是声明还是在构造方法中的属性,所有的属性都必须被赋值,构造方法的参数被传递就像方法调用一样。注意self如何从构造函数的参数和类的属性中区别 name

Use deinit to create a deinitializer if you need to perform(执行) some cleanup before the object is deallocated.
deinit方法被用于在对象被销毁前完成一些清理工作

Subclasses include their superclass name after their class name, separated by a colon. There is no requirement(要求,必要) for classes to subclass(成为…的子类) any standard root class, so you can include or omit a superclass as needed.
没有必要成为标准的根类的子类,所以需要时你可以包含或者省略父类,也就是说不用继承与NSObject

Methods on a subclass that override the superclass’s implementation are marked with override—overriding a method by accident(偶尔), without override, is detected(被检测到) by the compiler as an error. The compiler also detects methods with override that don’t actually override any method in the superclass.
子类重写父类方法时,需要加关键字override,没有加编译器会报错,同样,编译器也会对加了override的方法进行检测,看是不是真的覆盖了父类中的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

class NamedShape {
var numberOfSides: Int = 0
var name: String

init(name: String) {
self.name = name
}

func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}

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)."
}
}
let test = Square(sideLength: 5.2, name: "my test square")
test.area()
test.simpleDescription()

EXPERIMENT:

Make another subclass of NamedShape called Circle that takes a radius and a name as arguments to its initializer. Implement an area() and a simpleDescription() method on the Circle class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class NamedShape {
var numberOfSides: Int = 0
var name: String

init(name: String) {
self.name = name
}

func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}

class Circle: NamedShape {
var randius: Double
init(randius: Double, name: String)
{
self.randius = randius

super.init(name: name)
}

func area() -> Double
{
var area: Double = 0
area = randius * randius * M_PI

return area
}

override func simpleDescription() -> String {
return "this circle's randius is \(randius)"
}
}

let c = Circle(randius: 1.5, name: "circle")

print(c.area())
print(c.simpleDescription())

In addition to(除了…之外还) simple properties that are stored(存储), properties can have a getter and a setter.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class NamedShape {
var numberOfSides: Int = 0
var name: String

init(name: String) {
self.name = name
}

func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}

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 triangle with sides of length \(sideLength)."
}
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
print(triangle.perimeter)
triangle.perimeter = 9.9 // 设置周长时,边长会变
print(triangle.sideLength)

In the setter for perimeter(周长), the new value has the implicit name newValue. You can provide an explicit name in parentheses after set.

Notice that the initializer for the EquilateralTriangle class has three different steps:

  1. Setting the value of properties that the subclass declares(声明).

  2. Calling the superclass’s initializer.

  3. Changing the value of properties defined by the superclass. Any additional setup work that uses methods, getters, or setters can also be done at this point.

If you don’t need to compute the property but still need to provide code that is run before and after setting a new value, use willSet and didSet. The code you provide is run any time the value changes outside of an initializer. For example, the class below ensures that the side length(边长) of its triangle is always the same as the side length of its square.

如果你不要计算属性值,但是需要在属性值改变前后做一些事情,请用willSet和didSet,你提供的代码会在构造方法之外的值改变时运行,举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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)
}
}
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
print(triangleAndSquare.square.sideLength)
print(triangleAndSquare.triangle.sideLength)
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
print(triangleAndSquare.triangle.sideLength)

When working with optional values, you can write ? before operations(运算操作) like methods, properties, and subscripting(下标). If the value before the ? is nil, everything after the ? is ignored and the value of the whole expression(整个表达式) is nil. Otherwise, the optional value is unwrapped(被打开), and everything after the ? acts on the unwrapped value. In both cases, the value of the whole expression is an optional value.

1
2
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength

Enumerations and Structures

Use enum to create an enumeration. Like classes and all other named types, enumerations can have methods associated(联合关联) with them.

EXPERIMENT:

Write a function that compares two Rank values by comparing their raw values.”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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.rawValue)
}
}
func compareWithRankValue(rank: Rank) -> Rank {
return self.rawValue > rank.rawValue ? self : rank
}
}
let ace = Rank.Ace
let aceRawValue = ace.rawValue
let aceDes = ace.simpleDescription()
let two = Rank.Two
let twoDes = Rank.Two.simpleDescription()

let compareResult = ace.compareWithRankValue(two) // Two

In the example above, the raw-value type of the enumeration is Int, so you only have to specify(指定) the first raw value. The rest of the raw values are assigned in order(按顺序). You can also use strings or floating-point numbers as the raw type of an enumeration. Use the rawValue property to access the raw value of an enumeration case.

Use the init?(rawValue:) initializer to make an instance of an enumeration from a raw value.

1
2
3
4
if let rank3 = Rank(rawValue: 3)
{
rank3.simpleDescription()
}

The case values of an enumeration are actual values, not just another way of writing their raw(未加工的) values. In fact, in cases where there isn’t a meaningful raw value, you don’t have to provide one.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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()

EXPERIMENT:

Add a color() method to Suit that returns “black” for spades and clubs, and returns “red” for hearts and diamonds.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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"
}
}
func color() -> String
{
switch self {
case .Spades, .Clubs:
return "black"
case .Hearts, .Diamonds:
return "red"
}
}
}


let color = Suit.Hearts.color()

Notice the two ways that the Hearts case of the enumeration is referred to above: When assigning a value to the hearts constant, the enumeration case Suit.Hearts is referred to by its full name because the constant doesn’t have an explicit type specified. Inside the switch, the enumeration case is referred to by the abbreviated(简短的) form(形式) .Hearts because the value of self is already known to be a suit. You can use the abbreviated form anytime the value’s type is already known.

翻译:引用Suit.Hearts的两种形式,用常量表示Suit.Hearts时,没有显性指定常量的类型,因此需要用全名(类型.属性)。在switch语句中,我们已经知道case的类型了,因此可以用.Hearts来引用

Use struct to create a structure. Structures support many of the same behaviors as classes, including methods and initializers. One of the most important differences between structures and classes is that structures are always copied when they are passed around in your code, but classes are passed by reference.

结构体被在你的代码中被传递时总是被拷贝,而classes被传递时是被引用的

EXPERIMENT:

Add a method to Card that creates a full deck of cards, with one card of each combination(结合) of rank and suit.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
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.rawValue)
}
}
func compareWithRankValue(rank: Rank) -> Rank {
return self.rawValue > rank.rawValue ? self : rank
}
}

enum Suit: Int {
case Spades = 0, Hearts, Diamonds, Clubs
func simpleDescription() -> String {
switch self {
case .Spades:
return "spades"
case .Hearts:
return "hearts"
case .Diamonds:
return "diamonds"
case .Clubs:
return "clubs"
}
}
func color() -> String
{
switch self {
case .Spades, .Clubs:
return "black"
case .Hearts, .Diamonds:
return "red"
}
}
}

class Card: NSObject {

var rank: Rank
var suit: Suit
func simpleDescription() -> String {
return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
}

override var description :String {
get {
return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
}
}

init(rank: Rank, suit: Suit)
{
self.rank = rank
self.suit = suit
}

static func createFullDeckOfCards() -> [Card]
{
var cards: Array<Card> = []
for var i = 0; i < 4; i++
{
for var j = 1; j <= 13; j++
{
cards += [Card(rank: Rank(rawValue: j)!, suit: Suit(rawValue: i)!)]
}
}

return cards
}
}

let cards = Card.createFullDeckOfCards()
for card in cards
{
print(card.description)
}

An instance of an enumeration case can have values associated(绑定值) with the instance. Instances of the same “enumeration case can have different values associated with them. You provide the associated values when you create the instance. Associated values and raw values are different: The raw value(原始值) of an enumeration case is the same for all of its instances, and you provide the raw value when you define the enumeration.

绑定值是无意义的比如0,1,2…,一个枚举类的原始值都是相同的,每个枚举对象都有其绑定值,两个相同的原始值的枚举对象可能有不同的绑定值。比如 Rank 中的 Ace 是原始值,绑定值是0,而另一副牌的 Ace 绑定值是1

For example, consider the case of requesting(请求) the sunrise and sunset time from a server. The server either responds with the information or it responds with some error information.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
enum ServerResponse {
case Result(String, String)
case Error(String)
case
}

let success = ServerResponse.Result("6:00 am", "8:09 pm")
let failure = ServerResponse.Error("Out of cheese.")

switch success {
case let .Result(sunrise, sunset):
print("Sunrise is at \(sunrise) and sunset is at \(sunset).")
case let .Error(error):
print("Failure... \(error)")
}

EXPERIMENT:

Add a third case to ServerResponse and to the switch.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
enum ServerResponse {
case Result(String, String)
case Error(String)
case waitForResponse(String)
}

let success = ServerResponse.Result("6:00 am", "8:09 pm")
let failure = ServerResponse.Error("Out of cheese.")
let wait = ServerResponse.waitForResponse("wait for response.")

// 提取关联值优化写法
switch success {
case .Result(let sunrise, let sunset):
print("Sunrise is at \(sunrise) and sunset is at \(sunset).")
case let .Error(error):
print("Failure... \(error)")
case let .waitForResponse(wait):
print("wait...\(wait)")
}

提取关联值的优化写法

Protocols and Extensions

Use protocol to declare a protocol.

Classes, enumerations, and structs can all adopt protocols.
类、枚举、结构体都要执行协议

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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."
}
}
var a = SimpleClass()
a.adjust()
let aDescription = a.simpleDescription

struct SimpleStructure: ExampleProtocol {
var simpleDescription: String = "A simple structure"
mutating func adjust() {
simpleDescription += " (adjusted)"
}
}
var b = SimpleStructure()
b.adjust()
let bDescription = b.simpleDescription

EXPERIMENT:

Write an enumeration that conforms to this protocol.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
enum SimpleEnumeration : ExampleProtocol
{
case first(String), second(String), third(String)
var simpleDescription : String {
get {
switch self {
case .first(let f):
return f
case .second(let s):
return s
case .third(let t):
return t
}
}
set {
switch self {
case .first(_):
self = .first(newValue)
case .second(_):
self = .second(newValue)
case .third(_):
self = .third(newValue)
}
}
}
mutating func adjust() {
simpleDescription += "adjusted"
}
}

var e = SimpleEnumeration.first("hi")
e.simpleDescription
e.adjust()
e.simpleDescription
e.simpleDescription = "22"

Notice the use of the mutating keyword in the declaration of SimpleStructure to mark a method that modifies(修改) the structure. The declaration of SimpleClass doesn’t need any of its methods marked as mutating because methods on a class can always modify the class.
mutating有了这个关键字表示方法内部可以标记一个会修改结构体实例的方法,另外,在值类型的方法中也可以修改self的值,在类中的方法不需要该关键字

Use ·extension· to add functionality(功能性) to an existing type, such as new methods and computed properties. You can use an extension to add protocol conformance(顺应) to a type that is declared elsewhere, or even to a type that you imported from a library or framework.

给已有的类型增加扩展和功能

1
2
3
4
5
6
7
8
9
extension Int: ExampleProtocol {
var simpleDescription: String {
return "The number \(self)"
}
mutating func adjust() {
self += 42
}
}
print(7.simpleDescription)

EXPERIMENT:

Write an extension for the Double type that adds an absoluteValue property.

1
2
3
4
5
6
7
8
9
extension Double : SimpleProtocol {
var absoluteValue : Double {
get {
return self >= 0 ? self : -self
}
}
}

(-5.9).absoluteValue

You can use a protocol name just like any other named type—for example, to create a collection of objects that have different types but that all conform(遵守) to a single protocol. When you work with values whose type is a protocol type, methods outside the protocol definition are not available.

你可以想使用其他类型名一样使用协议名,比如创建一个对象集合,这些对象有着不同的类型但是全部都遵守同一个协议。当你操作遵守同一个协议类型的值,哪些协议里没有定义的方法将不可用

1
2
3
let protocolValue: ExampleProtocol = a
print(protocolValue.simpleDescription)
// print(protocolValue.anotherProperty) // Uncomment to see the error”

Even though the variable protocolValue has a runtime type of SimpleClass, the compiler treats it as the given type of ExampleProtocol. This means that you can’t accidentally(故意的) access(访问) methods or properties that the class implements in addition to(除了…之外) its protocol conformance.

虽然运行时,protocolValue 的类型是SimpleClass,但是编译器会将其当做 ExampleProtocol

Generics

Write a name inside angle brackets(尖括号) to make a generic function(泛型方法) or type.

1
2
3
4
5
6
7
8
func repeatItem<Item>(item: Item, numberOfTimes: Int) -> [Item] {
var result = [Item]()
for _ in 0..<numberOfTimes {
result.append(item)
}
return result
}
repeatItem("knock", numberOfTimes:4)

You can make generic forms of functions and methods, as well as classes, enumerations, and structures.

除了泛型函数和方法,你也可以创建泛型类,枚举和结构体

1
2
3
4
5
6
7
// Reimplement the Swift standard library's optional type(重新实现swift标准库的可选类型)
enum OptionalValue<Wrapped> {
case None
case Some(Wrapped)
}
var possibleInteger: OptionalValue<Int> = .None
possibleInteger = .Some(100)

Use where after the type name to specify a list of requirements—for example, to require the type to implement a protocol, to require two types to be the same, or to require a class to have a particular superclass.

要求这个类型必须遵守某个协议,来满足两个类型一致,或者要求一个类有一个特定的父类时,用where在类型名后来指定一系列对类型的要求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func anyCommonElements <T: SequenceType, U: SequenceType 
where T.Generator.Element: Equatable,
T.Generator.Element == U.Generator.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])

注:使用 Equatable协议 的类的对象,是可以用 == 判断其值是否相等,T.Generator.Element == U.Generator.Element 这句话表示他们的 Generator.Element 类型相等,rhs 前面的 _ 可以使调用时省略(参数名:)

Writing <T: Equatable> is the same as writing <T where T: Equatable>.


这个例子主要说明如何对泛型类型做约束,比如第一要求是 T 和 U 必须遵守 Sequence协议。然后 T 必须含有 GeneratorType 类型且 GeneratorType 类型必须含有Element 类型,而 Element 类型要遵守 Equatble 协议,最后提到了 T 和 U 的两个 Element 类型必须相同。Swift是第一个看到能用等号来比较类型的不。

对于具体函数的调用,编译器会在编译时做类型检查,不满足类型约束的则会马上报错

EXPERIMENT:

Modify(修改) the anyCommonElements(::) function to make a function that returns an array of the elements that any two sequences(序列) have in common(共有).
翻译:修改anyCommonElements函数,写一个函数返回两个序列共有元素的数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

func anyCommonElements <T: SequenceType, U: SequenceType
where T.Generator.Element: Equatable,
T.Generator.Element == U.Generator.Element>
(lhs: T, rhs: U) -> Array<T.Generator.Element>
{
// 这里不能换成var common = [T.Generator.Element]()
// 因为T.Generator.Element类型没有alloc init方法

var common: Array<T.Generator.Element> = []

for lhsItem in lhs
{
for rhsItem in rhs
{
if lhsItem == rhsItem
{
common += [lhsItem]
}
}
}
return common
}
let common = anyCommonElements([1, 2, 3], rhs: [3])
文章作者: Ammar
文章链接: http://lizhaoloveit.cn/2015/07/07/First-Tour-About-Swift-%E4%B8%80/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Ammar's Blog
打赏
  • 微信
  • 支付宝

评论