在页面间跳转的性能优化(一)中介绍了一些基础知识,讲述了情形一与情形二的优化方式及原理,但有许多人对情形二最后两种处理
方式的原理表示不理解,不清楚处理过程,接下来会详细分步地讲述这两种方式的原理,如果你还没看过页面间跳转的性能优化
(一),请先阅读。
点击下载Demo,或https://github.com/IOSDelpan/SmoothTransitionDemo。
页面间的跳转大致分为几个任务:1.生成将即显示的页面视图;2.生成我们所需要的UI元素;3.生成页面跳转的动画;而这几个任务
是在同一次Loop中执行的。我们知道每一次Loop都会检测图层树是否有更新,若图层树有更新,RunLoop会在观察者的回调函数
_ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv()执行完成时,发送图层树的更新到渲染服务进程进
行绘制渲染,如果一次Loop的时间过长,这将会使图层树的更新延迟,这也就是我们所说的屏幕卡顿(CPU层面的卡顿)。为了解决
页面跳转延迟,我们把原本在一次Loop中所需要执行的任务进行分解,分解成几次Loop来执行,这样就可以既不影响App的流畅
度,也不影响UI的更新。
在Demo中,我们用GCD的方式来实现“在RunLoop下一次循环加载UI”。
调用dispatch_async()函数把生成UI元素的任务[self loadAllLabels]提交到GCD的主队列,在Application的主线程RunLoop进入下一
次Loop时,会执行GCD主队列里面的任务,整个页面跳转的过程,即两次Loop的工作如下。
在第一次Loop中,把耗时的任务[self loadAllLabels]提交到Main Queue,生成即将显示的页面视图和页面跳转的过渡动画并发送到
渲染服务进程进行绘制渲染,与此同时,由于Main Queue有任务待处理,GCD发送消息mach_msg()到Mach Message Server,目
标端口为Application Main RunLoop的dispatch Port。
由于有端口事件待处理,RunLoop被唤醒并进入下一次Loop,RunLoop通过发送dispatch Port到Mach Message Server来接收
dispatch Port的消息,当RunLoop接收到dispatch Port消息后,获取Main Queue待处理的任务[self loadAllLabels]并处理,处理完成
后,把图层树的更新发送到渲染服务进程进行绘制渲染。
定时器处理方式的原理跟“在RunLoop下一次循环加载UI”的原理大致相同,但Loop的次数更多。
Main RunLoop的端口事件源基本分为三类,GCD事件,定时源事件,输入源事件(Source1),而这三类事件分别对应着三个不同的
端口,dispatch Port,Timer Port和Source Port。每次Loop都会有两次检测是否有端口事件需要处理的机会,但是一次Loop只有一
次机会处理端口事件,即在步骤5或步骤7触发处理端口事件。RunLoop在纯粹处理dispatch Port事件或Timer Port事件时,可以完整
地运行一次RunLoop从被唤醒到进入休眠,即从步骤8返回到步骤7(顺序8,9,2,3,4,5,6,7),所以,可以用GCD异步嵌套的方式来实现
跟定时器相同的效果。
当Main RunLoop处理dispatch Port事件时,会获取Main Queue的所有待处理任务并处理,需要注意的是以下两种方式的实际执行过
程是不一样的。
方式一是一次提交一个任务到Main Queue,即一次Loop处理一个任务,而方式二是一次提交三个任务到Main Queue,即一次处理
完三个任务。
所以,方式二跟以下这种方式是一样的。
以上便是“在RunLoop下一次循环加载UI”处理方式的实现原理。
情形三
看到Gif图是否有种似曾相识的感觉?对头,这一情形是最普遍存在的,存在于大部份App当中,其中还不乏一些大厂出品的App(对
此个人是比较好奇的,可能是临时工写的,作为天朝最基层的子民,我完全可以接受这个解释