注:本文是笔者向一位大佬请教后凭自己理解整理的,如有不妥欢迎指正。
要说循环引用,先举一个例子,我们有时需要在tableviewCell中添加一个按钮,点击可以跳转到下个页面,和cell本身的点击事件并不相同,这时我们需要用到闭包,在闭包的回调函数中我们要实现页面跳转,需要写以下代码:
// 闭包回调
cell.backClosure = {(str:String)-> Void in
let vc = newViewController()
self.navigationController?.pushViewController(vc, animated: true)
}
这样其实我们就已经进入了一个误区,造成了循环引用,导致内存无法释放。
cell最后持有了self,而它本身又在被self持有,我拿我自己?显然不合理
既然知道了问题所在,那么自然就有解决的办法,我们可以按如下修改代码:
weak var weakSelf = self //避免循环引用
// 闭包回调
cell.backClosure = {(str:String)-> Void in
let vc = newViewController()
weakSelf?.navigationController?.pushViewController(vc, animated: true)
}
这就相当于为self做了一个副本,但self的副本(wakeself)并没有持有和self一样的东西(tableView),我不能拿我自己,但我可以拿我自己的副本。cell用wakeself跳到下个页面,self不再需要做任何事,wakeself结束了它的任务,内存自然也就释放了。
swift中,有两个很关键的函数,init(在初始化时执行)和deinit(在被释放时执行),所以要想检测到底有没有被释放,我们可以在deinit函数中打印日志来观察。
deinit {
print("=== 释放 === \(self.classForCoder) ===")
}