Swift

Table of Contents

1 Swift

1.1 Swift历史版本

Swift历史版本如表 1 所示。

Table 1: Swift Version History
Date Version
2014-09-09 Swift 1.0
2014-10-22 Swift 1.1
2015-04-08 Swift 1.2
2015-09-21 Swift 2.0
2016-09-13 Swift 3.0
2017-09-19 Swift 4.0
2018-03-29 Swift 4.1
2018-09-17 Swift 4.2

1.2 Hello World

下面是Swift版本的Hello World程序:

print("Hello World!")

测试运行如下(假设文件名为hello.swift):

$ swift hello.swift
Hello World!

使用 swiftc 可以把源码编译为可执行程序,再执行,如:

$ swiftc hello.swift -o hello
$ ./hello
Hello World!

2 Swift基本介绍

2.1 变量(var)和常量(let)

关键字 var 用于定义变量,而关键字 let 用于定义常量。

let maximumNumberOfLoginAttempts = 10     // 常量
var currentLoginAttempt = 0               // 变量

一般地,编译器可以推导出常量或常量的类型,所以往往你可以不显式地指定类型,也可以显式地指定类型,如:

let implicitInteger = 70           // 会推导出常量类型为Int
let implicitDouble = 70.0          // 会推导出常量类型为Double
let explicitDouble: Double = 70    // 明确指定类型为Double

2.2 注释

Swift采用了类似C语言的注释。块注释以 /* 开始, */ 结束;行注释以 // 开始。

2.3 分号

Swift不要求每个语句结尾都有分号,只有当一行有多个语句时才要求用分号分隔它们。

3 基本数据类型

3.1 数字类型

Swift有下面数字类型:

Int8
UInt8
Int16
UInt16
Int32
UInt32
Int64
UInt64
Float
Double

3.2 Booleans类型(Bool)

布尔类型用关键字 Bool 表示,它有两个字面量: truefalse

3.3 Tuples类型

Tuples类型用于组合多个成分,它用 () 表示,各个成分之间用 , 分开,如:

let http404Error = (404, "Not Found")          // http404Error的类型为 (Int, String)

let (statusCode, statusMessage) = http404Error
print(statusCode)                              // 输出 404
print(statusMessage)                           // 输出 Not Found

如果不关心Tuple的某个成分,可以用 _ 表示,如:

let http404Error = (404, "Not Found")

let (justTheStatusCode, _) = http404Error

可以使用从零开始的索引来访问Tuple的成分,如:

let http404Error = (404, "Not Found")

print(http404Error.0)                           // 输出 404
print(http404Error.1)                           // 输出 Not Found

也可以对Tuple的成分进行命名,如:

let http404Error = (statusCode: 404, description: "Not Found")

print(http404Error.statusCode)                  // 输出 404
print(http404Error.description)                 // 输出 Not Found

3.3.1 Tuples类型支持比较操作

可以使用“比较操作符”对Tuples类型变量进行测试,如:

(1, "zebra") < (2, "apple")   // true because 1 is less than 2; "zebra" and "apple" are not compared
(3, "apple") < (3, "bird")    // true because 3 is equal to 3, and "apple" is less than "bird"
(4, "dog") == (4, "dog")      // true because 4 is equal to 4, and "dog" is equal to "dog"

注意,如果有Tuples分成不支持比较,则不能进行Tuples类型的比较,如:

("blue", false) < ("purple", true)  // Error because < can't compare Boolean values

3.4 Optionals类型

Optional类型的变量表达了两个可能:它可能包含某个类型的值,可能不包含任何值。

在类型后面加一个 ? 表示相应的Optional类型, 如:

var perhapsInt: Int?          // perhapsInt可能包含Int类型的值,也可能不包含任何值
var perhapsStr: String?       // perhapsStr可能包含String类型的值,也可能不包含任何值

通过测试其值是否等于 nil 可以检测Optional变量是否处于“不包含任何值”的状态,如:

 1: var myString: String?
 2: 
 3: if myString != nil {
 4:    print(myString)
 5: } else {
 6:    print("myString has nil value")       // 会执行这一行,输出 myString has nil value
 7: }
 8: 
 9: myString = "abcd";
10: 
11: if myString != nil {
12:    print(myString)                       // 会执行这一行,输出 Optional("abcd")
13: } else {
14:    print("myString has nil value")
15: }

需要说明的是,上面代码中第12行输出的是Optional("abcd"),而不是字符串"abcd"。想要获得Optional类型变量所包含的具体值,你需要对其进行Unwrap操作,详情请看下节内容。

3.4.1 Unwrapping

要获得Optional类型变量所包含的具体值,你需要对其进行Unwrap操作,具体来说就是在使用变量时在其后面加一个感叹号 ! ,比如 myString! 即可获得Optional类型变量 myString 所包含的具体值:

var myString: String?

myString = "abcd";

if myString != nil {
   print(myString)                       // 输出 Optional("abcd")
   print(myString!)                      // 输出 abcd
} else {
   print("myString has nil value")
}

3.4.2 Implicitly Unwrapped Optional

我们知道,要获得Optional类型变量所包含的具体值,需要显式地进行Unwrap操作(即在变量后面加个 ! 符号),如:

let possibleString: String? = "An optional string."
let forcedString: String = possibleString!          // 需要一个感叹号,不方便
print(forcedString)

如果在声明Optional变量时,不使用 ? ,而使用 ! ,则表示它是一个Implicitly Unwrapped Optional,当它赋值给其它变量时,不用再进行显式地Unwrap操作了,它会自动进行Unwrap。 这样,使用更加方便:

let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString          // 不再需要一个额外的感叹号了,方便多了!
print(implicitString)

3.4.3 Optional Binding

“Optional Binding”是指下面语法:

if let constantName = someOptional {
   statements
}

它的作用是:测试someOptional是否包含某个类型的值,如果包含则把这个类型的值赋给另一个变量(或常量)。

下面是“Optional Binding”的例子:

var myString:String? = "Hello, Swift 4!"

if let yourString = myString {       // 注:不能写为 `myString!` 即无需Unwrap操作(不用感叹号)
   print(yourString)                 // 输出 Hello, Swift 4!
} else {
   print("Your string does not have a value")
}

3.5 Strings和Characters

3.5.1 字符串常量

字符串常量用双引号引起来,如:

let someString:String = "Some string literal value"

三引号可以表示多行字符串,如:

let quotation = """
The White Rabbit put on his spectacles.  "Where shall I begin,
please your Majesty?" he asked.

"Begin at the beginning," the King said gravely, "and go on
till you come to the end; then stop."
"""

print(quotation)

“闭三引号”前的空格会被忽略,如上面例子也可以写为:

let quotation = """
    The White Rabbit put on his spectacles.  "Where shall I begin,
    please your Majesty?" he asked.

    "Begin at the beginning," the King said gravely, "and go on
    till you come to the end; then stop."
    """

print(quotation)

3.5.2 字符

字符也用双引号包围,如:

let c:Character = "a"

print(c)
let c:Character = "a"

var s = "abc"
s.append(c);          // 方法append可以在“字符串”后增加“字符”

print(c)
print(s)

3.5.3 String Interpolation

String Interpolation是构造新字符串的一种方式,用 \() 表示,如:

let apples = 3
let oranges = 5

let appleSummary = "I have \(apples) apples."
let fruitSummary = "I have \(apples + oranges) pieces of fruit."

print(appleSummary)    // I have 3 apples.
print(fruitSummary)    // I have 8 pieces of fruit.

3.6 类型别名(typealias)

使用关键字 typealias 可以给已存在的类型取一个新名字,如:

typealias Feet = Int       // Feet是Int的别名
var distance: Feet = 100
print(distance)            // 输出 100

4 组合数据类型

4.1 Array

数组用 [] 表示,下面是数组使用的一些例子:

var someInts: [Int] = [10, 20, 40]                 // 定义数组

print(someInts[0])                                 // 通过下标访问数组元素
print(someInts[1])                                 // 通过下标访问数组元素
print(someInts[2])                                 // 通过下标访问数组元素

print(someInts.count)                              // 数组元素个数保存在count属性中

for item in someInts {                             // 遍历数组
    print(item)
}

for (index, item) in someInts.enumerated() {       // 用enumerated()遍历数组,可得到下标
    print("Value at index = \(index) is \(item)")
}

var a: [String] = []

if a.isEmpty {                                     // 数组为空时,isEmpty属性为true
    print("Array a is empty.")
}

a.append("abc")                                    // 使用append可以在数组尾部增加元素
a += ["def", "xyz"]                                // 使用 += 也可以在数组尾部增加元素

a.insert("ijk", at: 0)                             // 使用insert可在指定位置插入元素
a.remove(at: 1)                                    // 使用remove可删除指定位置的元素

for item in a {
    print(item)
}

4.2 Set

Set 由无序元素组成,且没有相同的元素。

4.2.1 创建集合

使用 Set<SomeType> 可以初始化集合,如:

var s = Set<String>()

s.insert("abc")
s.insert("edf")

print(s)                  // ["abc", "edf"]
4.2.1.1 用数组字面量创建集合

可以使用数组字面量创建集合,如:

var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]          // 同上

4.2.2 访问和修改集合

下面是访问和修改集合的例子:

var someSet: Set = ["x", "y", "z"]

// "count" method can be used to show the number of elements in the set.
someSet.count           // the number of elements

// "insert" method can be used to insert values in set.
someSet.insert("c")     // adds the element to Set.

// Similarly, isEmpty can be used to check if set is empty.
someSet.isEmpty         // returns true or false depending on the set Elements.

// "remove" method can be used to remove value in set.
someSet.remove("c")     // removes a element , removeAll() can be used to remove all elements

// "contains" method can be used to check existence of value in a set.
someSet.contains("c")   // to check if set contains this value.

4.2.3 遍历集合

使用 for-in 可以遍历集合,如:

for items in someSet {
   print(someSet)
}

// Swift sets are not in an ordered way, to iterate over a set in ordered way use
for items in someSet.sorted() {
   print(someSet)
}

4.2.4 集合其它操作

使用 union, intersection, subtracting 可以求并集、交集和子集,如:

let evens: Set = [10,12,14,16,18]
let odds: Set = [5,7,9,11,13]
let primes = [2,3,5,7]

print(odds.union(evens).sorted())
// [5,7,9,10,11,12,13,14,16,18]

print(odds.intersection(evens).sorted())
//[]

print(odds.subtracting(primes).sorted())
//[9, 11, 13]

4.3 Dictionary

创建字典的语法为:

var someDict = [KeyType: ValueType]()

如:

var someDict1 = [Int: String]()                               // 创建空字典
var someDict2:[Int: String] = [1:"One", 2:"Two", 3:"Three"]
var someDict3 = [1:"One", 2:"Two", 3:"Three"]                 // 同上一行

5 操作符

Swift支持下面操作符:

  • Arithmetic Operators
  • Comparison Operators
  • Logical Operators
  • Bitwise Operators
  • Assignment Operators
  • Ternary Conditional Operator
  • Range Operators

除Range Operators外,上面操作符都存在于C语言中。

5.1 Range Operators

5.1.1 Closed Range Operator

a...b 表示从 ab 的范围(包含 ab ),它常用于 for-in 语句中,如:

for index in 1...5 {
    print(index)        // 按行分别输出 1 2 3 4 5
}

5.1.2 Half-Open Range Operator

a..<b 表示从 ab 的范围(包含 a ,但不包含 b ),如:

for index in 1..<5 {
    print(index)        // 按行分别输出 1 2 3 4
}

注:如果 ab 相等,则范围 a..<b 为空。

5.1.3 One-Sided Ranges

为方便,我们可以仅指定单侧区间,另一侧自动推导出来,如:

let names = ["Anna", "Alex", "Brian", "Jack"]

for name in names[2...] {
    print(name)
}
// Brian
// Jack

for name in names[...2] {
    print(name)
}
// Anna
// Alex
// Brian

for name in names[..<2] {
    print(name)
}
// Anna
// Alex

6 流程控制

6.1 条件

Swift支持 ifswitch 语句。

6.2 循环

Swift支持for-in,while,repeat-while(类似于其它语言的do-while)语句。

6.3 Early Exit(guard语句)

A guard statement, like an if statement, executes statements depending on the Boolean value of an expression. You use a guard statement to require that a condition must be true in order for the code after the guard statement to be executed. Unlike an if statement, a guard statement always has an else clause—the code inside the else clause is executed if the condition is not true.

下面是 guard 的使用例子:

func greet(person: [String: String]) {
    guard let name = person["name"] else {    // 当guard后面的条件不满足时,就会执行else语句
        return
    }

    print("Hello \(name)!")

    guard let location = person["location"] else {
        print("I hope the weather is nice near you.")
        return
    }

    print("I hope the weather is nice in \(location).")
}

greet(person: ["name": "John"])
// Prints "Hello John!"
// Prints "I hope the weather is nice near you."
greet(person: ["name": "Jane", "location": "Cupertino"])
// Prints "Hello Jane!"
// Prints "I hope the weather is nice in Cupertino.

7 函数

Swift中使用关键字 func 定义函数,如:

func greet(person: String) -> String {
    let greeting = "Hello, " + person + "!"
    return greeting
}

7.1 调用函数

调用函数时,要指定参数名字,如:

func greet(person: String) -> String {
    let greeting = "Hello, " + person + "!"
    return greeting
}

var s = greet(person: "xx")         // 调用函数时需要指定参数名字person

print(s)

调用函数时,可以为参数指定一个不一样的名字,如:

func pow(firstArg a: Int, secondArg b: Int) -> Int {
    var res = a
    for _ in 1..<b {
        res = res * a
    }
    return res
}

var i = pow(firstArg:5, secondArg:3)    // 函数内部使用a,b 调用它时使用firstArg, secondArg

print(i)

如果想在调用函数时,省略参数名字,则可以在定义函数指定参数label为 _ ,如:

func pow(_ a: Int, _ b: Int) -> Int {    // 参数label为 _ ,这样调用函数时不用指定参数名
    var res = a
    for _ in 1..<b {
        res = res * a
    }
    return res
}

var i = pow(5, 3)

print(i)

7.2 多个返回值

函数可以有多个返回值(也可以认为返回一个Tuple类型),如:

func minMax(array: [Int]) -> (min: Int, max: Int) {
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1..<array.count] {
        if value < currentMin {
            currentMin = value
        } else if value > currentMax {
            currentMax = value
        }
    }
    return (currentMin, currentMax)
}

let result = minMax(array: [5, -1, 3, 9])
print(result.min)
print(result.max)

7.3 嵌套函数

可以在函数内部定义函数,这称为嵌套函数(Nested Functions),如:

func chooseStepFunction(backward: Bool) -> (Int) -> Int {
    func stepForward(input: Int) -> Int { return input + 1 }     // Nested Function
    func stepBackward(input: Int) -> Int { return input - 1 }    // Nested Function
    return backward ? stepBackward : stepForward
}

var currentValue = -4
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)

// moveNearerToZero now refers to the nested stepForward() function
while currentValue != 0 {
    print("\(currentValue)... ")
    currentValue = moveNearerToZero(currentValue)
}

print("zero!")

// -4...
// -3...
// -2...
// -1...
// zero!

8 Closures

Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures in Swift are similar to blocks in Objective-C and to lambdas in other programming languages.

函数和嵌套函数可以看作是特殊形式的Closures。

8.1 Closure Expressions

Closure表达式的语法如下所示:

{
   (parameters) −> return type in
   statements
}

下面是Closure Expression的一个例子:

let divide = {
   (val1: Int, val2: Int) -> Int in
   return val1 / val2
}

let result = divide(200, 20)
print(result)                   // 输出 10

8.2 Shorthand Argument Names ($0, $1, etc)

Closure Expressions的各个参数可省写为 $0, $1, ..., $n ,参数类型说明,返回值类型说明等等都可以省略。

比如,代码:

var nums: Set = [2, 5, 10, 30, 60]

var biggerThanTen = nums.filter({(item) -> Bool in
    return (item > 10)
})

print(biggerThanTen)      // [30, 60]

等同于:

var nums: Set = [2, 5, 10, 30, 60]

var biggerThanTen = nums.filter({ $0 > 10 })          // 代码更简洁
// var biggerThanTen = nums.filter{ $0 > 10 }         // 同上,省略了小括号。

print(biggerThanTen)      // [30, 60]

8.3 函数、嵌套函数、Closure Expressions的区别

函数、嵌套函数、Closure Expressions的区别如表 2 所示。

Table 2: 函数、嵌套函数、Closure的区别
  说明
Global Functions Have a name. Do not capture any values
Nested Functions Have a name. Capture values from enclosing function
Closure expressions Unnamed Closures capture values from their surrounding context

9 枚举

枚举用关键字 enum 表示。

下面是使用枚举的例子:

enum CompassPoint {       // 定义枚举
    case north
    case south
    case east
    case west
}

var directionToHead = CompassPoint.south

switch directionToHead {
case .north:                       // 写为 case CompassPoint.north: 也行
    print("Lots of planets have a north")
case .south:
    print("Watch out for penguins")
case .east:
    print("Where the sun rises")
case .west:
    print("Where the skies are blue")
}

10 对象和类

下面是类的一个例子:

class Shape {
    var numberOfSides = 0
    func simpleDescription() -> String {
        return "A shape with \(numberOfSides) sides."
    }
}


var shape = Shape()             // 创建类实例(对象)
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()

print(shapeDescription)

10.1 init

我们可以为类指定 init 方法,这样在创建类实例时可以初始化它。如:

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

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

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

let test = NamedShape(name: "My test shape")     // 创建类实例(对象)

print(test.simpleDescription())

10.2 重写父类方法(override)

使用 override 关键字可以重写父类方法,如:

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

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

    deinit {
        print("Shamp #\(name) is being deinitialized")
    }

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

class Square: NamedShape {
    var sideLength: Double

    init(sideLength: Double, name: String) {
        self.sideLength = sideLength             // self 是当前对象
        super.init(name: name)                   // super 是父对象
        numberOfSides = 4
    }

    func area() -> Double {
        return sideLength * sideLength
    }

    override func simpleDescription() -> String {            // 重写父类的simpleDescription方法
        return "A square with sides of length \(sideLength)."
    }
}

let test = Square(sideLength: 5.2, name: "my test square")

print(test.simpleDescription())

10.3 Type Methods (static, class)

Swift中的Type Method类似于C++中的静态方法,它使用 static 关键字修饰。如:

class SomeClass {
    static func someTypeMethod() {
        // type method implementation goes here
    }
}

SomeClass.someTypeMethod()

除了 static 关键字外,Type Method还可以用 class 关键字修饰,它们的不同在于:~static~ 关键字修饰的TYpe Method在子类中不可以被重写,而 class 关键字修饰的Type Method可以在子类中被重写,如:

class Class1 {
    class func someTypeMethod() {
        print("Class1 someTypeMethod")
    }
}

class Class2 : Class1 {
    override class func someTypeMethod() {
        print("Class2 someTypeMethod")
    }
}

Class2.someTypeMethod()

10.4 测试对象是否相同( ===!==

使用 ===!== 可以测试两个变量(或常量)所关联的对象是否相同。如:

class Class1 {
}

let a = Class1()
let b = Class1()
let c = b

if a === b {
    print("a and b is same")
} else {
    print("a and b is not same")
}

if b === c {
    print("b and c is same")
} else {
    print("b and c is not same")
}

运行上面代码将输出:

a and b is not same
b and c is same

10.5 struct(值类型) vs. class(引用类型)

Swift中“结构体”和“类”很相似。它们的最大区别在于:结构体是值类型,而类是引用类型。

Structures are always copied when they are passed around in your code, but classes are passed by reference.

下面是“结构体”的一个例子:

struct DogStruct {
    var name: String
    init(name: String) {
        self.name = name
    }
}


var aDogStruct = DogStruct(name: "A-Doggo")
var bDogStruct = aDogStruct
bDogStruct.name = "B-Doggo"

print(aDogStruct.name)    // 输出 "A-Doggo"
print(bDogStruct.name)    // 输出 "B-Doggo"

上面代码会输出:

"A-Doggo"
"B-Doggo"

如果把第一行的 struct 关键字换为 class ,则会输出:

"B-Doggo"
"B-Doggo"

Author: cig01

Created: <2018-07-01 Sun 00:00>

Last updated: <2018-09-23 Sun 13:34>

Creator: Emacs 25.3.1 (Org mode 9.1.4)