含计时器的UIView类及关联类无法触发deinit问题咨询
问题分析与解决方案
咱们先直接戳中核心:这两个类的deinit无法被调用,本质是内存泄漏导致的,主要来自两个关键错误,咱们一步步拆解修复:
1. 第一个致命错误:layoutSubviews里重复创建实例
layoutSubviews是个会被频繁触发的方法(比如视图尺寸变化、父视图布局更新时都会调用),你现在每次调用都新建一个NextView并赋值给nextView——旧的NextView实例虽然失去了MyView的强引用,但如果它自身还有其他强引用(比如Timer),就会直接变成内存垃圾,永远无法释放。
2. 第二个循环引用陷阱
你在Timer的block里用了[weak self]看似没问题,但NextView的deinit逻辑写反了:你先打印再去invalidate Timer。而Timer被创建后会被RunLoop强引用,只有调用invalidate才能让RunLoop释放它。现在的情况是:
NextView持有Timer,RunLoop持有Timer,导致NextView的引用计数永远降不到0,deinit根本没机会执行;MyView又强引用着NextView,连锁导致MyView也无法被释放。
修复方案
第一步:修正layoutSubviews的创建逻辑
只在nextView为空时创建实例,避免重复创建导致的内存泄漏:
class MyView: UIView { var nextView: NextView? override func layoutSubviews() { super.layoutSubviews() // 仅在nextView未初始化时创建实例 guard nextView == nil else { return } nextView = NextView() nextView!.go() } deinit { print("MyView deinit: call me") } }
第二步:修正NextView的Timer生命周期管理
必须在deinit执行前先让RunLoop释放Timer,同时避免重复创建Timer:
class NextView { var timer: Timer? func go() { // 创建新Timer前先销毁旧的,避免重复调度 timer?.invalidate() timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { [weak self] _ in // 这里添加Timer的具体执行逻辑 }) } deinit { // 先让RunLoop释放Timer,再执行其他操作 timer?.invalidate() timer = nil print("NextView deinit: call me") } }
第三步:确保MyView的引用被正确释放
当你不再需要MyView时,要主动将持有它的变量置为nil(比如在ViewController中):
// 示例:在ViewController中持有MyView var myView: MyView? = MyView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) // 当不需要时释放引用 myView = nil
修复原理
- 修正
layoutSubviews后,避免了重复创建NextView导致的旧实例泄漏; - 提前
invalidateTimer,让RunLoop释放对Timer的强引用,NextView的引用计数能正常降到0,deinit得以执行; NextView正常释放后,MyView的nextView引用也会被释放,MyView的deinit就能正常触发。
内容的提问来源于stack exchange,提问作者NoKey
相关产品推荐
相关产品推荐

