大家对iOS代码中的循环引用导致内存泄漏都很了解,本博试图进一步完善和补充这一主题,谈谈间接循环引用造成泄漏的问题。
直接内存泄漏
直接内存泄漏是显而易见的,你不发现都难,举个例子:
class RootView:UIView{
var timeCounterView:TimeCounterView?
func pauseOrGoOnTimer() {...}
func resetTimer() {...}
}
class TimeCounterView:UIView{
var timerDidModifiedCallback:((TimerModfiyAction)->Void)?
}
现在RootView里有一个initView()方法,用来初始化,里面需要设置timeCounterView的回调:
func initView(){
timeCounterView = CounterView.create(with: cu)
if let timeCV = timeCounterView{
timeCV.timerDidModifiedCallback = {action in
self.otherSetupInvoke()
switch action{
case .puaseOrGoOn:
// 当计时暂停或继续时,执行对应的操作
timeCV.pauseOrGoOnTimer()
case .reset:
// 当计时取消或完成时重置计时器
timeCV.resetTimer()
}
}
}
}
首先,编译器不会让你过的!因为你在可逃脱闭包中引用了self!
什么叫可逃脱闭包?就是你不知道实际什么时候调用这个闭包!
所以上述代码需要修改:
func initView(){
timeCounterView = CounterView.create(with: cu)
if let timeCV = timeCounterView{
timeCV.timerDidModifiedCallback = {[weak self] action in
guard self != nil else {return}
self?.otherSetupInvoke()
switch action{
case .puaseOrGoOn:
// 当计时暂停或继续时,执行对应的操作
timeCV.pauseOrGoOnTimer()
case .reset:
// 当计时取消或完成时重置计时器
timeCV.resetTimer()
}
}
}
}
当然你也可以使用unowned self,不过从实际使用来看,使用weak的效果更好一些!因为一旦使用unowned self,你的代码会立即崩掉,但如果使用optional self,你还有机会活下来!
间接内存泄漏
但是先等一下,上面修改后的代码没问题么???
并不是!!!
泄漏依旧发生!!!
怎么回事呢?
故事还得从RootView的实例变量timeCounterView说起,如果你在RootView中引用它,铁定循环引用,但是我们引用的不是timeCV么!?
但timeCV和timeCounterView实际是一码事啊!!!
不管你中间经过多少层,你要记住,只要最终你引用timeCounterView,那么就是循环引用!!!
所以,我们还得修改上面的代码:
func initView(){
timeCounterView = CounterView.create(with: cu)
if let timeCV = timeCounterView{
timeCV.timerDidModifiedCallback = {[weak self,weak timeCV] action in
guard self != nil else {return}
self?.otherSetupInvoke()
switch action{
case .puaseOrGoOn:
// 当计时暂停或继续时,执行对应的操作
timeCV?.pauseOrGoOnTimer()
case .reset:
// 当计时取消或完成时重置计时器
timeCV?.resetTimer()
}
}
}
}
这样可以确保没有任何循环引用导致的泄漏了!
总结
寻找循环引用有时候需要擦亮眼睛,也不是那么轻松呢.
有机会再和大家讨论一下其他情况下的内存泄漏吧,下次再会啦 ?