Swift进阶(五)闭包与闭包表达式

2021-01-06  本文已影响0人  Jax_YD

一、闭包表达式(Closure Expression)

///函数
func sum(_ v1: Int, _ v2: Int) -> Int {v1 + v2}

///闭包表达式
var fn = {
    (v1: Int, v2: Int) -> Int in
    return v1 + v2
}
fn(10,20)
///或者这样
{
    (v1: Int, v2: Int) -> Int in
    return v1 + v2
}(10,20)

闭包表达式的格式

{
    (参数列表) -> 返回值类型 in
    函数体代码
}

闭包表达式的简写

func exec(v1: Int, v2: Int, fn: (Int, Int) -> Int) {
    print(fn(v1, v2))
}

///1、正常写法
exec(v1: 10, v2: 20) { (v1: Int, v2: Int) -> Int in
    return v1 + v2
}

///2、省略参数类型
exec(v1: 10, v2: 20) {
    v1, v2 in return v1 + v2
}

///3、省略return
exec(v1: 10, v2: 20) {
    v1, v2 in v1 + v2
}

///4、用美元符表示
exec(v1: 10, v2: 20) { $0 + $1 }

///5、最简单
exec(v1: 10, v2: 20, fn: +)

尾随闭包

func exec(v1: Int, v2: Int, fn: (Int, Int) -> Int) {
    print(fn(v1, v2))
}
exec(v1: 10, v2: 20) { $0 + $1 }
func exec(fn: (Int, Int) -> Int) {
    print(fn(v1, v2))
}
exec(fn: { $0 + $1 })
exec() { $0 + $1 }
exec { $0 + $1 }
示例:数组的排序
@inlinable public mutating func sort(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows

根据函数定义,我们只需要传入一个函数即可(排序规则)

/// 返回true: i1排在i2前面
/// 返回false: i1排在i2后面
func cmp(i1: Int, i2: Int) -> Bool {
// 大的排在前面
    return i1 > i2
}
var nums = [20, 1, 60, 25, 95, 8, 5]
nums.sort(by: cmp)

当然,我们还可以使用闭包表达式来定义排序规则

nums.sort(by: {
    (i1: Int, i2: Int) -> Bool in
    return i1 < i2
})
nums.sort(by: { i1, i2 in return i1 < i2 })
nums.sort(by: { i1, i2 in i1 < i2 })
nums.sort(by: { $0 < $1 })
nums.sort(by: <)
nums.sort() { $0 < $1 }
nums.sort { $0 < $1 }

忽略参数

func exec(fn: (Int, Int) -> Int) {
    print(fn(10,20))
}
exec { _,_ in 30}

二、闭包(Closure)

什么是闭包?

typealias Fn = (Int) -> Int
func getFn() -> Fn {
    var num = 10
    func plus(_ i: Int) -> Int {
        num += i
        return num
    }
    return plus
} /// 返回的plus和num形成了闭包
var fn1 = getFn()
print(fn1(1))
print(fn1(2))
/*输出结果*/
1
3

问题一

问题二

typealias Fn = (Int) -> Int
func getFn() -> Fn {
    var num = 10
    func plus(_ i: Int) -> Int {
        num += i
        return num
    }
    num = 20
    return plus
} /// 返回的plus和num形成了闭包

var fn1 = getFn()
print(fn1(1))
/*输出结果*/
21

总结:一个函数和它所捕获的变量\常量环境组合起来,称为闭包
① 一般指:定义在函数内部的函数
② 一般它捕获的是外层函数的 局部 变量\常量
我们也可以把闭包想象成一个类的实例对象
① 内存在堆空间
② 捕获的局部变量\常量就是对象的成员(存储属性)
③ 组成的闭包的函数就是类内部定义的方法

问题三

下面我们通过断点调试来看一下:
1、首先我们再return plus处打一个断点,窥探一下汇编代码。

image.png
image.png image1.png image2.png

问题四

image1.png image2.png

三、自动闭包(@autoclosure)

我们首先来看一下@autoclosure的使用
我们先定义一个函数,用来获取第一个正数

func getFirstPositive(_ v1: Int, _ v2: Int) -> Int {
    return v1 > 0 ? v1 : v2
}
print(getFirstPositive(10, 30))
print(getFirstPositive(-1, 2))
/*输出结果*/
10
2

这里我们可以将v2改成一个函数,如下:

func getFirstPositive(_ v1: Int, _ v2: () -> Int) -> Int {
    return v1 > 0 ? v1 : v2()
}
getFirstPositive(10) { 20 }
func getFirstPositive(_ v1: Int, _ v2: @autoclosure () -> Int) -> Int {
    return v1 > 0 ? v1 : v2()
}
getFirstPositive(10, 20)
func getFirstPositive(_ v1: Int, _ v2: () -> Int) -> Int {
    print("无 @autoclosure")
    return v1 > 0 ? v1 : v2()
}
func getFirstPositive(_ v1: Int, _ v2: @autoclosure () -> Int) -> Int {
    print("有 @autoclosure")
    return v1 > 0 ? v1 : v2()
}
print(getFirstPositive(10, 20))
print(getFirstPositive(45, {30}))
/*输出结果*/
有 @autoclosure
10
无 @autoclosure
45

注意:为了避免与期望冲突,使用了@autoclosure的地方最好注明清楚:这个值会被延迟执行

func fn() -> Int {
    print("延迟执行")
    return 20
}

func getFirstPositive(_ v1: Int, _ v2: @autoclosure () -> Int) -> Int {
    print("有 @autoclosure")
    return v1 > 0 ? v1 : v2()
}
print(getFirstPositive(10, fn()))
/*输出结果*/
有 @autoclosure
10

我们会发现,因为v1大于0,所以fn()并没有执行。
正常来讲fn()代表函数的执行,但是这里并没有执行,所以这一点要注意。

四、逃逸闭包

当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中\color{red}{逃逸}。当你定义接受闭包作为参数的函数时,你可以在参数名之前标注@escaping,用来指明这个闭包是允许逃逸出这个函数的。

func fn() -> Int {
    return 20
}

func getFirstPositive(_ v1: Int, _ v2: @escaping ()->Int) -> ()->Int {
    return v2
}
var fn1 = getFirstPositive(10, fn) 
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
    completionHandlers.append(completionHandler)
}

someFunctionWithEscapingClosure(_:)函数接受一个闭包作为参数,该闭包被添加到一个函数外定义的数组中。如果你不将这个参数标记为@escaping,就会得到一个编译错误。
将一个闭包标记为@escaping意味着你必须在闭包中显式的引用self。比如说,在下面的代码中,传递到someFunctionWithEscapingClosure(_:)中的闭包是一个逃逸闭包,这意味着它需要显式的引用self。相对的,传递到someFunctionWithNonescapingClosure(_:)中的闭包是一个非逃逸闭包,这意味着它可以隐式引用self

var compltionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
    compltionHandlers.append(completionHandler)
}

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

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

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

compltionHandlers.first?()
print(instance.x)
// 打印出“100”
上一篇下一篇

猜你喜欢

热点阅读