Swift的逃逸闭包
@escaping (SCCMDResult) -> Void 逃逸闭包的作用是
@escaping (SCCMDResult) -> Void 中的 @escaping(逃逸闭包) 在 Swift 中非常重要,它表示这个闭包可能会在函数返回后被调用。
基本概念
非逃逸闭包(默认)
swift
func executeCommand(completion: (SCCMDResult) -> Void) {// 闭包在函数结束前被调用completion(.success)// 函数返回
}逃逸闭包
swift
func executeCommand(completion: @escaping (SCCMDResult) -> Void) {// 将闭包存储起来,稍后调用self.completionHandler = completion// 函数返回,但闭包可能还没有被调用!
}在你的代码中的应用
在你之前的 ViewModel 代码中,可以看到逃逸闭包的使用:
swift
// 这些方法都使用了 @escaping 闭包
SCLibertyCMDSendManager().setEasychatInfoTest(data: data) { _, _ in// 这个闭包是逃逸的,因为蓝牙操作是异步的
}SCLibertyCMDSendManager().getEasychatInfoTest { [weak self] result, data in// 这个闭包会在网络/蓝牙请求完成后被调用// 此时 init() 函数可能已经返回很久了
}SCLibertyCMDSendManager().getEasychatStatusTest { result, data in// 同样,这个闭包会逃逸
}为什么需要 @escaping
1. 异步操作
蓝牙命令、网络请求、定时器等都需要逃逸闭包:
swift
class BluetoothManager {private var completionHandlers: [((SCCMDResult) -> Void)] = []func sendCommand(completion: @escaping (SCCMDResult) -> Void) {// 存储闭包,等待蓝牙响应completionHandlers.append(completion)// 发送蓝牙命令(异步)sendBluetoothData { response in// 在未来的某个时间点回调let handler = self.completionHandlers.removeFirst()handler(response)}}
}2. 存储闭包
在你的防抖代码中:
swift
private let emergencyDebounce = SCDebounce(interval: 0.5)func setTimeCMD() {let data = Data([...])emergencyDebounce.execute {// 这个闭包被 SCDebounce 存储起来,在 0.5 秒后执行SCLibertyCMDSendManager().setEasychatInfoTest(data: data) { _, _ in// 这也是一个逃逸闭包}}// 函数立即返回,但闭包还在等待执行
}3. DispatchQueue 异步执行
swift
func delayedExecution(completion: @escaping (SCCMDResult) -> Void) {DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {// 1秒后执行,此时函数早已返回completion(.success)}
}内存管理注意事项
由于逃逸闭包会延长生命周期,需要特别注意循环引用:
❌ 错误写法(循环引用)
swift
class MyViewModel {var result: SCCMDResult?func fetchData() {SCLibertyCMDSendManager().getEasychatInfoTest { result, data in// 闭包强引用了 selfself.result = result // 导致循环引用!}}
}✅ 正确写法(使用 weak self)
swift
class MyViewModel {var result: SCCMDResult?func fetchData() {SCLibertyCMDSendManager().getEasychatInfoTest { [weak self] result, data in// 使用弱引用self?.result = result // 不会导致循环引用}}
}实际场景分析
在你的代码中:
swift
private func getInfoCMD() {SCLibertyCMDSendManager().getEasychatInfoTest { [weak self] result, data in// 这个 @escaping 闭包在:// 1. 蓝牙命令发送后// 2. 设备响应数据时(可能是几毫秒或几秒后)// 3. 被调用,此时 getInfoCMD() 函数早已结束guard let self = self else { return }if result == .success {// 处理数据...}self.isLoading = false // 更新UI状态}
}总结
@escaping 闭包的作用:
允许异步回调 - 在函数返回后执行
支持闭包存储 - 可以保存在属性或数组中
启用延迟执行 - 通过 DispatchQueue 或定时器
需要内存管理 - 必须注意
[weak self]避免循环引用
在你的蓝牙命令场景中,@escaping 是必需的,因为蓝牙通信是异步的,响应会在未来的某个时间点到达,而不是在函数执行期间。
