闭包可以捕获和存储其所在上下文中任意常量和变量
的引用。被称为包裹常量和变量。 Swift 会为你管理在捕获过程中涉及到的所有内存操作。
全局和嵌套函数实际上也是特殊的闭包,闭包采用如下三种形式之一:
- 全局函数是一个有名字但不会捕获任何值的闭包
- 嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包
-
闭包表达式
是一个利用轻量级语法所写的可以捕获其上下文中变量或常量值的匿名闭包
Swift 的闭包表达式拥有简洁的风格,并鼓励在常见场景中进行语法优化
,主要优化如下:
-
利用上下文推断参数和返回值类型
-
隐式返回单表达式闭包,即单表达式闭包可以省略
return
关键字sorted(by:)
方法接受一个闭包,该闭包函数需要传入与数组元素类型相同的两个值,并返回一个布尔类型值来表明当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值前面,排序闭包函数需要返回true
,反之返回false
。闭包的函数体部分由关键字
in
引入。该关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始。var sour = { (s1:Int,s2:Int) -> Int in return s1+s2 } sour = { s1,s2 in //根据上下文推断类型 s1*s2 //单行表达式隐式返回 }
-
参数名称缩写
var arr:[Int]=[1,2,3] arr.sort(by: {$0>$1}) arr.sort(by: >)//Swift 可以自动推断找到系统自带的那个字符串函数的实现:
-
尾随闭包语法
func someFunctionThatTakesAClosure(closure: () -> Void) { // 函数体部分 } // 以下是不使用尾随闭包进行函数调用 someFunctionThatTakesAClosure(closure: { // 闭包主体部分 }) // 以下是使用尾随闭包进行函数调用 someFunctionThatTakesAClosure() { // 闭包主体部分 } func sum(block:(_ a:Int,_ b:Int)->Int){} sum(block: +)// 系统自动匹配 sum(){$0+$1}//尾随闭包,{}写在()外 sum{$0+$1}// 只有一个闭包参数,省略()
值捕获
如果你将闭包赋值给一个类实例的属性,并且该闭包通过访问该实例或其成员而捕获了该实例,你将在闭包和该实例间创建一个循环强引用。Swift 使用捕获列表来打破这种循环强引用。
-
嵌套函数的值捕获
func makeIncrementer(forIncrement amount: Int) -> () -> Int { var runningTotal = 0 func incrementer() -> Int { runningTotal += amount return runningTotal } return incrementer } let incrementByTen = makeIncrementer(forIncrement: 10) /* incrementer 从外围函数捕获了 runningTotal 和 amount 变量的引用。捕获引用保证了 runningTotal 和 amount 变量在调用完 makeIncrementer 后不会消失,并且保证了在下一次执行 incrementer 函数时,runningTotal 依旧存在。 */ incrementByTen() incrementByTen() let count = incrementByTen() // 30 let in7 = makeIncrementer(forIncrement: 7) let c = in7() // 7
逃逸闭包
当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中逃逸。很多启动异步操作的函数接受一个闭包参数作为 completion handler。这类函数会在异步操作开始之后立刻返回,但是闭包直到异步操作结束后才会被调用
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
// 被外部引用,在函数返回后仍然存在,逃逸闭包
completionHandlers.append(completionHandler)
}
func someFunctionWithNonescapingClosure(closure: () -> Void) {
// 闭包在函数返回前调用,非逃逸闭包
closure()
}
class SomeClass {
var x = 10
func doSomething() {
// 逃逸闭包,必须显性调用self
someFunctionWithEscapingClosure { self.x = 100 }
// 非逃逸闭包,隐式调用self
someFunctionWithNonescapingClosure { x = 200 }
}
}
自动闭包
var customersInLine = ["1","2","3","4"]
// 闭包作为函数传递
func serve(customer customerProvider: () -> String) {
print("Now serving \(customerProvider())!")
}
// 调用时加花括号
serve(customer: { customersInLine.remove(at: 0) } )
// 将函数自动封装成闭包
func serve(customer customerProvider: @autoclosure () -> String) {
print("Now serving \(customerProvider())!")
}
// 将该函数当作接受 String 类型参数(而非闭包)的函数来调用
serve(customer: customersInLine.remove(at: 0))
过度使用
autoclosures
会让你的代码变得难以理解。上下文和函数名应该能够清晰地表明求值是被延迟执行的。
网友评论