- 100 Days of SwiftUI - Day 6&7 Cl
- 100 Days of SwiftUI —— Day 100:期
- 100 Days of SwiftUI —— Day 65:In
- 100 Days of SwiftUI —— Day 67:In
- 100 Days of SwiftUI —— Day 66:In
- 100 Days of SwiftUI —— Day 63:In
- 100 Days of SwiftUI —— Day 62:In
- 100 Days of SwiftUI —— Day 64:In
- 100 Days of SwiftUI —— Day 3
- 100 Days of SwiftUI —— Day 2
Closure 闭包
1.创建基本的闭包
Swift 使我们可以像使用其他类型一样使用函数,你可以创建一个函数并将其分配给变量,使用变量调用该函数,甚至将该函数传递给其它函数。
这种方式成为“闭包”。
// 创建一个没有名称的函数
let driving = {
print("I'm driving my car")
}
driving()
Swift中闭包非常强大,而且常用。使用闭包的常见原因是存储一些功能。
比如我想在某些时候做一些工作,但不一定是现在去做,比如:
1.延迟运行一些代码。
2.动画完后运行一些代码。
3.下载完成后运行一些代码。
4.当用户从你的选项中选择一些功能。
2.闭包中接受参数
要使闭包接受参数,请在括号内列出它们,然后后面写in, 以便闭包知道主体的开始。
下面创建一个接受字符串的闭包,如下所示:
let driving = { (place: String) in
print("I'm going to \(place) in my car")
}
driving("Beijing")
函数和闭包区别是,闭包可以不使用参数标签。
为什么闭包的参数放在花括号内?
// 函数
func pay(user: String, amount: Int) {
// code
}
// 闭包
let payment = { (user: String, amount: Int) in
// code
}
闭包通过
in
关键词标记 参数列表的结尾和闭包主体的开始。
闭包捕捉的参数和主体放在花括号中,作为闭包的一部分将其存储在变量中,是一种很好的方式。
3.闭包的返回值
闭包也可以有返回值,它们的写法与参数类似,你可以写在闭包中,在in之前。
let drivingWithReturn = { (place: String) -> String in
return "I'm going to \(place) in my car"
}
let message = drivingWithReturn("London")
print(message)
4.将闭包作为参数
闭包可以像字符串和整数一样使用,所以可以将他们传递给函数。
先创建一个闭包,再创建一个接受闭包作为参数的函数。
let driving = {
print("I'm driving in my car")
}
func travel(action: () -> Void) {
print("I'm getting ready to go.")
action()
print("I arrived!")
}
使用driving闭包来调用它
travel(action: driving)
为什么要使用闭包作为参数?
举个例子来说明:
1.当你呼叫siri来使用app某项服务时,这时siri会在后台启动app,然后等待app响应,然后siri显示交互界面。假设app表现不佳,我们可能要等待很长时间才能出来完成,这时候siri完全卡在了等待,如果使用闭包代替,那么后台启动app时传递一个闭包,app完成后调用闭包,siri不用等待函数完成而处于冻结,还可以继续保持交互性。
2.另一个示例,网络请求,当我们连接国外的服务器时,从internet请求数据使用闭包,当获得响应时,运行闭包,这意味着进行一些缓慢工作时,我们不必强制冻结用户界面。
5.尾随闭包
如果函数的最后一个参数是闭包,则Swift可以让你使用尾随闭包的特殊语法。
func travel(action: () -> Void) {
print("I'm getting ready to go.")
action()
print("I arrived!")
}
因为他最后一个参数是闭包,所以我们可以:
travel() {
print("I'm driving in my car")
}
由于没有参数,
travel {
print("I'm driving in my car")
}
尾随闭包在Swift中非常普遍,你必须习惯它。
travel(action: {
print("I'm driving in my car")
})
上面的代码,在不使用尾随闭包的情况下,这样写。
使用尾随闭包使我们清理掉了后面的括号,而且还删除了action参数标签。
尾随闭包的语法旨在使Swift代码更易于阅读。
6.带接受参数的闭包作为参数
func travel(action: (String) -> Void) {
print("I'm getting ready to go.")
action("London")
print("I arrived!")
}
使用尾随闭包语法调用,
travel { (place: String) in
print("I'm going to \(place) in my car")
}
7. 带返回值的闭包作为参数
func travel(action: (String) -> String) {
print("I'm getting ready to go.")
let description = action("London")
print(description)
print("I arrived!")
}
travel { (place: String) -> String in
return "I'm going to \(place) in my car"
}
首先写一个简单的例子,
func reduce(_ values: [Int], using closure: (Int, Int) -> Int) -> Int {
var current = values[0]
for value in values[1...] {
current = closure(current, value)
}
return current
}
函数接收一个数组作为参数,内部从第一个开始,没两个参数做了一个处理(可能是加法或者减法等等),最后返回这个值。
reduce([1,2,3,4]) { $0 + $1 }
reduce 最后一个参数,我们用加法的方式来处理。
实际上+
函数和闭包内的处理是一样的,所以我们可以这样写:
reduce([1,2,3,4], using: +)
例子中,有很多错误没有处理。
实际上,Swift有标准版本的reduce()功能,而且他可以指定起始值,适用于任何类型的数组,可以优雅的处理错误。
随着进步,你会发现闭包作为返回值,是非常普遍的。
8.速记参数名称$0
Swift知道闭包的参数必须是字符串,因此可以将其删除。
travel { (place) -> String in
return "I'm going to \(place) in my car"
}
Swift同样知道闭包的返回值是字符串,因此可以将其删除,
travel { place in
return "I'm going to \(place) in my car"
}
由于闭包返回值只有一行代码,return可以删除,
travel place {
"I'm going to \(place) in my car"
}
Swift具有简化语法,为闭包的参数列表提供了自动命名 以$
开头,从0开始,
travel {
"I'm going to \($0) in my car"
}
什么时候应该使用速记参数名称?
是否应该使用取决于:
1.是否有很多参数?如果是,简写语法不再有用,而适得其反,给它们实际的名字,使含义更清晰。
2.功能是否常用,少数常见的函数使用闭包,使用$0
会容易理解。
3.在函数中使用了几次,当你需要使用2次或3次以上,你应该给它起个真实的名字。
重要的是你的代码要易于阅读和理解。
9.多个参数的闭包
闭包像函数一样接受任意数量的参数,0到3个是比较常见的,同时使用 $0
,$1
,$2
可以合理的工作。
10.函数返回闭包
函数可以返回闭包,
func travel() -> (String) -> Void {
return {
print("I'm going to \($0)")
}
}
通过调用函数获取闭包,然后再作为函数调用。
let result = travel()
result("London")
你可能会想到用以下方法调用,但是不建议这样做
let result2 = travel()("London")
为什么要用闭包作为返回值?
举一个例子,我想生成很多随机数
func makeRandomGenerator() -> () -> Int {
let function = { Int.random(in: 1...10) }
return function
}
let generator = makeRandomGenerator()
let random1 = generator()
print(random1)
我们将闭包作为返回值,生成随机数的函数隐藏在函数内部,当随机规则发生改变时,任何调用它的地方不需要改变,因为它只是一个每次调用会返回随机数的闭包。
11.捕获值
如果在闭包内部使用任何外部值,Swift会捕获它们,将它们存储在闭包旁边,
func travel() -> (String) -> Void {
var counter = 1
return {
print("\(counter). I'm going to \($0)")
counter += 1
}
}
调用travel以获取闭包,然后调用。
let result = travel()
result("London")
即使counter变量是在travel()内部创建的,它也会被闭包捕获,因此对闭包仍处于活动状态。
result("London")
result("London")
result("London")
当我们不断调用,counter不断增加。
Swift中的闭包为什么捕获值?
Swift捕获值,以便闭包始终可以访问要处理的数据,意味着Swift可以安全的运行闭包。
举个例子,我们再次编写一个生成随机数的函数,并且不会连续返回相同的数字。
func makeRandomNumberGenerator() -> () -> Int {
var previousNumber = 0
return {
var newNumber: Int
repeat {
newNumber = Int.random(in: 1...3)
} while newNumber == previousNumber
previousNumber = newNumber
return newNumber
}
}
previousNumber在函数内的闭包外部被创建,这样,闭包使用时会捕获这个变量。
let generator = makeRandomNumberGenerator()
for _ in 1...10 {
print(generator())
}
这样不会再连续出现相同的随机数字。
当makeRandomNumberGenerator()运行完成后,通常已经被销毁,但previousNumber被闭包捕获,
1.因为如果变量被销毁,那么闭包将不起作用,所以Swift将其处于活动状态,确保闭包功能正取。
2.尽管变量被闭包捕获,但它在闭包外部创建,这意味着只被创建一次,可以存储旧的值。
闭包捕获可以确保我们在内部使用闭包时,跟踪闭包外的某些状态。
12.总结
1.你可以为变量分配闭包,然后调用它们。
2.闭包可以接受参数和返回值,和普通函数一次。
3.闭包可以作为参数传递给函数,而且闭包也具有自己的参数和返回值。
4.函数最后一个参数是闭包,那么可以使用尾随闭包语法。
5.swift会提供,$0
, $1
这样的速记参数名称。
6.闭包内使用外部值时,会捕获它们,以便以后引用他们。
网友评论