我编写了 SwiftUI 代码,它使用组合来显示系统上每个核心的 CPU 使用率指标,例如
User
、System
、Nice
和 Idle
值。该功能最初的作用是在应用程序启动时,正确获取并显示使用数据。
但是,我遇到了一个问题:初始数据显示后,数值并没有按照我的预期实时更新。第一次获取后,Combine 发布者似乎停止触发更新,并且 UI 中没有反映任何新数据。
我正在寻找有关如何使值连续或定期刷新的指导。也许我在组合的设置方式中遗漏了一些东西,或者也许有一种更有效的方法来保持 UI 与实时数据更新。
任何建议或见解将不胜感激。预先感谢!
这是我的代码:
import SwiftUI
import Combine
struct ContentView: View {
@StateObject private var cpuMonitor = CPUUsageMonitor()
var body: some View {
ScrollView {
VStack {
Text("Overall CPU Usage")
.font(.largeTitle)
.padding()
ForEach(0..<cpuMonitor.cpuUsages.count, id: \.self) { index in
VStack(alignment: .leading) {
Text("Core \(index):")
.font(.headline)
HStack {
Text("User: ")
Text("\(cpuMonitor.cpuUsages[index].user, specifier: "%.2f")%")
}
HStack {
Text("System: ")
Text("\(cpuMonitor.cpuUsages[index].system, specifier: "%.2f")%")
}
HStack {
Text("Nice: ")
Text("\(cpuMonitor.cpuUsages[index].nice, specifier: "%.2f")%")
}
HStack {
Text("Idle: ")
Text("\(cpuMonitor.cpuUsages[index].idle, specifier: "%.2f")%")
}
}
.padding()
}
}
}
.onAppear {
cpuMonitor.startMonitoring()
}
.onDisappear {
cpuMonitor.stopMonitoring()
}
}
}
class CPUUsageMonitor: ObservableObject {
@Published var cpuUsages: [CoreUsage] = []
private var cpuInfo: processor_info_array_t!
private var prevCpuInfo: processor_info_array_t?
private var numCpuInfo: mach_msg_type_number_t = 0
private var numPrevCpuInfo: mach_msg_type_number_t = 0
private var numCPUs: uint = 0
private var timerCancellable: AnyCancellable?
init() {
let mibKeys: [Int32] = [CTL_HW, HW_NCPU]
mibKeys.withUnsafeBufferPointer { mib in
var sizeOfNumCPUs: size_t = MemoryLayout<uint>.size
let status = sysctl(processor_info_array_t(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))
}
func startMonitoring() {
timerCancellable = Timer.publish(every: 1, on: .main, in: .common)
.autoconnect()
.sink { [weak self] _ in
self?.updateInfo()
}
}
func stopMonitoring() {
timerCancellable?.cancel()
}
func updateInfo() {
var numCPUsU: natural_t = 0
var cpuLoadInfo: processor_info_array_t!
var cpuLoadInfoCount: mach_msg_type_number_t = 0
let err: kern_return_t = host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numCPUsU, &cpuLoadInfo, &cpuLoadInfoCount)
if err == KERN_SUCCESS {
var updatedUsages: [CoreUsage] = []
for i in 0..<Int32(numCPUs) {
let userTicks = cpuLoadInfo[Int(CPU_STATE_MAX * i + CPU_STATE_USER)]
let systemTicks = cpuLoadInfo[Int(CPU_STATE_MAX * i + CPU_STATE_SYSTEM)]
let niceTicks = cpuLoadInfo[Int(CPU_STATE_MAX * i + CPU_STATE_NICE)]
let idleTicks = cpuLoadInfo[Int(CPU_STATE_MAX * i + CPU_STATE_IDLE)]
let total = userTicks + systemTicks + niceTicks + idleTicks
let userUsage = Float(userTicks) / Float(total) * 100
let systemUsage = Float(systemTicks) / Float(total) * 100
let niceUsage = Float(niceTicks) / Float(total) * 100
let idleUsage = Float(idleTicks) / Float(total) * 100
let coreUsage = CoreUsage(user: userUsage, system: systemUsage, nice: niceUsage, idle: idleUsage)
updatedUsages.append(coreUsage)
}
DispatchQueue.main.async {
self.cpuUsages = updatedUsages
}
// Deallocate previous CPU info
if let prevCpuInfo = prevCpuInfo {
let prevCpuInfoSize: size_t = MemoryLayout<integer_t>.stride * Int(numPrevCpuInfo)
vm_deallocate(mach_task_self_, vm_address_t(bitPattern: prevCpuInfo), vm_size_t(prevCpuInfoSize))
}
// Store the current info for future deallocation
prevCpuInfo = cpuLoadInfo
numPrevCpuInfo = cpuLoadInfoCount
} else {
print("Error gathering CPU info!")
}
}
}
struct CoreUsage {
var user: Float
var system: Float
var nice: Float
var idle: Float
}
#Preview {
ContentView()
}
您对
ticks
的计算不正确,这就是您得到错误值的原因。您必须正确考虑 prevCpuInfo
:
let userTicks = cpuLoadInfo[Int(CPU_STATE_MAX * i + CPU_STATE_USER)] - (prevCpuInfo?[Int(CPU_STATE_MAX * i + CPU_STATE_USER)] ?? 0)
let systemTicks = cpuLoadInfo[Int(CPU_STATE_MAX * i + CPU_STATE_SYSTEM)] - (prevCpuInfo?[Int(CPU_STATE_MAX * i + CPU_STATE_SYSTEM)] ?? 0)
let niceTicks = cpuLoadInfo[Int(CPU_STATE_MAX * i + CPU_STATE_NICE)] - (prevCpuInfo?[Int(CPU_STATE_MAX * i + CPU_STATE_NICE)] ?? 0)
let idleTicks = cpuLoadInfo[Int(CPU_STATE_MAX * i + CPU_STATE_IDLE)] - (prevCpuInfo?[Int(CPU_STATE_MAX * i + CPU_STATE_IDLE)] ?? 0)
Combine 发布者似乎在第一次获取后停止触发更新,并且 UI 中没有反映新数据。
我无法重现此问题,并且您的代码在这方面似乎没有任何错误。