逃逸闭包
存储生命周期超过其函数的闭包。
逃逸闭包 是 CoddyKit 上的免费 Swift Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Swift Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Swift Academy 课程共包含 4 节课。
逃逸与非逃逸
默认情况下,闭包参数是非逃逸的:它必须在函数返回之前被调用。如果闭包可能被存储并在之后调用,请为它标记 @escaping。
非逃逸闭包
这个闭包会同步运行,并在函数返回时消失:
func run(_ work: () -> Void) {
work()
}
run { print("done now") } // "done now"为什么需要 @escaping
要将闭包存储起来供之后使用,就需要 @escaping:
var handlers: [() -> Void] = []
func register(_ handler: @escaping () -> Void) {
handlers.append(handler) // escapes the function
}
register { print("later") }
handlers[0]() // "later"使用异步工作进行逃逸
完成处理器会发生逃逸,因为它们会在函数返回之后运行:
func load(then completion: @escaping (String) -> Void) {
// imagine async work
completion("data")
}
load { result in print(result) } // "data"必须显式指定 self
在逃逸闭包内部访问实例成员时,必须显式写出 self. — 这是在提醒您,闭包可能会持有 self。
逃逸闭包中的显式 self
编译器要求使用显式引用:
class Loader {
var name = "L"
func start(_ done: @escaping () -> Void) {
let cb = { print(self.name); done() }
cb()
}
}
Loader().start { print("finished") } // "L" then "finished"存储多个回调
逃逸闭包让您可以构建观察者列表:
class Emitter {
private var listeners: [(Int) -> Void] = []
func on(_ l: @escaping (Int) -> Void) { listeners.append(l) }
func fire(_ v: Int) { listeners.forEach { $0(v) } }
}
let e = Emitter()
e.on { print("got \($0)") }
e.fire(7) // "got 7"返回存储的闭包
保存在属性中的闭包都是逃逸闭包:
class Button {
var action: (() -> Void)?
func setAction(_ a: @escaping () -> Void) { action = a }
}
let b = Button()
b.setAction { print("tapped") }
b.action?() // "tapped"可选闭包会逃逸
可选闭包参数会隐式逃逸(它被包装在可选值中),因此可以被存储:
func maybe(_ f: (() -> Void)?) {
f?()
}
maybe { print("ran") } // "ran"逃逸到任务数组中
延迟工作的队列是逃逸闭包的经典使用场景 — 每个闭包都会先被存储,然后在之后运行:
var tasks: [() -> Int] = []
func schedule(_ task: @escaping () -> Int) {
tasks.append(task)
}
schedule { 1 + 1 }
schedule { 10 * 2 }
print(tasks.map { $0() }) // [2, 20]性能提示
非逃逸闭包可以进行更积极的优化(不需要堆分配)。在可以保持非逃逸时,请保持非逃逸;只有在确实需要存储或延迟调用时才让闭包逃逸。
func sum(_ xs: [Int], _ f: (Int) -> Int) -> Int {
xs.reduce(0) { $0 + f($1) } // non-escaping, fast
}
print(sum([1, 2, 3]) { $0 * $0 }) // 14快速检查
什么时候必须为闭包参数标记 @escaping?
回顾
您已经了解了逃逸闭包:
- 非逃逸是默认行为;闭包会在函数返回前运行
- 要存储或延迟调用闭包,就必须使用
@escaping - 逃逸闭包要求显式访问
self. - 可选闭包参数会隐式逃逸;为了速度,请优先使用非逃逸闭包
下一步:捕获列表与弱引用 self。
常见问题解答
「逃逸闭包」课时是免费的吗?
是的 — 「逃逸闭包」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Swift Academy 课程的其余内容,请升级到 CoddyKit PRO。 Swift Academy 课程共包含 4 节课。
「逃逸闭包」这节课中我会学到什么?
存储生命周期超过其函数的闭包。 你通过在浏览器中直接运行的动手代码来练习 Swift Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Swift Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Swift Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「逃逸闭包」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Swift Academy 课中编写并运行代码吗?
能。每节 Swift Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。