闭包
1.全局函数和嵌套函数实际上时特殊的必包,必包采取如下三种形式之一:
- 全局函数是一个有名字但不会捕获任何值的必包
- 嵌套函数是一个有名字并可以捕获其封闭函数区域内值的必包
- 必包表达式是一个利用轻量级语法所写的可以捕获其上下文中变量或常量值的匿名必包
2.Swift的必包表达式拥有简洁的风格,并鼓励在常见场景中进行语法优化,主要优化如下:
- 利用上下文推断参数和返回值类型
- 隐式返回单表达式必包,即单表达式必包可以省略return关键字
- 参数名称缩写
- 尾随必包语法
闭包表达式
1.闭包表达式是一种利用简洁语法构建内联闭包的方式。闭包表达式提供了一些语法优化,使得撰写必包变得简单明了。
使用Swift标准库sort方法举例:
func backwards(s1:Int , s2:Int) ->Bool {
return s1 > s2
}
let arr = [12,23,2,1,34,26]
let sortArr = arr.sort(backwards)
闭包表达式的语法
1.闭包表达式语法如下:
{(parameters) -> returnType in
statements
}
闭包表达式语法可以使用常量、变量和inout类型作为参数,不能提供默认值。也可以在参数列表的最后使用可变参数。元组也可以作为参数和返回值。
let arr = [12,23,2,1,34,26]
let sortArr = arr.sort({ (s1:Int,s2:Int) ->Bool in
return s1 > s2
})
print(sortArr)
根据上下文推断类型
实际情况下,通过内联闭包表达式构造的闭包作为参数传递给函数或方法是,都可以推断出闭包的参数和返回值类型。这意味着闭包作为函数或者方法参数时,我们几乎不需要利用完整格式构造内联闭包。因为所有的类型都可以被正确推断,返回箭头->和围绕在参数周围的括号()也可以被省略:
let arr = [12,23,2,1,34,26]
let sortArr2 = arr.sort({ s1,s2 in return s1 > s2 })
print(sortArr2)
单表达式闭包隐式返回
单行表达式闭包可以通过省略return关键字来隐式返回单行表达式的结果,
let arr = [12,23,2,1,34,26]
let sortArr2 = arr.sort({s1,s2 in s1 > s2 })
print(sortArr2)
参数名称缩写
Swift自动为内联闭包提供了参数名称缩写功能,我们可以直接通过$0,$1,$2来顺序调用闭包的参数,以此类推。
如果我们在闭包表达式中使用参数名称缩写,我们可以在闭包参数列表中省略对其的定义,并且对应参数名称缩写的类型会通过函数类型进行推断。in关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成:
let arr = [12,23,2,1,34,26]
let sortArr2 = arr.sort { $0 > $1 }
print(sortArr2)
运算符函数
实际上还有一种更简短的方式来撰写闭包表达式,swift的String类型定义了关于大于号(>)的字符串实现,其作为一个函数接受两个String类型的参数并返回Bool类型的值。而这正好与sort(_:)方法的参数类型相符合。因此,我们可以简单的传递一个大于号,Swift可以自动的推断出我们使用大于号的字符串函数实现:
let arr = ["a","c","b","z","g"]
let sortArr = arr.sort(>)
print(sortArr)
尾随闭包
如果我们需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用尾随闭包来增强函数的可读性。尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用:
func test(parameter:()->Void){
//do something...
}
test({
})
test(){
}
如果函数只需要闭包表达式一个参数,当我们使用尾随闭包时,我们可以把()省略掉:
func test(parameter:()->Void){
//do something...
}
test{
}
捕获值
闭包可以在其被定义的上下文中捕获常量或变量。即使定义这些常量和变量的原作用域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。
1.Swift中,可以捕获值的闭包的最简单形式是嵌套函数,也就是定义在其他函数的函数体内的函数。嵌套函数可以捕获其外部函数所有的参数以及定义的常量和变量。
func makeIncrementor(forIncrement amount: Int)-> ()->Int {
var runningTotal = 0
func incrementor() -> Int {
runningTotal += amount
return runningTotal
}
return incrementor
}
var function = makeIncrementor(forIncrement: 100)
var result = function()
result = function()
result = function()
result = function()
note:
1.为了优化,如果一个值是不可变的,Swift可能会改为捕获并保存一份对值的拷贝。Swift也会负责被捕获变量的所有内存管理工作,包括释放不再需要的变量。
2.如果我们将闭包赋值给一个类实例的属性,并且该闭包通过访问实例或其成员而捕获了该实例,将会创建一个在闭包和该闭包实例间的循环强引用。Swift使用捕获列表来打破这种循环强引用。
闭包是引用类型
1.无论将函数或闭包赋值给一个常量还是变量,我们实际上都是将常量或变量的值设置为对应函数或闭包的引用。这也意味着如果我们将闭包赋值给两个不同的常量或变量,两个值都会指向同一个闭包。
func makeIncrementor(forIncrement amount: Int)-> ()->Int {
var runningTotal = 0
func incrementor() -> Int {
runningTotal += amount
return runningTotal
}
return incrementor
}
var function = makeIncrementor(forIncrement: 100)
let function2 = function
function()
function2()
function2()
function()
非逃逸闭包
当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们就称该闭包从函数中逃逸。当我们定义接受闭包作为参数的函数时,我们可以在参数名之前标注@noescapte ,用来指明这个闭包是不允许“逃逸”出这个函数的。将闭包标准@noescape能使编译器直到这个闭包的生命周期,从而可以进行一些比较激进的优化。
func functionName(@noescape closure:()->Void){
closure()
}
2.eg:sort(_:)方法接受一个用来进行元素比较的闭包作为参数。这个参数被标注了@noescape,因为它确保自己在排序结束之后就没用了
3.一种能使用闭包“逃逸”出函数的方法是,将这个闭包保存在一个函数外部定义的变量中。举个例子,很多自动启动异步操作的函数接受一个闭包参数作为completion handler。这类函数会在异步操作开始之后立刻返回,但是闭包知道异步操作结束才会被调用。在这种情况下,闭包需要“逃逸”出函数,因为闭包需要在函数返回之后被调用。
var completionHandlers: [()->Void] = []
func test1(completion:()->Void){
completionHandlers .append(completion)
//异步网络请求
//网络请求回调,嗲用completion()
}
自动闭包
1.自动闭包是一种自动创建的闭包,用于包装传递给函数作为参数的表达式。这种闭包不接受任何参数,当它被调用的时候,会返回被包装在其中的表达式的值。这种便利语法让我们能够用一个普通的表达式来代替显示的闭包,从而省略闭包的花括号。
2.自动闭包让我们能够延迟求值,因为代码段不会被执行直到我们调用这个闭包。
var arr = [1,2,3,4,5,6]
func test(@autoclosure closure:()->Int){
print("\(closure())")
}
test(arr.removeAtIndex(2))
print(arr)
@autoclosure特性暗含了@noescape特性,如果你想让这个闭包可以“逃逸”,则应该使用@autoclosure(escaping)特性.