Swift 闭包(Closures)全解析(2025 版)
“闭包是自包含的函数代码块” —— 掌握 闭包语法、捕获、@escaping、autoclosure、内存管理,写出 优雅、强大、函数式 的 Swift 代码!
一、什么是闭包?
闭包 = 匿名函数 + 捕获的上下文环境
{ (parameters) -> returnType in
statements
}
三种形式
| 形式 | 示例 |
|---|---|
| 全局函数 | func add(_ a: Int, _ b: Int) -> Int |
| 嵌套函数 | func makeAdder() -> ((Int) -> Int) |
| 闭包表达式 | { $0 + $1 } |
二、闭包基本语法
1. 完整写法
let sum: (Int, Int) -> Int = { (a: Int, b: Int) -> Int in
return a + b
}
2. 类型推断(推荐)
let add = { (a, b) in a + b }
3. 简写参数 $0, $1
let multiply = { $0 * $1 }
4. 单表达式隐式返回
let square = { $0 * $0 }
5. 尾随闭包(Trailing Closure)
numbers.sorted { $0 < $1 } // 推荐
三、闭包捕获(Capture)
闭包可以捕获外部变量,即使外部作用域已结束
func makeIncrementer(step: Int) -> () -> Int {
var total = 0
let incrementer = {
total += step
return total
}
return incrementer
}
let incBy5 = makeIncrementer(step: 5)
print(incBy5()) // 5
print(incBy5()) // 10
捕获的是引用,不是值!
四、闭包是引用类型
let closure1 = { print("A") }
let closure2 = closure1 // 复制引用
closure2() // 打印 "A"
值捕获:
[weak self]、[unowned self]防止循环引用
五、@escaping 闭包(逃逸闭包)
函数返回后,闭包仍被调用 → 必须标记
@escaping
class NetworkManager {
var handlers: [() -> Void] = []
func fetch(completion: @escaping () -> Void) {
handlers.append(completion)
DispatchQueue.global().async {
completion() // 逃逸!
}
}
}
何时加 @escaping?
| 场景 | 是否需要 |
|---|---|
| 立即调用 | 不需要 |
| 存储到属性 | 需要 |
| 异步回调 | 需要 |
| 返回闭包 | 需要 |
六、@autoclosure(自动闭包)
延迟执行,简化语法
func log(if condition: @autoclosure () -> Bool) {
if condition() {
print("条件为真")
}
}
// 调用时不用写 {}
log(if 1 > 0) // 自动包装成闭包
适用场景
assert&&短路求值- 调试日志
七、闭包循环引用(Retain Cycle)
问题
class Person {
var name = "小明"
var onChange: (() -> Void)?
func setup() {
onChange = {
print(self.name) // 强引用 self
}
}
deinit { print("Person deinit") }
}
Person和onChange互相强引用 → 内存泄漏
解决方案
onChange = { [weak self] in
guard let self = self else { return }
print(self.name)
}
// 或 unowned(不安全,self 必须存在)
onChange = { [unowned self] in
print(self.name)
}
八、高阶函数 + 闭包实战
let numbers = [1, 6, 2, 8, 3, 9]
// map
let doubled = numbers.map { $0 * 2 }
// filter
let evens = numbers.filter { $0 % 2 == 0 }
// reduce
let sum = numbers.reduce(0) { $0 + $1 }
// sorted
let sorted = numbers.sorted { $0 < $1 }
// 链式
let result = numbers
.filter { $0 > 5 }
.map { "\($0 * 10)" }
.joined(separator: ", ")
print(result) // "60, 80, 90"
九、闭包性能优化
| 技巧 | 说明 |
|---|---|
| 避免频繁创建 | 缓存闭包 |
用 [weak self] | 防止循环引用 |
| 复杂逻辑抽取函数 | 可读性 > 简写 |
inout 替代闭包 | 性能敏感场景 |
// 缓存闭包
let expensiveClosure: () -> String = {
// 复杂计算
return "结果"
}()
十、闭包 vs 函数
| 特性 | 闭包 | 函数 |
|---|---|---|
| 匿名 | Yes | No |
| 捕获上下文 | Yes | No |
| 可内联 | Yes | Yes |
| 可赋值 | Yes | No |
| 尾随语法 | Yes | No |
十一、实战项目:事件总线(EventBus)
class EventBus {
private var listeners: [String: [() -> Void]] = [:]
func on(_ event: String, handler: @escaping () -> Void) {
listeners[event, default: []].append(handler)
}
func emit(_ event: String) {
listeners[event]?.forEach { $0() }
}
func off(_ event: String) {
listeners[event] = nil
}
}
// 使用
let bus = EventBus()
bus.on("login") { print("用户登录") }
bus.on("login") { print("记录日志") }
bus.emit("login")
// 输出:
// 用户登录
// 记录日志
十二、闭包速查表
| 语法 | 示例 |
|---|---|
| 完整 | { (a: Int) -> Int in return a * 2 } |
| 推断 | { a in a * 2 } |
| $0 简写 | { $0 * 2 } |
| 隐式返回 | { $0 * 2 } |
| 尾随闭包 | array.sorted { $0 < $1 } |
| 捕获列表 | [weak self] in |
| @escaping | @escaping () -> Void |
| @autoclosure | @autoclosure () -> Bool |
十三、练习题(当场写!)
// 1. 实现 once 函数:闭包只执行一次
func once(_ closure: @escaping () -> Void) -> () -> Void {
// 你的代码
}
// 2. 实现 memoize:缓存函数结果
func memoize<T: Hashable, U>(_ f: @escaping (T) -> U) -> (T) -> U {
// 你的代码
}
// 3. 实现链式加法:add(1)(2)(3)() == 6
func add(_ x: Int) -> (Int) -> Any {
// 你的代码
}
答案(展开查看)
点击查看答案
// 1.
func once(_ closure: @escaping () -> Void) -> () -> Void {
var executed = false
return {
guard !executed else { return }
executed = true
closure()
}
}
// 使用
let sayHi = once { print("Hi") }
sayHi() // Hi
sayHi() // (无输出)
// 2.
func memoize<T: Hashable, U>(_ f: @escaping (T) -> U) -> (T) -> U {
var cache: [T: U] = [:]
return { arg in
if let cached = cache[arg] {
return cached
}
let result = f(arg)
cache[arg] = result
return result
}
}
// 使用
let fib = memoize { (n: Int) -> Int in
n <= 1 ? n : fib(n-1) + fib(n-2)
}
// 3.
func add(_ x: Int) -> (Int) -> Any {
return { y in
if y == 0 { return x }
return add(x + y)
}
}
// 使用
let result = add(1)(2)(3)(0) as! Int // 6
总结:闭包黄金法则
| 法则 | 说明 |
|---|---|
| 1. 优先用尾随闭包 | 可读性高 |
2. 异步/存储闭包加 @escaping | 必须 |
3. 防止循环引用用 [weak self] | 防泄漏 |
| 4. 复杂闭包抽取为函数 | 可维护 |
5. 用 @autoclosure 简化条件 | 优雅 |
6. 性能敏感用 inout 替代闭包 | 更快 |
你已完全掌握 Swift 闭包!
回复关键词继续学习:
Swift 闭包内存管理Swift 函数式编程进阶Swift async/await 与闭包SwiftUI @State & 闭包绑定
现在就实现一个 debounce 函数:输入频繁变化,只在停止 0.5 秒后执行!