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
            // 闭包强引用了 self
            self.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 闭包的作用:

  1. 允许异步回调 - 在函数返回后执行

  2. 支持闭包存储 - 可以保存在属性或数组中

  3. 启用延迟执行 - 通过 DispatchQueue 或定时器

  4. 需要内存管理 - 必须注意 [weak self] 避免循环引用

在你的蓝牙命令场景中,@escaping 是必需的,因为蓝牙通信是异步的,响应会在未来的某个时间点到达,而不是在函数执行期间。

相关推荐
周厉几秒前
ERP系统全景解析:核心模块与业务逻辑框架
java·服务器·前端
开开心心就好39 分钟前
课堂随机点名工具,绿色版免安装双击即用
java·服务器·前端·ffmpeg·音视频·perl·symfony
chu_bingzhi1 小时前
GNU 汇编器
服务器·gnu
北京阿尔泰科技厂家2 小时前
阿尔泰SC5061八串口联网服务器技术解析与工程应用指南
服务器·物联网·串口服务器·串口通讯·远程监测·自动化控制·工业测试测量
小白跃升坊2 小时前
PHP2Shell:AI 两天挖出 9.2 分漏洞,让全球 70% 网站连夜打补丁
php·1panel·ai安全·漏洞披露
骑着蜗牛撵大象3272 小时前
字节序、TLV 到 netlink:Linux 网络消息的编解码全链路
linux·网络·php·etl·序列化·字节序·tlv
tianyuanwo2 小时前
RPM Spec中Name: %{?scl_prefix}xxx中增加宏定义的设计原理
linux·服务器·rpm·scl
cc_瀚海知行2 小时前
工具调用 6|助手什么时候该停下来?
java·服务器·前端·人工智能·python·ai
huaweichenai2 小时前
php搭建mcp服务
开发语言·php·ai编程
张小姐的猫3 小时前
【MySQL】数据类型
android·linux·数据库·c++·mysql·adb·php