闭包
闭包是自包含的功能块,可以在代码中传递和使用
闭包可以从定义它们的上下文中捕获和存储对任何常量和变量的引用。Swift处理所有的捕获的内存管理。
全局函数和嵌套函数实际上是闭包的特殊情况:
全局函数是具有名称并且不捕获任何值的闭包。
嵌套函数是具有名称的闭包,并且可以从其封闭函数捕获值。
1.闭包表达式
闭包表达式的一般形式:
{(parameters) - > return type in
语句
}}
闭包的主体的开始由in关键字引入。in关键字表示闭包的参数和返回类型的定义已完成,闭包的主体即将开始。
闭包表达式中的参数可以是inout参数,但它们不能具有默认值;可以是可变参数;元组也可以用作参数类型和返回类型。
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
var reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
//闭包的主体
return s1 > s2
})
print(reversedNames)//["Ewa", "Daniella","Chris", "Barry", "Alex"]
2.单表达式闭包的隐式返回
单表达式闭包可以通过在其声明中省略return关键字来隐式返回其单个表达式的结果
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
3.速记参数名称
Swift自动为内联闭包提供了速记参数名称,可以用来通过名称$ 0,$ 1,$ 2等引用闭包的参数的值。
如果在闭包表达式中使用这些速记参数名称,则可以从其定义中省略闭包的参数列表,并且将从预期的函数类型推断速记参数名称的数量和类型, 也可以省略in关键字.
reversedNames = names.sorted(by: { $0 > $1 } )
//更简单的运算符方法
reversedNames = names.sorted(by: >)
4.尾随闭包
funcsomeFunctionThatTakesAClosure(closure: () -> Void) {
//函数体
}
//不使用尾随闭包调用函数
someFunctionThatTakesAClosure(closure: {
//闭包主体
})
//使用尾随闭包调用函数
someFunctionThatTakesAClosure() {
//尾随闭包主体
}
5.捕获值
闭包可以从定义它的上下文捕获常量和变量。然后,闭包可以引用并修改那些常量和变量的值,即使定义常量和变量的原始作用域不再存在。
在Swift中,可以捕获值的闭包的最简单形式是嵌套函数。嵌套函数可以捕获其任何外部函数的参数,也可以捕获外部函数中定义的任何常量和变量。
6.闭包是引用类型
无论何时将函数或闭包分配给常量或变量,实际上都将该常量或变量设置为函数或闭包的引用。如果给两个不同的常量或变量赋值一个闭包,这两个常量或变量都将引用同一闭包。
7.逃逸闭包
当闭包作为参数传递给函数时,闭包被称为逃逸函数,但在函数返回后闭包被调用。
当声明一个函数将一个闭包作为其参数之一时,可以在参数的类型之前写入@escaping来表示允许闭包逃逸。@escaping标记闭包意味着你必须在闭包内自我引用
//闭包逃逸的一种方式是通过存储在函数外部定义的变量中
var completionHandlers:[() -> Void] = []
//someFunctionWithEscapingClosure(_ :)的闭包是一个转义闭包,它需要自我引用
funcsomeFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}
//someFunctionWithNonescapingClosure(_ :)的闭包是一个非转义闭包,它可以自我引用
funcsomeFunctionWithNonescapingClosure(closure: () -> Void) {
closure()
}
class SomeClass {
var x = 10
func doSomething() {
someFunctionWithEscapingClosure { self.x = 100 }
someFunctionWithNonescapingClosure { x = 200 }
}
}
let instance = SomeClass()
instance.doSomething()
print(instance.x)
completionHandlers.first?()
print(instance.x)
//Prints "100"
8.自动闭包
//autoclosure是一个闭包,它被自动创建以包装一个表达式,该表达式作为参数传递给函数。它不接受任何参数,当它被调用时,它返回包装在其中的表达式的值。
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count)//5
let customerProvider = {customersInLine.remove(at: 0) }
print(customersInLine.count)//5 因为customerProvider的隐式autoclosure使程序进行延迟操作,因为里面的代码不会运行,直到你调用闭包。
print("Now deleting \(customerProvider())!")//Now deleting Chris! //这里调用闭包,才会真正删除元素
print(customersInLine.count)//4
//显示自动闭包
var customers = ["Alex", "Ewa", "Barry", "Daniella"]
func serve(customercustomerProvider: () -> String) {
print("Now serving \(customerProvider())!")//Now serving Alex!
}
serve(customer: { customers.remove(at: 0) } )
//使用@autoclosure属性标记其参数的类型来进行自动闭包
var customers2 = ["Alex", "Ewa", "Barry", "Daniella"]
func serve2(customercustomerProvider: @autoclosure () -> String) {
print("Now serving \(customerProvider())!")
}
serve2(customer: customers2.remove(at: 0))//Now serving Alex!
//如果希望允许自动隐藏,可同时使用@autoclosure和@escaping属性。
var customersHide = ["aaaa", "bbbb", "cccc", "dddd"]
var customersClosure:[() -> String] = []
let customerCollect = { customersHide.remove(at: 0) }
funccollectCustomerProviders(_ customerCollect: @autoclosure@escaping () -> String) {
customersClosure.append(customerProvider)
}
collectCustomerProviders(customersHide.remove(at: 0))
collectCustomerProviders(customersHide.remove(at: 0))
print("Collected \(customersHide.count) closures.")//Collected 4 closures.
for customersHide in customersClosure {
print("Now serving \(customerCollect())!")
}
/*
Nowserving aaaa!
Nowserving bbbb!
*/