You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS自定义键盘扩展内存泄漏求助:Persistent ViewController对象递增

我之前也碰到过一模一样的问题!结合你用Instruments排查的情况,这个Persistent ViewController对象递增的问题,其实是iOS自定义键盘扩展里挺常见的内存泄漏场景,我来给你捋捋原因和实操解决办法:

问题根源分析

自定义键盘扩展的生命周期和宿主应用深度绑定,每次键盘隐藏再显示,系统会重新触发键盘的初始化流程。但如果你的UIInputViewController(也就是键盘的主控制器)被强引用循环或者外部强引用持有,ARC就无法正常回收旧的控制器实例,导致每次操作都会新增一个无法释放的对象,最终造成内存堆积。而且真机的内存管理策略比模拟器严格得多,所以这个问题只会在真机上暴露出来。

具体解决方案

1. 排查并修复强引用循环

这是最常见的原因,重点检查这几个地方:

  • 键盘控制器里的闭包有没有用[weak self]或者[unowned self]?如果闭包捕获了self但没做弱引用,很容易形成循环引用。
  • 有没有把键盘控制器实例赋值给全局变量、单例的强引用属性?比如某些工具类里存了static var currentKeyboardVC: UIInputViewController?,这种写法直接就把控制器死死持有了。
  • 子视图或子控制器有没有被父控制器以外的对象强引用?比如某个自定义视图里存了var parentVC: UIInputViewController,而父控制器又持有这个视图,就会形成循环。

2. 在生命周期方法里主动清理资源

键盘控制器的viewWillDisappear:、didReceiveMemoryWarning:是清理资源的关键节点,一定要在这里做主动释放:

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    
    // 清空所有闭包引用,切断循环
    self.someAsyncCompletion = nil
    
    // 销毁定时器、观察者
    self.keyboardTimer?.invalidate()
    self.keyboardTimer = nil
    NotificationCenter.default.removeObserver(self)
    
    // 清理子控制器和子视图的强引用
    self.childViewControllers.forEach {
        $0.willMove(toParent: nil)
        $0.view.removeFromSuperview()
        $0.removeFromParent()
    }
}

3. 不要强行持有键盘控制器实例

别试图把键盘控制器做成“单例”或者持久化存储,系统会根据键盘的激活状态自动管理控制器的创建和销毁。强行持有旧实例只会让它们无法被ARC回收,进而导致对象数量递增。

4. 用Instruments精准定位泄漏点

你已经用了Allocations工具,接下来可以用Leaks工具进一步深挖:

  • 打开Instruments,选择Leaks模板
  • 运行键盘扩展,多次切换应用、隐藏/显示键盘
  • 点击工具栏的「Mark Generation」按钮,对比每次生成的对象差异
  • 找到Persistent ViewController的引用链,就能清楚看到是谁在持有它不释放
额外提醒

真机上的内存阈值比模拟器低很多,如果泄漏问题不解决,长时间使用后键盘扩展很可能会被系统强制杀掉,影响用户体验。一定要在真机上做完整的内存测试,不要只依赖模拟器。

内容的提问来源于stack exchange,提问作者Oak Soe Shein

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 03:23:43