一、UITableView相关
重用机制:cell=[tableView dequeueReusableCellWithIndentifier:identifier] 重用池 存 取 标识符 (图1)
存 滑出屏幕之外的cell 会被加入重用池
取 屏幕显示到第n个,继续滑动n+1就会从重用池中根据标识符取一个可重用的cell
标识符 若滑出屏幕的第1个和即将显示的第n+1个的标识符相同,第n+1个就会复用第1个控件创建的cell
自定义UI控件字母索引条:实现重用机制的类ViewReusePool,三个方法取,存,重置即使用中的视图移动到可重用队列中。等待使用的队列,使用中的队列
取:等待使用的队列,若没有就返回nil,若有把它从等待使用队列移除,加入到使用中队列,同时该视图返回给调用方
存:首先做异常判断,若视图为nil就什么都不做。添加视图到使用中的队列
重置:声明view是nil,然后遍历如果使用中的队列有使用中的视图,将其从使用中队列移除,加入等待使用的队列。
IndexedTableView 定义IndexedTableViewDataSource协议里定义一个获取索引条数据的方法,IndexedTableView里定义一个weak属性的数据源
定义一个装载视图的容器view和重用池;懒加载containerView并将其插入到当前视图最上面,避免索引条随着table滚动。懒加载重用池,标记所有视图为可重用状态,之后reload字母索引条。
数据源同步问题:数据源删除会引用LoadMore后降刷新,删除操作是用户交互操作,往往在主线程进行,而LoadMore在子线程进行。故涉及多线程对共享数据的访问。(图2)
解决方案:并发访问、数据拷贝(记录同时操作 大量数据拷贝时内存开销大)。数据拷贝一般在主线程,拷贝后的结果给子线程使用,同时在子线程进行新数据的网络请求、解析、预排版。主线程删一行数据,reloadUI后数据消失不见了,主线程有事件会做其他工作,子线程会返回请求的结果,reloadUI会包含数据删除前的数据。主线程记录删除操作,子线程将要返回数据更新UI的时候,同步一下该删除操作,然后回到主线程同步UI。(图3)
缺点:需要记录数据同步删除动作;存在大量数据源的拷贝,内存开销较大
串行访问:GCD中串行队列 ;子线程中进行网络请求、数据解析,将请求回来的网络数据放到 串行队列进行新增数据预排版;此时主线程删除某数据需要以同步的方式在串行队列中处理,主线程就需要等前一个block任务完成后再同步主线程数据删除操作,最后回到主线程更新UI。(图4)
缺点:若子线程处理任务特别耗时,某删除动作可能会有延时。
二、事件传递和响应机制
2.1事件传递机制:从上至下即UIApplication->UIWindow->hitTest:withEvent:返回最终响应的视图->
若不可交互则父遍历兄弟节点视图,若可交互则调用pointInside:withEvent:判断当前点击的点是否在window范围内,是则倒序遍历子视图,在每子视图都调用hitTest:withEvent:即递归调用,
有则该视图为最终响应者,无且在Window范围内则window作为最终响应者。
2.2事件响应机制:从下至上即UI控件->UIView->若有则UIViewController->UIWindow->UIApplication->UIApplicationDelegate
若最终无视图处理该事件,则忽略,当作什么都没有发生。
三、图像显示原理:
3.1CPU 在合适时机回调drawRect方法绘制好位图,经过总线 在合适时机 由CoreAnimation 传给GPU 的OpenGL(ES)渲染管线
CPU工作:LayoutUI布局/文本计算、DIsplay绘制如drawRect、Prepare图片编/解码、Commit提交位图
3.2GPU 对拿到的位图进行图层渲染和纹理合成,处理结果放到 帧缓冲区的FrameBuffer中
GPU渲染管线:顶点着色、图源装配 光栅化 片段着色/处理
3.3视频控制器 根据Vsync信号在指定时间之前提取对应缓冲区对应的屏幕显示内容,讲内容显示到屏幕
四、UI卡顿、掉帧原因
4.1卡顿、掉帧产生原因:
界面滑动流畅性60F/S帧/秒,即16.7ms产生一帧画面,CPU在一定时间内绘制好并输出位图给GPU,CPU进行图层合成管理渲染,两者协同产生一帧数据,在Vsync垂直信号来到之前就得准备好需要显示的画面。若CPU工作时长过长,留给GPU渲染合成图片的时间就非常少,这样完成总时间若超过16.7ms,下一帧Vsync到来前还没准备好该画面,就会视觉上出现滑动卡顿。
4.2滑动优化方案:
CPU 对象的创建/调整/销毁、预排版(布局、文本计算)、预渲染(文本异步绘制、图片编解码)放到子线程,这样主线程就有更多的时间响应交互。
GPU减少纹理渲染,依托CPU异步绘制减轻GPU压力、减少视图复杂度(视图层级复杂,GPU需做大量的计算进行视图合成、像素点对应像素值合成)
五、UIView绘制原理、异步绘制
5.1 UIView绘制原理:
调用 [ UIView setNeedsDisplay]并不立刻绘制,而在之后某一时机才进行当前视图的真正绘制。
而是调用view.layer的同名方法[view.layer setNeedsDisplay],相当于给layer打上了标记,再当前runloop将要结束时才会调用[CALayer display],这个时候才真正开始绘制工作。
在[CALayer display]内部会判断layer.delegate respondsToSelector:@selector(displayLayer:),若不响应,开始系统绘制流程;若响应,提供异步绘制入口。
5.2系统绘制流程:
CALayer内部创建backingstore(CGContextRef) 一般在drawRect方法中可以通过上下文堆栈取出栈顶的Context,拿到当前视图或控件的上下文,然后该layer会判断是否有delegate,若无则调用[CALyer drawInContext:],若有调用[layer.delegate drawLayer:inContext:]做当前视图的绘制工作,这步发生在系统内部,在合适时机给予一个回调方法[UIView drawRect:],开这个口子允许做一些其他绘制工作;这两个分支最终都是CALayer上传backing store到GPU,结束。
5.3异步绘制:
[layer.delegate drawLayer:]代理负责生成对应的bitmap,设置该bitmap作为layer.contents属性的值。
主队列 在某一时机调用[AsyncDrawingView setNeedsDisplay]方法,在当前runloop将要结束时,由系统调用[CALayer display],若代理实现了display函数,
然后通过子线程全局并发队列切换做位图绘制CGBitmapContextCreate( )创建位图上下文、CoreGraphicAPI当前UI绘制工作、CGBitmapContextCreateImage( )根据当前绘制的上下文生成CGImage图,然后回到主队列中设置[CALayer setContents]
六、离屏渲染
在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作,未合成前不能直接显示。
layer的圆角与maskToBounds一起用、阴影、蒙层、光栅化都会触发离屏渲染。