ISSUE-004
CPU占用异常偏高
常见表现
CPU占用异常偏高,电脑发热厉害。
此问题通常会在Unity游戏和使用了重力感应功能(摇一摇)的App中出现。
相关源码
UIKitMacHelper.framework
Mac运行iOS应用时,CMMotionManager的底层实现为UINSVirtualMotionDevice。
void -[UINSVirtualMotionDevice startAccelerometerUpdates]
{
if (!_accelerometerDispatchIsActive)
{
dispatch_resume(_accelerometerDispatchSource);
_accelerometerDispatchIsActive = YES;
}
}
id -[UINSVirtualMotionDevice accelerometerData]
{
if (!_accelerometerDispatchIsActive)
{
dispatch_resume(_accelerometerDispatchSource);
_accelerometerDispatchIsActive = YES;
}
return _accelerometerData;
}
Unity引擎 iPhone_Sensors.mm
extern "C" void UnityCoreMotionStart()
{
if (sMotionManager.accelerometerAvailable)
{
const int frequency = UnityGetAccelerometerFrequency();
if (frequency > 0)
{
sMotionManager.accelerometerUpdateInterval = 1.0f / frequency;
[sMotionManager startAccelerometerUpdates];
}
}
}
extern "C" void UnityUpdateAccelerometerData()
{
CMAccelerometerData* data = sMotionManager.accelerometerData;
if (data != nil)
{
Vector3f res = UnityReorientVector3(data.acceleration.x, data.acceleration.y, data.acceleration.z);
UnityDidAccelerate(res.x, res.y, res.z, data.timestamp);
}
}
原因分析
第一个问题:CMMotionManager的默认更新间隔为0秒,如果不修改更新间隔,直接调用startAccelerometerUpdates就会导致频繁更新,从而占用大量CPU时间。
第二个问题:访问CMMotionManager的accelerometerData数据时,会自动触发startAccelerometerUpdates,也就是说这个接口有副作用。
而Unity引擎在某种情况下,实例化了CMMotionManager,没有修改更新间隔,也没有主动调用startAccelerometerUpdates。
但是因为之后读取了accelerometerData,间接触发了startAccelerometerUpdates,从而导致更新间隔0秒的频繁更新。
解决方法
玩家方面:使用PlayCover Nightly版,右键游戏图标 - 设置 - 杂项设置,启用“limitMotionUpdateFrequency”选项。
开发者方面:调整默认的更新间隔,避免使用0秒间隔。
extern "C" void UnityCoreMotionStart()
{
//...
bool initMotionManager = (sMotionManager == nil);
if (initMotionManager) {
sMotionManager = [[CMMotionManager alloc] init];
sMotionManager.accelerometerUpdateInterval = 0.01;
sMotionManager.deviceMotionUpdateInterval = 0.01;
sMotionManager.gyroUpdateInterval = 0.01;
}
//...
}