美文网首页
Swift3.0_闭包(Closure)

Swift3.0_闭包(Closure)

作者: Michael杨 | 来源:发表于2016-12-15 08:53 被阅读0次

    闭包基础

    ** 闭包是自包含的函数代码块,可以在代码中被传递和使用。Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的匿名函数比较相似 **

    如果要使用闭包,首先要闭包赋值给变量或者常量
    定义一个可以持有闭包的变量

    var multiplyClosure: (Int, Int) -> Int
    

    将闭包赋值给前面定义的变量

    multiplyClosure = { (a: Int, b: Int) -> Int in
          return a * b
    }
    

    使用定义好的闭包

    let result = multiplyClosure(4, 2)
    // result is 8
    

    闭包函数的简易写法

    1. 如果闭包只声明了一个返回值,那么可以将return省略掉
    multiplyClosure = { (a: Int, b: Int) -> Int in
            a*b
    }
    
    1. 可以省略所有的类型信息,因为前面定义的变量中已经指明了闭包的参数类型和返回值类型
    multiplyClosure = { (a, b) in
            a*b
    }
    
    1. 省略掉所有参数列表。闭包中可以通过$加一个从0开始的数字来表示参数。
      不过这种写法个人认为可读性不强,不建议这么写
    multiplyClosure = {
          $0 * $1
    }
    

    下面来通过一个例子来简单的使用一下这几种写法

    首先定义一个函数,其中第三个参数是一个函数

    func operateOnNumbers(_ a: Int, _ b: Int, operation: (Int,Int) -> Int) -> Int{
        let result = operation(a, b)
        print(result)
        return result
    }
    

    接着调用这个函数

    let addClosure = { (a: Int, b: Int) in   //定义一个闭包  
            a+b
    }
    operateOnNumbers(4, 2, operation: addClosure)
    

    当然,operation这个参数,也可以直接传函数,传闭包是因为闭包也是没有名称的函数,对于operateOnNumbers来说,调用闭包或者函数对它来说没有区别

    func addFunction(_ a: Int, _ b: Int) -> Int {
    return a + b}
    operateOnNumbers(4, 2, operation: addFunction)
    

    下面来看几种简易的写法

    operateOnNumbers(3, 4, operation: {$0 * $1}) //上文的第三种简写
    
    
    operateOnNumbers(3, 4, operation: {(a,b) in
          a * b   //第二种,省略类型的简写
    })
    
    operateOnNumbers(3, 4, operation: {
        (a: Int, b: Int) -> Int in //第一种省略return
        a * b
    })
    
    operateOnNumbers(3, 4, operation:  * ) //最简易的写法
    

    尾随闭包

    operateOnNumbers(3, 4){
        $0 * $1   // 这种简写比较特殊,只有当闭包是作为函数的最后一个参数的时候才可以这么用
    }
    

    定义无参数无返回值的闭包

    let voidClosure: () -> Void = {
     print("Swift Apprentice is awesome!")}
    voidClosure()
    

    闭包--值捕获

    闭包可以在其被定义的上下文中捕获常量或变量。即使定义这些常量和变量的原作用域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。

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

    func makeIncrementer(forIncrement amount: Int) -> () -> Int {               
           var runningTotal = 0
           func incrementer() -> Int {
                 runningTotal += amount
                 return runningTotal 
          }
          return incrementer
    }
    

    incrementer闭包可以捕获makeIncrementer(forIncrement:)中的runningTotal和amount变量的引用,捕获引用保证了runningTotal和amount变量在调用完makeIncrementer后不会消失,并且保证了在下一次执行incrementer函数时,runningTotal依旧存在。

    let incrementByTen = makeIncrementor(forIncrement: 10)
    incrementByTen()// 返回的值为10
    incrementByTen()// 返回的值为20
    incrementByTen()// 返回的值为30
    
    let incrementBySeven = makeIncrementor(forIncrement: 7)
    incrementBySeven()// 返回的值为7
    
    incrementByTen()// 返回的值为40
    

    可以看到incrementByTen中的变量和incrementBySeven中的变量没有任何联系,而且,他们分别保持着对变量的引用。

    闭包是引用类型

    ** 函数和闭包都是引用类型 **

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

    如果将闭包赋值给了两个不同的常量或变量,两个值都会指向同一个闭包:

    let alsoIncrementByTen = incrementByTenalso
    IncrementByTen()// 返回的值为50
    

    逃逸闭包(Escaping Closures)

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

    一种能使闭包逃逸出函数的方法是,将这个闭包保存在函数外部定义的变量中。
    例如:很多启动异步操作的函数接受一个闭包参数作为completion handler。这类函数会在异步操作开始之后立刻返回,但是闭包知道异步操作结束后才会被调用。
    这种情况,闭包需要逃逸出函数,因为闭包需要在函数返回之后被调用。

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

    如果不加@escaping关键字,将会得到一个编译错误

    将一个闭包标记为@escaping意味着你必须在闭包中显示地引用self

    var completionHandlers: [() -> Void] = []
    
    func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void){
        completionHandlers.append(completionHandler)
    }
    
    func someFunctionWithNoneEscapingClosure(closure: () -> Void){
        closure()
    }
    
    class SomeClass{
        var x = 10
        func doSomething() {
            someFunctionWithEscapingClosure {
                self.x = 100
            }
            someFunctionWithNoneEscapingClosure {
                x = 200
            }
        }
    }
    
    let instance = SomeClass()
    instance.doSomething()
    print(instance.x) //打印出200
    
    completionHandlers.first?()
    print(instance.x) //打印出100
    

    自动闭包

    自动闭包是一种自动创建的闭包,用于包装传递给函数作为参数的表达式。这种闭包不接受任何参数,当它被调用的时候,会返回被包装在其中的表达式的值。

    自动闭包能够延迟求职,直到调用这个闭包,代码段才回被执行。

    var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]print(customersInLine.count)
    // 打印出 "5"
    let customerProvider = { customersInLine.remove(at: 0) }
    print(customersInLine.count)
    // 打印出 "5"
    print("Now serving \(customerProvider())!")
    // Prints "Now serving Chris!"
    print(customersInLine.count)
    // 打印出 "4"
    

    只有当闭包customerProvider执行时,才会执行remove操作

    利用闭包来自定义排序方法

    数组的排序

    let names = ["ZZZZZZ", "BB", "A", "CCCC", "EEEEE"]names.sorted()
    // ["A", "BB", "CCCC", "EEEEE", "ZZZZZZ"]
    

    可以通过制定闭包来为数组指定一个新的排序方式

    names.sorted {
     $0.characters.count > $1.characters.count}
    // ["ZZZZZZ", "EEEEE", "CCCC", "BB", "A"]
    

    用闭包来遍历集合

    集合实现了很多很实用的功能,比如排序,筛选等等。
    这些功能都配合闭包实用,来定义不同的规则

    var prices = [1.5, 10, 4.99, 2.30, 8.19]
    let largePrices = prices.filter { 
              return $0 > 5
    }
    

    利用闭包配合map方法,可以将数组里面的每个值,进行指定的操作,并返回一个新的数组

    let salePrices = prices.map {
     return $0 * 0.9}
    
    let sum = prices.reduce(0) {
         return $0 + $1
    }
    

    同样的,也可以对字典进行对应的操作

    let stock = [1.5:5, 10:2, 4.99:20, 2.30:5, 8.19:30]let stockSum = stock.reduce(0) {
     return $0 + $1.key * Double($1.value)}
    

    字典遍历的时候返回的是一个元组,所以可以分别拿到key和value进行操作

    举个例子来说明下上面几个集合的功能函数配合闭包的用法

    let namesAndAges = ["Owen": 40, "Pogba": 21, "Martial": 21, "Rashford": 19, "Marry": 17, "Lina": 15]
    

    // 使用reduce将数组中的名字连成一串

    names.reduce(""){
        return $0 + $1
    }
    

    //将上面的数组,先筛选出所有名字超过4个字符的,然后将数组中的名字连成一串

    let filteredNames = names.filter{
        return $0.characters.count > 4
    }
    filteredNames.reduce(""){
        $0 + $1
    }
    

    相关文章

      网友评论

          本文标题:Swift3.0_闭包(Closure)

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