iOS间接循环引用造成内存泄漏一例

博客聚焦iOS代码中循环引用引发的内存泄漏问题,先介绍直接内存泄漏,指出可逃脱闭包引用self需修改代码,使用weak效果更佳;接着说明间接内存泄漏,强调即便中间多层引用,最终引用特定实例变量仍会造成循环引用,需再次修改代码避免泄漏。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

大家对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()
             }
		}
	}
}

这样可以确保没有任何循环引用导致的泄漏了!

总结

寻找循环引用有时候需要擦亮眼睛,也不是那么轻松呢.

有机会再和大家讨论一下其他情况下的内存泄漏吧,下次再会啦 ?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大熊猫侯佩

赏点钱让我买杯可乐好吗 ;)

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值