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

SwiftUI 6.0.3开发Mac菜单栏CPU使用率显示问题求助

解决Mac菜单栏应用CPU使用率不更新的问题

问题根源分析

  1. 监控逻辑未启动:你定义了startCPUMonitoring()但从未调用它,导致没有定时任务去更新CPU使用率,界面一直停留在默认的“Calculating...”。
  2. 使用率计算逻辑错误:单次调用host_statistics拿到的是系统启动以来的累计CPU tick数,直接计算会得到错误的极低值(因为 idle 时间占比极大),必须通过两次采样的差值才能算出真实的实时使用率。
  3. Timer适配问题:直接用Timer.scheduledTimer在SwiftUI App场景中容易出现运行循环适配问题,改用Swift Concurrency的异步任务更可靠。

修复后的完整代码

import SwiftUI
import Foundation
import MachO

@main
struct MenuBarApp: App {
    @State private var cpuUsage: String = "Calculating..."
    // 存储上一次采样的CPU tick数据
    private var previousCPULoad: host_cpu_load_info_data_t?
    
    var body: some Scene {
        MenuBarExtra("icon \(cpuUsage)") {
            Button("Quit") {
                NSApp.terminate(nil)
            }
        }
        .onAppear {
            // 界面出现后启动CPU监控
            Task {
                await startCPUMonitoring()
            }
        }
    }
    
    func startCPUMonitoring() async {
        while true {
            if let usage = await getCPUUsage() {
                cpuUsage = String(format: "%.1f%%", usage)
            } else {
                cpuUsage = "N/A"
            }
            // 每隔1秒更新一次
            try? await Task.sleep(for: .seconds(1))
            // 让出线程给其他任务
            Task.yield()
        }
    }
    
    func getCPUUsage() async -> Double? {
        var size = mach_msg_type_number_t(MemoryLayout<host_cpu_load_info_data_t>.size / MemoryLayout<integer_t>.size)
        var cpuLoad = host_cpu_load_info_data_t()
        
        let result = withUnsafeMutablePointer(to: &cpuLoad) {
            $0.withMemoryRebound(to: integer_t.self, capacity: Int(size)) {
                host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, $0, &size)
            }
        }
        
        guard result == KERN_SUCCESS else {
            print("获取CPU负载失败: \(result)")
            return nil
        }
        
        // 第一次采样只存储数据,等待第二次采样计算差值
        guard let previousLoad = previousCPULoad else {
            previousCPULoad = cpuLoad
            return nil
        }
        
        // 计算两次采样的差值
        let userDiff = Double(cpuLoad.cpu_ticks.0 - previousLoad.cpu_ticks.0)
        let systemDiff = Double(cpuLoad.cpu_ticks.1 - previousLoad.cpu_ticks.1)
        let idleDiff = Double(cpuLoad.cpu_ticks.2 - previousLoad.cpu_ticks.2)
        let niceDiff = Double(cpuLoad.cpu_ticks.3 - previousLoad.cpu_ticks.3)
        
        let totalDiff = userDiff + systemDiff + idleDiff + niceDiff
        guard totalDiff > 0 else {
            previousCPULoad = cpuLoad
            return nil
        }
        
        // 计算使用率:(用户+系统+nice时间) / 总时间 * 100%
        let usage = (userDiff + systemDiff + niceDiff) / totalDiff * 100.0
        previousCPULoad = cpuLoad
        
        return usage
    }
}

关键修改说明

  • 启动监控任务:通过MenuBarExtra的onAppear回调启动异步任务,确保界面出现后立刻开始监控CPU。
  • 差值计算逻辑:新增previousCPULoad变量存储上一次的采样数据,通过两次tick的差值计算真实的实时使用率,这是解决数值错误的核心。
  • 改用Swift Concurrency:用Task.sleep替代传统Timer,更适配SwiftUI的异步环境,避免运行循环和生命周期问题。
  • 显式导入MachO:确保Mach内核API的定义能被Xcode正确识别,避免编译警告或错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 10:25:20