Swift- 闭包

//闭包closures 类似于 OC的 block, 闭包可以捕获(capture)并且存储代码块中指向常量或变量的指针

        //Global and nested functions 都是闭包的特例, 闭包有三种形式:

        //(1)Global functions 是有名字但是没有 capture 任何值的闭包

        //(2)Nested functions 是有名字并且 capture 封闭函数中值的闭包

        //(3)闭包语句是没有名字的,轻量级的语法(syntax), 可以 capture 周围 context中的常量和变量指针


//一. 闭包的基本形式
        /**
            闭包的表达形式
            { (parameters) -> return type in
                statements
            }
            内联闭包(inline closure)
            闭包语句体由关键字 in 引出, in 表明参数和返回值定义完成, 闭包语句体开始
            可以为 inout 参数, 但是不能有默认值.可变数量参数也可以使用.元组可以作为参数和返回值
         */
        
        //用闭包实现 sorted(by: (String, String) throws -> Bool) 方法,参数 by 是一个闭包
        let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
        var reversedNames: [String] = []
        //调用sorted(by: (String, String) throws -> Bool),参数 by 是一个闭包
        reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
            return s1 > s2
        })
        
        //闭包优化(optimizations)
        //(1)从 context 中推断参数和返回值类型
        //上述 sorted 例子中,因为 sorted(by:) 方法是由字符串数组调用,所以它的参数一定是 (String, String) -> Bool 函数类型的, 因此可以忽略不写简写为以下形式
        reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 })
        //inline closure 作为函数或者方法的参数时, 总是可以推断参数和返回值类型, 因此这种情况下都可以简写
        
        //(2) Single-Expression Closures, 单语句闭包的隐式返回
        //必须是单语句闭包, 且返回值类型明确
        reversedNames = names.sorted(by: { s1, s2 in s1 > s2 })
        
        //(3) 简写参数名
        //对于 inline closures, Swift 提供简写参数名, $1, $2, ....
        //如果在闭包语句中使用简写参数名,可以忽略闭包的参数列表.参数类型和数量会从函数类型推断, 关键字 in 也可以省略
        reversedNames = names.sorted(by: { $0 > $1 })
        
        //(4) 运算符方法
        //Swift 的 String 类型定义了 string 特定的 > 方法的实现, 比较两个 String, 返回 Bool 值.类似这种特定的运算符方法可以直接使用
        reversedNames = names.sorted(by: >)
        
        
        //二. 尾随闭包(trailing closure)
        //如果用闭包作为函数参数,并且闭包语句较长,可以使用 trailing closure.
        //调用时,写在函数括号外,不用写 argument label
        
        //函数声明
        func someFunctionThatTakesAClosure(closure: () -> Void) {
            //函数实现
        }
        
        //函数调用: 使用 trailing closures
        someFunctionThatTakesAClosure() {
            //闭包实现
        }
        
        //函数调用: 不使用 trailing closures
        someFunctionThatTakesAClosure(closure: {
            //闭包实现
        })
        
        //例1.
        //上述 sorted(by:) 方法用 trailing closures 可以写为
        reversedNames = names.sorted() { $0 > $1 }
        //如果闭包语句是唯一的参数, 那么调用时函数后的括号()可以省略
        reversedNames = names.sorted { $0 > $1 }
        
        //例2.
        //以下例子将数组中的数字变为英文形式
        let digitNames = [
            0: "Zero", 1: "One", 2: "Two",   3: "Three", 4: "Four",
            5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
        ]
        let numbers = [16, 58, 510]
        //参数 number 的类型不必说明,因为类型会根据映射(map)的数组中的值推断
        //指定返回值类型为 String, 来表明输出数组中存储的数据类型
        let strings = numbers.map() { (number) -> String in
            //用参数给变量赋值,因为函数和闭包中的参数都是常量
            var number = number
            var output = ""
            repeat {
                //使用 !, 因为字典下标访问返回的是可选类型, 此处可以确定不为空所以强制解包
                output = digitNames[number % 10]! + output
                number /= 10
            } while number > 0
            return output
        }
        // strings 被推断为 [String] 类型
        //输出 ["OneSix", "FiveEight", "FiveOneZero"]
        
        
        //三. 闭包特性
        //1. 捕获值 Capturing Values
        //闭包可以 capture 周围 context 的常量和变量的指针, 访问并修改这个值
        //capture 的是 value 的指针, 因此在函数调用后不会消失, 下次调用时还可以使用
        //Swift 中闭包 capture 值的最简单例子是 nested function.一个 nested function 可以 capture 外部函数的参数,以及外部函数中定义的常量和变量
        
        //函数
        func makeIncrementer(forIncrement amount: Int) -> () -> Int {
            var runningTotal = 0
            func incrementer() -> Int {
                runningTotal += amount
                return runningTotal
            }
            return incrementer
        }
        
        //调用
        //capture 的是 value 的指针, 因此在函数调用后不会消失, 下次调用时还可以使用
        let incrementByTen = makeIncrementer(forIncrement: 10)
        incrementByTen()
        // returns a value of 10
        incrementByTen()
        // returns a value of 20
        incrementByTen()
        // returns a value of 30
        
        //再定义一个incrementBySeven, 它将会有自己新的独立的指针指向 runningTotal
        let incrementBySeven = makeIncrementer(forIncrement: 7)
        incrementBySeven()
        // returns a value of 7
        
        //incrementByTen 捕获的变量是独立的,不受影响
        incrementByTen()
        // returns a value of 40
        
        //如果把一个类的一个属性值赋给闭包, 闭包又 capture 这个实例变量,会造成闭包和实例变量的strong reference cycle. Swift 使用 capture lists 来打破循环引用
        
        //2. 闭包是指针类型
        //上述例子中, incrementBySeven 和 incrementByTen是常量,但是这些闭包仍然可以改变它所捕获的runningTotal的值,这是因为 functions 和 closures 是指针类型
        //incrementByTen 是常量,并非闭包本身是常量
        let alsoIncrementByTen = incrementByTen
        alsoIncrementByTen()
        // returns a value of 50

        //3. Escaping Closures
        //闭包作为参数, 但是在函数执行结束后调用.(不在主线程中立即执行的)
        class SomeClass {
            var x = 10
            //在参数类型前加 @escaping 表明这个闭包允许 escape
            //Escaping Closures 的一种实现方式是存储在一个函数外部定义的变量中, 使用外部变量存储时必须加 @escaping, 否则会编译报错
            var completionHandlersArray: [() -> Void] = []
            //escape 的闭包
            func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
                completionHandlersArray.append(completionHandler)
            }
            
            //没有 escape 的闭包
            func someFunctionWithNonescapingClosure(closure: () -> Void) {
                closure()
            }
            //没有逃逸的闭包, 没有逃逸的闭包不能写 argumentLabel
            func nonEscapingClosure(closure: (_ param1: Int) -> Int) -> Void {
                closure(1)
            }
            
            func doSomething() {
                //将一个闭包声明为 @escaping, 意味着在闭包内部必须明确的指向 self
                //没有声明为 @escaping, 意味着可以隐性的指向 self
                someFunctionWithEscapingClosure { self.x = 100 }
                someFunctionWithNonescapingClosure { x = 200 }
            }
        }
        
        //调用函数
        let instance = SomeClass()
        instance.doSomething()
        print(instance.x)
        // Prints "200"
        //没有逃逸的闭包
        instance.nonEscapingClosure { (param1) -> Int in
            return param1 + 1
        }
        
        //调用completionHandler
        instance.completionHandlersArray.first?()
        print(instance.x)
        // Prints "100"
    
        //4. 自动闭包
        //自动闭包能动态的封装一个表达式为一个函数的参数,自动闭包不能带任何的参数,返回值是内部语句的返回值
        //通常会调用有自动闭包的函数,而不是实现它,不能过度使用
        //自动闭包让你可以delay evaluation, 因为闭包内部的代码在调用时才执行.
        //delay evaluation通常用于有副作用(如耗时)或者需要大量计算的代码
        
        var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
        print(customersInLine.count)
        // Prints "5"
        
        //customerProvider 是 () -> String 类型的
        let customerProvider = { customersInLine.remove(at: 0) }
        print(customersInLine.count)
        // Prints "5"
        
        //闭包调用时,才执行 remove 操作
        print("Now serving \(customerProvider())!")
        // Prints "Now serving Chris!"
        print(customersInLine.count)
        // Prints "4"
        
        //(1) @autoclosure
        //显式闭包(explicit closure)
        func serve(customer customerProvider: () -> String) {
            print("Now serving \(customerProvider())!")
        }
        serve(customer: { customersInLine.remove(at: 0) } )
        
        //在以上显式闭包类型前加 @autoclosure
        func serve1(customer customerProvider: @autoclosure () -> String) {
            print("Now serving \(customerProvider())!")
        }
        serve1(customer: customersInLine.remove(at: 0)) //不用写大括号 {}
        
        //(2) @autoclosure 和 @escaping 同时使用
        //在闭包类型前加 @autoclosure @escaping
        var customerProviders: [() -> String] = []
        //因为将customerProvider 添加到了外部的数组中,所以要将 @escaping
        func collectCustomerProviders(_ customerProvider: @autoclosure @escaping () -> String) {
            customerProviders.append(customerProvider)
        }
        collectCustomerProviders(customersInLine.remove(at: 0))
        collectCustomerProviders(customersInLine.remove(at: 0))
        
        print("Collected \(customerProviders.count) closures.")
        //"Collected 2 closures."
        for customerProvider in customerProviders {
            print("Now serving \(customerProvider())!")
        }
        //"Now serving Barry!"
        //"Now serving Daniella!"

猜你喜欢

转载自blog.csdn.net/leecsdn77/article/details/80707093