8.闭包

作者: LucXion | 来源:发表于2021-07-14 09:52 被阅读0次

闭包可以捕获和存储其所在上下文中任意常量和变量的引用。被称为包裹常量和变量。 Swift 会为你管理在捕获过程中涉及到的所有内存操作。

全局和嵌套函数实际上也是特殊的闭包,闭包采用如下三种形式之一:

  • 全局函数是一个有名字但不会捕获任何值的闭包
  • 嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包
  • 闭包表达式是一个利用轻量级语法所写的可以捕获其上下文中变量或常量值的匿名闭包

Swift 的闭包表达式拥有简洁的风格,并鼓励在常见场景中进行语法优化,主要优化如下:

  • 利用上下文推断参数和返回值类型

  • 隐式返回单表达式闭包,即单表达式闭包可以省略 return 关键字

    sorted(by:) 方法接受一个闭包,该闭包函数需要传入与数组元素类型相同的两个值,并返回一个布尔类型值来表明当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值前面,排序闭包函数需要返回 true,反之返回 false

    闭包的函数体部分由关键字 in 引入。该关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始。

    var sour = {
        (s1:Int,s2:Int) -> Int in
        return s1+s2
    }
    sour = {
        s1,s2 in //根据上下文推断类型
        s1*s2 //单行表达式隐式返回
    }
    
  • 参数名称缩写

    var arr:[Int]=[1,2,3]
    arr.sort(by: {$0>$1})
    arr.sort(by: >)//Swift 可以自动推断找到系统自带的那个字符串函数的实现:
    
  • 尾随闭包语法

    func someFunctionThatTakesAClosure(closure: () -> Void) {
        // 函数体部分
    }
    // 以下是不使用尾随闭包进行函数调用
    someFunctionThatTakesAClosure(closure: {
        // 闭包主体部分
    })
    // 以下是使用尾随闭包进行函数调用
    someFunctionThatTakesAClosure() {
        // 闭包主体部分
    }
    
    func sum(block:(_ a:Int,_ b:Int)->Int){}
    sum(block: +)// 系统自动匹配
    sum(){$0+$1}//尾随闭包,{}写在()外
    sum{$0+$1}// 只有一个闭包参数,省略()
    

值捕获

如果你将闭包赋值给一个类实例的属性,并且该闭包通过访问该实例或其成员而捕获了该实例,你将在闭包和该实例间创建一个循环强引用。Swift 使用捕获列表来打破这种循环强引用。

  • 嵌套函数的值捕获

    func makeIncrementer(forIncrement amount: Int) -> () -> Int {
        var runningTotal = 0
        func incrementer() -> Int {
            runningTotal += amount
            return runningTotal
        }
        return incrementer
    }
    let incrementByTen = makeIncrementer(forIncrement: 10)
    /*
     incrementer 从外围函数捕获了 runningTotal 和 amount 变量的引用。捕获引用保证了 runningTotal 和 amount 变量在调用完 makeIncrementer 后不会消失,并且保证了在下一次执行 incrementer 函数时,runningTotal 依旧存在。
     */
    incrementByTen()
    incrementByTen()
    let count = incrementByTen() // 30
    
    let in7 = makeIncrementer(forIncrement: 7)
    let c = in7() // 7
    

逃逸闭包

当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中逃逸。很多启动异步操作的函数接受一个闭包参数作为 completion handler。这类函数会在异步操作开始之后立刻返回,但是闭包直到异步操作结束后才会被调用

var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
    // 被外部引用,在函数返回后仍然存在,逃逸闭包
    completionHandlers.append(completionHandler)
}
func someFunctionWithNonescapingClosure(closure: () -> Void) {
    // 闭包在函数返回前调用,非逃逸闭包
    closure()
}
class SomeClass {
    var x = 10
    func doSomething() {
        // 逃逸闭包,必须显性调用self
        someFunctionWithEscapingClosure { self.x = 100 }
        // 非逃逸闭包,隐式调用self
        someFunctionWithNonescapingClosure { x = 200 }
    }
}

自动闭包

var customersInLine = ["1","2","3","4"]
// 闭包作为函数传递
func serve(customer customerProvider: () -> String) {
    print("Now serving \(customerProvider())!")
}
// 调用时加花括号
serve(customer: { customersInLine.remove(at: 0) } )

// 将函数自动封装成闭包
func serve(customer customerProvider: @autoclosure () -> String) {
    print("Now serving \(customerProvider())!")
}
// 将该函数当作接受 String 类型参数(而非闭包)的函数来调用
serve(customer: customersInLine.remove(at: 0))

过度使用 autoclosures 会让你的代码变得难以理解。上下文和函数名应该能够清晰地表明求值是被延迟执行的。

相关文章

  • 8.闭包

    定义1.函数运行的环境2.持有函数运行状态3.函数内部可以定义函数4.函数内部也可以定义类5.在函数中返回一个函数...

  • 8.闭包

    闭包可以捕获和存储其所在上下文中任意常量和变量的引用。被称为包裹常量和变量。 Swift 会为你管理在捕获过程中涉...

  • 8. 函数和闭包

    转载自http://wanwu.tech/2017/03/15/functions-and-closures/ 简...

  • Swift -- 8.闭包(下)

    一.@convention 之前在执行vtable取出的函数地址时,使用到了@convention 用于修饰函数类...

  • 前端(十四)

    1.循环语句 2.数组去重 3.字符串反转 4.定时器的基本用法 5.定时器动画 6.时钟 7.闭包 8.闭包存循...

  • swift-闭包

    闭包 闭包定义 闭包简化 - 尾随闭包 闭包参数 闭包返回值 闭包的循环引用

  • 闭包,闭包,闭包

    1、这家伙到底是什么? 网上关于这个的讨论的太多了太多了,有各种的举例子,但是大部分还在寻找这个答案的小伙伴对于变...

  • 闭包-Closures [swift 5.1]

    闭包的语法 尾随闭包 闭包逃离 自动闭包

  • Day7 闭包(Closures)

    本页包含内容:• 闭包表达式• 尾随闭包• 值捕获• 闭包是引用类型• 逃逸闭包• 自动闭包 1、闭包表达式 闭包...

  • Python闭包

    闭包 = 环境变量 + 函数 调用闭包内部的环境变量 闭包的经典误区 闭包与非闭包实现人类走路 非闭包 闭包

网友评论

      本文标题:8.闭包

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