Closed MoLice closed 2 years ago
我也遇到了这个问题,我屏蔽了[在后台切换主题]的代码之后就没有用户反馈过了。不知是不是同样的问题。
连接真机,通过系统控制台可得知在出现 bug 现象时,第三方输入法会收到内存警告,在没使用 QMUI 的 App 之间互相切换不会引起这个内存警告,证明是 QMUI 里的某些代码引起该问题。
自己创建一个 Custom Keyboard Extension,按照 issue 的重现步骤操作后,我们的输入法确实遇到了系统内存限制的报错(上限 66MB,不同的 iOS 版本、iPhone 设备,该上限不同,所以老旧机器更容易触发该现象)。
在不断重复的测试过程中可看到,宿主应用(Host App,也即当前正在使用键盘的 App)更新 UIAppearance 的行为会通信给第三方键盘:
进而对 UIAppearance 中 UIImage 类型的对象进行反序列化:
通过在第三方键盘 Target 里 hook -[UIImage initWithCoder:]
可知在 Host App 退到后台时,第三方键盘里进行了上千次的图片生成操作,而且每次都比上一次的次数要翻很多倍。
而且按地址过滤后可看到其中有大量重复的 image 操作,这现象明显是异常的。
以 QMUI Demo 为例,通过这些图片的 size
(width = 4, height = 88) 可知是 UINavigationBar
的 backgroundImage
、shadowImage
等图片,也即业务 App 自己设置的图片。而 QMUI Demo 里是在配置表里设置这些图片的,当系统的 DarkMode 发生切换时,会重新应用配置表,将图片设置到 UINavigationBar.appearance 的同时,遍历当前界面里的所有 UINavigationBar 实例,修改它们的样式。
结合上述的 crash 堆栈,尝试将上面截图里的①注释掉,发现问题得到解决。
将 backgroundImage 以 UINavigationBar.appearance 的方式使用是一个常规的操作,本身应该没问题,而问题出现在切换 App 时,此时 QMUI 会重新应用配置表,再走一次 UIAppearance 的更新,猜测可能是时机问题引起的。于是新建一个空项目,在 - [UIViewController traitCollectionDidChange:]
里设置 UIAppearance
,当切换 App 时,系统会将 App 快速切换到 Light、Dark,分别对 App 截两张图,此时会触发 traitCollectionDidChange:
,进而触发 UIAppearance
的更新,然后成功重现该问题。点击下载测试 Demo
到这里该 Bug 可以定性为系统 Bug:在 App 处于后台时修改 UIAppearance 里的 UIImage 类型的属性,会导致第三方键盘在短时间内异常进行大量 UIImage 的反序列化操作,最终触达系统对输入法分配的内存上限,输入法 crash。
进一步测试可知几个信息点:
至此该系统 Bug 的解决方式就很清晰了:在设置 UIAppearance 前判断当前 App 是否在后台(建议监听 UIApplicationDidFinishLaunchingNotification、UIApplicationWillEnterForegroundNotification 等通知,而非判断 UIApplicationState,因为当 App 启动时走到某些类的 +load 方法时,UIApplicationState 为 Active,但走到 AppDelegate 的 didFinishLaunching 时又变成 Inactive 了),是则不设置。 由于该问题没有合适的统一修复的方法,因此 QMUI 仅修复自身的场景,不提供通用的方案。业务项目里非 QMUI 的代码如果也有这种操作,也需自行判断。
Bug 表现
预期的表现 不管进行多少次 App 间的切换,都不应该导致第三方输入法频繁被杀掉。
其他信息