001 UITableView
重用机制:
cell = [tableView dequeueReusableCellWithIdentifier:identifier];
从下往上刷动列表 顶部滑出的cell1 会被放在一个重用池中,那么底部即将出来的cell会从重用池中根据identitifier找到对应的celllast加载出来。这样就可以复用cell1创建的内存属性等。
数据源同步
并发访问,数据拷贝
在删除之前开启一个子线程进行网络请求 数据解析 以及预排班操作等操作,在用户删除一行数据的时候 记录下删除数据的标记(在项目中应该是cell对应model中的ID)在子线程找到删除标记model进行删除操作然后返回主线程进行reload UI
串行访问
建立一个串行队列,开启一个子线程,在子线程中开启网络请求和数据解析,在串行队列中进行新增数据预排版操作的时候同步数据删除操作,然后返回到主线程进行reload UI
串行访问如果遇到主线程处理事务比较多的话 会有一定的延时,并发访问数据同步和数据拷贝操作对内存开销比较大
002 事件传递和视图响应链
UIView 和CALayer之间的关系
UIView 为其提供内容,以及负责处理触摸等事件,参与响应链
CALayer 负责显示内用contents
这就体现了设计原则中的单一职责原则 职责的
事件传递涉及到两个方法
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
hitTest方法返回是UIView判断该视图是否触发事件,pointInside是判断该点击是否触发事件。
事件传递流程:
点击屏幕-UIApplication-UIWindow-hitTest:withEvent:-pointInside:withEvent:-subviews-[倒序遍历视图]-subview hittestf方法-->hit不为空-就返回view 反之返回nil
示例代码:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
if(!self.userInteractionEnabled || self.isHidden || self.alpha <=0){
return nil;
}
if([self pointInside:point withEvent:event]){
//遍历当前对象的子视图
__block UIView *hit = nil;
[self.subviews enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
//坐标转换
CGPoint convertPoint = [self convertPoint:point toView:obj];
//调用子视图的hittest方法
hit = [obj hitTest:convertPoint withEvent:event];
if(hit){
*stop = YES;
}
}];
if(hit){
return hit;
}else{
return self;
}
}
return nil;
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
CGFloat x1 = point.x;
CGFloat y1 = point.y;
CGFloat x2 = self.frame.size.width / 2;
CGFloat y2 = self.frame.size.height /2;
double dis = sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
if(dis <= self.frame.size.width /2){
return YES;
}else{
return NO;;
}
}
视图响应链:----->nextResponder =====>Delegate
UIView----->UIView------>UIViewController/UIWindow----->UIApplication=====>UIApplicationDelegate
如果找不到响应是视图 就忽略不做处理
示例代码 展示如何找到一个视图的视图控制器思路找到视图的nextResponder 在一个循环中匹配是否是视图控制器类 如果不是该类则继续找下一个响应对象
- (UIViewController *)viewController
{
UIResponder *nextVC = self.nextResponder;
while ([nextVC isKindOfClass:UIViewController.class] == NO)
nextVC = nextVC.nextResponder;
return [nextVC isKindOfClass:UIViewController.class] ? nextVC : nil;
}
- (void)touchesBegan:(NSSet
- (void)touchesMoved:(NSSet
- (void)touchesEnded:(NSSet
- (void)touchesCancelled:(NSSet
003 图像显示原理
在计算系统中CPU和GPU是通过总线来进行数据传输,图像通过CPU和GPU处理后通过一个帧缓冲区渲染到屏幕上
具体操作步骤为:UIView中CALayer中的content负责渲染内容,在UIView中drawRect方法进行绘制传递给coreAnimation 通过CA传递给OpenGLES(这里就是GPU)-->screen中
CPU做了以下操作:
Layout(UI 文本计算 )
Display(绘制)
Prepare(图片的编解码)
Commit(提交位图)
GPU做了以下操作:
【顶点着色,图元装配 ,光栅化,片段着色,片段处理 】-->FrameBuffer
004 卡顿&掉帧原因
在规定16.7ms之内,在下一个16.7ms到来之前,CPU和GPU没有完成下一帧图面的合成。
005 绘制原理和异步绘制(滑动性能优化)
滑动优化方案
001减轻CPU 工作压力:
对象的创建、调整、销毁放在子线程中做;
预排版(布局计算,文本计算)放在子线程;
预渲染(文本的异步绘制,图片编解码等);
002 减轻GPU工作压力:
纹理渲染;尽量避免离屏渲染
视图混合,减少视图层级
UIView绘制原理
[UIView setNeedsDispay]--[UILayer setNeedsDispay] 会做一个脏标记 等到当前runloop即将结束的时候[CALayer display] --进入一个判断操作(layer.delegate isResponseTo:@selector(displaylayer:))如果判断为No则进行系统绘制,反之则进行异步绘制
系统绘制:CALayer 创建backing store(CGContentRef)-->判断layer.delegate? --YES-->[layer.delegate drawLayer:InContext:]-->[UIView drawInRect:]--->[CALayer uploads backing store]--[end];
--NO-->[CALayer drawInRect:]--->[CALayer uploads backing store]--[end];
异步绘制:
Main Queue: [异步绘制视图 setNeedsDisplay] ---下一个runloop-->[CALayer display]-->[异步绘制视图 setNeedsDisplay]
Global Queue:CGBitmapContextCreate(),CoreGraphic API (进行编解码),CGBitmapContextCreateImage()--回到MainQueue-->[CALayer setContents:]
006 离屏渲染
On-Screen Rendering :当前屏幕渲染,指的是GPU的渲染操作是在当前用于显示屏幕缓冲区中进行。
Off-Screen Rendering:离屏渲染,指的是GPU的渲染操作是在当前屏幕缓冲区以外开辟的一个缓冲区用于进行渲染操作。
当我们指定UIView的某些属性,标记成它未合成之前不能用于当前屏幕直接显示的时候,离屏渲染是在GPU层面上在当前屏幕缓区之外开辟的屏幕缓冲区,进行渲染操作。
何时触发:
圆角(当MaskToBounds为YES)
图层蒙版
阴影
光栅化
如何避免离屏渲染呢?
GPU OpenGL 触发多通道渲染的开销
创建新的渲染缓冲区,多次上下文切换耗时耗时
本质:增加GPU的工作量,很有可能GPU 和CPU工作总耗时会超过16.7毫秒。
007 懒加载
就是说真正使用的时候 才会去创建该对象