The Swift Programming Language - Closures
Closures “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中的blocks并且和其他程序语言中的lambdas表达式类似。 “Closures can capture and store references to any constants and variables from the context in which they are defined. This is known as closing over those constants and variables,hence the name “closures”. Swift handles all of the memory management of capturing for you.” 闭包可以从他们被定义的上下文中捕获以及存储常量和变量的引用。这就是所说的关闭那些常量和变量,因此被叫做闭包。Swift为你处理所有捕获的内存管理工作。 “NOTE “全局函数是一种有名字到那时没有捕获任何值的闭包” 嵌套函数是一种有名字并且可以通过他们的嵌套功能来捕获值的闭包 闭包表达式是一种通过轻量级语法写出的未命名的闭包,它可以从他们的周围的上下文中捕获值 “Swift’s closure expressions have a clean,clear style,with optimizations that encourage brief,clutter-free syntax in common scenarios. These optimizations include: 从上下文中推断参数和返回值类型 从单一表达式闭包返回隐式返回值 简约参数名 结尾闭包语法 Closure Expressions “Nested functions,as introduced in Nested Functions,are a convenient means of naming and defining self-contained blocks of code as part of a larger function. However,it is“sometimes useful to write shorter versions of function-like constructs without a full declaration and name. This is particularly true when you work with functions that take other functions as one or more of their arguments. 闭包表达式是一种以简洁,紧凑的语法写出内联闭包的方式。闭包表达式提供了集中书写闭包的语法优化,方式简短,并且不丢失精确度与含义。以下闭包表达式的例子列举了这些优化,一个排序函数通过几次迭代得到了提炼,并且每次迭代都用一种更高效的方式表达了同样的功能。 The Sorted Function “Swift’s standard library provides a function called sorted,which sorts an array of values of a known type,based on the output of a sorting closure that you provide. Once it completes the sorting process,the sorted function returns a new array of the same type and size as the old one,with its elements in the correct sorted order. The original array is not modified by the sorted function.” Swift的标准库中提供了一种叫做排序的函数,他把一个已知类型的数组中的值进行排序,基于你提供的排序闭包的输出结果。一旦它完成了排序过程,排序函数就会返回一个与旧数组包含元素类型相同大小相同的新数组,并且元素有了正确的顺序,而原始数组并没有被排序函数修改。 “The closure expression examples below use the sorted function to sort an array of String values in reverse alphabetical order. Here’s the initial array to be sorted:”
“let names = ["Chris","Alex","Ewa","Barry","Daniella"]” “The sorted function takes two arguments: An array of values of a known type. A closure that takes two arguments of the same type as the array’s contents,and returns a Bool value to say whether the first value should appear before or after the second value once the values are sorted. The sorting closure needs to return true if the first value should appear before the second value,and false otherwise.” 排序函数有两个参数: 已知元素类型的一个数组 一个带有两个参数的闭包,这连个参数与数组内容有相同的类型,并且值一旦被排序就会返回一个布尔值以表明第一个值是应该在第二个值之前还是之后出现。如果第一个值应该在第二个值之前出现,这个排序闭包就需要返回true,否则的话返回false “This example is sorting an array of String values,and so the sorting closure needs to be a function of type (String,String) -> Bool.” “One way to provide the sorting closure is to write a normal function of the correct type,and to pass it in as the sorted function’s second parameter:”
“func backwards(s1: String,s2: String) -> Bool { return s1 > s2 } var reversed = sorted(names,backwards) // reversed is equal to ["Ewa","Daniella","Chris","Alex"]” “If the first string (s1) is greater than the second string (s2),the backwards function will return true,indicating that s1 should appear before s2 in the sorted array. For characters in strings,“greater than” means “appears later in the alphabet than”. This means that the letter "B" is “greater than” the letter "A",and the string "Tom" is greater than the string "Tim". This gives a reverse alphabetical sort,with "Barry" being placed before "Alex",and so on.” 如果第一个字符串s1比第二个字符串要大,逆序函数将会返回true,表示在排序好的数组中s1应该在s2之前出现。对于字符串中的字符,“比...大”意味着“在字母表中后出现的”。这就意味着字母“B”比字母“A”要大,并且字符串“Tom”比字符串“Tim”要大。这就给了一个逆序的字符排序,因而“Barry”会被放过在“Alex”之前等等 “However,this is a rather long-winded way to write what is essentially a single-expression function (a > b). In this example,it would be preferable to write the sorting closure inline,using closure expression syntax.” “Closure Expression Syntax
“{ (parameters) -> return type in statements }” “Closure expression syntax can use constant parameters,variable parameters,and inout parameters. Default values cannot be provided. Variadic parameters can be used if you name the variadic parameter and place it last in the parameter list. Tuples can also be used as parameter types and return types.” 闭包表达式语法合适使用常量参数,变量参数和输入输出参数。不能提供默认值。如果你命名了可变参数并且将它放在了参数列表中最后的位置它将会被使用。元祖也可以并用作参数类型和返回值类型。 “The example below shows a closure expression version of the backwards function from earlier:”
“reversed = sorted(names,{ (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 backwards 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.” 注意这个内联闭包的参数和返回值的生命和逆序函数的声明是相同的。在这两者中,它被写作(s1:String,s2:String) -> Bool.然后,对于内联闭包表达式,参数和返回值被写在花括号里面,而不是在外面。
“reversed = sorted(names,s2: String) -> Bool in return s1 > s2 } )” “This illustrates that the overall call to the sorted function has remained the same. A pair of parentheses still wrap the entire set of arguments for the function. However,one of those arguments is now an inline closure.” 这个证明了排序函数的整体调用还保持原状。一对圆括号仍然包含了这个函数的所有参数。然而,其中一个蚕食现在已经是内联闭包了。 “Inferring Type From Context” 从上下文中推断类型 “Because the sorting closure is passed as an argument to a function,Swift can infer the types of its parameters and the type of the value it returns from the type of the sorted function’s second parameter. This parameter is expecting 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可以从排序函数的第二个参数的类型中推断他的参数的类型和它的返回值的类型。这个参数是函数类型的(String,String) -> Bool。这意味着(String,String)和布尔类型不需要作为闭包表达式定义的一部分被写出来。因为所有的类型都可以被推断,箭头->和圆括号也可以被省略。
“reversed = sorted(names,{ s1,s2 in return s1 > s2 } )” “It is always possible to infer the parameter types and return type when passing a closure to a function as an inline closure expression. As a result,you never need to write an inline closure in its fullest form when the closure is used as a function argument. 当传递一个闭包作为函数的内联闭包表达式的时候,总是可以推断参数类型和返回值类型的。因此,当闭包作为函数参数时你从不需要以完整形式写出一个内联闭包。 Nonetheless,you can still make the types explicit if you wish,and doing so is encouraged if it avoids ambiguity for readers of your code. In the case of the sorted function,the purpose of the closure is clear from the fact that sorting is taking place,and it is safe for a reader to assume that the closure is likely to be working with String values,because it is assisting with the sorting of an array of strings.” 虽然如此,如果你愿意你仍然可以写下精确的类型,并且如果他能避免你的代码含糊不清这样的做法将会被鼓励。在排序函数的例子中,相对于排序正在发生的事实闭包的目的更为清晰,并且如果用户假定闭包可能通过字符串类型使用那也将是安全的,因为他正在帮助一个字符串数组排序。 “Implicit Returns from Single-Expression Closures” “Single-expression closures can implicitly return the result of their single expression by omitting the return keyword fromtheir declaration,as in this version of the previous example:”
单一表达式闭包可以通过从声明中省略return关键字隐式地返回单一表达式的结果,就像之前的例子在下面的版本中的那样:
“reversed = sorted(names,s2 in s1 > s2 } )” “Here,the function type of the sorted function’s second argument makes it clear that a Bool value must be returned by the closure. Because the closure’s body contains a single expression (s1 > s2) that returns a Bool value,there is no ambiguity,and the return keyword can be omitted.” 在这儿,通过排序函数的第二个参数的函数类型可以更清楚的看到闭包必须返回一个Bool值。因为闭包的主体包含了表达式(s1 > s2)并返回一个Bool类型,没有含糊不清,并且return关键字可以被省略。 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.” “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:”
“reversed = sorted(names,{ $0 > $1 } )” “Here,$0 and $1 refer to the closure’s first and second String arguments.” 在这儿,$0和$1指的是闭包的第一个和第二个参数
Operator Functions 操作符函数 “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 function that has two parameters of type String,and returns a value of type Bool. This exactly matches the function type needed for the sorted function’s second parameter. Therefore,you can simply pass in the greater-than operator,and Swift will infer that you want to use its string-specific implementation:” 其实还有一种更加简洁的方式来写上述的闭包表达式。Swift的字符串类型定义了关于字符串对于比较运算符的实施实际上等同于有两个字符串参数的函数,并且返回Bool类型的值。这个精确地符合了排序函数中第二个参数所需要的函数类型。因此,你只需要传入比较运算符,Swift就能推断出你想要使用他的对应字符串的特定的实施方法。
reversed = sorted(names,>) Trailing Closures 尾随闭包 “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 a closure expression that is written outside of (and after) the parentheses of the function call it supports:”
“func someFunctionThatTakesAClosure(closure: () -> ()) { // function body goes here } // here's how you call this function without using a trailing closure: someFunctionThatTakesAClosure({ // closure's body goes here }) // here's how you call this function with a trailing closure instead: someFunctionThatTakesAClosure() { // trailing closure's body goes here } ”
If a closure expression is provided as the function’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’s name when you call the function.”
如果一个闭包表达式是某个函数唯一的参数并且你将其写成尾随闭包,那么当你调用函数的时候不需要在函数名后面写小括号 “The string-sorting closure from the Closure Expression Syntax section above can be written outside of the sorted function’s parentheses as a trailing closure:” 上述的字符串排序闭包可以写在函数的小括号外面作为尾随闭包。
“reversed = sorted(names) { $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.” “Here’s how you can use the map(_:) method with a trailing closure to convert an array of Int values into an array of String values. The array [16,58,510] is used to create the new array ["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,510]” “The code above creates a dictionary of mappings between the integer digits and English-language versions of their names. It also defines an array of integers,ready to be converted into strings.” 上述代码创建了一个在整形数字和他们的名字的英文版本之间相互映射的字典。它也定义个一个整型数组,准备转换成字典。 “You can now use the numbers array to create an array of String values,by passing a closure expression to the array’s map(_:) method as a trailing closure. Note that the call to numbers.map does not need to include any parentheses after map,because the map(_:) method has only one parameter,and that parameter is provided as a trailing closure:” 现在,你可以通过给数组的map方法传一个闭包作为尾随闭包,将一个数字数组装换成字符串数组。注意调用numbers.map不需要在map后面添加任何的小括号,因为map方法只有一个参数,并且那个参数是一个尾随闭包。
“let strings = numbers.map { (var number) -> String in var output = "" while number > 0 { output = digitNames[number % 10]! + output number /= 10 } return output } // strings is inferred to be of type [String] // its value is ["OneSix","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函数的尾随闭包。你不需要规定闭包中输入的参数number的类型,因为类型可以从要被执行map操作的数组中被推断出来。 “In this example,the closure’s number parameter is defined as a variable parameter,as described in Constant and Variable Parameters,so that the parameter’s value can be modified within the closure body,rather than declaring a new local variable and assigning the passed number value to it. The closure expression also specifies a return type of String,to indicate the type that will be stored in the mapped outputarray. 在这个例子中,闭包的number参数被定义为一个可变的参数,就像常量和变量参数中描述的那样,所以参数的值可以在闭包体内被修改,而不需要再声明一个本地的变量再把值传递给它。闭包表达式也规定了字符串类型的返回值,表明了map之后返回数组的元素类型。 “The closure expression builds a string called output each time it is called. It calculates the last digit of number by using the remainder operator (number % 10),and uses this digit to look up an appropriate string in the digitNames dictionary. The closure can be used to create a string representation of any integer number greater than zero.” “NOTE 字典下标后面跟着一个感叹号,因为从字典中取值会返回一个可选类型,因为如果key不存在就可能查询失败。在上述例子中,能够保证对于数字名称字典来说number %10将会总是一个有效的key,因为感叹号被用来强制解包出可选的返回值中存储的字符串。 “The string retrieved from the digitNames dictionary is added to the front of output,effectively building a string version of the number in reverse. (The expression number % 10 gives a value of 6 for 16,8 for 58,and 0 for 510.)” “The number variable is then divided by 10. Because it is an integer,it is rounded down during the division,so 16 becomes 1,58 becomes 5,and 510 becomes 51.”
上例中尾随闭包语法在函数后整洁封装了具体的闭包功能,而不再需要将整个闭包包裹在 Capturing Values 捕获值 “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.” “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.”
“func makeIncrementer(forIncrement amount: Int) -> () -> Int { var runningTotal = 0 func incrementer() -> Int { runningTotal += amount return runningTotal } return incrementer } ” “The return type of makeIncrementer is () -> Int. This means that it returns a function,rather than a simple value. The function it returns has no parameters,and returns an Int value each time it is called. To learn how functions can return other functions,see Function Types as Return Types.” makeIncrementer的返回值类型是()-> Int.这意味着它返回一个函数,而不是一个简单的值。它返回的函数没有参数,但在每次调用的时候返回一个int类型的值。想知道函数怎样返回另外一个函数,请看 Function Types as Return Types “The makeIncrementer function defines an integer variable called runningTotal,to store the current running total of the incrementer that will be returned. This variable is initialized with a value of 0.” “The makeIncrementer function has a single Int parameter with an external name of forIncrement,and a local name of amount. The argument value passed to this parameter specifies how much runningTotal should be incremented by each time the returned incrementer function is called.” “makeIncrementer defines a nested function called incrementer,which performs the actual incrementing. This function simply adds amount to runningTotal,and returns the result.” “When considered in isolation,the nested incrementer function might seem unusual:”
“func incrementer() -> Int { 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 the existing values of runningTotal and amount from its surrounding function and using them within its own function body.” incrementer函数没有任何参数,但是他从他的函数体内引用了RunningTotal和amount。它从他的周围函数体重捕获了runningTotal和amount的值并在它自己的函数体重使用它们。 “Because it modifies the runningTotal variable each time it is called,incrementer captures a reference to the current runningTotal variable,and not just a copy of its initial value. Capturing a reference ensures that runningTotal does not disappear when the call to makeIncrementer ends,and ensures that runningTotal is available the next time the incrementer function is called..” “However,because it does not modify amount,and amount is not mutated outside it,incrementer actually captures and stores a copy of the value stored in amount. This value is stored along with the new incrementer function.” “NOTE Swift规定了什么应该通过引用捕获而什么是通过拷贝捕获的。你不需要通过注释来说amount或者runningTotal可以在嵌套函数incrementer内部使用。Swift也会处理当incrementer函数不再需要runningTotal时的所有的内存管理工作。 “Here’s an example of makeIncrementer in action:”
“let incrementByTen = makeIncrementer(forIncrement: 10)” “This example sets a constant called incrementByTen to refer to an incrementer function that adds 10 to its runningTotal variable each time it is called. Calling the function multiple times shows this behavior in action:” 这个例子创建了一个叫做incrementByTen的例子来指向一个增量函数,这个增量函数每次被调用时RunningTotal都会加10.实际中多次调用这个函数的表现如下:
“incrementByTen() // returns a value of 10 incrementByTen() // returns a value of 20 incrementByTen() // returns a value of 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() // returns a value of 7” “Calling the original incrementer (incrementByTen) again continues to increment its own runningTotal variable,and does not affect the variable captured by incrementBySeven:” 再次调用原始的incrementer将会继续在他自己的runningTotal变量上增加值,但是这并不影响incrementBySeven的值。 “incrementByTen() // returns a value of 40” “NOTE
如果你给一个类的实例指派了一个闭包属性,那么这个闭包将会通过引用实例或者他的成员来捕获这个实例,你将会在闭包和他的实例中创建一个强引用循环。Swif使用引用列表打破这些强引用循环。更多信息,请看Strong Reference Cycles for Closures “Closures Are Reference Types”
“In the example above,incrementBySeven and incrementByTen are constants,but the closures these constants refer to are still able to increment the runningTotal variables that they have captured. This is because functions and closures are reference types.” “Whenever you assign a function or a closure to a constant or a variable,you are actually setting that constant or variable to be a reference to the function or closure. In the example above,it is the choice of closure that incrementByTen refers to that is constant,and not the contents of the closure itself.” 无论您将函数/闭包赋值给一个常量还是变量,您实际上都是将常量/变量的值设置为对应函数/闭包的引用。 上面的例子中, “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() // returns a value of 50” (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |