11.闭包
闭包是可以在代码中传递和使用的自包含的功能模块。Swift 的闭包是类似于 C 、 Objective - C 的 block 、其他编程语言的lambdas。
闭包可以捕获和存储上下文任何引用常量和变量,也就是关闭这些常量和变量,因此得名“闭包”。Swift 管理所有闭包的捕获和内存操作。
注意:如果不熟悉捕获(capturing)概念不用担心,可以在值捕获章节详细了解。
全局函数和嵌套函数,实际上是特殊情况的闭包。闭包有三种形式:
1.全局函数是一个有名字但不会捕获任何值的闭包
2.嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包
3.闭包表达式是一个通过轻量级语法定义的可以捕获其上下文中变量或常量值的匿名闭包
Swift 的闭包表达式是简洁的、清晰的。并鼓励使用简短整齐的语法,主要优化如下:
1.通过上下文推断参数和返回值类型
2.隐式返回单表达式闭包
3.速记参数名称
4.尾随(Trailing)闭包语法
11.1.闭包表达式
嵌套函数:在一个复杂函数中独立的命名和定义一部分代码块。有时候没有完整定义和命名的类函数结构是很有用处的,比如,在处理将其他函数作为一个函数参数的时候。
闭包表达式是一种通过简洁语法构建内联闭包的方式。闭包表达式提供了一些语法优化,使闭包的写法简洁明了。
来看一组例子:通过 sort 函数闭包的定义和语法优化,展示相同的功能下的每一次优化的闭包实现。
11.2.Sort函数
Swift 标准库提供了sort 函数。sort 函数主要是对同一类型的数组值进行排序,如何排序是基于使用者提供的排序闭包。当排序完成的时候,会返回一个与原数组有着相同大小和类型的已经排序的新数组。
例如:对 String 类型的数组进行逆序排序,初始数组如下:
sort 函数需要传入两个参数:
1. 已知类型的数组;
2. 闭包函数。需要传入与数组元素类型相同的两个参数值,并返回一个布尔值来告诉 sort 函数传入的两个参数的顺序:如果第一个参数值在第二个参数值之前,返回true,否则返回 false。
该例子对 String 类型的数组进行排序,所以闭包函数类型是: (String, String) -> Bool
return s1 > s2
}
var reversed = sort(names, backwards)
// reversed 为 ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
如果 s1 > s2 ,backwards函数返回true,在新的数组中 s1 会在 s2 前。 对于字符串中的字符来说,“大于” 表示 “按照字母顺序出现较晚”。 比如字母"B"大于字母"A",字符串 "Tom"大于字符串 "Tim" 。 这里是字母逆序排序,"Barry" 将会排在 "Alex" 之后。
这是一个比较冗长的写法。本质上我们只需要一个单表达式 (a > b),我们可以进一步进行优化。
11.3.闭包表达式语法
闭包表达式语法:
statements
}
闭包表达式语法可以使用常量、变量和 inout 类型作为参数,不提供默认值。 也可以在参数列表的最后使用可变参数。 元组也可以作为参数和返回值。
下面的例子展示了之前 backwards 函数对应的闭包表达式版本的代码:
return s1 > s2
})
内联闭包参数和返回值类型与 backwards 函数类型相同:(s1: String, s2: String) -> Bool 。在内联闭包中,函数和返回值类型都写在大括号内,而不是大括号外。
内联闭包的函数体部分由关键字 in 引入。 该关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始。
这个闭包的函数体部分可以写成一行代码:
11.4.根据上下文推断类型
因为 sort 函数的第二个参数是闭包,所以 Swift 可以推断闭包的参数和返回值类型。
sort 函数期望第二个参数类型是: (String, String) -> Bool 类型的闭包。所以此闭包并不需要声明其参数和返回值类型,那么箭头 (->) 和围绕在参数周围的括号也可以被省略:
因为 Swift 可以推断出闭包的参数和返回值类型,所以不需要完整格式构造内联闭包。
11.5.单表达式闭包隐式返回
单行表达式闭包可以通过隐藏 return 关键字来隐式返回结果,上例可以改写为:
sort 函数的闭包参数(第二个参数)明确了必须返回一个 Bool 类型值。
因为闭包函数体只包含了一个单一表达式 (s1 > s2),该表达式返回 Bool 类型值,所以 return 关键字可以省略。
11.6.参数名称缩写
Swift 为内联函数提供了参数名称缩写功能,通过 $0,$1,$2 顺序使用闭包的参数。
如果使用了参数缩写名称,在函数参数列表中可以省略对闭包的定义,Swift 也会推断闭包参数缩写名称的类型。 in 关键字同样被省略。
此时闭包表达式完全由闭包函数体构成:
在这个例子中,$0 和 $1 表示闭包中第一个和第二个 String 类型的参数
11.7.运算符函数
上面例子中的闭包表达式还有一种更简短的写法。
Swift 的 String 类型定义了大于号 (>) 的字符串实现(运算符重载):作为一个函数接受两个 String 类型的参数并返回 Bool 类型的值。这正好符合 sort 函数的第二个参数类型。 因此,可以简单地只传递一个大于号,Swift 可以自动推断出字符串的大于号函数实现:
关于运算符表达式的更多内容请查看 运算符函数 一章。
11.8.尾随闭包(Trailing Closures)
尾随闭包是一个在函数被调用时,写在函数括号之后的闭包表达式。
// 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
}
在上例中 sort 函数的字符串排序闭包可以改写为:
当 闭包代码很长,不能在一行代码中写完时,尾随闭包会非常有用。 比如,Swift 的 Array 类型有一个 map 方法,它使用闭包表达式作为其唯一参数。 数组中的每一个元素都会调用一次该闭包,并返回该元素对应的值(也可以是不同类型的值)。 具体的映射方式和返回值类型由闭包来指定。
数组使用闭包函数后,map 方法将返回一个新的数组,数组中包含了原数组中元素映射后的值。
例子:在 map 方法中使用尾随闭包将 Int 类型数组 [16,58,510] 转换为包含对应 String 类型的数组 ["OneSix", "FiveEight", "FiveOneZero"]:
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]
通过传递一个尾随闭包给 numbers 的 map 方法来获取对应的字符串数组。 需要注意的是调用 numbers.map 不需要在 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"]
闭包 number 参数被声明为一个变量参数 ,因此可以在闭包函数体内对其进行修改。闭包的返回值类型为 String,因为新数组的元素类型为 String。
闭包在每次被调用的时候都创建了一个字符串并返回。 使用求余运算符 (number % 10) 计算最后一位数字,digitNames 字典根据数字值获取对应字符串。
注意:字典 digitNames 下标后跟着一个叹号 (!),因为字典下标返回一个可选值 (optional value),表明即使该 key 不存在也不会出现查找失败。
11.9.捕获值
闭包可以捕获在上下文中定义过的常量或变量。 即使这些常量和变量的原域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。
Swift 最简单的闭包形式是嵌套函数,也就是定义在其他函数体内的函数。 内部(嵌套)函数可以捕获外部函数所有的参数以及常量和变量。
例子: makeIncrementor 函数包含了 incrementor 函数。 嵌套函数 incrementor 从上下文中捕获了两个值,runningTotal 和 amount。 之后 makeIncrementor 将 incrementor 作为闭包返回。 每次调用 incrementor 时,runningTotal 都会增加一次 amount 值。
var runningTotal = 0
func incrementor() -> Int {
runningTotal += amount
return runningTotal
}
return incrementor
}
makeIncrementor 函数定义了一个整型变量 runningTotal(初始为0) 用来存储总数。 该值通过 incrementor 函数返回。
makeIncrementor 有一个 Int 类型的参数,其外部命名为 forIncrement, 内部命名为 amount。表示每次 incrementor 被调用时 runningTotal 将要增加的量。
incrementor 函数用来执行增加的操作。 runningTotal += amount ,并将其返回。
单独来看 incrementor 函数:
runningTotal += amount
return runningTotal
}
注意:Swift 会决定捕获引用还是拷贝值。Swift 会管理 runingTotal 变量的内存,如果不再被 incrementor 函数使用,会被释放。
调用 makeIncrementor :
// returns a value of 10
incrementByTen()
// returns a value of 20
incrementByTen()
// returns a value of 30
如果您创建了另一个 incrementor 函数,它会有自己独立的 runningTotal 变量的引用。 下面的例子中 incrementBySevne 捕获了一个新的 runningTotal 变量,该变量和 incrementByTen 中捕获的变量没有任何关系:
incrementBySeven()
// returns a value of 7
incrementByTen()
// returns a value of 40
11.10.闭包作为引用类型
上面的例子中,incrementBySeven 和 incrementByTen 是常量,但是它们指向的闭包仍然可以增加其捕获的变量值。这是因为函数和闭包都是引用类型。
无论将闭包/函数赋值给一个常量或变量,实际上都是指向闭包/函数的引用。 incrementByTen 是指向闭包引用的一个常量。
如果将闭包/函数赋值给了两个不同的常量或变量,它们指向同一个闭包/函数:
alsoIncrementByTen()
// returns a value of 50
相关推荐
总的来说,Swift语言为iOS开发提供了一个高效、安全且易于学习的环境。通过深入学习Swift,不仅可以提升个人技能,也为参与苹果生态系统的创新和开发打开了大门。无论是初入编程的新手还是经验丰富的开发者,Swift都...
Swift语言实战入门源代码是针对初学者的一份宝贵资源,它包含了《Swift语言实战入门》这本书中的所有示例和练习代码。Swift是由Apple开发的一种强大且直观的编程语言,用于构建iOS、iPadOS、macOS、watchOS和tvOS的...
swift语言实战晋级的扫描版 非常清晰 附带书签 供大家学习参考
swift学习例子集合 Swift语言基础 【实例简介】swiftui 学习例子集合,多个页面集合到一个项目中,可供参考学习 【核心代码】. ├── swift-example │ ├── Example │ │ ├── AppDelegate.swift │ │ ├─...
在“Swift语言快速入门”这个教程中,我们可以期待学习到以下几个关键知识点: 1. **基础语法**:Swift的基础语法简洁明了,包括变量和常量的声明(var和let)、数据类型(Int、Double、String等)、运算符(算术、...
自 2014年 WWDC 发布 Swift 语言以来,本项目 一直致力于将主流 Swift 中文学习、开发资源汇集于此,并且尽力紧密地跟踪、甄选优秀 Swift 开源项目,以方便开发者快速获得并使用。考虑 Swift 已经正式发布超过四年半...
本资源“Swift语言学习基础Demo集合”旨在为初学者提供一系列实践示例,帮助他们更好地理解和掌握Swift的基础概念。 1. **变量与常量**:在Swift中,我们使用`var`声明变量,`let`声明常量。通过这些基础元素,你...
Swift语言是苹果公司推出的一种强大的、现代化的编程语言,用于构建iOS、iPadOS、macOS、watchOS和tvOS的应用程序。本教程“Swift语言教程:Swift项目实战”旨在通过实际项目开发,深入理解Swift语言的各个方面,...
综上,"Swift语言实战晋级-课件代码源文件(第二版)[基于Xcode6.3]"提供了丰富的学习材料,涵盖了Swift的核心概念和实际应用,对于希望深入理解Swift编程的开发者来说是一份宝贵的资源。通过实践这些代码示例,可以...
通过"Swift语言快速入门第七章"和"Swift语言快速入门第八章"的学习,初学者将能够掌握Swift的基本语法和面向对象编程概念,为进一步深入开发iOS应用打下坚实基础。同时,提供的"大学霸淘宝店.url"可能是一个资源链接...
Swift语言是苹果公司于2014年推出的一种面向对象的编程语言,旨在为iOS、macOS、watchOS和tvOS等平台的开发提供更高效、简洁和安全的编程体验。本快速入门教程将帮助初学者掌握Swift的基础知识,逐步进阶到高级编程...
Swift编程入门教程,从零开始,深入浅出的Swift语言学习指南; Swift编程入门教程,从零开始,深入浅出的Swift语言学习指南; Swift编程入门教程,从零开始,深入浅出的Swift语言学习指南; Swift编程入门教程,从零...
Swift语言是一种现代、安全且性能优异的编程语言,特别适用于苹果操作系统的应用程序开发。Swift语言的特点在于其简洁、易学和强大的编译器,它能够快速发现程序中的错误,并提供实时反馈。 首先,Swift语言的基本...
Swift语言实战精讲的课程源文件是一份深入学习Swift编程的宝贵资料,涵盖了从基础到高级的各种主题。Swift是由Apple开发的一种强大且易学的编程语言,主要用于iOS、iPadOS、macOS、watchOS和tvOS的应用开发。这份...
对于Swift语言的广大开发者而言,这不仅是一个编程语言的学习资源,也是一个关于团队协作和奉献精神的启示录。通过本教程的学习,开发者们可以更好地掌握Swift语言,为构建强大的iOS应用打下坚实的基础。
Swift语言案例资源涵盖了从基础语法到高级功能...总之,Swift语言案例资源是开发者学习和实践Swift语言的重要参考,它们提供了从基础到高级的系统学习材料,以及丰富的实际应用场景和工具库支持。通过学习和实践这些案
《Swift语言》中文版API是苹果公司为开发者提供的官方文档,旨在帮助中文用户更好地理解和使用Swift编程语言。Swift是一款高效、安全、互动性强的开源编程语言,被广泛应用于iOS、iPadOS、macOS、watchOS以及tvOS的...
Swift 通过向其他现代编程模式学习,定义了大量类来避免常⻅的编程错误: 变量一定是在使用前初始化的; 数组索引会检查越界错误; 整数会检查溢出; 可选项保证了 nil 值会显式处理; 内存自动管理; 错误处理允许从意外...