美文网首页
第十四章 Swift 闭包

第十四章 Swift 闭包

作者: 我有小尾巴快看 | 来源:发表于2019-06-05 15:04 被阅读0次

    Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的匿名函数比较相似,全局函数和嵌套函数其实就是特殊的闭包。

    闭包的形式有:

    全局函数 嵌套函数 闭包表达式
    有名字但不能捕获任何值。 有名字,也能捕获封闭函数内的值。 无名闭包,使用轻量级语法,可以根据上下文环境捕获值。

    Swift中的闭包有很多优点:

    1. 根据上下文推断参数和返回值类型
    2. 从单行表达式闭包中隐式返回(也就是闭包体只有一行代码,可以省略return)
    3. 可以使用简化参数名,如$0, $1(从0开始,表示第i个参数...)
    4. 提供了尾随闭包语法

    闭包表达式

    闭包表达式是一种利用简洁语法构建内联闭包的方式。 闭包表达式提供了一些语法优化,使得撰写闭包变得简单明了。

    //语法
    {(parameters) -> ReturnType in
        statements
        return  ReturnType
    }
    
    let studname = { print("Swift 闭包实例。") }
    studname()
    

    1. 参数名称缩写

    闭包的外部参数名必须为_,您可以直接通过$0,$1,$2来顺序使用闭包的参数。

    let names = ["AT", "AE", "D", "S", "BE"]
    
    var reversed = names.sorted( by: { $0 > $1 } ) // $0表示当前循环到元素,$1为$0后面一个,以此类推。
    print(reversed) // ["S", "D", "BE", "AT", "AE"]
    

    如果你在闭包中使用参数名称缩写, 您可以在闭包参数列表中省略对其定义, 并且对应参数名称缩写的类型会通过函数类型进行推断。in 关键字同样也可以被省略.

    2. 运算符函数

    Swift支持一些更简短的方式来使用闭包中的比较。

    Swift 的String类型定义了关于大于号 > 的字符串实现,其作为一个函数接受两个String类型的参数并返回Bool类型的值。 而这正好与sort(_:)方法的第二个参数需要的函数类型相符合。 因此,您可以简单地传递一个大于号,Swift可以自动推断出您想使用大于号的字符串函数实现:

    let names = ["AT", "AE", "D", "S", "BE"]
    
    var reversed = names.sorted(by: >)
    print(reversed) // ["S", "D", "BE", "AT", "AE"]
    

    3. 尾随闭包

    尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。

    func someFunctionThatTakesAClosure(closure: () -> Void) {
        // 函数体部分
    }
    
    // 以下是不使用尾随闭包进行函数调用
    someFunctionThatTakesAClosure({
        // 闭包主体部分
    })
    
    // 以下是使用尾随闭包进行函数调用
    someFunctionThatTakesAClosure() {
      // 闭包主体部分
    }
    
    let names = ["AT", "AE", "D", "S", "BE"]
    
    //尾随闭包
    var reversed = names.sorted() { $0 > $1 } // sort() 后的 { $0 > $1} 为尾随闭包。
    print(reversed) // ["S", "D", "BE", "AT", "AE"]
    

    注意: 如果函数只需要闭包表达式一个参数,当您使用尾随闭包时,您甚至可以把()省略掉。
    reversed = names.sorted { $0 > $1 }

    4. 捕获值

    闭包可以在其定义的上下文中捕获常量或变量,如果是对象类型则会进行只读复制,其他类型会进行复制,和OC一样。

    Swift最简单的闭包形式是嵌套函数,也就是定义在其他函数的函数体内的函数,嵌套函数可以捕获其外部函数所有的参数以及定义的常量和变量。

    func makeIncrementor(forIncrement amount: Int) -> () -> Int {
        var runningTotal = 0
        func incrementor() -> Int {
            runningTotal += amount
            return runningTotal
        }
        return incrementor
    }
    
    /*
    incrementor函数并没有获取任何参数,但是在函数体内访问了runningTotal和amount变量。这是因为其通过捕获在包含它的函数体内已经存在的runningTotal和amount变量而实现。
    由于没有修改amount变量,incrementor实际上捕获并存储了该变量的一个副本,而该副本随着incrementor一同被存储。
    所以我们调用这个函数时会累加:
    */
    
    let incrementByTen = makeIncrementor(forIncrement: 10)
    print(incrementByTen()) // 10
    print(incrementByTen()) // 20
    print(incrementByTen()) // 30
    

    5. 闭包是引用类型

    上面的例子中,incrementByTen是常量,但是这些常量指向的闭包仍然可以增加其捕获的变量值,这是因为函数和闭包都是引用类型。

    无论您将函数/闭包赋值给一个常量还是变量,您实际上都是将常量/变量的值设置为对应函数/闭包的引用。 上面的例子中,incrementByTen指向闭包的引用是一个常量,而并非闭包内容本身。

    这也意味着如果您将闭包赋值给了两个不同的常量/变量,两个值都会指向同一个闭包:

    func makeIncrementor(forIncrement amount: Int) -> () -> Int {
        var runningTotal = 0
        func incrementor() -> Int {
            runningTotal += amount
            return runningTotal
        }
        return incrementor
    }
    
    let incrementByTen = makeIncrementor(forIncrement: 10)
    incrementByTen() // 10
    incrementByTen() // 20
    incrementByTen() // 30
    incrementByTen() // 40
    
    let alsoIncrementByTen = incrementByTen
    print(alsoIncrementByTen()) // 50
    

    6. 无主引用和弱引用

    为了避免引用循环,Swift为闭包提供了两种方式打断循环。
    unowned:无主引用,被引用的对象不会增加引用计数,并且一定有值(如果原本就有的话),当对象被释放后,再次调用将会抛出异常。
    weak:弱引用,被引用的对象会通过弱引用的方式被引用,此时对象类型变更为可选类型,对象被释放后不会有异常出现。

    let block1 = { [unowned self] in
        print(self.view.frame)
    }
    
    let block2 = { [weak self] in
        print(self?.view.frame)
    }
    
    

    7.逃逸闭包

    闭包分为逃逸闭包非逃逸闭包

    • 若闭包的生命周期就在创建它的上下文中,那么它就是非逃逸闭包,其捕获的变量就没有必要增加其引用计数。
    • 若闭包被赋值存储起来等待延时调用,为了避免捕获的值被释放,故会对捕获的值进行一份只读拷贝。

    逃逸闭包才需要使用unownedweak来避免引用循环。

    8. 作为参数和返回值

    闭包可以作为参数传递,和OC用法一样。
    Swift额外支持了将闭包作为返回值来返回,使得代码能够更加紧凑。

    9. 常见的闭包使用场景

    • 懒加载
    lazy var label: UILabel = {
        return UILabel()
    }()
    
    • 计算属性
    var index: Int {
        return 1
    }
    
    • 普通闭包
    let block = { [unowned self] (a: Int, b: Int) -> Int in
        return Int(self.view.frame.minX) + a + b
    }
    
    block(1,2)
    
    • 作为参数
    func eat(food: ((String,Int) -> ())?) {
    
    }
    
    eat { (food, count) in //尾随闭包
    
    }
    
    • 作为返回值
    func eat(food: ((String,Int) -> ())?) -> ((String) -> ()) {
        return { (key) -> () in
             print(key)
        }
    }
    
    let block = eat { _ in // 参数可用`_`无视
    }
    
    block("Apple") // Apple
    

    相关文章

      网友评论

          本文标题:第十四章 Swift 闭包

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