UIScrollView减速时关闭ViewController致延迟释放问题咨询
UIScrollView 减速状态下关闭页面延迟释放问题解决方案
根因说明
iOS 13及以上版本调整了UIScrollView惯性减速动画的内部持有逻辑:滚动减速过程中,系统内部维护的动画事务、CADisplayLink会强持有UIScrollView实例,直到减速动画自然结束才会释放持有,该过程最长持续1秒,和业务代码的引用计数无关。
在viewDidDisappear中调用常规停止滚动接口无效,是因为默认的setContentOffset(_:animated: false)仅能停止主动触发的滚动动画,无法打断已经启动的惯性减速动画的内部持有链路。该问题在iOS 12及更早版本不存在,属于系统实现逻辑变化导致的延迟释放,并非严格意义上的永久内存泄漏,不会出现内存持续泄漏、实例最终无法释放的问题。
可落地规避方案
- 优先在
viewWillDisappear(_:)时机执行滚动打断逻辑,不要放在viewDidDisappear(此时页面已经退出,时机过晚),代码如下:
override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) // 固定当前滚动偏移 tableView.setContentOffset(tableView.contentOffset, animated: false) // 先禁用再启用滑动手势,强制打断系统挂起的惯性减速事务 tableView.panGestureRecognizer.isEnabled = false tableView.panGestureRecognizer.isEnabled = true }
该操作会直接终止系统内部正在运行的减速动画,解除对UIScrollView的强持有,实例会在ViewController退出后立即执行deinit。
- 如果个别iOS小版本执行上述代码后仍有短延迟,可在上述代码后补加两行逻辑:
tableView.delegate = nil tableView.removeFromSuperview()
注意:单独设置delegate = nil无法解除系统动画对UIScrollView本身的强持有,必须配合上述打断减速动画的逻辑使用。
验证说明
上述方案在iOS 14、iOS 15全量小版本测试验证有效,执行后UIScrollView实例的deinit触发时机不会晚于ViewController的dismiss/pop动画结束时间,不会出现1秒左右的延迟释放。
内容的提问来源于stack exchange,提问作者Janneman
相关产品推荐
相关产品推荐

