Swift从入门到精通第六篇 - 闭包(Closures) 初识

闭包(学习笔记)

环境Xcode 11.0 beta4 swift 5.1

  • 闭包(Closures)
    • 闭包有三种形式:
      • 全局函数是具有名称且不捕获任何值的闭包。
      • 嵌套函数是具有名称的闭包,可以从其封闭函数中捕获值。
      • 闭包表达式(closure expressions)是用轻量级语法编写的未命名闭包,可以从它们周围的上下文捕获值。
  • 闭包表达式(Closure Expressions)
    • Swift的闭包表达式有一个干净、清晰的风格,通过优化,在常见场景中鼓励使用简洁、整洁的语法。这些优化包括:
      • 从上下文中可以推导参数和返回值
      • 隐式返回值即如果只有一句语句,如果需要返回值return关键字可以省略
      • 简写的参数名字。例如$0
      • 尾随闭包语法
    • 闭包表达式基本语法
    { (<#parameters#>) -> <#return type#> in
        <#statements#>
    }
    // in 关键字表示闭包的参数、返回值结束,接下来的是闭包体的开始
    • 尾随闭包:如果闭包是函数的最后一个参数,传入的实参可以写成尾随闭包这种形式,见下面的示例代码

    • 闭包表达式简单示例
    // 定义一个字符串数组
    let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
    // 定义一个闭包表达式
    func backward(_ s1: String, _ s2: String) -> Bool {
        return s1 > s2
    }
    // 使用数自带的 sorted 方法, 此方法是接收一个闭包表达式
    var reversedNames = names.sorted(by: backward)
    // 输出结果
    // reversedNames is equal to ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
    // 由于此闭包比较简单,可以直接写成内联形式,如下
    reversedNames = names.sorted(by: {(s1: String, s2: String) -> Bool in 
        return s1 > s2
    }) 
    // 由于闭包可以自动推导类型,因为传入函数的参数类型肯定是 (String, String) -> Bool ,这意味着参数类型和返回值类型已确定,可以省略,简写如下
    reversedNames = names.sorted(by: {s1, s2 in return s1 > s2 })
    // 只有一句表达式,还可以简写如下, return 关键字也可以省略
    reversedNames = names.sorted(by: {s1, s2 in s1 > s2 })
    // 如果用简写的参数
    reversedNames = names.sorted(by: {$0 > $1 })
    // 如果用字符串已实现的运算符 >, 还可以简写
    reversedNames = names.sorted(by: > )
    // 用尾随闭包
    reversedNames = names.sorted(){$0 > $1}
    // 因为此函数只有一个参数,函数的小括号也可以省略
    reversedNames = names.sorted{$0 > $1}
  • 闭包值捕获(Capturing Values)
    • 示例代码如下
    func makeIncrementer(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
        func incrementer() -> Int {
            runningTotal += amount
            return runningTotal
        }
        return incrementer
    }
    let incrementByTen = makeIncrementer(forIncrement: 10)
    incrementByTen()   // returns a value of 10 
    incrementByTen()   // returns a value of 20
    incrementByTen()   // returns a value of 30
    let incrementBySeven = makeIncrementer(forIncrement: 7)
    incrementBySeven() // returns a value of 7
    incrementByTen()   // returns a value of 40
    • 如果您将闭包分配给类实例的属性,并且闭包通过引用实例或其成员来捕获该实例,那么您将在闭包和实例之间创建一个强引用循环
    class HTMLElement {
        let name: String
        let text: String?
        lazy var asHTML: () -> String = {
            if let text = self.text {
                return "<\(self.name)>\(text)</\(self.name)>"
            } else {
                return "<\(self.name) />"
            }
        }
        init(name: String, text: String? = nil) {
            self.name = name
            self.text = text
        }
        deinit {
            print("\(name) is being deinitialized")
        }
    }
    var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
    print(paragraph!.asHTML()) // Prints "<p>hello, world</p>"
    paragraph = nil // 此时 deinit方法并不会调用
    // 这样会产生循环引用,见下图

    Closure 循环引用

    // 可以使用 unowned \ weak 解决循环引用问题
    class HTMLElement {
        let name: String
        let text: String?
        lazy var asHTML: () -> String = {
            [unowned self] in
            if let text = self.text {
                return "<\(self.name)>\(text)</\(self.name)>"
            } else {
                return "<\(self.name) />"
            }
        }
        init(name: String, text: String? = nil) {
            self.name = name
            self.text = text
        }
        deinit {
            print("\(name) is being deinitialized")
        }
    }
    var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
    print(paragraph!.asHTML()) // Prints "<p>hello, world</p>"
    paragraph = nil // Prints "p is being deinitialized"

    Closure 循环引用消除

    • 如何定义一个捕获列表,用关键字 weak or unowned 中间用 ','分割
    lazy var someClosure: (Int, String) -> String = {
        [unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
        // closure body goes here
    }
      - `unowned` 经常使用在闭包和相互引用的实例对象同时销毁的时候
      - `weak` 经常使用在当捕获的引用可能在将来的某个时刻变为nil时,弱引用始终是可选的类型,当它们引用的实例被释放时,将自动变为nil。这使您能够检查它们是否存在于闭包的主体中。
    • 闭包是引用类型(Closures Are Reference Types)
    // 继续上面的例子,如果将 incrementByTen 将赋值给一个新的常量或者变量,示例代码如下
    let alsoIncrementByTen = incrementByTen
    alsoIncrementByTen() // returns a value of 50
    incrementByTen() // returns a value of 60
  • 逃逸闭包(Escaping Closures)
    • 用关键字 @escaping 修饰
    • 如下例子当一个函数参数是闭包,要存入外部一个变量,此时必须要用 @escaping 修饰
    var completionHandlers: [() -> Void] = []
    func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
        completionHandlers.append(completionHandler)
    }
    • 如果用 @escaping 修饰,那必须显示的调用 self
    func someFunctionWithNonescapingClosure(closure: () -> Void) {
        closure()
    }
    class SomeClass {
        var x = 10
        func doSomething() {
            someFunctionWithEscapingClosure { self.x = 100 }
            someFunctionWithNonescapingClosure { x = 200 }
        }
    }
    let instance = SomeClass()
    instance.doSomething()
    print(instance.x)  // Prints "200"
    completionHandlers.first?()
    print(instance.x)  // Prints "100"
  • 自动闭包(Autoclosures)
    • 用标识符 @autoclosure 修饰
    • 作为参数传递给函数的表达式,不接收任何参数并返回封装在表达式内的值( () -> T 这种形式),如果只有一个数不可以省略大括号
    • 自动闭包通常用在延迟计算,因为在调用闭包之前,内部的代码不会运行
    var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
    print(customersInLine.count) // Prints "5"
    let customerProvider = { customersInLine.remove(at: 0) } // 定义一个闭包表达式 () -> String 
    print(customersInLine.count)  // Prints "5"
    print("Now serving \(customerProvider())!")  // Prints "Now serving Chris!"
    print(customersInLine.count)  // Prints "4"
    // 观察以下两段代码
    // customersInLine now is ["Alex", "Ewa", "Barry", "Daniella"]
    func serve(customer customerProvider: () -> String) {
        print("Now serving \(customerProvider())!")
    }
    serve(customer: { customersInLine.remove(at: 0) } )  // Prints "Now serving Alex!"
    // customersInLine now is ["Ewa", "Barry", "Daniella"]
    // 此处用了自动闭包, {}都省略了
    func serve(customer customerProvider: @autoclosure () -> String) {
        print("Now serving \(customerProvider())!")
    }
    serve(customer: customersInLine.remove(at: 0))
    // Prints "Now serving Ewa!"
    • 可以同时使用自动闭包和逃逸闭包, 用 @autoclosure @escaping 修饰,不分先后
    // customersInLine is ["Barry", "Daniella"]
    var customerProviders: [() -> String] = []
    func collectCustomerProviders(_ customerProvider: @autoclosure @escaping () -> String) {
        customerProviders.append(customerProvider)
    }
    collectCustomerProviders(customersInLine.remove(at: 0))
    collectCustomerProviders(customersInLine.remove(at: 0))
    print("Collected \(customerProviders.count) closures.")
    // Prints "Collected 2 closures."
    for customerProvider in customerProviders {
        print("Now serving \(customerProvider())!")
    }
    // Prints "Now serving Barry!"
    // Prints "Now serving Daniella!"

[Swift从入门到精通]

猜你喜欢

转载自www.cnblogs.com/tzsh1007/p/11481452.html
今日推荐