闭包(Closures)是独立的函数代码块,能在代码中传递及使用。Swift中的闭包与C和Objective-C中的代码块及其它编程语言中的匿名函数相似。
闭包可以在上下文的范围内捕获、存储任何被定义的常量和变量引用。因这些常量和变量的封闭性,而命名为“闭包(Closures)”。Swift能够对所有你所能捕获到的引用进行内存管理。
NOTE
假如你对“捕获(capturing)”不熟悉,请不要担心,具体可以参考Capturing Values(捕获值)。
全局函数和嵌套函数已在 Functions(函数)中介绍过,实际上这些都是特殊的闭包函数
全局函数都是闭包,特点是有函数名但没有捕获任何值。
嵌套函数都是闭包,特点是有函数名,并且可以在它封闭的函数中捕获值。
闭包表达式都是闭包,特点是没有函数名,可以使用轻量的语法在它所围绕的上下文中捕获值。
Swift的闭包表达式有着干净,清晰的风格,并常见情况下对于鼓励简短、整洁的语法做出优化。这些优化包括:
推理参数及返回值类型源自上下文
隐式返回源于单一表达式闭包
简约参数名
尾随闭包语法
1、闭包表达式
嵌套函数已经在Nested Functions(嵌套函数)中有所介绍,是种方便命名和定义自包含代码块的一种方式,然而,有时候在编写简短函数式的构造器时非常有用,它不需要完整的函数声明及函数名,尤其是在你需要调用一个或多个参数的函数时。
闭包表达式是一种编写内联闭包的方式,它简洁、紧凑。闭包表达式提供了数种语义优化,为的是以最简单的形式编程而不需要大量的声明或意图。以下以同一个sort函数进行几次改进,每次函数都更加简洁,以此说明闭包表达式的优化。
Sort函数
Swift的标准函数库提供了一个名为sort的函数,它通过基于输出类型排序的闭包函数,给已知类型的数组数据的值排序。一旦完成排序工作,会返回一个同先前数组相同大小,相同数据类型,并且的新数组,并且这个数组的元素都在正确排好序的位置上。
The closure expression examples below use the sort function to sort an array of String values in reverse alphabetical order. Here’s the initial array to be sorted:
以下的闭包表达式通过sort函数将String值按字母顺序进行排序作说明,这是待排序的初始化数组。
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
sort函数需要两个参数:
一个已知值类型的数组
一个接收两个参数的闭包函数,这两个参数的数据类型都同于数组元素。并且
返回一个Bool表明是否第一个参数应排在第二个参数前或后。
这个例子是一组排序的字符串值,因此需要排序的封闭类型的函数(字符串,字符串)-> 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 sort function’s second parameter:
func backwards(s1: String, s2: String) -> Bool {
return s1 > s2
}
var reversed = sort(names, backwards)
// reversed is equal to ["Ewa", "Daniella", "Chris", "Barry", "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.
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
Closure expression syntax has the following general form:
{ (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 = sort(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.
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.
Because the body of the closure is so short, it can even be written on a single line:
reversed = sort(names, { (s1: String, s2: String) -> Bool in return s1 > s2 } )
This illustrates that the overall call to the sort 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 sort 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:
reversed = sort(names, { s1, s2 in return s1 > s2 } )
It is always possible to infer parameter types and return type when passing a closure to a function as an inline closure expression. As a result, you rarely need to write an inline closure in its fullest form.
Nonetheless, you can 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 sort 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 withString 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 returnkeyword from their declaration, as in this version of the previous example:
reversed = sort(names, { s1, s2 in s1 > s2 } )
Here, the function type of the sort 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 aBool value, there is no ambiguity, and the return keyword can be omitted.
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 = sort(names, { $0 > $1 } )
Here, $0 and $1 refer to the closure’s first and second String arguments.
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 typeString, and returns a value of type Bool. This exactly matches the function type needed for the sortfunction’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:
reversed = sort(names, >)
For more about operator functions, see Operator Functions.
2、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
• }
NOTE
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 thesort function’s parentheses as a trailing closure:
• reversed = sort(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.
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, 58, 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:
• 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", "FiveEight", "FiveOneZero"]
The map function 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.
In this example, the closure’s number parameter is defined as a variable parameter, as described inConstant 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 output array.
The closure expression builds a string called output each time it is called. It calculates the last digit of numberby using the remainder operator (number % 10), and uses this digit to look up an appropriate string in thedigitNames dictionary.
NOTE
The call to the digitNames dictionary’s subscript is followed by an exclamation mark (!), because dictionary subscripts return an optional value to indicate that the dictionary lookup can fail if the key does not exist. In the example above, it is guaranteed that number % 10 will always be a valid subscript key for the digitNames dictionary, and so an exclamation mark is used to force-unwrap the String value stored in the subscript’s optional return value.
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, and0 for 510.)
The number variable is then divided by 10. Because it is an integer, it is rounded down during the division, so16 becomes 1, 58 becomes 5, and 510 becomes 51.
The process is repeated until number /= 10 is equal to 0, at which point the output string is returned by the closure, and is added to the output array by the map function.
The use of trailing closure syntax in the example above neatly encapsulates the closure’s functionality immediately after the function that closure supports, without needing to wrap the entire closure within themap function’s outer parentheses.
3、获取值
闭包可以在其定义的范围内捕捉(引用/得到)常量和变量,闭包可以引用和修改这些值,即使定义的常量和变量已经不复存在了依然可以修改和引用。牛逼吧、
在Swift中最简单形式是一个嵌套函数,写在另一个函数的方法里面。嵌套函数可以捕获任何外部函数的参数,也可以捕获任何常量和变量在外部函数的定义。
看下面这个例子,一个函数方法为makeIncrementor、这是一个嵌套函数,在这个函数体内嵌套了另一个函数方 法:incrementor,在这个incrementor函数体内有两个参数: runningTotal和amount,实际运作时传进所需的两个参数后,incrementor函数每次被调用时都会返回一个 runningTotal值提供给外部的makeIncrementor使用:
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementor() -> Int {
runningTotal += amount
return runningTotal
}
return incrementor
}
而函数makeincrementor的返回类型值我们可以通过函数名后面的()-> int得知返回的是一个Int类型的值。如需想学习了解更多地函数返回类型,可以参考: Function Types as Return Types.(超链接跳转)
我们可以看见makeincrementor这个函数体内首先定义了一个整型变量:runningtotal,初始值为 0 ,而incrementor()函数最终运行的出来的返回值会赋值给这个整型变量。
makeincrementor函数()中向外部抛出了一个forIncrement参数供外部穿参进来、一旦有值进入函数体内会被函数实例化替代 为amount,而amount会被传递进内嵌的incrementor函数体中与整型常量runningTotal相加得到一个新的 runningTotal并返回。而我们这个主函数要返回的值是Int类型,runningTotal直接作为最终值被返回出去、 makeincrementor函数()执行完毕。
makeincrementor函数()在其内部又定义了一个新的函数体incrementor,作用就是将外部传递过来的值amount 传进incrementor函数中与整形常量runningTotal相加得到一个新的runningTotal,
单独的看incrementor函数、你会发现这个函数不寻常:
func incrementor() -> Int {
runningTotal += amount
return runningTotal
}
因为incrementor函数没有任何的参数,但是在它的函数方法体内却指向runningTotal和amount,显而易见、这是 incrementor函数获取了外部函数的值amount,incrementor不能去修改它但是却可以和体内的runningTotal相加得出新 的runningTotal值返回出去。
不过,由于runningtotal每次被调用时都会相加改变一次实际值,相应地incrementor函数被调用时会去加载最新的 runningtotal值,而不再是第一次舒适化的0.并且需要保证每次runningTotal的值在makeIncrementor函数体内不会丢 失直到函数完全加载完毕。要能确保在函数体内下一次引用时上一次的值依然还在。
注意
Swift中需要明确知道什么时候该引用什么时候该赋值,在incrementor函数中你不需要注解amount 和runningTotal。Swift还负责处理当函数不在需要runningTotal的时候,内存应该如何去管理。
这里有一个例子makeIncrementor函数:
let incrementByTen = makeIncrementor(forIncrement: 10)
4、引用类型闭包
在上面的例子中,incrementBySeven和incrementByTen是常量,但是这些常量在闭包的状态下依然可以被修改。为何?很简单,因为函数和闭包是引用类型。
当你指定一个函数或一个闭包常量/变量时、实际上是在设置该常量或变量是否为一个引用函数。在上面的例子中,它是闭合的选择,incrementByTen指的是恒定的,而不是封闭件本身的内容。
这也意味着,如果你分配一个封闭两种不同的常量或变量,这两个常量或变量将引用同一个闭包:
let alsoIncrementByTen = incrementByTen
alsoIncrementByTen()
// returns a value of 50
来自letsswift.com
感谢翻译小组成员:李起攀(微博)、若晨(微博)、YAO、粽子、山有木兮木有枝、渺-Bessie、墨离、矮人王、CXH、Tiger大顾(微博)
个人转载请注明出处,商业转载请联系我们~ 感谢您对我们工作的支持~
相关推荐
苹果Swift语言入门教程中文版是非常经典的教程,为初学者学习Swift编程语言提供了很好的指导。Swift是苹果公司为iOS和OSX应用开发推出的新编程语言,于2014年发布。该语言基于C和Objective-C编程语言,但并未保留C的...
Swift语言是苹果公司推出的一种强大的、现代化的编程语言,它被广泛用于开发iOS、macOS、watchOS和tvOS的应用程序。在Swift中,Closure(闭包)是一种强大的功能,可以捕获和存储其所在上下文中的引用。本Demo通过...
Swift是苹果公司在2014年WWDC(全球开发者大会)上推出的一种全新编程语言,设计用于开发iOS和macOS应用程序。它是基于C和Objective-C,但摒弃了C的一些兼容性约束,以提供更安全、现代和有趣的编程体验。Swift采用...
Swift是由Apple开发的一种新型编程语言,它被设计用来编写iOS和macOS应用程序。Swift在2014年的WWDC(Worldwide Developers Conference)上首次亮相,是与iOS 8一同推出的,目的是为了提升开发者的编程体验和效率。...
Swift语言是苹果公司推出的一种强大的编程语言,尤其适用于iOS、macOS、watchOS以及tvOS等平台的应用开发。在Swift中,Closure(闭包)是一种非常重要的特性,它允许我们定义一段可移动和可重用的代码块,可以捕获和...
Swift是由Apple开发的一种新型编程语言,它被设计用来编写iOS和macOS应用程序。Swift在2014年的WWDC(Worldwide Developers Conference)上首次亮相,伴随着iOS 8一起发布。Swift的设计目标是融合C和Objective-C的...
Swift编程语言是由苹果在2014年的WWDC大会上推出的一种新型编程语言,设计用于构建iOS和OS X应用程序。它的诞生旨在结合C和Objective-C的优点,同时去除C语言的兼容性限制,提供更加安全、灵活和有趣的编程体验。...
通过阅读《苹果Swift编程语言入门教程》的PDF或TXT文档,你可以逐步学习Swift的基本概念、语法结构以及高级特性。这个教程将带你从安装Xcode开发环境开始,到编写第一个“Hello, World!”程序,再到深入理解Swift的...
### 苹果Swift编程语言入门教程知识点概览 #### 1. 简介 - **Swift背景**:Swift是一种全新的编程语言,专为iOS和OS X应用开发设计。它继承了C和Objective-C的优点,但摆脱了一些C语言的限制,如指针运算等低级特性...
本教程为“苹果Swift编程语言入门教程-中文完整版”,适合初学者学习Swift的基础知识和实践技巧。 Swift的特点包括简洁的语法、类型安全、互动性以及对现代编程概念的支持,如可选类型、闭包和泛型。以下是一些主要...
Swift教程是针对苹果平台开发的一款强大且现代的编程语言,由Apple公司于2014年WWDC(全球开发者大会)上发布。Swift的设计理念强调安全性、可读性以及易于学习,它结合了C和Objective-C的优点,同时摒弃了它们的...
### Swift语言教程基础语法函数和闭包 Swift是由苹果公司推出的一种高性能且易于学习的编程语言,主要用于iOS、macOS、watchOS以及tvOS等苹果生态系统的应用开发。本篇文章将详细解读Swift语言中的基本概念及其语法...
请注意,以下内容是基于常规的Swift编程语言教程内容所构建的,并非直接翻译或复述给定文件的部分内容。 ### Swift编程语言基础知识点 #### 1. Swift编程语言概述 Swift是由苹果公司开发的一种强类型、编译型、...
本教程为全中文教程,基于Xcode 6.1版本,介绍了Swift的全部最新语法,覆盖了Swift编程语言的基础知识、特性、开发历史以及高级主题。 在Swift语言中,包含了多个重要的概念和特性。例如,它支持基本运算符如加、减...
### 苹果Swift编程语言入门教程知识点概览 #### 1. 简介 - **Swift编程语言**: Swift是一种由苹果公司开发的全新编程语言,专为iOS和macOS应用设计。它汲取了C和Objective-C的优点,同时避免了一些传统C语言的限制...
根据提供的文件信息,我们可以推断出这是一套关于iOS开发中的Swift编程语言基础教程的视频课程。虽然实际的课程内容无法直接获取,但基于标题、描述及部分可见内容,可以整理出与该课程相关的几个重要知识点。下面将...
苹果Swift编程语言入门教程【中文版,吾乐吧软件站分享】 目录 1 简介 2 Swift入门 3 简单值 4 控制流 5 函数与闭包 6 对象与类 7 枚举与结构 8 接口和扩展 9 泛型