如何在SwiftUI中编写CPU占用更低的高效TimerView计时器视图
计时器视图CPU占用过高优化方案
现有实现CPU高的核心原因
- 刷新频率远高于实际显示需求:现有实现的刷新频率是60~100次/秒,如果你的计时器不需要精确到0.01秒级显示,所有高于显示精度的刷新都是无效计算,白白占用CPU
- 资源未及时释放:全局Timer、未invalidate的CADisplayLink会在页面退出后仍持续运行,额外占用CPU
- 字符串格式化效率低:直接调用
time.description每次都会生成全量时间字符串,比按需格式化消耗更多CPU
优化方案
1. 按需调整刷新频率
根据你的显示精度设置刷新间隔:
- 仅显示到秒:刷新间隔1s
- 显示到0.1秒:刷新间隔0.1s
- 仅需要毫秒级精度时才设置0.01s的刷新间隔
2. 优化计时器生命周期
计时器仅在页面可见时运行,页面消失后立即销毁释放
3. 复用格式化工具
全局复用DateFormatter/NumberFormatter,不要每次刷新都重新创建格式化对象
4. 增加调度容忍度
给Timer设置tolerance参数,允许系统小幅调整调度时机,合并唤醒降低CPU功耗
优化后代码示例
SwiftUI版本(显示到0.1秒,CPU占用可降至2%以下)
import SwiftUI struct OptimizedTimerView: View { @State private var time: TimeInterval = 0 @State private var timer: Cancellable? // 复用格式化逻辑 private func formatTime(_ time: TimeInterval) -> String { String(format: "%.1f", time) } var body: some View { Text(formatTime(time)) .onAppear { // 按需设置刷新间隔,这里用0.1s对应0.1秒的显示精度 timer = Timer.publish(every: 0.1, tolerance: 0.01, on: .main, in: .common) .autoconnect() .sink { [weak self] date in self?.time = date.timeIntervalSinceReferenceDate } } .onDisappear { // 页面消失时销毁计时器 timer?.cancel() } } }
UIKit CADisplayLink版本
import UIKit class OptimizedTimerViewController: UIViewController { @IBOutlet weak var timeLabel: UILabel! private var displayLink: CADisplayLink? // 全局复用格式化器 private let numberFormatter: NumberFormatter = { let formatter = NumberFormatter() formatter.maximumFractionDigits = 1 formatter.minimumFractionDigits = 1 return formatter }() override func viewDidLoad() { super.viewDidLoad() displayLink = CADisplayLink(target: self, selector: #selector(updateTime)) // 按需设置刷新率,30帧对应0.03秒左右精度,远高于0.1秒的显示需求 if #available(iOS 15.0, *) { displayLink?.preferredFrameRateRange = .init(minimum: 10, maximum: 30) } else { displayLink?.preferredFramesPerSecond = 30 } displayLink?.add(to: .main, forMode: .default) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) // 及时销毁displayLink避免内存泄漏和无效CPU占用 displayLink?.invalidate() displayLink = nil } @objc private func updateTime() { guard let timestamp = displayLink?.targetTimestamp else { return } timeLabel.text = numberFormatter.string(from: NSNumber(value: timestamp)) } }
iOS15+ TimelineView优化版本
import SwiftUI struct OptimizedTimelineTimerView: View { // 按需设置step,0.1s对应0.1秒的显示精度 private let refreshStep = 0.1 var body: some View { TimelineView(.periodic(from: .now, by: refreshStep)) { timeline in Text(String(format: "%.1f", timeline.date.timeIntervalSinceReferenceDate)) } } }
内容的提问来源于stack exchange,提问作者joshmori
相关产品推荐
相关产品推荐

