SwiftUI结合Combine实现CPU使用率实时更新失败求助
解决SwiftUI+Combine CPU使用率实时更新问题
问题分析
你的代码首次启动能正确显示CPU数据,但后续无法实时更新,核心问题出在CPU使用率的计算逻辑,而非Combine的Timer配置:
- 当前代码直接用系统启动以来的累计ticks计算比例,得到的是开机到现在的平均使用率,数值几乎不会变化,看起来像是UI没更新
- 另外,
host_processor_info返回的内存未正确释放,存在内存泄漏风险
修正后的完整代码
import SwiftUI import Combine struct ContentView: View { @StateObject private var cpuMonitor = CPUUsageMonitor() var body: some View { ScrollView { VStack { Text("CPU核心使用率") .font(.largeTitle) .padding() ForEach(cpuMonitor.cpuUsages.enumerated().map { $0 }, id: \.offset) { index, usage in VStack(alignment: .leading) { Text("核心 \(index):") .font(.headline) HStack { Text("用户态: ") Text("\(usage.user, specifier: "%.2f")%") } HStack { Text("系统态: ") Text("\(usage.system, specifier: "%.2f")%") } HStack { Text("Nice: ") Text("\(usage.nice, specifier: "%.2f")%") } HStack { Text("空闲: ") Text("\(usage.idle, specifier: "%.2f")%") } } .padding() .background(Color.gray.opacity(0.1)) .cornerRadius(8) } } .padding() } .onAppear { cpuMonitor.startMonitoring() } .onDisappear { cpuMonitor.stopMonitoring() } } } class CPUUsageMonitor: ObservableObject { @Published var cpuUsages: [CoreUsage] = [] private var numCPUs: uint = 0 private var timerCancellable: AnyCancellable? // 保存上一次的CPU ticks数据,用于计算差值 private var prevTicks: [CoreTicks] = [] struct CoreTicks { var user: UInt64 var system: UInt64 var nice: UInt64 var idle: UInt64 } init() { let mibKeys: [Int32] = [CTL_HW, HW_NCPU] mibKeys.withUnsafeBufferPointer { mib in var sizeOfNumCPUs: size_t = MemoryLayout<uint>.size let status = sysctl(mutating: mib.baseAddress, 2, &numCPUs, &sizeOfNumCPUs, nil, 0) if status != 0 { numCPUs = 1 } } cpuUsages = Array(repeating: CoreUsage(user: 0.0, system: 0.0, nice: 0.0, idle: 0.0), count: Int(numCPUs)) prevTicks = Array(repeating: CoreTicks(user: 0, system: 0, nice: 0, idle: 0), count: Int(numCPUs)) } func startMonitoring() { timerCancellable = Timer.publish(every: 1, on: .main, in: .common) .autoconnect() .sink { [weak self] _ in self?.updateCPUUsage() } } func stopMonitoring() { timerCancellable?.cancel() } private func updateCPUUsage() { var numCPUsU: natural_t = 0 var cpuLoadInfo: processor_info_array_t! var cpuLoadInfoCount: mach_msg_type_number_t = 0 let err = host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numCPUsU, &cpuLoadInfo, &cpuLoadInfoCount) guard err == KERN_SUCCESS else { print("获取CPU信息失败!") return } defer { // 确保当前获取的cpuLoadInfo内存被释放 let size = MemoryLayout<integer_t>.stride * Int(cpuLoadInfoCount) vm_deallocate(mach_task_self_, vm_address_t(bitPattern: cpuLoadInfo), vm_size_t(size)) } var updatedUsages: [CoreUsage] = [] for coreIndex in 0..<Int(numCPUs) { let offset = CPU_STATE_MAX * Int32(coreIndex) let userTicks = UInt64(cpuLoadInfo[Int(offset + CPU_STATE_USER)]) let systemTicks = UInt64(cpuLoadInfo[Int(offset + CPU_STATE_SYSTEM)]) let niceTicks = UInt64(cpuLoadInfo[Int(offset + CPU_STATE_NICE)]) let idleTicks = UInt64(cpuLoadInfo[Int(offset + CPU_STATE_IDLE)]) // 计算两次采样的差值,得到实时使用率 let deltaUser = userTicks - prevTicks[coreIndex].user let deltaSystem = systemTicks - prevTicks[coreIndex].system let deltaNice = niceTicks - prevTicks[coreIndex].nice let deltaIdle = idleTicks - prevTicks[coreIndex].idle let totalDelta = deltaUser + deltaSystem + deltaNice + deltaIdle // 避免除以0的异常情况 guard totalDelta > 0 else { updatedUsages.append(cpuUsages[coreIndex]) continue } let userUsage = Float(deltaUser) / Float(totalDelta) * 100 let systemUsage = Float(deltaSystem) / Float(totalDelta) * 100 let niceUsage = Float(deltaNice) / Float(totalDelta) * 100 let idleUsage = Float(deltaIdle) / Float(totalDelta) * 100 updatedUsages.append(CoreUsage(user: userUsage, system: systemUsage, nice: niceUsage, idle: idleUsage)) // 更新基准ticks,用于下一次计算 prevTicks[coreIndex] = CoreTicks(user: userTicks, system: systemTicks, nice: niceTicks, idle: idleTicks) } DispatchQueue.main.async { self.cpuUsages = updatedUsages } } } struct CoreUsage { var user: Float var system: Float var nice: Float var idle: Float } #Preview { ContentView() }
关键修正点
- 实时使用率计算:保存上一次的CPU ticks数据,通过两次采样的差值比例计算真实的实时使用率,而非累计平均
- 内存管理优化:用
defer语句确保每次host_processor_info返回的内存都被正确释放,避免泄漏 - UI遍历优化:改用
enumerated()遍历数组,更符合SwiftUI的最佳实践 - 异常防护:增加除以0的判断,避免数值异常
内容的提问来源于stack exchange,提问作者Karl Ehrlich
相关产品推荐
相关产品推荐

