主线程刷新UI原理

「这是我参与11月更文挑战的第26天,活动详情查看:2021最后一次更文挑战」。

  • 像UIKit这样大的框架上确保线程安全是一个重大的任务,会带来巨大的成本。UIKit不是线程安全的
    比如:某一个线程中遍历找寻某个subView,然而在另一个线程中删除了该subView,那么就会出现问题
  • 事实上在子线程中如果要对其他UI 进行更新,必须等到该子线程运行结束。而子线程结束了,相当于是又回到了主线程。
  • 在子线程中是不能进行UI 更新的,我们看到的UI更新其实是子线程代码执行完毕了,又自动进入到了主线程,执行了子线程中的UI更新的函数栈,这中间的时间非常的短,就让大家误以为分线程可以更新UI。如果子线程一直在运行,则子线程中的UI更新的函数栈 主线程无法获知,即无法更新。只有极少数的UI能直接进行UI更新,因为开辟线程时会获取当前环境,如点击某个按钮,这个按钮响应的方法是开辟一个子线程,在子线程中对该按钮进行UI 更新是能及时的。

一、出于安全方面考虑

因为UIKit框架不是线程安全的,当多个线程同时操作UI的时候,抢夺资源,导致崩溃,UI异常等问题。假如在两个线程中设置了同一张背景图片,很有可能就会由于背景图片被释放两次,使得程序崩溃。或者某一个线程中遍历找寻某个subView,然而在另一个线程中删除了该subView,那么就会造成错乱。apple有对大部分的绘图方法和诸如UIColor等类改写成线程安全可用,可还是建议将UI操作保证在主线程中。例如说,我们需要在子线程中读取一个image对象,使用接口 [UIImage imageNamed:] ,但 imageNamed: 实际上在 iOS9 以后才是线程安全的, iOS9 之前都需要在主线程获取。所以,我们需要从子线程切换到主线程获取image,然后再切回子线程拿到这个image,这里我们必须使用sync。

二、出于用户的体验

iOS中只有主线程才能立即刷新UI。在子线程中是不能够更新UI,我们看到的子线程能够更新UI的原因是,等到子线程执行完毕,自动进入了主线程去执行子线程中更新UI的代码。由于子线程执行时间非常短暂,让我们误以为子线程可以更新UI。如果子线程一直在运行,则无法更新UI,因为没有办法进入主线程。
主线程中用于显示\刷新UI界面,处理UI事件(比如点击事件、滚动事件、拖拽事件等)。耗时操作会卡住主线程,严重影响UI的流畅度,给用户一种“卡”的坏体验,所以解决办法是:异步开启一条子线程,让耗时操作在子线程中完成,这样又不会影响主线程的任务。当子线程中的任务完成之后,回到主线程刷新UI,显示UI即可。

三、程序一开始运行就进入了主线程

在子线程中,如果要对UI进行更新,只有两种做法:
做法1:等到该子线程运行结束,子线程结束之后系统自动回到主线程新UI
做法2:不用等到该子线程运行结束,我们直接在UI代码的外面写上GCD的异步函数+主队列,就可以实现立即主线程更新UI

猜你喜欢

转载自juejin.im/post/7035597370591035400