美文网首页swift
swift 中的闭包

swift 中的闭包

作者: freemanIT | 来源:发表于2022-05-10 18:36 被阅读0次

闭包

闭包表达式

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))
}

调用这个方法

完整写完是这样的

exec(v1: 10, v2: 20, fn: {
    (v1: Int, v2: Int) -> Int in
    return v1 + v2
})

可以将类型省略

exec(v1: 10, v2: 20, fn: {
    v1, v2 in return v1 + v2
})

也可以将return 省略

exec(v1: 10, v2: 20, fn: {
    v1, v2 in v1 + v2
})

使用美元符号$ 代替参数

exec(v1: 10, v2: 20, fn: {$0 + $1})

最终可以省略参数, 直接使用加号

exec(v1: 10, v2: 20, fn: +)

尾随闭包

如果将一个很长的闭包表达式作为函数的最后一个实参, 使用尾随闭包可以增强函数可读性

尾随闭包是一个被书写在函数调用括号外面的闭包表达式

如果闭包表达式是函数的唯一实参, 而且使用了尾随闭包的语法, 就不需要再函数名后边加圆括号了

exec(v1: 10, v2: 20) { v1, v2 in
    v1 + v2
}

exec(v1: 10, v2: 20) { $0 + $1 }

唯一实参

func exec(fn: (Int, Int) -> Int) {
    print(fn(1, 2))
}

exec(fn: {
    $0 + $1
})

exec(fn: {
    $0 + $1
})

exec {
    $0 + $1
}

swift 中的数组排序sort 函数

@inlinable public func sorted(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> [Element]
        let nums = [11, 2, 6, 5, 18, 45, 99, 65]
    // 自己定一个比较
    func cmp(i1: Int, i2: Int) -> Bool {
        // 值比较大的放在左边
        return i1 > i2
    }
    
    let arr = nums.sorted(by: cmp(i1:i2:))
    print("\(#line) ~~~~~~ \(#function) ~~~~~~ \(arr)")

sort 传入参数的简写

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

        nums.sorted {
        $0 > $1
    }

闭包

一个函数和它所捕获的变量\常量环境组合起来, 称为闭包

func getFn() -> (Int) -> Int {
    var num = 0
    func plus(_ i: Int) -> Int {
        num += i
        return num
    }
    return plus(_:)
}

var fn1 = getFn()
fn1(1)
fn1(2)
fn1(3)
fn1(4)
// 1 3 6 10

fn 捕获了变量num

如果不捕获num, 直接返回i, 调用函数, 则为以下结果

func getFn() -> (Int) -> Int {
    var num = 0
    func plus(_ i: Int) -> Int {
        return i
    }
    return plus(_:)
}

rax 通常保存返回值

读取rax 值

register read rax
     rax = 0x0000000100003df0  TestSwift`plus(Swift.Int) -> Swift.Int at main.swift:19
fn的地址

恢复代码, 返回num, 则汇编为

alloc申请内存

alloc 申请一块内存, 将num 拷贝到堆中

看下rax 返回值

rax返回值 断点在21行

可以看到x/5xg 0x0000000103827eb0

第三组中为0x0000000000000001, 初始化为了1.

对应到汇编中

继续跳过一个断点, 赋值为了0x0000000000000003

(lldb) x/5xg 0x0000000103827eb0
0x103827eb0: 0x0000000100004018 0x0000000200000003
0x103827ec0: 0x0000000000000003 0x0000000000000000
0x103827ed0: 0x0000000000000000

如何看到alloc 申请了多少内存空间给fn 呢?

需要24 个字节存储数据, 但是真正给的空间为32 字节, 16 的倍数

esi存储为0x18

可以将闭包想想成一个类的实例对象

  • 内存在堆空间
  • 捕获的局部变量就是对象的成员
  • 组成闭包的函数就是类内部定义的方法
class Closure {
    var num = 0
    func plus(_ i: Int) -> Int {
        num += i
        return i
    }
}

var cs1 = Closure()
cs1.plus(1)
调用了rax,fn函数

调用到rax, 即为getFn 函数地址, jmp 为plus 地址0x100003ed0

进入到rax中

每次调用fn1, 调用的都为plus, 同一个函数地址, 第三个字节存储的是num 的地址

如果num 为全局变量, 则

没有捕获num

如果如下代码

typealias Fn = (Int) -> (Int, Int)
func getFns() -> (Fn, Fn) {
    var num1 = 0
    var num2 = 0
    func plus(_ i: Int) -> (Int, Int) {
        num1 += i
        num2 += i << 1
        return (num1, num2)
    }
    func minus(_ i: Int) -> (Int, Int) {
        num1 -= i
        num2 -= i >> 1
        return (num1, num2)
    }
    return (plus, minus)
}

let (p, m) = getFns()
p(6) // (6, 12)
m(5) // (1, 10)
p(4) // (5, 18)
m(3) // (2, 17)

num1num2, 分别alloc 一次, 给两个函数共用

自动闭包

使用@autoclosure, 会将() -> T 格式的参数自动封装成闭包, 自动闭包也支持中间的参数, 有无自动闭包@autoclosure, 可以构成函数重载

例如下面代码, 如果第一个参数已经大于0, 则没必要调用第二个参数的内容

// 如果第一个数大于0, 返回第一个, 否则返回第二个
func getFirstPositive(_ v1: Int, _ v2: Int) -> Int {
    return v1 > 0 ? v1 : v2
}
getFirstPositive(10, 20)
getFirstPositive(-2, 20)
getFirstPositive(0, -4)

改写成下面, 将第二个参数修改为一个闭包函数

// 如果第一个参数已经大于0, 则第二个参数不必调用执行
func getFirstPositive(_ v1: Int, _ v2: () -> Int) -> Int? {
    return v1 > 0 ? v1 : v2()
}

在swift 中可以使用@autoclosure, 将它封装成一个自动闭包

// 可以使用自动闭包来实现
func getFirstPositive(_ v1: Int, _ v2: @autoclosure () -> Int) -> Int? {
    return v1 > 0 ? v1 : v2()
}
getFirstPositive(10, 20)

则在调用时, 最后一个参数自动封装成{20}

相关文章

  • Swift-闭包

    Swift 闭包 函数 ()->() Swift 中的闭包和 Objective-C 中的 block 类似,闭包...

  • Swift闭包和函数

    函数在Swift中只是一种特殊的闭包,闭包在Swift语言中是一等公民,支持闭包嵌套和闭包传递。Swift中的闭包...

  • swift4 闭包

    swift 闭包 闭包:swift 中 函数是闭包的一种类似于oc的闭包闭包表达式(匿名函数) -- 能够捕获上下...

  • Swift学习笔记(1)

    SWift学习笔记 闭包 闭包表达式 闭包是自包含的函数代码块,可以在代码中被传递和使用。Swift 中的闭包与 ...

  • swift中的闭包

    swift中的闭包 闭包是自包含的函数代码块,可以在代码中被传递和使用。swift中的闭包与C和Objective...

  • Swift总结

    1.闭包 swift中的闭包类似于oc中的block回调,但是swift的闭包有很多种写法,具有多变性,今天就来总...

  • swift 闭包(闭包表达式、尾随闭包、逃逸闭包、自动闭包)

    闭包是自含的函数代码块,可以在代码中被传递和使用 闭包和swift的对比 Swift 中闭包与OC的 block ...

  • 闭包

    闭包 本节内容包括: 闭包表达式 尾随闭包 值捕获 闭包是引用类型 Swift 中的闭包与 C 和 Objecti...

  • Swift入门基础5——闭包

    何为闭包? Swift中的闭包和OC中的block很相似(其实也有其他语言有闭包的概念)。所谓闭包,就是可以捕获其...

  • swift学习笔记 ⑥ —— 闭包

    Swift学习笔记 - 文集 闭包,就是能够读取其他函数内部变量的函数。Swift 中的闭包与 C 和 OC 中的...

网友评论

    本文标题:swift 中的闭包

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