Swift 中重要特性——逃逸闭包@escaping

@escaping 是 Swift 中一个非常重要的特性,通常用于闭包(closure)的参数,尤其是在处理异步操作或回调时。它用于标记闭包参数"逃逸"出了函数的作用域,即闭包的生命周期超出了函数的执行范围。

为什么需要 @escaping

在 Swift 中,闭包默认是非逃逸(non-escaping)的,也就是说,闭包只能在函数调用过程中执行,并且不会保存到外部的变量或常量中。这样做的目的是提高性能,因为闭包不需要被保持,编译器可以进行优化。

然而,在处理异步操作时,比如网络请求或定时器,我们需要将闭包传递出去,让它在函数执行完毕后(甚至在函数退出后)继续执行。这时,闭包需要逃逸 出函数的作用域,这时就需要使用 @escaping 来显式标记这个闭包参数。

@escaping 的作用

@escaping 表示闭包会逃逸出函数的作用域,可以在函数返回后被执行。这通常用于处理异步回调或者其他延迟执行的场景。

关键点

  • 非逃逸闭包(non-escaping closure):闭包只能在函数内部执行,并且会在函数返回前执行完毕。默认情况下,函数的闭包参数是非逃逸的。
  • 逃逸闭包(escaping closure):闭包可以在函数返回之后仍然执行,通常用于异步回调。

例子:非逃逸闭包

如果没有使用 @escaping,闭包是非逃逸的,不能存储到函数外部。

swift 复制代码
func performTask(task: () -> Void) {
    task()  // 这里闭包被执行并且在函数内完成
}

例子:逃逸闭包

当闭包需要在函数执行完毕后仍然执行,通常会标记为 @escaping。最典型的例子是异步操作,例如网络请求或定时器。

swift 复制代码
func fetchData(completion: @escaping (Data?) -> Void) {
    DispatchQueue.global().async {
        // 模拟网络请求
        let data = Data()
        completion(data)  // 闭包会在函数返回后执行
    }
}

在上面的例子中,completion 闭包会逃逸出 fetchData 函数,因为它是在一个异步线程中执行的,函数返回后闭包才会被调用。

逃逸闭包与内存管理

由于逃逸闭包的生命周期可能超过函数的执行时间,它可能会导致内存管理问题。逃逸闭包会被持有到函数执行完成后,因此需要特别小心避免强引用循环(retain cycles)。

通常,为了避免强引用循环,我们会将闭包声明为 weakunowned,从而防止闭包持有对象的强引用。

使用 weakunowned 避免循环引用

swift 复制代码
func fetchData(completion: @escaping (Data?) -> Void) {
    DispatchQueue.global().async { [weak self] in
        // 使用 weak 或 unowned 防止循环引用
        guard let self = self else { return }
        let data = Data()
        completion(data)
    }
}

使用 @escaping 的实际场景

@escaping 主要用于异步操作或回调函数,它的作用是使闭包可以在函数执行完毕后,甚至在函数返回后继续执行。

  1. 网络请求回调:在网络请求成功或失败后执行回调操作。
  2. 定时器回调:在定时器触发时执行闭包操作。
  3. UI 更新回调:例如,在多线程中更新 UI,闭包可能需要在主线程执行。

总结

  • @escaping 标记闭包为逃逸闭包,即它可能在函数返回后被调用。
  • 逃逸闭包通常用于处理异步操作、回调等情况。
  • 逃逸闭包的生命周期可能会超过函数的作用域,因此需要注意内存管理,避免出现强引用循环。
相关推荐
子兮曰1 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰1 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万1 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝1 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋1 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁2 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
汉堡大王95272 天前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端
梦想很大很大2 天前
从运行事实到回归证据:Workrun 的 Telemetry 与 Evaluation 实践
前端·人工智能·后端
计算机魔术师2 天前
Meta Muse agent 接入 Shopify 的 Shop Pay 实现代理式购物
前端
沙蒿同学2 天前
我用 Go 搭了一条 AI Agent 流水线:从 1 张商品图到一整套淘宝详情页
前端·javascript·后端