UITableView重用机制let cell tableView.dequeueReusableCell(withIdentifier: cellReuseID, for: indexPath)UITableView 内部维护了一个 重用缓存池核心流程缓存池结构字典形式: key 重用标识符value 闲置的 cellcell 滑出屏幕系统自动把它扔进缓存池标记为「可重用」需要新 cell调用 dequeueReusableCell 方法优先去缓存池找对应重用标识符的闲置 cell找到直接复用没找到系统进行创建重用标识符唯一标识一种 cell 样式UI数据源同步问题在实际的设计中我们经常会遇到多线程竞态问题比如请求server的数据这需要放到子线程来进行但是在等待server返回的过程中UITableView的数据有可能会被修改server返回后再刷新UI就会出现已经修改的数据重置的问题实际解决方案一般有两种串行队列 设计一个串行队列对于UI数据的操作放到串行队列去做即可并发访问 数据拷贝 我们为子线程拷贝一份主线程的数据当主线程对数据进行修改的时候记录一下然后当server数据返回时让其做同步修改即可UI事件传递UIView和CALayerUIView为其提供内容处理触摸等事件参与响应链CALayer负责显示内容contents这体现了单一职责原则事件传递和视图响应链事件传递流程 传递方向从上到下传递顺序点击屏幕 → UIApplication → UIWindow → 控制器根视图 → 父视图 → 子视图倒序遍历也就是说最后被添加的子视图最先响应note对于每个子视图先调用 pointInside 判断点是否在视图内再递归调用子视图的 hitTestpoint(inside:with:) 判断触摸点是否在当前视图范围内 hitTest(_:with:) 寻找并返回最终的响应视图视图响应链 传递方向从下往上传递顺序最佳响应视图 → 父视图 → ... → 控制器根视图 → UIViewController → UIWindow → UIApplication → AppDelegate最佳视图重写 touchesBegantouchesMovetouchedEnded方法 → 自己处理事件终止如果不重写会一直向上抛直到最顶层都不处理该事件丢弃图像显示原理UIView 本身不负责绘制和显示真正负责像素显示、渲染、动画的是它的底层属性CALayer。iOS 屏幕显示是一套固定的流水线作业分为 3 个阶段CPU 预处理 → GPU 硬件渲染 → 屏幕刷新显示CPU的工作布局计算计算所有视图的 frame、AutoLayout 约束、文字宽高确定视图位置大小CPU绘制位图如果你重写了 drawRect:CPU 会进行软件绘制生成位图CPU 把所有图层、位图数据打包通过Core Animation框架IPC 通信提交给 GPU的OpenGL管线GPU的工作顶点着色图源装配光栅化片段着色片段处理提交像素点到帧缓冲区中屏幕显示手机屏幕默认 60Hz 刷新率每 16.7ms 刷新一次屏幕发出 VSync垂直同步信号系统从帧缓冲区取出渲染好的画面电子枪逐行扫描屏幕显示像素完成一帧显示循环往复UI掉帧在规定的16.7ms内在下一帧VSync信号到来之前CPU和GPU并没有完成下一帧画面的合成滑动优化方案解决主线程卡顿所有耗时操作全部丢子线程图片子线程预解码主线程只赋值展示列表优先用 reloadRows 局部刷新少用 reloadData简化约束复杂布局尽量用 Frame提前初始化复用控件不要滑动时临时创建 解决 GPU 渲染掉帧尽量少用 圆角 masksToBounds避免离屏渲染阴影一定要加 shadowPath避免实时计算减少视图嵌套扁平化 UI 层级删掉无用透明重叠 View减少过度绘制非必要不使用模糊、蒙版UIView绘制原理异步绘制UIView 默认就是自己底层 layer 的 delegateCALayer 绘制内容时会优先询问代理- (void)displayLayer:(CALayer *)layer;只要实现了这个方法系统就不会走默认的主线程 drawRect:该方法负责代理生成对应的位图并生成对应的CGImage并且设置其为Layer.contents属性的值时序图离屏渲染在屏渲染GPU的渲染操作在当前用于显示的屏幕缓冲区进行画完直接等屏幕刷新显示离屏渲染GPU在当前屏幕缓冲区外新开辟一个缓冲区进行渲染操作这就需要进行中转绘制 拷贝何时触发图层圆角 maskToBounds图层蒙版 || 阴影光栅化渐变叠加debug顶部菜单 Debug → Show Debug SettingsColor Offscreen - Rendered Yellow 界面黄色区域 正在发生离屏渲染