You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

异步网络请求的逃逸闭包中,是否应使用weak self捕获?

关于逃逸闭包中self的捕获:weak还是strong?

先直接给结论:当前你的场景下不会形成循环引用,但从代码健壮性和最佳实践出发,推荐使用weak self。下面来一步步拆解原因:

1. 为什么你测试中ViewModel的deinit能正常调用?

你担心的循环引用链是:ViewModel → service → success闭包 → ViewModel,但这个链要形成内存泄漏的前提是所有引用都是强引用,且这个链不会被自动打破。

实际你的fetchProducts方法应该是这样的实现逻辑:发起网络请求时,把闭包暂存起来,当请求完成(成功/失败)后,立刻执行闭包并将其释放。也就是说,service对闭包的持有是临时的——请求结束后,闭包就被销毁了,这条引用链也就断了。所以当持有ViewModel的ViewController被销毁时,ViewModel的引用计数能降到0,deinit自然会被调用,不会有内存泄漏。

2. 什么时候需要用weak self?

只有当service会长期持有这个success闭包时(比如把闭包存在全局数组、自己的存储属性里,即使请求完成也不释放),才会形成永久的强引用闭环,这时候必须用weak self来打破循环。

但问题是:你现在的service实现是临时持有,但未来如果有人修改service的逻辑(比如加了闭包缓存机制),那原本没问题的代码就会突然出现内存泄漏。所以从代码健壮性考虑,提前用weak self是更稳妥的做法。

另外还有一种极端场景:如果网络请求耗时非常久,而用户在请求完成前就退出了ViewController(ViewModel被标记为可销毁),这时候如果用strong self,闭包会强持有ViewModel,导致它无法及时释放,直到请求完成——虽然这不是永久泄漏,但会延迟内存释放,也是不推荐的。

3. 正确的写法示例

推荐使用weak self配合guard let来确保安全:

service.fetchProducts(parameters: params, success: { [weak self] response in
    guard let self = self else {
        // 这里可以做一些清理,比如忽略过期的请求结果
        return
    }
    self.isLoading?(false)
    // 处理响应数据
})

如果你能100%确定ViewModel在闭包执行时一定存在(比如请求发起后ViewModel不会被销毁),也可以用unowned self,但weak self的容错性更强,是更通用的选择。

总结

  • 当前你的代码没有循环引用,是因为service对闭包的持有是临时的,请求完成后引用链就断了;
  • 但为了避免未来代码变更带来的潜在问题,以及处理极端场景下的延迟释放,推荐始终在逃逸闭包中使用weak self捕获ViewModel;
  • 循环引用的核心是「永久的强引用闭环」,临时的引用链不会导致内存泄漏,但提前做好防御性编码总是没错的。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 06:52:46