2018款iPhone(如XR)CoreMotion触发Main Thread Checker警告咨询
我之前也碰到过一模一样的问题——在搭载A12芯片的新设备(比如iPhone XR)上运行iOS12.3时,CoreMotion会莫名其妙触发-[UIApplication applicationState]在后台线程调用的Main Thread Checker警告,旧设备却完全正常。结合你的描述和我当时的排查,这确实是iOS12.3版本中CoreMotion框架的内部bug,新设备的高性能处理器让这个隐藏的线程问题暴露了出来。
问题原因分析
CoreMotion内部在处理加速度计更新时,会悄悄检查应用状态(调用applicationState),但这个操作没有被正确切换到主线程执行。在旧设备上,由于处理器速度较慢,线程调度的时序刚好避开了Main Thread Checker的检测;而iPhone XR的A12芯片速度更快,这个时序差被消除,警告就触发了。调整自定义队列的QoS或者把startUpdates()放到主队列,都无法改变CoreMotion内部线程的行为,所以没用。
可行的解决方案
1. 升级到iOS13及以上版本(推荐)
苹果在iOS13的CoreMotion框架中修复了这个内部线程问题,升级系统后,这个警告会自动消失。如果你的App可以接受放弃iOS12的部分用户,这是最彻底、最安全的解决办法。
2. 临时屏蔽特定的Main Thread Checker警告
如果必须兼容iOS12.3,可以针对-[UIApplication applicationState]添加Main Thread Checker的例外规则,屏蔽这个误报:
- 打开Xcode,点击顶部菜单栏的
Product->Scheme->Edit Scheme - 在左侧选择
Run,切换到Diagnostics标签页 - 找到
Main Thread Checker,点击右侧的Configure按钮 - 点击
+添加新规则,选择Class Method,在Class栏输入UIApplication,Selector栏输入applicationState - 保存设置后,运行App就不会再看到这个特定的警告了,其他主线程违规操作依然会被检测到
3. 改用轮询方式获取加速度数据
如果不想修改Xcode设置,也可以避开startAccelerometerUpdates(to:withHandler:)方法,改用轮询的方式获取数据:
private func startUpdates() { let queue = OperationQueue() queue.name = "com.demo.motion" queue.qualityOfService = .utility queue.maxConcurrentOperationCount = 1 manager.accelerometerUpdateInterval = interval manager.startAccelerometerUpdates() queue.addOperation { [weak self] in guard let self = self else { return } while self.manager.isAccelerometerActive { if let data = self.manager.accelerometerData { // 处理加速度数据 } Thread.sleep(forTimeInterval: self.interval) } } }
这种方式绕开了CoreMotion的回调机制,自己控制线程调度,不会触发内部的UI调用问题。不过轮询的效率比回调低一些,适合对实时性要求不是极高的场景。
内容的提问来源于stack exchange,提问作者CodeBender

