news 2026/8/10 12:00:39

Swift闭包详解:从基础到高级应用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Swift闭包详解:从基础到高级应用

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中的闭包有三种表现形式:

  1. 全局函数:有名字但不捕获任何值的闭包
  2. 嵌套函数:有名字且可以捕获其封闭函数域内值的闭包
  3. 闭包表达式:轻量级语法,可以捕获上下文中常量或变量

最常用的是第三种——闭包表达式。它的完整语法看起来可能有些吓人:

{ (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闭包捕获了runningTotalamount变量,即使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, +) // 15

6.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的调试器可以显示所有捕获的变量:

  1. 在闭包内设置断点
  2. 运行程序触发断点
  3. 在调试区域查看"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时,块参数会自动桥接为闭包。但需要注意:

  1. 逃逸性:Objective-C中的所有块都视为逃逸的
  2. 内存管理:Swift闭包与Objective-C块相互捕获时的内存管理规则
  3. 可空性: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 }) // 2

15.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) // 42

18.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 避免闭包捕获开销

频繁创建的闭包可能带来性能问题。对于性能关键代码,可以考虑:

  1. 将闭包提升为静态属性
  2. 使用@inline(__always)提示编译器内联小闭包
  3. 避免在循环中创建新闭包
// 不推荐 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函数时,需要注意:

  1. 闭包必须标记为@convention(c)
  2. 不能捕获任何上下文
  3. 必须处理内存管理
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)) // 15

23. 闭包与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: 42

31.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 }

32.2 多行

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/10 11:57:39

Python实现本地化文本二维码生成器:从原理到工程实践

1. 这篇文章真正要解决的问题 你是不是也遇到过这样的场景&#xff1f;需要把一个网址、一段Wi-Fi密码、或者几句重要的配置信息&#xff0c;快速分享给同事或用户。直接发一长串字符&#xff0c;对方容易输错&#xff1b;发个文件&#xff0c;又显得太重。这时候&#xff0c;一…

作者头像 李华
网站建设 2026/8/10 11:57:04

Unity GPU物理模拟:Verlet积分与Compute Shader实战解析

1. 项目概述&#xff1a;当物理模拟遇上GPU计算 如果你在Unity里做过布料、绳索或者软体这类需要柔体物理的效果&#xff0c;大概率被Unity自带的Cloth组件或者一堆复杂的关节&#xff08;Joint&#xff09;和刚体&#xff08;Rigidbody&#xff09;组合折磨过。性能开销大、参…

作者头像 李华
网站建设 2026/8/10 11:55:11

League Akari:5大功能助你成为英雄联盟数据大师

League Akari&#xff1a;5大功能助你成为英雄联盟数据大师 【免费下载链接】League-Toolkit An all-in-one toolkit for LeagueClient. Gathering power &#x1f680;. 项目地址: https://gitcode.com/gh_mirrors/le/League-Toolkit League Akari是一款专为《英雄联盟…

作者头像 李华
网站建设 2026/8/10 11:55:04

SSD更换与系统迁移全攻略:从硬件安装到性能优化

1. 项目概述&#xff1a;为什么要自己动手换固态硬盘&#xff1f; 去年双十一入手的海康威视C2000 Pro突然掉速到200MB/s&#xff0c;用CrystalDiskInfo一看健康度只剩23%。作为从业八年的IT工程师&#xff0c;我决定亲自操刀更换这块服役三年的老将。自己动手不仅能省下至少20…

作者头像 李华
网站建设 2026/8/10 11:53:18

蓝牙配对(1)just work模式

文章目录 1. 触发条件(何时使用) 2. 加密密钥的生成(数学核心) 3. BlueZ Agent 的交互行为(用户界面层) 4. Just Works 完整工作流程图(含 Agent) 5. 安全性深度解析(必须了解的“坑”) 6. 实际生活中的案例 总结一句话 Just Works 是蓝牙 SSP(安全简单配对)中最简…

作者头像 李华
网站建设 2026/8/10 11:52:57

ComfyUI Manager:AI绘画工作流节点管理的终极解决方案

ComfyUI Manager&#xff1a;AI绘画工作流节点管理的终极解决方案 【免费下载链接】ComfyUI-Manager ComfyUI-Manager is an extension designed to enhance the usability of ComfyUI. It offers management functions to install, remove, disable, and enable various custo…

作者头像 李华