iOS 面试 -- UI视图

前言

做 APP 开发与我们联系最紧密的就是 UI 的开发了,你做 APP 开发,不做 UI 恐怕就是你很努力地学习,但是你从来不参加考试,所以在面试中 UI 视图一定会涉及,而具体面试都面啥,主要有以下几个方面,如下图 :

本文主要就是从 UITableView、事件传递、视图响应、图像显示原理、卡顿掉帧、异步绘制和绘制原理以及离屏渲染 6 大块进行相关的讲解。

1. UITableView

重用机制

上图灰色部分就是tableView 中已经出现过得 A1 cell,当滑动不在屏幕显示时,将其放到重用池,当再次划出时从重用池里面取出,这样避免频繁创建,消耗内存和CPU,从而引起卡顿,如果继续向上滑动 A7 就将会从重用池里面取出,这个就是 tableView 的重用机制

数据源同步

数据源同步

数据源同步的问题多出现在新闻、资讯类 App中,对当前显示的内容进行删除操作的同时,进行 loadmore 操作,loadmore 操作时,会将当前的 data 进行 copy,loadmore 是在子线程进行处理,当服务器返回结果时,那么 loadmore 将原来的 data 和现在新的 data 合并一起,返回主线程data,这个时候,删除操作已经完成,而我们再次 reload 的时候,发现删除的数据,这个时候又显示了,这个就是数据源同步的问题。这种问题解决方案有两种

  • 并发访问解决方案

    在主线程执行删除的操作的时候,记录下来要删除的数据,然后等到子线程 loadmore 解析完成,回到主线程,将返回的新 data,再次删除即可,然后 reloadUI 即可

  • 串行访问解决方案

    串行就是,删除操作放到串行队列中,当删除完成以后将数据传到子线程中进行 loadmore 等操作,子线程完成数据的解析将数据传到主线程从而 reloadUI,这个也可以解决,但是会更加耗时,但是却比第一种更加节约内存,具体情况具体分析,牺牲时间换取空间

2. 事件传递与事件响应

UIView和CALayer的关系

  • UIView 为其提供内容,以及负责处理触摸等事件,参与响应链
  • CALayer 负责显示内容的 contents
    深入问答:为什么会这么设计?
    遵循程序设计的原则:\color{red}{单一职责原则}

事件响应流程

事件响应流程

点击屏幕,事件响应的核心其实方法就是

- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;   // recursively calls -pointInside:withEvent:. point is in the receiver's coordinate system
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event;   // default returns YES if point is in bounds

第一个方法返回的是一个UIView,是用来寻找最终哪一个视图来响应这个事件;第二个方法是用来判断某一个点击的位置是否在视图范围内,如果在就返回YES

流程描述
  • 我们点击屏幕产生触摸事件,系统将这个事件加入到一个由UIApplication管理的事件队列中,UIApplication会从消息队列里取事件分发下去,首先传给UIWindow
  • 在UIWindow中就会调用hitTest:withEvent:方法去返回一个最终响应的视图
  • 在hitTest:withEvent:方法中就回去调用pointInside: withEvent:去判断当前点击的point是否在UIWindow范围内,如果是的话,就会去遍历它的子视图来查找最终响应的子视图
  • 遍历的方式是使用倒序的方式来遍历子视图,也就是说最后添加的子视图会最先遍历,在每一个视图中都回去调用它的hitTest:withEvent:方法,可以理解为是一个递归调用
  • 最终会返回一个响应视图,如果返回视图有值,那么这个视图就作为最终响应视图,结束整个事件传递;如果没有值,那么就会将UIWindow作为响应者
hitTest:withEvent: 方法调用
调用流程
  • 首先会判断当前视图的hiden属性、是否可以交互以及透明度是否大于0.01,如果满足条件则进入下一步,否则返回nil
  • 调用pointInside: withEvent:方法来判断这个点是否在当前视图范围内,如果满足条件则进入下一步,否则返回nil
  • 然后以倒序的方式遍历它的子视图,在每个子视图中去调用hitTest:withEvent:方法,如果有一个子视图返回了一个最终的响应视图,那么就将这个视图返回给调用方;如果全部遍历完成都没有找到一个最终的响应视图,因为点击位置在当前视图范围内,就将当前视图作为最终响应视图返回

3. 图像显示原理

图像显示原理 1

图像显示原理则总线连接 CPU 和 GPU 协同工作,CPU 提交位图,GPU进行位图图层渲染和纹理合成,最终将结果,提交到帧缓冲区,然后由帧缓冲区提交到视频控制器,最后显示在屏幕上
在 iOS 开发上, 显示的流程则是创建一个UIView,然后调用CALayer,生成要显示的内容,最后经过 drawRect绘制,提交到 CoreAnimation 生成位图,以上部分则是 CPU 的工作;然后经过 GPU 的 OpenGL管线渲染生成结果,最后显示在屏幕上

CPU 工作

cpu 的主要工作就是 UI 布局,frame 计算、文本计算、绘制、图片编码器、最后提交位图


图像显示原理 2
CPU 1
CPU 2

CPU 3

CPU 4

GPU
GPU

GPU 主要是对 CPU 提供过来的位图顶点着色、图元装配、光栅化、片段着色、片段处理,最后将结果提交到帧缓存区

4. 卡顿&掉帧

卡顿&掉帧
为什么会掉帧和卡顿?

页面滑动一般就是每一秒钟会有 60 帧的画面更新,基于此就是每隔 16.7ms 也就是 1/60就有一帧画面刷新,在这 16.7ms 内就需要 CPU 和 GPU协同工作,产生一帧的数据。如果当CPU消耗的时间过长,或者 GPU 渲染的时间过长,产生一帧的数据耗时超过 16.7ms,那么在下一个 Vsyc 信号到来之前,当前画面没有准备好,那么就会产生掉帧,而在肉眼看来就是卡顿。

滑动优化方案
  • CPU优化
    • 对象创建、调整、销毁(子线程)
    • 预排版(布局计算、文本计算)放到子线程处理
    • 预渲染(文本的异步绘制、图片解码等)
  • GPU优化
    其实就是避免离屏渲染
    • 纹理渲染
    • 视图混合

5. 绘制原理&异步绘制

UIView 的绘制原理

当 UIView的控件在调用[UIView setNeedsDisplay]其实并没有立刻执行绘制工作,而是在当前的 layer 上面打上一个脏标记,接着会调用 [view.layer setNeedsDisplay], 在当前 runloop 将要结束以后,则会调用 [CALayer dispaly]这个方法进行绘制


系统绘制流程
系统绘制流程
流程描述
  • 在layer内部会创建一个backing store,我们可以理解为CGContextRef上下文
  • 判断layer是否有delegate:
    • 如果有delegate,则会执行[layer.delegate drawLayer:inContext](这个方法的执行是在系统内部执行的),然后在这个方法中会调用view的drawRect:方法,也就是我们重写view的drawRect:方法才会被调用到
    • 如果没有delegate,会调用layer的drawInContext方法,也就是我们可以重写的layer的该方法,此刻会被调用到
  • 最后把绘制完的backing store(可以理解为位图)提交给GPU
异步绘制

怎么进行异步绘制呢,其实就是基于系统给我们开的口子layer.delegate,如果遵从或者实现了displayLayer方法,我们就可以进入到异步绘制流程当中,在异步绘制的过程当中通过代理负责生成 bitmap,最后将绘制的bitmap 作为 layer.contents 属性的值


异步绘制时序图

6. 离屏渲染

什么是在屏渲染(On-Screen Rendering)?

就是说当前的屏幕渲染,指的是 GPU 操作发生在当前用于显示屏幕缓冲区进行

什么是离屏渲染?(Off-Screen Rendering)

指的是 GPU 在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作
比如说我们设置的圆角属性,蒙层遮罩都会触发离屏渲染

何时会触发?
  • 设置图层的圆角且和 maskToBounds=YES 一起使用时
  • 图层蒙版
  • 阴影
  • 光栅化
为何要避免?
  • 创建新的渲染缓冲区,消耗内存
  • 上下文切换(GPU额外的开销)
    触发离屏渲染时,会增加 GPU 的工作量,增加 GPU 工作量可能导致 GPU+CPU 工作总耗时(60fps 为例)超过 16.7ms,从而造成UI卡顿 和掉帧,因为我们要避免离屏渲染

总结

以上就是我们 ui 视图部分 iOS 面试中常问到的。高频的题目有

  • 系统事件的 UI 的传递机制是怎样的?
  • 使 UITableView滑动更加流畅的方案或者思路有哪些?
  • 什么是离屏渲染?
  • UIView 和 CALayer之间的关系是怎么样的?

参考文章

https://www.jianshu.com/p/2c16077b50f8
https://www.jianshu.com/p/254ef1640f68

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342