You need to enable JavaScript to run this app. Swift @escaping逃逸闭包与completed回调执行逻辑疑问
优惠活动
大模型
产品
解决方案
定价
更多

Swift @escaping逃逸闭包与completed回调执行逻辑疑问

关于@escaping修饰符的作用

你之前的理解有一定偏差,@escaping的核心作用是标记当前闭包的生命周期超过当前函数的执行周期:

  • Swift默认函数的闭包参数为非逃逸类型,函数执行结束后闭包就会被系统释放,不允许在函数外部、函数执行结束后调用。
  • 结合你提供的代码来看,downloadJSON内部的URLSession.shared.dataTask是异步网络请求,调用resume()之后downloadJSON函数就会直接执行结束返回,而网络请求、JSON解码的逻辑是在后台线程异步执行的,要等请求返回、解码完成之后才会调用你传入的completed闭包,此时downloadJSON函数已经执行完毕很久了,所以必须用@escaping标记completed闭包允许它“逃出”函数的生命周期,否则编译器会直接报错。
  • 你可以做个测试:在调用downloadJSON的下一行加一行打印日志,会发现这行日志的打印时间远早于completed闭包内部的代码执行时间,也能验证闭包是在函数执行结束后才触发的。

关于DispatchQueue.main.async中调用completed()的作用

这是为了保证completed闭包的所有代码都在主线程执行,符合iOS的UI操作规则:

  • URLSession的dataTask回调默认是在后台子线程执行的,目的是避免网络请求、JSON解码这类耗时操作阻塞主线程导致界面卡顿。
  • 而你传入的completed闭包内部执行的是self.textViewScroll.text = ...的UI更新操作,iOS明确要求所有UI相关的操作必须在主线程执行,否则会出现UI更新延迟、界面错乱甚至直接崩溃的问题,控制台也会弹出紫色的线程安全警告。
  • 这里用DispatchQueue.main.async把completed()的执行调度到主线程队列,就能保证你闭包里的UI更新逻辑是合规的,不会出现线程问题。

内容的提问来源于stack exchange,提问作者Vasile

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.21 06:45:03