swift学习笔记6 闭包

闭包

Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures in Swift are similar to blocks in C and Objective-C and to lambdas in other programming languages.

闭包是一种自包含的功能块。swift中的闭包类似于c和oc中的block,其他编程语言中的lambdas。

Global and nested functions, as introduced in Functions, are actually special cases of closures. Closures take one of three forms:

全局和嵌套函数都是特殊的闭包。
闭包有三种形式:

Global functions are closures that have a name and do not capture any values.

全局函数是一种有一个名字但是不捕获(capture)任何值的闭包

Nested functions are closures that have a name and can capture values from their enclosing function.

嵌套函数是一种有名字并且可以从它的包含函数中捕获值的闭包。

Closure expressions are unnamed closures written in a lightweight syntax that can capture values from their surrounding context.

闭包表达式是用轻量级语法写的能从它上下文捕获值的无命名的闭包。

let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]

    func backward(_ s1:  a href="" String /a , _ s2:  a href="" String /a ) ->  a href="" Bool /a  {
        return s1 > s2
    }
    var reversedNames = names.sorted(by: backward)
    // reversedNames is equal to ["Ewa", "Daniella", "Chris", "Barry", "Alex"]

闭包表达式通常的语法:

    { (parameters) -> return type in
        statements
    }

The parameters in closure expression syntax can be in-out parameters, but they can’t have a default value. Variadic parameters can be used if you name the variadic parameter. Tuples can also be used as parameter types and return types.

闭包表达式中的参数可以是in-out参数,但是不能有默认值。
参数可以是可变参数。tuple既可以作参数也可以作返回值。
前面的backward(::)函数的闭包表达式版本可以这样写:

    reversedNames = names.sorted(by: { (s1:String, s2:String) ->Bool in
        return s1 > s2
    })

Note that the declaration of parameters and return type for this inline closure is identical to the declaration from the backward(::) function. In both cases, it is written as (s1: String, s2: String) -> Bool. However, for the inline closure expression, the parameters and return type are written inside the curly braces, not outside of them.

注意,闭包的参数和返回类型都和backward(::)函数的声明一样。都是(s1: String, s2: String) -> Bool。但是对于闭包表示式来说,参数和返回类型都在大括号之内,而不是在大括号之外。

The start of the closure’s body is introduced by the in keyword. This keyword indicates that the definition of the closure’s parameters and return type has finished, and the body of the closure is about to begin.

闭包体由in关键字引导。in关键字表示闭包的参数和返回类型的定义已经完成,闭包体要开始了。
由于此闭包体太短,它可以写成一行:
reversedNames = names.sorted(by: { (s1:String, s2:String) ->Bool in return s1 > s2 } )

从上下文引用类型

Because the sorting closure is passed as an argument to a method, Swift can infer the types of its parameters and the type of the value it returns. The sorted(by:) method is being called on an array of strings, so its argument must be a function of type (String, String) -> Bool. This means that the (String, String) and Bool types do not need to be written as part of the closure expression’s definition. Because all of the types can be inferred, the return arrow (->) and the parentheses around the names of the parameters can also be omitted:

因为上面的排序闭包是传递给函数的一个参数,swift可以引用它的参数类型和它返回值的类型。sorted(by:)函数是被字符串数组调用的,所以它的参数必须是函数类型(String, String) -> Bool。这意味着
(String, String) 和Bool 在闭包表达式的定义里不必写。因为所有类型都可以被推理得出,返回箭头和参数名两边的括号也都可以省略:
reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )

从单行闭包表达式中隐式返回

Single-expression closures can implicitly return the result of their single expression by omitting the return keyword from their declaration,

单行闭包表达式可以隐式地返回它们的结果,从而可以在声明中省略return关键字
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )

参数名缩写 Shorthand Argument Names

Swift automatically provides shorthand argument names to inline closures, which can be used to refer to the values of the closure’s arguments by the names 0, 1, $2, and so on.

swift为内联闭包提供了参数名的缩写Shorthand Argument Names,你可以使用 0, 1, $2,等名字来引用闭包的参数值。

If you use these shorthand argument names within your closure expression, you can omit the closure’s argument list from its definition, and the number and type of the shorthand argument names will be inferred from the expected function type. The in keyword can also be omitted, because the closure expression is made up entirely of its body:

如果你在你的闭包表达式中使用这种参数名缩写,你可以在声明中省略闭包的参数列表,参数的类型和数量将会从传入的函数类型中推导得出。in关键字也可以省略,因为闭包表达式只由它的闭包体组成:
reversedNames = names.sorted(by: { $0 > $1 } )
在此, 0 1分别指向闭包的第一个和第二个string参数

操作符方法

There’s actually an even shorter way to write the closure expression above. Swift’s String type defines its string-specific implementation of the greater-than operator (>) as a method that has two parameters of type String, and returns a value of type Bool. This exactly matches the method type needed by the sorted(by:) method. Therefore, you can simply pass in the greater-than operator, and Swift will infer that you want to use its string-specific implementation:

其实对于上面的闭包表达式还有一种更短的写法。
swift的string类型定义了自己特定的大于符号—一个有两个string类型参数返回一个bool值的方法。
这完全匹配了方法sorted(by:)的需要。因此,你可以只把大于符号传过去,swift会推导出你用的是它的string版本的实现:
reversedNames = names.sorted(by:)

追踪闭包

If you need to pass a closure expression to a function as the function’s final argument and the closure expression is long, it can be useful to write it as a trailing closure instead. A trailing closure is written after the function call’s parentheses, even though it is still an argument to the function. When you use the trailing closure syntax, you don’t write the argument label for the closure as part of the function call.

如果你需要把一个闭包表达式传给一个函数,作为函数的最后的参数,并且闭包表达式很长,那么可以用追踪闭包来替代。
一个追踪闭包要写在函数调用的括号之后,即使它依然是函数的参数。
当你使用追踪闭包语法时,你在函数调用处不必为闭包写参数标记。

    func someFunctionThatTakesAClosure(closure: () ->  a href="" Void /a ) {
        // function body goes here
    }

    // 不用追踪闭包的函数调用:

    someFunctionThatTakesAClosure(closure: {
        // 闭包体
    })

    // 使用追踪闭包的函数调用:

    someFunctionThatTakesAClosure() {
        // 追踪闭包体
    }

上面的字符串排序闭包的追踪闭包写法:
reversedNames = names.sorted() { $0 > $1 }

If a closure expression is provided as the function or method’s only argument and you provide that expression as a trailing closure, you do not need to write a pair of parentheses () after the function or method’s name when you call the function:

如果一个闭包表达式是作为函数或方法的唯一参数,而且你又把该闭包表达式写作了追踪闭包,那么在调用方法时就不需要再写那对括号了
reversedNames = names.sorted { $0 > $1 }

Trailing closures are most useful when the closure is sufficiently long that it is not possible to write it inline on a single line. As an example, Swift’s Array type has a map(_:) method which takes a closure expression as its single argument. The closure is called once for each item in the array, and returns an alternative mapped value (possibly of some other type) for that item. The nature of the mapping and the type of the returned value is left up to the closure to specify.

如果一个闭包很长,长到不可能一行写完,这时候追踪闭包就最有用。
举个例子,swift的数组有一个map(_:)方法,该方法只有一个闭包表达式参数。
这个闭包在数组内的每个元素都调用一次,然后为那个元素返回一个映射的值。

After applying the provided closure to each array element, the map(_:) method returns a new array containing all of the new mapped values, in the same order as their corresponding values in the original array.

在对数组内的每个元素调用了闭包之后,map(_:)返回一个包含所有的新映射的值的数组,数组元素的顺序与元素在原始数组中的对应值一致。

举个例子,应用追踪闭包的map(_:)方法把一个包含int值的数组转换为包含字符串值的数组。
数组[16, 58, 510]转换为新的数组[“OneSix”, “FiveEight”, “FiveOneZero”]:

    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]
    let strings = numbers.map { (number) ->  a href="" String /a  in
        var number = number
        var output = ""
        repeat {
            output = digitNames[number % 10]! + output
            number /= 10
        } while number > 0
        return output
    }
    // strings 的类型是 [String]
    // 值是 ["OneSix", "FiveEight", "FiveOneZero"]

The map(_:) method calls the closure expression once for each item in the array. You do not need to specify the type of the closure’s input parameter, number, because the type can be inferred from the values in the array to be mapped.

数组中的每个元素都调用了一次 map(_:) 方法的闭包表达式。你不需要特别的指定闭包的输入参数类型,因为参数类型会根据数组中的值推导得出。

捕获值

A closure can capture constants and variables from the surrounding context in which it is defined. The closure can then refer to and modify the values of those constants and variables from within its body, even if the original scope that defined the constants and variables no longer exists.

一个闭包可以在定义的时候从周围上下文捕获常量或变量。
该闭包就可以在其闭包体内获取或修改这些常量或变量的值,即使最初定义那些常量变量的范围已经不存在了。

In Swift, the simplest form of a closure that can capture values is a nested function, written within the body of another function. A nested function can capture any of its outer function’s arguments and can also capture any constants and variables defined within the outer function.

在swift中,捕获值的闭包的最简单形式就是嵌套函数。嵌套函数可以捕获它外层函数的任意参数,也能捕获外围函数定义的任意常量和变量。

Here’s an example of a function called makeIncrementer, which contains a nested function called incrementer. The nested incrementer() function captures two values, runningTotal and amount, from its surrounding context. After capturing these values, incrementer is returned by makeIncrementer as a closure that increments runningTotal by amount each time it is called.

下面是个例子,makeIncrementer函数,内部有一个嵌套函数incrementer,嵌套函数incrementer() 从它的上下文捕获了两个值,runningTotal 和amount,捕获了这两个值之后,incrementer函数被makeIncrementer函数当作一个闭包返回,每次被调用的时候,runningTotal就增加amount:

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

The return type of makeIncrementer is () -> Int

makeIncrementer的返回类型是() -> Int

When considered in isolation, the nested incrementer() function might seem unusual:

单独地看,incrementer() 函数看起来有点奇怪:

    func incrementer() ->  a href="" Int /a  {
        runningTotal += amount
        return runningTotal
    }

​ The incrementer() function doesn’t have any parameters, and yet it refers to runningTotal and amount from within its function body. It does this by capturing a reference to runningTotal and amount from the surrounding function and using them within its own function body. Capturing by reference ensures that runningTotal and amount do not disappear when the call to makeIncrementer ends, and also ensures that runningTotal is available the next time the incrementer function is called.

incrementer()函数没有任何参数,但是在函数体内它引用了runningTotal 和 amount。它能这么做是因为它从上下文中捕获了runningTotal 和 amount的引用,并在它自己的函数体内使用了这两个值。
通过引用捕获确保了runningTotal 和amount 在makeIncrementer函数调用结束时不会消失,同时确保了runningTotal在下次调用incrementer时依然可用。
一个makeIncrementer的使用的例子:
let incrementByTen = makeIncrementer(forIncrement: 10)

    incrementByTen()
    // 返回值 10
    incrementByTen()
    // 返回值 20
    incrementByTen()
    // 返回值 30

If you create a second incrementer, it will have its own stored reference to a new, separate runningTotal variable:

如果你创建一个新的incrementer,它会保存它自己的runningTotal变量

    let incrementBySeven = makeIncrementer(forIncrement: 7)
    incrementBySeven()
    // 返回值 7

Calling the original incrementer (incrementByTen) again continues to increment its own runningTotal variable, and does not affect the variable captured by incrementBySeven:

再次调用原来的incrementer —incrementByTen— 会增加它自己的runningTotal的变量,而不会影响incrementBySeven捕获的变量:

    incrementByTen()
    // 返回值 40

闭包是引用类型

This also means that if you assign a closure to two different constants or variables, both of those constants or variables will refer to the same closure:

这意味着如果你把一个闭包赋给两个不同的变量或常量,这两个变量或常量将指向同一个闭包:

    let alsoIncrementByTen = incrementByTen
    alsoIncrementByTen()
    // 返回值 50

退出闭包:

A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns. When you declare a function that takes a closure as one of its parameters, you can write @escaping before the parameter’s type to indicate that the closure is allowed to escape.

说闭包退出一个函数是指当该闭包被当作一个参数传给该函数时,闭包的调用却晚于函数的返回。当你声明一个接收一个闭包作为它的一个参数的函数时,你可以在此参数类型前写上 @escaping,以此说明该闭包允许退出。

One way that a closure can escape is by being stored in a variable that is defined outside the function. As an example, many functions that start an asynchronous operation take a closure argument as a completion handler. The function returns after it starts the operation, but the closure isn’t called until the operation is completed—the closure needs to escape, to be called later. For example:

一个闭包可以返回的一个方式是被一个在函数外定义的变量保存。举个例子,很多开启异步操作的函数都接收一个闭包参数作为完成处理器(completion handler)。函数在它开始异步操作后就返回了,但是闭包却在异步操作完成之后才被调用—该闭包需要退出。
举个例子:

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

The someFunctionWithEscapingClosure(_:) function takes a closure as its argument and adds it to an array that’s declared outside the function. If you didn’t mark the parameter of this function with @escaping, you would get a compile-time error.

someFunctionWithEscapingClosure(_:)函数接受一个闭包作为参数,并把它加到函数外声明的数组中。如果你不用@escaping标记参数,会报一个编译时错误。

Marking a closure with @escaping means you have to refer to self explicitly within the closure. For example, in the code below, the closure passed to someFunctionWithEscapingClosure(:) is an escaping closure, which means it needs to refer to self explicitly. In contrast, the closure passed to someFunctionWithNonescapingClosure(:) is a nonescaping closure, which means it can refer to self implicitly.

用@escaping标记一个闭包表示你必须在闭包内部显式地引用self。
举个例子,下面的代码中,传递到someFunctionWithEscapingClosure(:)的闭包是一个可退出的闭包,这表示它需要显式的引用self。相反的,传递到someFunctionWithNonescapingClosure(:)的闭包是一个不可退出的闭包,这表示它可以隐式地引用self:

    func someFunctionWithNonescapingClosure(closure: () ->  a href="" Void /a ) {
        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"

自动闭包

An autoclosure is a closure that is automatically created to wrap an expression that’s being passed as an argument to a function. It doesn’t take any arguments, and when it’s called, it returns the value of the expression that’s wrapped inside of it. This syntactic convenience lets you omit braces around a function’s parameter by writing a normal expression instead of an explicit closure.

自动闭包是一种把传到一个函数作参数的表达式封装起来的自动创建的闭包。
自动闭包没有参数,当被调用时,它返回它所封装的那个表达式的值。这种语法上的便利使你可以用一个普通的表达式替代掉闭包从而省略函数参数的括号,

It’s common to call functions that take autoclosures, but it’s not common to implement that kind of function. For example, the assert(condition:message:file:line:) function takes an autoclosure for its condition and message parameters; its condition parameter is evaluated only in debug builds and its message parameter is evaluated only if condition is false.

调用接收自动闭包的函数很常见,但是实现这种函数就不太常见。
举个例子,assert(condition:message:file:line:)函数接收一个自动闭包作为condition和message参数。

An autoclosure lets you delay evaluation, because the code inside isn’t run until you call the closure. Delaying evaluation is useful for code that has side effects or is computationally expensive, because it lets you control when that code is evaluated. The code below shows how a closure delays evaluation.

一个自动闭包可以让你延迟求值,因为内部代码直到你调用该闭包时才真正执行。
求值延迟对于有副作用或者计算成本高昂的代码非常有用,因为它让你控制代码何时被求值。
举个例子:

    var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
    print(customersInLine.count)
    // Prints "5"

    let customerProvider = { customersInLine.remove(at: 0) }
    print(customersInLine.count)
    // Prints "5"

    print("Now serving \(customerProvider())!")
    // Prints "Now serving Chris!"
    print(customersInLine.count)
    // Prints "4"

Even though the first element of the customersInLine array is removed by the code inside the closure, the array element isn’t removed until the closure is actually called. If the closure is never called, the expression inside the closure is never evaluated, which means the array element is never removed. Note that the type of customerProvider is not String but () -> String—a function with no parameters that returns a string.

即使customersInLine数组的第一个元素被闭包内的代码移除了,但是数组元素并没有被真正移除直到闭包被真正调用。如果该闭包没有被调用,则闭包内的表达式就不会求值,这意味着数组元素就不会被移除。
注意customerProvider的类型不是string而是() -> String—一个没有参数,返回一个string的函数。

You get the same behavior of delayed evaluation when you pass a closure as an argument to a function.

当你把一个闭包当作参数传给一个函数时,也可以延迟求值

    // customersInLine is ["Alex", "Ewa", "Barry", "Daniella"]
    func serve(customer customerProvider: () ->  a href="" String /a ) {
        print("Now serving \(customerProvider())!")
    }
    serve(customer: { customersInLine.remove(at: 0) } )
    // Prints "Now serving Alex!"

The serve(customer:) function in the listing above takes an explicit closure that returns a customer’s name. The version of serve(customer:) below performs the same operation but, instead of taking an explicit closure, it takes an autoclosure by marking its parameter’s type with the @autoclosure attribute. Now you can call the function as if it took a String argument instead of a closure. The argument is automatically converted to a closure, because the customerProvider parameter’s type is marked with the @autoclosure attribute.

serve(customer:)函数接收一个返回顾客名字的闭包。下面版本的serve(customer:)具有一样的功能,但是,
它接收一个自动闭包—用@autoclosure标记。现在你可以调用此函数,就像它接收一个字符串参数而不是一个闭包参数那样。这个参数被自动转换为一个闭包,因为customerProvider的参数类型被用@autoclosure标记了。

    // customersInLine is ["Ewa", "Barry", "Daniella"]
    func serve(customer customerProvider: @autoclosure () ->  a href="" String /a ) {
        print("Now serving \(customerProvider())!")
    }
    serve(customer: customersInLine.remove(at: 0))
    // Prints "Now serving Ewa!"

If you want an autoclosure that is allowed to escape, use both the @autoclosure and @escaping attributes.

如果你想让一个自动闭包允许退出,可以同时使用 @autoclosure 和 @escaping

    // customersInLine is ["Barry", "Daniella"]
    var customerProviders: [() ->String /a ] = []
    func collectCustomerProviders(_ customerProvider: @autoclosure @escaping () ->String /a ) {
        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)