You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

优化Swift Charts性能:降低高CPU占用并维持屏幕刷新率

优化Swift Chart高CPU占用的解决方案

你的代码CPU占用居高不下的核心原因是每帧全量替换数据源+View层重复计算,导致三个Chart被迫频繁执行全量重绘。以下是针对性的优化方案:

1. 避免全量替换数据源,改用滚动增量更新

原代码中_regen每次生成全新的300个DataPoint数组并替换@Published var data,SwiftUI会判定整个数据源完全变更,触发三个Chart的全量重绘。改成滚动更新:保持数组引用不变,每次添加新数据时移除最旧的数据,仅更新局部内容。

2. 提前预处理计算值,减少View层开销

原Chart中$0.x - viewModel.data[0].x和各类y轴变换计算,会在每帧重绘时重复执行。把这些计算逻辑移到ViewModel中提前处理,直接给View层提供最终可用的数值。

3. 同步屏幕刷新率,避免过度更新

用CADisplayLink替代Task.sleep,确保更新频率与屏幕刷新率完全同步(比如60Hz/120Hz),避免不必要的额外更新操作。

修改后的完整代码

import SwiftUI
import Charts
import UIKit

struct DataPoint: Identifiable {
    let id: Int
    let relativeX: Int // 提前计算的相对X值
    let baseY: Float
    let transformedY1: Float // 预处理1 - y
    let transformedY2: Float // 预处理y * 0.2
}

class ViewModel: ObservableObject {
    @Published var data: [DataPoint] = []
    private var displayLink: CADisplayLink?
    private let maxDataCount = 300
    private var currentX = 0

    init() {
        // 初始化初始数据集
        for x in 0..<maxDataCount {
            let y = sin(Float(x) / 10.0)
            let point = DataPoint(
                id: x,
                relativeX: x,
                baseY: y,
                transformedY1: 1 - y,
                transformedY2: y * 0.2
            )
            data.append(point)
        }
        currentX = maxDataCount
        
        // 启动与屏幕刷新率同步的更新链路
        setupDisplayLink()
    }
    
    private func setupDisplayLink() {
        displayLink = CADisplayLink(target: self, selector: #selector(updateData))
        displayLink?.add(to: .main, forMode: .common)
    }
    
    @objc private func updateData() {
        currentX += 1
        let y = sin(Float(currentX) / 10.0)
        let newPoint = DataPoint(
            id: currentX,
            relativeX: currentX - (currentX - maxDataCount), // 维持相对X从0开始
            baseY: y,
            transformedY1: 1 - y,
            transformedY2: y * 0.2
        )
        
        // 滚动更新:移除最旧数据,添加新数据
        DispatchQueue.main.async { [weak self] in
            guard let self = self else { return }
            if self.data.count >= self.maxDataCount {
                self.data.removeFirst()
            }
            self.data.append(newPoint)
        }
    }
    
    deinit {
        displayLink?.invalidate()
    }
}

struct ContentView: View {
    @StateObject var viewModel: ViewModel = ViewModel()
    
    var body: some View {
        VStack {
            Chart(viewModel.data) { point in
                LineMark(
                    x: .value("", point.relativeX),
                    y: .value("Value", point.transformedY1)
                )
            }
            
            Chart(viewModel.data) { point in
                LineMark(
                    x: .value("", point.relativeX),
                    y: .value("Value", point.transformedY2)
                )
            }
            
            Chart(viewModel.data) { point in
                LineMark(
                    x: .value("", point.relativeX),
                    y: .value("Value", point.baseY)
                )
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

额外优化建议

  • 如果业务不需要严格每帧更新,可以降低更新频率(比如每秒30次),进一步降低CPU占用。
  • 给Chart添加chartXScale(domain:)固定X轴范围,避免Chart每次重绘时重新计算轴范围的开销。
  • 确保DataPoint的id唯一且稳定,避免SwiftUI错误识别元素变更导致的无效重绘。

内容的提问来源于stack exchange,提问作者niklassaers

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 10:15:39