逃逸闭包
- 当闭包作为一个实际参数传递给一个函数的时候,并且他会在函数返回之后调用,我们就说这个闭包逃逸了.当你声明一个接受闭包作为参数的函数时,你可以在形式参数前写 @escaping 来明确闭包是允许逃逸的.
- 闭包可以逃逸的一种方法是呗存储再定义于函数外的变量里.
- 例:很多函数接收闭包实际参数作为启动异步任务的回调.函数的启动任务后返回,但是闭包要直到任务完成后才会调用,我们就说闭包逃逸了,以便于稍后调用.
- 让闭包 @escaping 意味着你必须在闭包中显示的引用self
var completionHandlers: [() -> Void] = []
// @escaping () -> Void 是个逃逸闭包, 因为将闭包添加到外部变量数组中,并没有调用这个闭包, 而是知道数组completionHandlers[] 被调用的时候,运行数组中的闭包时闭包才会调用,这就是逃逸闭包
func comeFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}
// 全局函数
func comeFunctionWithNoEscapingClosure(closure: () -> Void) {
closure()
}
class SomeClass {
var x = 10
func doSomething() {
comeFunctionWithEscapingClosure {
self.x = 100
}
comeFunctionWithNoEscapingClosure {
x = 200
}
}
}
let instance = SomeClass()
instance.doSomething()
// print 200,表示着调用了全局函数 comeFunctionWithNoEscapingClosure,而 comeFunctionWithEscapingClosure 逃逸闭包函数没有被调用,闭包已逃逸了
print(instance.x)
// comeFunctionWithEscapingClosure 中的闭包已经逃逸到数组中了, 只有在调用数组中的闭包时,才会重新给x赋值,所以现在的x 的值 是 100
completionHandlers.first?()
print(instance.x)
自动闭包
自动闭包是一种自动创建的用来把作为时间参数传递给函数的表达式打包的闭包.它不接受任何实际参数,并且当他被调用时,他会返回内部打包的表达式的值.
优点: 通过写普通表达式代替显示闭包而使你省略保卫函数形式参数的括号.
自动闭包允许你延迟处理,因为闭包内部的代码直到你调用他的时候才会运行.对于有副作用或者占用资源的代码来说很有用,因为他可以允许你控制代码何时进行求值.
var customersInLines = ["张三", "赵云", "安其拉", "邓紫棋"]
print(customersInLines.count) // print: 4
// 只是创建了闭包,但是没有调用,只有调用该闭包的时候,才会实现闭包内部的方法
let customerProvider = { customersInLines.remove(at: 0)}
print(customersInLines.count) // print: 4
print("Now serving \(customerProvider())!")
print(customersInLines.count) // print: 3
- 当你传一个闭包作为实际参数到函数的时候,你会得到与延迟处理相同的行为.
var customersInLines = ["张三", "赵云", "安其拉", "邓紫棋"]
func serve(customer customerProvider: () -> String) {
print("Now serving \(customerProvider())!")
}
serve(customer: { customersInLines.remove(at: 0)})
- 通过 @autoclosure 标记他的形式参数使用了自动闭包.现在你可以调用函数就像它接收了一个String类型的实际参数,而不是闭包.实际参数自动转换为闭包,因为形式参数类型被标记为 @autoclosure.
var customersInLines = ["张三", "赵云", "安其拉", "邓紫棋"]
func serve(customer customerProvider: @autoclosure () -> String) {
print("Now serving \(customerProvider())!")
}
// 使用 @autoclosure 标签, 传入的实际参数可以不使用{},也不是一个闭包了,传入后会自动转换成闭包
serve(customer: customersInLines.remove(at: 0))
自动 + 逃逸闭包
- @autoclosure @escaping 不需要排序,谁在前都可以
var customerProviders: [() -> String] = []
var customersInLines = ["张三", "赵云", "安其拉", "邓紫棋"]
func collectCustomerProvider(customerProvider: @autoclosure @escaping () -> String) {
customerProviders.append(customerProvider)
}
collectCustomerProvider(customerProvider: customersInLines.remove(at: 0))
collectCustomerProvider(customerProvider: customersInLines.remove(at: 0))
print("Collected \(customerProviders.count) closures.")
for customerProvider in customerProviders {
print("Now serving \(customerProvider())!")
}