1.函数

1.1 函数基础

使用func声明函数,之后是函数名,参数名,最后用->表明返回值类型

e.g.

1
2
3
4
func greet(person: String, day: String) -> String {
return "Hello \(person), today is \(day)."
}
greet(person: "Bob", day: "Tuesday")

函数默认使用参数名作为参数的标识,也可以在参数名之前加标识,或者用_表明没有标识.

e.g.

1
2
3
4
func greet(_ person: String, on day: String) -> String {
return "Hello \(person), today is \(day)."
}
greet("John", on: "Wednesday")

返回值可以使用元组(tuple)来返回多个值,使用返回值时可以使用名称,也可以使用数字

e.g.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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(scores: [5, 3, 100, 3, 9])
print(statistics.sum)
print(statistics.2)

如果返回的元组类型可能为空,可以使用可选元组类型作为返回值 .e.g.(Int,Int)?

函数可以嵌套,函数可以把函数作为返回值,函数可以把函数作为参数.

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
//嵌套
func returnFifteen() -> Int {
var y = 10
func add() {
y += 5
}
add()
return y
}
returnFifteen()

//返回函数
func makeIncrementer() -> ((Int) -> Int) {
func addOne(number: Int) -> Int {
return 1 + number
}
return addOne
}
var increment = makeIncrementer()
increment(7)

//函数参数
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(list: numbers, condition: lessThanTen)

如果一个函数的整个函数体是一个单行表达式,这个函数可以隐式地返回这个表达式。

1.2 参数标签和参数名称

每个函数参数都有一个参数标签(argument label)以及一个参数名称(parameter name).在调用函数时使用参数标签,在函数中使用参数名称.函数默认把参数名称作为参数标签.

可以使用_来代替函数标签.

1.3 输入输出参数(In-Out Parameters)

在参数定义前加关键字inout,这个值被函数修改后,然后被传出函数,将替换原来的值.在调用函数时,需要在输入输出参数前加&符号,表示这个参数可以被函数修改.

e.g.

1
2
3
4
5
6
7
8
9
10
11
  func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}

var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// 打印“someInt is now 107, and anotherInt is now 3”

2. 闭包(Closure)

闭包类似于C和OC中的代码块,已经其他语言中的匿名函数(lambda 表达式).闭包可以捕获和存储其所在上下文中任意常量和变量的引用。被称为包裹常量和变量。

全局和嵌套函数都是特殊的闭包,除此之外,闭包还有一种形式就是闭包表达式.

2.1 闭包表达式

swift标准库有sorted(by:)方法,可以利用闭包表达式对数组进行排序,并返回新数组.以此为例,介绍闭包表达式的用法.

1
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]

sorted(by:)接收一个闭包,排序函数根据该闭包对数组进行排序

  1. 第一种提供闭包函数的方法是将一个普通函数作为参数传入

    1
    2
    3
    4
    5
      func backward(_ s1: String, _ s2: String) -> Bool {
    return s1 > s2
    }
    var reversedNames = names.sorted(by: backward)
    // reversedNames 为 ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
  2. 使用闭包表达式的方法如下

    1
    2
    3
    reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
    return s1 > s2
    })

    闭包的函数体部分由关键字 in 引入。该关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始。

  3. 根据上下文判断类型

因为排序闭包函数是作为 sorted(by:) 方法的参数传入的,Swift 可以推断其参数和返回值的类型。sorted(by:) 方法被一个字符串数组调用,因此其参数必须是 (String, String) -> Bool 类型的函数。这意味着 (String, String)Bool 类型并不需要作为闭包表达式定义的一部分。因为所有的类型都可以被正确推断,返回箭头(->)和围绕在参数周围的括号也可以被省略:

1
reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )
  1. 单表达式隐式返回

    单行表达式闭包可以通过省略 return 关键字来隐式返回单行表达式的结果

    1
    reversedNames = names.sorted(by:{s1,s2 in s1 > s2})
    1. 参数缩写

    Swift 自动为内联闭包提供了参数名称缩写功能,你可以直接通过 $0$1$2 来顺序调用闭包的参数.

    1
    reversedNames = names.sorted(by: { $0 > $1 } )
    1. 运算符方法
      Swift 的 String 类型定义了关于大于号(>)的字符串实现,其作为一个函数接受两个 String 类型的参数并返回 Bool 类型的值。而这正好与sorted(by:)方法的参数需要的函数类型相符合。因此,你可以简单地传递一个大于号,Swift 可以自动推断找到系统自带的那个字符串函数的实现:

      1
      reversedNames = names.sorted(by: >)
    2. 尾随闭包
      尾随闭包是一个书写在函数圆括号之后的闭包表达式,函数支持将其作为最后一个参数调用.可以不用写出参数标签

    e.g.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
      func someFunctionThatTakesAClosure(closure: () -> Void) {
    // 函数体部分
    }

    // 以下是不使用尾随闭包进行函数调用
    someFunctionThatTakesAClosure(closure: {
    // 闭包主体部分
    })

    // 以下是使用尾随闭包进行函数调用
    someFunctionThatTakesAClosure() {
    // 闭包主体部分
    }

    reversedNames = names.sorted() { $0 > $1 }

    2.2 逃逸闭包

    闭包是一个引用类型

    当一个闭包作为参数传入到函数中,但在函数返回之后才会被执行,这样的闭包就是逃逸闭包,需要在定义时,在参数名之前标注@escaping.比如当闭包作为异步操作的函数的参数时,闭包在异步操作之后才会调用,那么这个闭包就需要被标注为”逃逸”.

    将一个闭包标记为”逃逸”,那么在闭包中必须显示地引用self

    e.g.

    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
     var completionHandlers: [() -> Void] = []
    func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
    completionHandlers.append(completionHandler)
    }

    func someFunctionWithNonescapingClosure(closure: () -> Void) {
    closure()
    }

    class SomeClass {
    var x = 10
    func doSomething() {
    someFunctionWithEscapingClosure { self.x = 100 }
    someFunctionWithNonescapingClosure { x = 200 }
    }
    }

    let instance = SomeClass()
    instance.doSomething()
    print(instance.x)
    // 打印出“200”

    completionHandlers.first?()
    print(instance.x)
    // 打印出“100”

3.结构体和类

3.1 存储属性

如果创建了一个结构体并将其赋值给一个常量,则无法修改该实例的任何属性.

1
2
3
4
5
6
7
8
9
struct FixedLengthRange {
var firstValue: Int
let length: Int
}

let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
// 该区间表示整数 0,1,2,3
rangeOfFourItems.firstValue = 6
// 尽管 firstValue 是个可变属性,但这里还是会报错

延时加载存储属性

延时加载属性指当第一次被调用的时候才会计算其初始值的属性.在属性声明前使用lazy来标示一个延时加载存储属性.必须是var

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 class DataImporter {
/*
DataImporter 是一个负责将外部文件中的数据导入的类。
这个类的初始化会消耗不少时间。
*/
var fileName = "data.txt"
// 这里会提供数据导入功能
}

class DataManager {
lazy var importer = DataImporter()
var data = [String]()
// 这里会提供数据管理功能
}

let manager = DataManager()
manager.data.append("Some data")
manager.data.append("Some more data")
// DataImporter 实例的 importer 属性还没有被创建

由于使用了 lazyDataImporter 的实例 importer 属性只有在第一次被访问的时候才被创建。比如访问它的属性 fileName 时.

全局变量常量都是延迟计算的,不需要lazy修饰,局部变量则从不延迟计算.

3.2 计算属性

计算属性不直接存储值,而是通过一个getter和可选的setter来间接获取和设置其他属性变量的值.

3.3 属性观察器

属性观察器监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,即使新值和当前值相同的时候也不例外。
可以为除了延时加载存储属性之外的其他存储属性添加属性观察器,也可以在子类中通过重写属性的方式为继承的属性(包括存储属性和计算属性)添加属性观察器。
观察器有两种:
1)willSet 在新的值被设置之前调用
2)didSet 在新的值被设置之后调用

e.g.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotalSteps) {
print("将 totalSteps 的值设置为 \(newTotalSteps)")
}
didSet {
if totalSteps > oldValue {
print("增加了 \(totalSteps - oldValue) 步")
}
}
}
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// 将 totalSteps 的值设置为 200
// 增加了 200 步
stepCounter.totalSteps = 360
// 将 totalSteps 的值设置为 360
// 增加了 160 步
stepCounter.totalSteps = 896
// 将 totalSteps 的值设置为 896
// 增加了 536 步

3.4 类型属性

使用关键字static定义类型属性.在 Swift 中,类型属性是作为类型定义的一部分写在类型最外层的花括号内,因此它的作用范围也就在类型支持的范围内.可以改用关键字 class 来支持子类对父类的实现进行重写。

原文链接 http://blog.wuqingzhe.cn/2019/08/14/swift2/