Swift 4中URLSession.downloadTask内存循环解决及Web实例未释放排查
排查Web()实例未释放的常见原因
嘿,这个问题我踩过好多次坑了!虽然你的ViewController能正常触发deinit,但Web实例没被释放,90%的概率是循环引用或者未清理的全局持有导致的,咱们一步步捋:
1. Web类内部的闭包循环引用
最常见的情况是Web类里的网络请求(比如URLSession的dataTask)回调中,没有正确处理对self的引用,导致URLSession的任务和Web实例互相强持有,形成循环。
举个错误的示例:
class Web { func downloadImage(from url: URL) { URLSession.shared.dataTask(with: url) { data, response, error in // 这里的self是强引用,URLSession的任务会持有闭包,闭包又持有Web实例 self.handleDownloadedData(data) }.resume() } }
解决办法是在闭包捕获列表里加上[weak self](如果确定Web实例不会提前释放,也可以用[unowned self]):
URLSession.shared.dataTask(with: url) { [weak self] data, response, error in guard let self = self else { return } // 可选绑定避免多次写self? self.handleDownloadedData(data) }.resume()
2. ViewController传递给Web的闭包捕获了强引用
虽然你说没给ViewController传强引用,但如果在创建Web实例时,你给它传了一个回调闭包,而闭包里捕获了ViewController的self却没加弱引用,也会导致Web实例被ViewController间接持有(如果Web实例同时持有这个闭包,就会形成循环引用)。
错误写法示例:
// ViewController中的按钮点击方法 @IBAction func downloadButtonTapped(_ sender: UIButton) { let web = Web() web.downloadImage(from: imageURL) { [self] image in // 这里用[self]强引用了ViewController,Web实例持有闭包就会间接持有VC self.imageView.image = image } }
修正成弱引用捕获:
web.downloadImage(from: imageURL) { [weak self] image in self?.imageView.image = image }
3. Web实例被全局对象强持有了
如果Web类里做了这些操作,也会导致它无法被释放:
- 注册了通知但没在deinit中移除观察者
- 加入了全局的数组、字典等容器
- 启动了定时器但没停止
比如注册通知的错误写法:
class Web { init() { NotificationCenter.default.addObserver(self, selector: #selector(handleNotification), name: .someGlobalNotification, object: nil) } }
需要在Web类的deinit中清理:
deinit { NotificationCenter.default.removeObserver(self) // 如果有定时器,也要在这里调用invalidate() }
快速排查技巧
你可以给Web类加一个deinit方法,打印日志确认它是否被调用:
class Web { deinit { print("Web instance deinitialized!") } }
如果这个日志没打印,就对照上面的三点逐一排查,大概率能找到问题~
内容的提问来源于stack exchange,提问作者Ahmad
相关产品推荐
相关产品推荐

