Swift Learning Notes
Basic
元组:
- 元组可以包含任意多个任意类型的元素,
- 元组内的元素用.索引访问
- 数组内的元素用[索引]访问
- 可以给单个元素命名,命名后可以用元素名称访问:
1
2
3
4let http200Status = (statusCode: 200, description: "OK")
print("The status code is \(http200Status.statusCode)")
// prints "The status code is 200"
- 当你分解元组的时候,如果只需要使用其中的一部分数据,不需要的数据可以用下滑线( _ )代替:可见下划线的作用在Swfit里就是用来填坑
1
2
3let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)")
// prints "The status code is 404"
可选项:
- Swift 中的 nil 和Objective-C 中的 nil 不同,在 Objective-C 中 nil 是一个指向不存在对象的指针。在 Swift中, nil 不是指针,他是值缺失的一种特殊类型,任何类型的可选项都可以设置成 nil 而不仅仅是对象类型。
- 可选项符号?用在类型名后
Int?
- 可选变量或者常量或者为=该类型的某个值,或者没有值=nil
- nil不能用于非可选的变量或者常量
- 确认一个可选有值以后可以用!强制展开/取值
1
2
3if convertedNumber != nil {
print("convertedNumber has an integer value of \(convertedNumber!).")
}
隐式展开可选项
- 就是在类型声明的时候用exclamation mark标记,而不是用question mark标记
1
2
3
4
5let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // requires an exclamation mark
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // no need for an exclamation mark注意:
不要在一个变量将来会变为 nil 的情况下使用隐式展开可选项。如果你需要检查一个变量在生存期内是否会变为 nil ,就使用普通的可选项。
函数和闭包
语法表达式
一般形式:
1 | { |
这里的参数(parameters),可以是in-out(输入输出参数),但不能设定默认值。如果是可变参数,必须放在最后一位,不然编译器报错。元组也可以作为参数或者返回值。
“in”关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始。即由in引入函数
例子”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//一般形式
let calAdd:(Int,Int)->(Int) = {
(a:Int,b:Int) -> Int in
return a + b
}
print(calAdd(100,150))
//Swift可以根据闭包上下文推断参数和返回值的类型,所以上面的例子可以简化如下
let calAdd2:(Int,Int)->(Int) = {
a,b in //也可以写成(a,b) in
return a + b
}
print(calAdd2(150,100))
//上面省略了返回箭头和参数及返回值类型,以及参数周围的括号。当然你也可以加括号,为了好看点,看的清楚点。(a,b)
//单行表达式闭包可以隐式返回,如下,省略return
let calAdd3:(Int,Int)->(Int) = {(a,b) in a + b}
print(calAdd3(50,200))
//如果闭包没有参数,可以直接省略“in”
let calAdd4:()->Int = {return 100 + 150}
print("....\(calAdd4())")
//这个写法,我随便写的。打印出“我是250”
//这个是既没有参数也没返回值,所以把return和in都省略了
let calAdd5:()->Void = {print("我是250")}
calAdd5()归纳
闭包类型是由参数类型和返回值类型决定,和函数是一样的。比如上面前三种写法的闭包的闭包类型就是(Int,Int)->(Int),后面的类型分别是()->Int和()->Void。分析下上面的代码:let calAdd:(add类型)。这里的add类型就是闭包类型 (Int,Int)->(Int)。意思就是声明一个calAdd常量,其类型是个闭包类型。
“=”右边是一个代码块,即闭包的具体实现,相当于给左边的add常量赋值。兄弟们,是不是感觉很熟悉了,有点像OC中的block代码块。起别名
也可以关键字“typealias”先声明一个闭包数据类型。类似于OC中的typedef起别名
1
2
3
4
5
6
7
8
9typealias AddBlock = (Int, Int) -> (Int)
let Add:AddBlock = {
(c,d) in
return c + d
}
let Result = Add(100,150)
print("Result = \(Result)")尾随闭包
若将闭包作为函数最后一个参数,可以省略参数标签,然后将闭包表达式写在函数调用括号后面
1 | func testFunction(testBlock: ()->Void){ |
值捕获
- 闭包可以在其被定义的上下文中捕获常量或变量。Swift中,可以捕获值的闭包的最简单形式是嵌套函数,也就是定义在其他函数的函数体内的函数。这里没有值捕获的原因是,没有去用一个常量或变量去引用函数,所以每次使用的函数都是新的。有点类似于OC中的匿名对象。
1
2
3
4
5
6
7
8
9
10
11
12
13func captureValue(sums amount:Int) -> ()->Int{
var total = 0
func incrementer()->Int{
total += amount
return total
}
return incrementer
}
print(captureValue(sums: 10)())
print(captureValue(sums: 10)())
print(captureValue(sums: 10)())
//打印"10 10 10"
1 | let referenceFunc = captureValue(sums: 10) |
这里值捕获了,是因为函数被引用了,所以没有立即释放掉。所以函数体内的值可以被捕获
闭包形式
1 | func captureValue2(sums amount:Int) -> ()->Int{ |
由上面的例子都可以证得,函数和闭包都是引用类型。
逃逸闭包
- 当一个闭包作为参数传到一个函数中,需要这个闭包在函数返回之后才被执行,我们就称该闭包从函数种逃逸。一般如果闭包在函数体内涉及到异步操作,但函数却是很快就会执行完毕并返回的,闭包必须要逃逸掉,以便异步操作的回调。
- 逃逸闭包一般用于异步函数的回调,比如网络请求成功的回调和失败的回调。语法:在函数的闭包形参前加关键字“@escaping”。
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//例1
func doSomething(some: @escaping () -> Void){
//延时操作,注意这里的单位是秒
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) {
//1秒后操作
some()
}
print("函数体")
}
doSomething {
print("逃逸闭包")
}
//例2
var comletionHandle: ()->String = {"约吗?"}
func doSomething2(some: @escaping ()->String){
comletionHandle = some
}
// 若将闭包作为函数最后一个参数,可以省略参数标签,然后将闭包表达式写在函数调用括号后面,甚至还可以去掉括号,这是推荐写法:
doSomething2 {
return "叔叔,我们不约"
}
// 正常写法: doSomething2(some: { return "xxxx" })
// 省略写法: doSomething2() { return "xxxx" }
// 下回看到上面的代码别懵圈了,这不是函数定义,是函数调用,大括号时作为参数的闭包
print(comletionHandle())
//将一个闭包标记为@escaping意味着你必须在闭包中显式的引用self。
//其实@escaping和self都是在提醒你,这是一个逃逸闭包,
//别误操作导致了循环引用!而非逃逸包可以隐式引用self。
//例子如下
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 }
}
}
自动闭包
- 顾名思义,自动闭包是一种自动创建的闭包,封装一堆表达式在自动闭包中,然后将自动闭包作为参数传给函数。而自动闭包是不接受任何参数的,但可以返回自动闭包中表达式产生的值。
- 自动闭包让你能够延迟求值,直到调用这个闭包,闭包代码块才会被执行。说白了,就是语法简洁了,有点懒加载的意思。
1
2
3
4
5
6
7
8
9
10
11
12
13
14var array = ["I","have","a","apple"]
print(array.count)
//打印出"4"
let removeBlock = {array.remove(at: 3)}
//定义一个变量,初始化为闭包。
print(array.count)
//打印出"4"
print("执行代码块移除\(removeBlock())")
//打印出"执行代码块移除apple" 这里自动闭包返回了apple值
print(array.count)
//打印出"3"