Time Profiler 性能分析
1 | 1.59 s 15.4% 1.31 s closure #6 in LineChartView.generateSingleLineChart(from:) |
1. 分析 Time Profiler 的耗时情况
闭包内部耗时较高
- 在
generateSingleLineChart中,ForEach循环内部的闭包花费了884毫秒,其中.lineStyle(...)调用占了 561 毫秒,说明对每个数据点的样式计算非常耗时。 - 同理,
generateLineChart和generateGroupLineChart中,也分别花费了259毫秒和261毫秒。这表明在遍历数据并生成LineMark时,SwiftUI在计算各个修饰符(如lineStyle、foregroundStyle)的过程中开销较大。
- 在
重复计算和视图重构
- 每次调用这些生成图表的函数时,都需要重新计算
x轴、y轴的最小值、最大值、刻度值等,而且对每个数据点都要构建一个新的LineMark。如果数据点较多,这些计算和视图构造操作叠加起来就会变得非常耗时。
- 每次调用这些生成图表的函数时,都需要重新计算
样式计算问题
- 例如在
generateSingleLineChart中,对每个数据点调用viewModel.getLineColor(for:)和创建StrokeStyle都消耗了大量时间。这提示我们,样式的计算和生成可能没有复用,每次都在重新构建。
- 例如在
2. 关于 NavigationLink 导航后返回页面时卡顿现象
重绘整个视图
- 当从
LineChartView跳转到子页面后返回,SwiftUI会重新调用body生成视图。即使数据没有改变,所有的闭包(如generateSingleLineChart、generateGroupLineChart、generateLineChart等)都会被重新执行,导致整个图表视图重新计算和绘制。
- 当从
视图重构导致的重复计算
- 由于
SwiftUI的声明式特性,每次视图出现时都会重新计算其内部状态,进而触发大量重复的计算操作(比如对时间范围、轴值和样式的计算),所以返回时会明显卡顿,耗时达到5到6秒。
- 由于
3. 优化思路和解决方案
针对上述问题,可以从以下几个方面考虑优化:
a. 数据和样式的预计算与缓存
提前计算图表数据范围和样式
- 将
x轴和y轴的计算、刻度值生成、以及各个数据点对应的样式等计算移到viewModel中,提前计算好并缓存。这样在视图构建时,只需直接使用缓存结果,避免重复计算。
- 将
缓存 StrokeStyle 和颜色
- 如果
chartLineWidth、lineCap、lineJoin等参数不会变化,可以将生成的StrokeStyle定义为常量,避免在ForEach内部重复创建。
- 如果
b. 减少不必要的视图重构
局部视图拆分和懒加载
- 将复杂且耗时的图表部分拆分成独立的子视图,利用
SwiftUI的@StateObject或@ObservedObject缓存数据,防止因父视图重新构建而全部重绘。 - 如果页面上图表较多,可以考虑使用
LazyVStack替换VStack,使得只有当视图真正需要显示时才进行计算和绘制。
- 将复杂且耗时的图表部分拆分成独立的子视图,利用
视图缓存策略
- 当数据不频繁变化时,可以考虑在视图中利用
.drawingGroup()或其他缓存手段,将渲染结果缓存成图像,在下一次展示时直接使用缓存图像而不是重新绘制所有的线条。
- 当数据不频繁变化时,可以考虑在视图中利用
c. 关注 SwiftUI 的绘图和布局机制
审查 Chart 的内部实现
SwiftUI的Chart组件可能在处理大量数据点时没有做到最优。如果可能,考虑对数据进行适当的抽样或者合并,减少绘制的元素数量。
分析 View 重构触发机制
- 检查是否存在不必要的状态更新或绑定,导致整个
LineChartView被频繁重构。如果能做到局部状态隔离,只让真正需要更新的部分重绘,也会大大降低耗时。
- 检查是否存在不必要的状态更新或绑定,导致整个
总结
问题定位:
- 耗时主要在
ForEach循环中生成各个LineMark的过程中,特别是.lineStyle的计算,以及在视图重构时重复计算各项轴值和样式。 - 导航返回时,因为整个视图重新构建,所有的图表闭包都会再次执行,造成明显卡顿。
- 耗时主要在
优化方案:
- 预计算和缓存:在
viewModel中提前计算好各个图表需要的数据、刻度、样式等,避免在视图中重复计算。 - 减少重复视图构建:将耗时较大的图表部分拆分为独立的子视图,并使用懒加载、缓存视图或
.drawingGroup()技术来减少重绘。 - 调整数据量:如果数据点非常多,可以考虑做数据抽样或聚合,降低绘图时的计算量。
- 预计算和缓存:在
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 技术成长笔记!