小米电池休眠自动解除 手机充不进电7个小妙招( 六 )


通过对上图中 blraa 指令 step in , 我们发现这个 block 实际上是由 UIKitCore 注册的:

找到引用了该符号的 UIKit 的私有方法 __UIUpdateCycleSchedulerStart ,反汇编结果也验证了这点 。

同时发现这个 block 的返回值固定为 0x0 。

而同样的 symbol 在之前的 iOS 版本上并不存在,也就是说这个应该是 iOS 15 的变动 。换安装了 iOS 15 的非 ProMotion 设备,重走上面的逆向流程发现,该设备的 CA::display_link_will_fire_handler 为 nil,未注册:

这里 cbz 执行了跳转 , 说明 x0 为 nil,而 x0 是由 ldr x0, [x8, #0x1c8] 得到 。

可以看到 x0 就是 CA::display_link_will_fire_handler 。继续分析之前找到的私有符号 __UIUpdateCycleSchedulerStart 的相关实现,可以知道这是因为在非 ProMotion 设备上 _UIUpdateCycleEnabled 返回了 NO 导致的 。

在返回 NO 的情况下 __UIUpdateCycleSchedulerStart 方法不会执行,CA::display_link_will_fire_handler 也就不会被注册 。
_UIUpdateCycleEnabled 所带来的变化继续研究 _UIUpdateCycleEnabled 相关的代码,笔者发现这个的改动并不是仅仅影响 DisplayLink 驱动方式那么简单 。
当 _UIUpdateCycleEnabled 返回 YES 时,UIKit 会在 UIApplicationMain 中执行 _UIUpdateCycleSchedulerStart 。分析该函数,发现 _UIUpdateCycleEnabled 启用时会调用 [CATransaction setDisableRunLoopObserverCommits:YES] 。

Core Animation 是绝大部分 iOS 应用的渲染引擎 , 熟悉 iOS 渲染流程的同学想必都知道它的执行也是由 MainRunLoop 驱动 , 大致为:
MainRunLoop 因为用户操作/Timer/GCD 等被唤醒,派发相应的事件/回调回调中应用修改 Layer Tree,触发 setNeedsLayout 或 setNeedsDisplayMainRunLoop 即将完成本次执行 , 在即将休眠前向 Observer 派发 BeforeWaiting 事件BeforeWaiting 中触发 Core Animation 注册的 MainRunLoop Observer,触发事务提交 CA::Transaction::commit():自顶向下触发各种 Layout/Display 等逻辑,更新布局/内容Core Animation 将更新后的 Layer Tree 打包发送给 Render Server5.随后 MainRunLoop 进入休眠
6.Render Server 将打包好的 Layer Tree 解码,生成并提交对应的 draw calls
7.GPU 执行渲染指令 , 渲染出 FrameBuffer , 待后续 VSync 信号来临时上屏展示
上图中 +[CATransaction setDisableRunLoopObserverCommits:YES] 这个调用给了笔者提示 , 让我们验证一下 CA::Transaction::commit() 在 iOS 15 ProMotion 设备上的执行时机 , 会发现确实不再由 BeforeWaiting 事件驱动了:

实际上同样的 Source 0 信号同时也驱动了 CADisplayLink 的回调:

关注这个 Source 0 的回调符号 runloopSourceCallback,会发现这个 Source0 是由 signalChanges 函数驱动:

推荐阅读