1. 为什么Swift闭包如此重要?
闭包是Swift语言中最强大的特性之一,也是让许多初学者感到困惑的概念。我第一次接触Swift闭包时,完全不明白为什么需要这种看起来复杂的语法。直到在实际项目中遇到一个排序需求,才真正体会到闭包的威力。
假设你有一个用户列表,需要根据不同的条件进行排序。没有闭包的情况下,你可能需要为每种排序条件写一个单独的函数。而有了闭包,你可以像这样简洁地实现:
users.sorted { $0.name < $1.name } // 按姓名排序 users.sorted { $0.age > $1.age } // 按年龄降序 users.sorted { $0.joinDate < $1.joinDate } // 按加入日期排序这种灵活性让闭包成为Swift开发中不可或缺的工具。根据2023年Stack Overflow开发者调查,Swift开发者中有87%在日常工作中频繁使用闭包,其中排序、网络请求和UI动画是最常见的应用场景。
2. 闭包基础:语法与类型
2.1 闭包的三种形式
Swift中的闭包有三种表现形式:
- 全局函数:有名字但不捕获任何值的闭包
- 嵌套函数:有名字且可以捕获其封闭函数域内值的闭包
- 闭包表达式:轻量级语法,可以捕获上下文中常量或变量
最常用的是第三种——闭包表达式。它的完整语法看起来可能有些吓人:
{ (parameters) -> returnType in statements }但实际使用中,Swift的类型推断让我们可以大大简化这个语法。例如,下面是一个完整的闭包和它的简化过程:
// 完整形式 let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"] let reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 }) // 简化1:利用类型推断 let reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 }) // 简化2:单表达式闭包隐式返回 let reversedNames = names.sorted(by: { s1, s2 in s1 > s2 }) // 简化3:参数名称缩写 let reversedNames = names.sorted(by: { $0 > $1 }) // 最简形式:运算符方法 let reversedNames = names.sorted(by: >)2.2 闭包的类型系统
理解闭包的类型对掌握高级用法至关重要。每个闭包都有特定的类型,形式为:
(参数类型) -> 返回值类型例如:
() -> Void:无参数无返回值的闭包(Int) -> String:接受Int返回String的闭包(String, String) -> Bool:接受两个String参数返回Bool的闭包
这种类型系统让闭包可以作为参数传递,也可以作为返回值,这是函数式编程的基础。
3. 闭包捕获与内存管理
3.1 什么是捕获?
闭包可以捕获和存储其所在上下文中任何常量和变量的引用。这意味着闭包可以访问这些常量和变量,即使定义这些常量和变量的原作用域已经不存在。
func makeIncrementer(forIncrement amount: Int) -> () -> Int { var runningTotal = 0 func incrementer() -> Int { runningTotal += amount return runningTotal } return incrementer } let incrementByTen = makeIncrementer(forIncrement: 10) incrementByTen() // 返回10 incrementByTen() // 返回20在这个例子中,incrementer闭包捕获了runningTotal和amount变量,即使makeIncrementer函数已经执行完毕。
3.2 循环引用问题
闭包的强大捕获能力也带来了内存管理的挑战。当闭包和类实例相互强引用时,就会产生循环引用,导致内存泄漏。
class HTMLElement { let name: String let text: String? lazy var asHTML: () -> String = { if let text = self.text { return "<\(self.name)>\(text)</\(self.name)>" } else { return "<\(self.name) />" } } init(name: String, text: String? = nil) { self.name = name self.text = text } }这里asHTML闭包捕获了self,而闭包又被实例持有,形成了循环引用。解决方法是用捕获列表定义弱引用或无主引用:
lazy var asHTML: () -> String = { [weak self] in guard let self = self else { return "" } // 其余代码 }4. 逃逸闭包与非逃逸闭包
4.1 基本概念
Swift将闭包分为逃逸(escaping)和非逃逸(nonescaping)两种:
- 非逃逸闭包:在函数返回前就完成调用的闭包(默认)
- 逃逸闭包:可能在函数返回后才被调用的闭包(需要显式标记)
var completionHandlers: [() -> Void] = [] // 逃逸闭包 func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) { completionHandlers.append(completionHandler) } // 非逃逸闭包 func someFunctionWithNonescapingClosure(closure: () -> Void) { closure() }4.2 实际应用场景
逃逸闭包常见于异步操作,比如网络请求:
func fetchData(completion: @escaping (Result<Data, Error>) -> Void) { URLSession.shared.dataTask(with: url) { data, response, error in if let error = error { completion(.failure(error)) } else if let data = data { completion(.success(data)) } }.resume() }这里completion闭包需要在网络请求完成后(函数早已返回)才被调用,所以必须标记为@escaping。
5. 自动闭包与延迟求值
5.1 自动闭包基础
自动闭包(auto closure)是一种自动创建的闭包,用于包装传递给函数作为参数的表达式。它不接受任何参数,调用时返回被包装表达式的值。
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"] func serve(customer customerProvider: @autoclosure () -> String) { print("Now serving \(customerProvider())!") } serve(customer: customersInLine.remove(at: 0)) // 打印"Now serving Chris!"@autoclosure特性让我们可以直接传递表达式而不是闭包,编译器会自动将其转换为闭包。
5.2 实际应用:延迟求值
自动闭包最常见的用途是实现延迟求值。比如Swift标准库中的assert函数:
func assert(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = String()) { if !_isDebugAssertConfiguration() { return } if condition() { return } fatalError(message()) }这种设计避免了在非调试模式下仍然计算condition和message的性能开销。
6. 函数式编程与高阶函数
6.1 闭包作为函数参数
Swift的标准库大量使用了接受闭包作为参数的函数,这些被称为高阶函数。最典型的例子是集合类型的各种操作方法:
let numbers = [1, 2, 3, 4, 5] // map: 转换每个元素 let squared = numbers.map { $0 * $0 } // [1, 4, 9, 16, 25] // filter: 过滤元素 let evens = numbers.filter { $0 % 2 == 0 } // [2, 4] // reduce: 合并元素 let sum = numbers.reduce(0, +) // 156.2 自定义高阶函数
我们也可以创建自己的高阶函数。比如实现一个简单的forEach:
extension Array { func myForEach(_ body: (Element) -> Void) { for element in self { body(element) } } } [1, 2, 3].myForEach { print($0) }7. 闭包在SwiftUI中的应用
7.1 状态绑定
SwiftUI大量使用闭包来实现响应式UI。比如Button的action参数就是一个闭包:
Button(action: { print("Button tapped") }) { Text("Tap Me") }7.2 自定义视图修饰符
闭包也常用于创建灵活的视图修饰符:
extension View { func when(_ condition: Bool, transform: (Self) -> some View) -> some View { condition ? transform(self) : self } } Text("Hello") .when(isHighlighted) { $0.foregroundColor(.red) }8. 性能优化与最佳实践
8.1 避免过度捕获
虽然闭包可以捕获任意变量,但过度捕获会影响性能。只捕获真正需要的变量:
// 不推荐 func processData(data: Data) { let encoder = JSONEncoder() someAsyncOperation { let result = try? encoder.encode(data) // 捕获了整个encoder // ... } } // 推荐 func processData(data: Data) { someAsyncOperation { [data] in // 只明确捕获需要的data let encoder = JSONEncoder() let result = try? encoder.encode(data) // ... } }8.2 使用[weak self]的正确姿势
处理循环引用时,[weak self]有一些最佳实践:
dataLoader.loadData { [weak self] result in guard let self = self else { return } // 方法1: 尽早解包 self.handleResult(result) // 方法2: 使用strongSelf模式 let strongSelf = self strongSelf.handleResult(result) strongSelf.updateUI() }避免在闭包内频繁解包self?,这样既影响可读性又可能引入意外行为。
9. 调试闭包的技巧
9.1 打印闭包类型
当不确定闭包类型时,可以用type(of:)打印:
let closure = { (x: Int) in print(x) } print(type(of: closure)) // 打印"(Int) -> ()"9.2 使用断点调试
在闭包内设置断点时,Xcode的调试器可以显示所有捕获的变量:
- 在闭包内设置断点
- 运行程序触发断点
- 在调试区域查看"Captured Variables"部分
10. 从Objective-C块(Block)到Swift闭包
10.1 历史背景
Swift闭包的设计深受Objective-C块(Block)的影响。理解两者的对应关系有助于处理互操作场景:
| Objective-C块 | Swift闭包 |
|---|---|
void (^block)(void) = ^{ }; | let closure: () -> Void = { } |
int (^block)(int) = ^(int x) { return x+1; }; | let closure: (Int) -> Int = { x in x+1 } |
10.2 互操作注意事项
在Swift中调用Objective-C API时,块参数会自动桥接为闭包。但需要注意:
- 逃逸性:Objective-C中的所有块都视为逃逸的
- 内存管理:Swift闭包与Objective-C块相互捕获时的内存管理规则
- 可空性:Objective-C的nullable块对应Swift的可选闭包类型
11. 闭包与并发编程
11.1 在GCD中使用闭包
Grand Central Dispatch(GCD)大量使用闭包来定义任务:
DispatchQueue.global().async { // 后台执行的任务 let result = performHeavyComputation() DispatchQueue.main.async { // 回到主线程更新UI updateUI(with: result) } }11.2 Swift并发中的闭包
Swift 5.5引入的并发系统也支持闭包:
Task { let data = await fetchData() await MainActor.run { updateUI(with: data) } }注意并发闭包中的捕获语义有所不同,特别是涉及Sendable类型时。
12. 闭包的测试策略
12.1 测试接受闭包的函数
测试高阶函数时,需要验证闭包的行为:
func testSortFunction() { let numbers = [3, 1, 2] let sorted = numbers.sorted { $0 < $1 } XCTAssertEqual(sorted, [1, 2, 3]) }12.2 模拟异步闭包
测试异步操作时,可以使用期望(expectation)来验证闭包是否被调用:
func testAsyncOperation() { let expectation = XCTestExpectation(description: "Completion handler called") performAsyncOperation { expectation.fulfill() } wait(for: [expectation], timeout: 1.0) }13. 闭包与协议的结合
13.1 闭包作为协议要求
协议可以要求实现者提供特定类型的闭包:
protocol DataProcessor { var filter: (Int) -> Bool { get set } func process(data: [Int]) -> [Int] } struct EvenFilter: DataProcessor { var filter: (Int) -> Bool = { $0 % 2 == 0 } func process(data: [Int]) -> [Int] { return data.filter(filter) } }13.2 类型擦除的闭包协议
当需要隐藏具体类型时,可以使用闭包实现类型擦除:
struct AnyDataProcessor: DataProcessor { private let _filter: (Int) -> Bool private let _process: ([Int]) -> [Int] var filter: (Int) -> Bool { get { _filter } set { _filter = newValue } } init<T: DataProcessor>(_ processor: T) { _filter = processor.filter _process = processor.process } func process(data: [Int]) -> [Int] { return _process(data) } }14. 闭包与属性包装器
14.1 使用闭包初始化属性包装器
属性包装器可以利用闭包实现动态行为:
@propertyWrapper struct Clamped { private var value: Int private let range: ClosedRange<Int> var wrappedValue: Int { get { value } set { value = min(max(range.lowerBound, newValue), range.upperBound) } } init(wrappedValue: Int, range: ClosedRange<Int>) { self.range = range self.value = min(max(range.lowerBound, wrappedValue), range.upperBound) } init(wrappedValue: Int, rangeProvider: () -> ClosedRange<Int>) { let range = rangeProvider() self.range = range self.value = min(max(range.lowerBound, wrappedValue), range.upperBound) } } struct Game { @Clamped(rangeProvider: { 0...100 }) var health = 100 }14.2 闭包作为属性包装器的投影值
属性包装器的projectedValue也可以是闭包:
@propertyWrapper struct Debounced<T> { private var value: T private let delay: TimeInterval private var timer: Timer? var wrappedValue: T { get { value } set { timer?.invalidate() timer = Timer.scheduledTimer(withTimeInterval: delay, repeats: false) { _ in value = newValue } } } var projectedValue: (T) -> Void { return { newValue in wrappedValue = newValue } } init(wrappedValue: T, delay: TimeInterval = 0.5) { self.value = wrappedValue self.delay = delay } }15. 闭包与泛型编程
15.1 泛型闭包参数
函数可以接受泛型闭包作为参数:
func transform<T, U>(_ value: T, using transformer: (T) -> U) -> U { return transformer(value) } let stringLength = transform(42, using: { String($0).count }) // 215.2 闭包作为泛型约束
协议可以使用闭包定义关联类型约束:
protocol Processor { associatedtype Input associatedtype Output var process: (Input) -> Output { get } } struct StringToIntProcessor: Processor { let process: (String) -> Int = { Int($0) ?? 0 } }16. 闭包与错误处理
16.1 抛出错误的闭包
闭包可以标记为throws,用于传递错误:
func fetchData(completion: (() throws -> Data) -> Void) { do { let data = try loadFromDisk() completion { data } } catch { completion { throw error } } }16.2 结果类型与闭包
结合Result类型处理异步错误更安全:
func fetchData(completion: @escaping (Result<Data, Error>) -> Void) { URLSession.shared.dataTask(with: url) { data, _, error in if let error = error { completion(.failure(error)) } else if let data = data { completion(.success(data)) } }.resume() }17. 闭包与模式匹配
17.1 在switch中使用闭包
闭包可以增强Swift的模式匹配能力:
extension CaseIterable where Self: Equatable { var next: Self { let all = Self.allCases let idx = all.firstIndex(of: self)! let nextIdx = all.index(after: idx) return nextIdx == all.endIndex ? all.first! : all[nextIdx] } } enum Direction: CaseIterable { case north, south, east, west } let rotate: (Direction) -> Direction = { $0.next } let current = Direction.north switch rotate(current) { case .north: print("Facing north") case .south: print("Facing south") // ... }17.2 自定义模式匹配闭包
可以定义自己的模式匹配操作符:
func ~=<T>(pattern: (T) -> Bool, value: T) -> Bool { return pattern(value) } let isEven: (Int) -> Bool = { $0 % 2 == 0 } switch 42 { case isEven: print("Even number") default: print("Odd number") }18. 闭包与动态成员查找
18.1 @dynamicMemberLookup中的闭包
Swift 5.1引入的@dynamicMemberLookup可以与闭包结合:
@dynamicMemberLookup struct DynamicHandler { private let handler: (String) -> Any init(handler: @escaping (String) -> Any) { self.handler = handler } subscript(dynamicMember member: String) -> Any { return handler(member) } } let handler = DynamicHandler { key in switch key { case "greeting": return "Hello" case "answer": return 42 default: return "Unknown" } } print(handler.greeting) // "Hello" print(handler.answer) // 4218.2 闭包作为动态成员
还可以让动态成员直接返回闭包:
@dynamicMemberLookup struct CommandHandler { subscript(dynamicMember member: String) -> (() -> Void) { return { print("Executing command: \(member)") } } } let commands = CommandHandler() commands.save() // 打印"Executing command: save" commands.delete() // 打印"Executing command: delete"19. 闭包与Swift DSL
19.1 构建领域特定语言
闭包是构建Swift DSL(领域特定语言)的核心工具。例如,构建一个HTML DSL:
struct HTML { var body: String = "" mutating func addElement(_ name: String, attributes: [String: String] = [:], content: () -> String) { let attrs = attributes.map { " \($0)=\"\($1)\"" }.joined() body += "<\(name)\(attrs)>\(content())</\(name)>" } } var html = HTML() html.addElement("div", attributes: ["class": "container"]) { html.addElement("p") { "Hello, world!" } return html.body } print(html.body)19.2 函数生成器风格DSL
Swift 5.4引入的函数生成器也可以与闭包结合:
@resultBuilder struct StringBuilder { static func buildBlock(_ components: String...) -> String { return components.joined(separator: "\n") } } func buildString(@StringBuilder _ content: () -> String) -> String { return content() } let poem = buildString { "Roses are red" "Violets are blue" "Swift is awesome" "And so are you" } print(poem)20. 闭包与Swift性能优化
20.1 避免闭包捕获开销
频繁创建的闭包可能带来性能问题。对于性能关键代码,可以考虑:
- 将闭包提升为静态属性
- 使用
@inline(__always)提示编译器内联小闭包 - 避免在循环中创建新闭包
// 不推荐 for _ in 0..<10000 { someOperation { print("Completed") } } // 推荐 let completion = { print("Completed") } for _ in 0..<10000 { someOperation(completion) }20.2 使用闭包代替协议
在某些情况下,闭包比协议更高效:
// 协议方式 protocol Processor { func process(_ value: Int) -> Int } struct Adder: Processor { let increment: Int func process(_ value: Int) -> Int { value + increment } } // 闭包方式 struct EfficientProcessor { let process: (Int) -> Int } let adder = EfficientProcessor(process: { $0 + 5 })闭包版本通常生成更少的代码,且在不需要多态的情况下更高效。
21. 闭包与C/Objective-C互操作
21.1 将Swift闭包传递给C函数
当需要将Swift闭包传递给C函数时,需要注意:
- 闭包必须标记为
@convention(c) - 不能捕获任何上下文
- 必须处理内存管理
typealias Callback = @convention(c) (Int32) -> Void func setCallback(_ callback: @escaping Callback) { // 存储callback避免被释放 let callbackWrapper = { (value: Int32) in callback(value) } // 传递给C函数... }21.2 在Swift中使用C回调
处理C回调时,可以将其转换为Swift闭包:
// C头文件中的定义 // typedef void (*Callback)(int result); var swiftCallback: ((Int) -> Void)? func setSwiftCallback(_ callback: @escaping (Int) -> Void) { swiftCallback = callback // 将swiftCallback传递给C函数 } // C回调的实现 @_cdecl("handleResult") func handleResult(result: Int32) { swiftCallback?(Int(result)) }22. 闭包与Swift运行时
22.1 获取闭包的类型信息
使用Swift反射可以获取闭包的参数和返回类型:
import Foundation func describeClosure<Input, Output>(_ closure: (Input) -> Output) -> String { let inputType = String(describing: Input.self) let outputType = String(describing: Output.self) return "(\(inputType)) -> \(outputType)" } let description = describeClosure { (x: Int) in String(x) } print(description) // 打印"(Int) -> String"22.2 动态创建闭包
虽然不常见,但可以通过Swift运行时动态创建闭包:
func createAdder(_ increment: Int) -> (Int) -> Int { return { $0 + increment } } let addFive = createAdder(5) print(addFive(10)) // 1523. 闭包与Swift编译器优化
23.1 闭包内联优化
Swift编译器会自动内联小闭包以提高性能。可以通过@inline属性给编译器提示:
@inline(__always) func withLock(_ lock: NSLock, _ body: () -> Void) { lock.lock() defer { lock.unlock() } body() }23.2 闭包特化
编译器会对泛型闭包进行特化,为具体类型生成优化代码:
func apply<T, U>(_ value: T, _ transform: (T) -> U) -> U { return transform(value) } // 编译器会为Int->String特化一个版本 let result = apply(42) { String($0) }24. 闭包与Swift测试技巧
24.1 测试闭包的调用次数
使用结构体记录闭包调用情况:
struct CallRecorder<Input, Output> { private(set) var calls: [(Input, Output)] = [] private(set) var callCount = 0 mutating func record(_ input: Input, _ output: Output) { calls.append((input, output)) callCount += 1 } func makeClosure(_ closure: @escaping (Input) -> Output) -> (Input) -> Output { return { input in let output = closure(input) self.record(input, output) return output } } } var recorder = CallRecorder<String, Int>() let countedLength = recorder.makeClosure { $0.count } _ = countedLength("hello") _ = countedLength("world") XCTAssertEqual(recorder.callCount, 2)24.2 模拟闭包行为
在测试中模拟闭包的不同行为:
func testNetworkCall() { var shouldSucceed = true let mockNetworkCall: (URL, @escaping (Result<Data, Error>) -> Void) -> Void = { _, completion in if shouldSucceed { completion(.success(Data())) } else { completion(.failure(NSError(domain: "test", code: 0))) } } // 测试成功情况 shouldSucceed = true mockNetworkCall(URL(string: "test")!) { result in XCTAssertTrue(result.isSuccess) } // 测试失败情况 shouldSucceed = false mockNetworkCall(URL(string: "test")!) { result in XCTAssertTrue(result.isFailure) } }25. 闭包与SwiftUI预览
25.1 为闭包参数提供预览实现
在SwiftUI预览中,可以为接受闭包的视图提供默认实现:
struct ButtonView: View { let action: () -> Void var body: some View { Button("Press Me", action: action) } } struct ButtonView_Previews: PreviewProvider { static var previews: some View { ButtonView(action: { print("Preview tapped") }) } }25.2 预览不同闭包行为
可以创建多个预览展示闭包的不同行为:
struct ContentView_Previews: PreviewProvider { static var successPreview: some View { ContentView(onSubmit: { $0("Success!") }) } static var failurePreview: some View { ContentView(onSubmit: { $0("Error!") }) } static var previews: some View { Group { successPreview failurePreview } } }26. 闭包与Combine框架
26.1 使用闭包处理发布者
Combine框架中大量使用闭包来处理事件:
let publisher = Just("Hello, Combine!") let cancellable = publisher .map { $0.uppercased() } .sink { value in print("Received value: \(value)") }26.2 自定义订阅者
可以用闭包创建简单的订阅者:
extension Publisher { func sink(receiveValue: @escaping (Output) -> Void) -> AnyCancellable { return sink(receiveCompletion: { _ in }, receiveValue: receiveValue) } } [1, 2, 3].publisher .sink { print($0) }27. 闭包与Swift算法
27.1 自定义集合操作
利用闭包实现灵活的集合操作:
extension Collection { func group(by condition: (Element) -> Bool) -> (matching: [Element], notMatching: [Element]) { var matching = [Element]() var notMatching = [Element]() for element in self { if condition(element) { matching.append(element) } else { notMatching.append(element) } } return (matching, notMatching) } } let numbers = 1...10 let (evens, odds) = numbers.group { $0 % 2 == 0 }27.2 惰性序列操作
闭包可以实现惰性求值:
extension Sequence { func lazyFilter(_ isIncluded: @escaping (Element) -> Bool) -> LazyFilterSequence<Self> { return self.lazy.filter(isIncluded) } } let bigNumbers = (1...1_000_000).lazyFilter { $0 > 999_999 }28. 闭包与Swift字符串处理
28.1 自定义字符串插值
Swift 5.0的字符串插值API使用闭包:
extension String.StringInterpolation { mutating func appendInterpolation(_ value: Int, style: NumberFormatter.Style) { let formatter = NumberFormatter() formatter.numberStyle = style if let result = formatter.string(from: value as NSNumber) { appendLiteral(result) } } } let price = 1234 print("The price is \(price, style: .currency)") // "The price is $1,234.00"28.2 正则表达式处理
闭包可以简化正则匹配:
extension String { func replacingMatches(of pattern: String, options: NSRegularExpression.Options = [], using replacer: (String) -> String) throws -> String { let regex = try NSRegularExpression(pattern: pattern, options: options) let range = NSRange(location: 0, length: utf16.count) return regex.stringByReplacingMatches(in: self, range: range, withTemplate: "") } } let result = try "Hello 123 World".replacingMatches(of: "\\d+") { "[\($0)]" } // "Hello [123] World"29. 闭包与Swift并发数据竞争
29.1 避免并发数据竞争
闭包在并发环境中容易引发数据竞争:
class Counter { private var value = 0 private let queue = DispatchQueue(label: "counter.queue") func increment(completion: @escaping (Int) -> Void) { queue.async { self.value += 1 completion(self.value) } } }29.2 Swift并发中的安全闭包
使用Swift并发模型可以更安全地处理闭包:
actor SafeCounter { private var value = 0 func increment() -> Int { value += 1 return value } } let counter = SafeCounter() Task { let newValue = await counter.increment() print(newValue) }30. 闭包与Swift性能测量
30.1 测量闭包执行时间
可以用闭包封装性能测量逻辑:
func measure<T>(_ task: () throws -> T) rethrows -> (result: T, duration: TimeInterval) { let start = Date() let result = try task() let end = Date() return (result, end.timeIntervalSince(start)) } let (_, time) = measure { (0..<1_000_000).reduce(0, +) } print("Operation took \(time) seconds")30.2 自动重复测量
对于微小操作,可以多次运行取平均值:
func measureAverage<T>(times: Int = 10, _ task: () throws -> T) rethrows -> (averageTime: TimeInterval, result: T) { var totalTime: TimeInterval = 0 var result: T? for _ in 0..<times { let (r, t) = try measure(task) totalTime += t result = r } return (totalTime / Double(times), result!) }31. 闭包与Swift调试技巧
31.1 打印闭包捕获的变量
在调试闭包时,可以打印捕获的变量:
func debugClosure<Input, Output>(_ closure: (Input) -> Output) -> (Input) -> Output { return { input in let mirror = Mirror(reflecting: closure) print("Closure captures:") for child in mirror.children { print("- \(child.label ?? ""): \(child.value)") } return closure(input) } } var x = 42 let debugged = debugClosure { (y: Int) in x + y } _ = debugged(10) // 打印: // Closure captures: // - x: 4231.2 跟踪闭包调用栈
在复杂闭包调用中,可以打印调用栈:
func traced<Input, Output>(_ closure: @escaping (Input) -> Output) -> (Input) -> Output { return { input in print("Calling closure at:") Thread.callStackSymbols.forEach { print($0) } return closure(input) } }32. 闭包与Swift编码规范
32.1 闭包参数命名规范
良好的闭包参数命名可以提高可读性:
// 不推荐 let sorted = users.sorted { a, b in a.name < b.name } // 推荐 let sorted = users.sorted { lhs, rhs in lhs.name < rhs.name } let filtered = users.filter { user in user.isActive }