闭包
- 闭包是自包含的函数代码块,可以在代码中被传递和使用。Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的匿名函数(Lambdas)比较相似。
- 闭包可以捕获和存储其所在上下文中任意常量和变量的引用。被称为包裹常量和变量。 Swift 会为你管理在捕获过程中涉及到的所有内存操作。
- 闭包采用如下三种形式之一:
全局函数是一个有名字但不会捕获任何值的闭包。
嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包。
闭包表达式是一个利用轻量级语法所写的可以捕获其上下文中变量或常量值的匿名闭包
- Swift 的闭包表达式主要优化如下:
利用上下文推断参数和返回值类型。
隐式返回单表达式闭包,即单表达式闭包可以省略return关键字。
参数名称缩写。
尾随闭包语法。
闭包和block的对比
-
swift中闭包与OC的 block 比较相似
-
swift中闭包是一个特殊函数,OC中
block是一个匿名函数 -
闭包和
block都经常用于回调// block 类型:返回值类型(^block的名称)(block的参数) 返回值(^block的名称)(形参列表) = ^( 形参列表){ // 执行代码 }; NSString* (^blockName)(NSString *str) = ^(NSString *str){ return str; }; blockName(@"block") // 闭包 类型:(参数)->(返回值类型) 闭包名称 = { (形参列表) -> return 返回值 in // 执行代码 } let closure = { (str:String) -> String in return str } closure("closure")
闭包表达式
-
闭包表达式是一种构建内联闭包的方式,它的语法简洁。在保证不丢失它语法清晰明了的同时,闭包表达式提供了几种优化的语法简写形式。
// 闭包表达式语法 { (parameters) -> return type in statements } let studname = { print("Swift 闭包实例。") } studname()
排序方法 : sorted(by:)
-
Swift 标准库提供了名为
sorted(by:)的方法,它会基于你提供的排序闭包表达式的判断结果对数组中的值(类型确定)进行排序。一旦它完成排序过程,sorted(by:)方法会返回一个与旧数组类型大小相同类型的新数组,该数组的元素有着正确的排序顺序。原数组不会被sorted(by:)方法修改。let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"] // sorted(by:)方法需要传入两个参数: // 闭包函数,该闭包函数需要传入与数组元素类型相同的两个值,并返回一个布尔类型值来表明当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值前面,排序闭包函数需要返回 true,反之返回 false。 func backward(_ s1: String, _ s2: String) -> Bool { return s1 > s2 } var reversedNames = names.sorted(by: backward) // ["Ewa", "Daniella", "Chris", "Barry", "Alex"] // 闭包表达式参数 可以是 in-out 参数,但不能设定默认值。如果你命名了可变参数,也可以使用此可变参数。元组也可以作为参数和返回值。 reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 }) // 简写 reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 } )
根据上下文推断类型
-
因为排序闭包函数是作为
sorted(by:)方法的参数传入的,Swift 可以推断其参数和返回值的类型。 -
通过内联闭包表达式构造的闭包作为参数传递给函数或方法时,总是能够推断出闭包的参数和返回值类型。这意味着闭包作为函数或者方法的参数时,你几乎不需要利用完整格式构造内联闭包。
// 简写 reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )
单表达式闭包的隐式返回
-
单行表达式闭包可以通过省略
return关键字来隐式返回单行表达式的结果,// 省略写 reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
参数名称缩写
-
Swift 自动为内联闭包提供了参数名称缩写功能,可以直接通过
$0,$1,$2来顺序调用闭包的参数。 -
如果你在闭包表达式中使用参数名称缩写,你可以在闭包定义中省略参数列表,并且对应参数名称缩写的类型会通过函数类型进行推断。
in关键字也同样可以被省略。// $0 和 $1 表示闭包中第一个和第二个 String 类型的参数。 reversedNames = names.sorted(by: { $0 > $1 } )
运算符方法
-
可以简单地传递一个大于号,Swift 可以自动推断找到系统自带的那个字符串函数的实现。
reversedNames = names.sorted(by: >)
尾随闭包
-
尾随闭包是一个书写在函数圆括号之后的闭包表达式,函数支持将其作为最后一个参数调用。在使用尾随闭包时,你不用写出它的参数标签。
-
如果函数只需要闭包表达式一个参数,当您使用尾随闭包时,您甚至可以把
()省略掉。func someFunctionThatTakesAClosure(closure: () -> Void) { // 函数体部分 } // 以下是不使用尾随闭包进行函数调用 someFunctionThatTakesAClosure(closure: { // 闭包主体部分 }) // 以下是使用尾随闭包进行函数调用 someFunctionThatTakesAClosure() { // 闭包主体部分 } // 使用尾随闭包 eversedNames = names.sorted() { $0 > $1 } reversedNames = names.sorted { $0 > $1 }
值捕获
-
闭包可以在其定义的上下文中捕获常量或变量。
-
即使定义这些常量和变量的原域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。
-
Swift最简单的闭包形式是嵌套函数,也就是定义在其他函数的函数体内的函数。
-
嵌套函数可以捕获其外部函数所有的参数以及定义的常量和变量,并且保证了在下一次执行函数时,之前捕获的值依旧存在。
func add(num:Int) -> ()->Int { var value = 0 func result() -> Int{ value += num return value } /* 这有一个叫做 add 的函数,其包含了一个叫做 result 的嵌套函数 嵌套函数result()从上下文中捕获了两个值,value 和 num 捕获这些值之后,add 将 result 作为闭包返回 每次调用 result 时,其会以 num 作为增量增加 value 的值 */ return result } let result = add(num: 10) print(result()) //10 print(result()) //20 print(result()) //30 //如果你创建了另一个result1,它会有属于自己的引用,指向一个全新、独立的value变量 let result1 = add(num: 10) print(result1()) //10 //再次调用原来的result会继续增加它自己的value变量,该变量和result1中捕获的变量没有任何联系 print(result()) //40
闭包是引用类型
-
函数和闭包都是引用类型。
-
无论您将函数/闭包赋值给一个常量还是变量,您实际上都是将常量/变量的值设置为对应函数/闭包的引用。
-
将闭包赋值给了两个不同的常量/变量,两个值都会指向同一个闭包。
//这两个常量或变量都引用相同的闭包 let method = result
逃逸闭包
-
一个传入函数的闭包如果在函数执行结束之后才会被调用,那么这个闭包就叫做逃逸闭包。通俗点讲,不在当前方法中使用闭包,而是在方法之外使用
-
定义函数的参数为逃逸闭包时,只需要在参数名之前标注
@escaping,用来指明这个闭包是允许“逃逸”出这个函数的 -
将一个闭包标记为
@escaping意味着你必须在闭包中显式地引用selfvar result: ()->Void = {} var str = "" func showA(closure: @escaping () -> Void) { result = closure } func showB(closure: () -> Void) { closure() } func doSomething() { showA {str = "我是逃逸闭包"} showB {str = "我是普通闭包"} } doSomething() print(str) //我是普通的闭包 result() print(str) //我是逃逸的闭包 // 逃逸闭包是在函数执行之后再执行,于是这段代码最后输出“我是逃逸的闭包”
自动闭包
-
自动闭包是一种自动创建的闭包,用于包装传递给函数作为参数的表达式。这种闭包不接受任何参数,当它被调用的时候,会返回被包装在其中的表达式的值。这种便利语法让你能够省略闭包的花括号,用一个普通的表达式来代替显式的闭包。
-
自动闭包让你能够延迟求值,因为直到你调用这个闭包,代码段才会被执行。延迟求值对于那些有副作用(Side Effect)和高计算成本的代码来说是很有益处的,因为它使得你能控制代码的执行时机。
var arr = ["a","b","c"] print(arr.count) //3 let closure = { arr.remove(at: 0) } print(arr.count) //3 print(closure()) //a print(arr.count) //2 // 将闭包作为参数传递给函数时,同样可以延时求值 func delete(closure: ()->String){ print(closure()) } var arr = ["a","b","c"] delete(closure:{arr.remove(at: 0)}) //log:a // 通过将参数标记为@autoclosure 来接收一个自动闭包,该函数接受的是String类型参数而非闭包 func delete(closure: @autoclosure ()-> String){ print(closure()) } var arr = ["a","b","c"] delete(closure:arr.remove(at: 0)) // log:a













网友评论