美文网首页100 Days of SwiftUI
100 Days of SwiftUI - Day 6&7 Cl

100 Days of SwiftUI - Day 6&7 Cl

作者: 星星星宇 | 来源:发表于2020-09-17 17:37 被阅读0次

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.闭包内使用外部值时,会捕获它们,以便以后引用他们。

相关文章

网友评论

    本文标题:100 Days of SwiftUI - Day 6&7 Cl

    本文链接:https://www.haomeiwen.com/subject/ipifyktx.html