排查内存泄漏时,从参数传入闭包的函数是否会被强捕获?
闭包捕获函数入参回调的问题解答
核心捕获规则
Swift 闭包的捕获逻辑和值的来源完全无关,只看两个判断维度:
- 只要值在闭包的执行逻辑内被引用,且没有被显式列在捕获列表里做弱/无主修饰,就一定会被闭包以强引用的方式持有
- 被闭包捕获的值会脱离函数执行栈的生命周期,和闭包本身的存在周期完全绑定,哪怕创建这个值的函数已经返回,也不会被提前销毁
对应代码场景的具体分析
你在TODO注释位置创建的process.onFailed闭包:
process.onFailed { [unowned self, unowned operation] failed in onProcessFailed(operation, failed, title, isProgress, isFailedDialog, onInternetFailed, onSuccess) }
- 你当前的捕获列表只做了
self和operation的无主修饰,没有覆盖逻辑里引用的其他值:包括作为参数传入的onInternetFailed、onSuccess,以及title、isProgress、isFailedDialog这些参数,这些值会被这个onFailed闭包强捕获。 - 同样的逻辑也适用于代码里另一处
onSuccess.notNil对应的闭包:这个闭包在执行逻辑里用到了作为入参的onSuccess,就算捕获列表里只修饰了process,onSuccess本身也会被这个闭包强持有。
泄漏风险的判断逻辑
不是所有的强捕获都会引发内存泄漏,只要引用链没有形成闭环,就不会有实际问题:
- 如果传入的
onInternetFailed、onSuccess本身没有强引用绑定到持有process、持有当前类实例self的对象,就算被闭包强持有,也会在闭包被释放的时候同步被销毁,不会造成泄漏 - 如果传入的两个回调本身携带了对
self或者operation/process的强引用,引用链就会形成:process强持有闭包 → 闭包强持有传入的回调 → 回调强持有self→self持有operation/process,最终形成循环引用,导致内存无法释放
代码优化建议
- 不要在封装层(也就是这个
send方法的内部)对传入的回调做捕获修饰,因为你无法预知外部传入的闭包内部的引用逻辑,更合理的做法是要求所有调用方在传入闭包的时候,自行给闭包添加捕获列表,避免闭包本身造成的循环引用 - 你当前给
self、operation、progress做的无主捕获是合理的,但要注意无主修饰的前提是被修饰的对象在闭包存在的周期内一定不会被提前释放,否则会引发野指针崩溃
常见认知误区:很多开发者误以为函数参数是栈上的临时值,函数返回后就会自动销毁,不会被闭包长期持有。实际上一旦值被闭包捕获,这些值会被自动移动到堆上,由闭包持有引用计数,和栈的生命周期完全无关。
内容的提问来源于stack exchange,提问作者Renetik
相关产品推荐
相关产品推荐

