mirror of
https://github.com/morgan9e/macos-stats
synced 2026-04-14 00:04:15 +09:00
fix CPU leak in network module (I hope for that);
fix GCD initialization; starting to remove observable from project (because of memory leak);
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// AppDelegate.swift
|
||||
// Mini Stats
|
||||
// Stats
|
||||
//
|
||||
// Created by Serhiy Mytrovtsiy on 28.05.2019.
|
||||
// Copyright © 2019 Serhiy Mytrovtsiy. All rights reserved.
|
||||
@@ -10,7 +10,7 @@ import Cocoa
|
||||
import ServiceManagement
|
||||
import LaunchAtLogin
|
||||
|
||||
let modules: Observable<[Module]> = Observable([CPU(), Memory(), Disk(), Battery(), Network()])
|
||||
let modules: [Module] = [CPU(), Memory(), Disk(), Battery(), Network()]
|
||||
let updater = macAppUpdater(user: "exelban", repo: "stats")
|
||||
let popover = NSPopover()
|
||||
var menuBar: MenuBar?
|
||||
@@ -67,8 +67,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ aNotification: Notification) {
|
||||
if modules.value.count != 0 {
|
||||
for module in modules.value{
|
||||
if modules.count != 0 {
|
||||
for module in modules {
|
||||
module.stop()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ class MenuBar {
|
||||
self.menuBarItem = menuBarItem
|
||||
self.menuBarButton = menuBarButton
|
||||
|
||||
for module in modules.value {
|
||||
for module in modules {
|
||||
module.active.subscribe(observer: self) { (value, _) in
|
||||
if !value {
|
||||
let emptyWidget = Empty()
|
||||
@@ -37,7 +37,7 @@ class MenuBar {
|
||||
}
|
||||
|
||||
public func updateWidget(name: String) {
|
||||
let newViewList = modules.value.filter{ $0.name == name }
|
||||
let newViewList = modules.filter{ $0.name == name }
|
||||
if newViewList.isEmpty {
|
||||
return
|
||||
}
|
||||
@@ -56,7 +56,7 @@ class MenuBar {
|
||||
|
||||
private func updateWidth() {
|
||||
var WIDTH: CGFloat = 0
|
||||
for module in modules.value {
|
||||
for module in modules {
|
||||
if module.active.value && module.available.value {
|
||||
WIDTH = WIDTH + module.view.frame.size.width
|
||||
}
|
||||
@@ -82,7 +82,7 @@ class MenuBar {
|
||||
self.stackView = stackView
|
||||
|
||||
var WIDTH: CGFloat = 0
|
||||
for module in modules.value {
|
||||
for module in modules {
|
||||
if module.available.value {
|
||||
if module.active.value {
|
||||
module.initWidget()
|
||||
|
||||
@@ -58,6 +58,10 @@ class BatteryReader: Reader {
|
||||
if self.available {
|
||||
self.read()
|
||||
}
|
||||
|
||||
self.timer = Repeater.init(interval: .seconds(1), observer: { _ in
|
||||
self.read()
|
||||
})
|
||||
}
|
||||
|
||||
func start() {
|
||||
@@ -184,6 +188,6 @@ class BatteryReader: Reader {
|
||||
}
|
||||
|
||||
self.updateInterval = value
|
||||
self.timer?.reset(.seconds(Double(value)))
|
||||
self.timer?.reset(.seconds(Double(value)), restart: false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,12 +59,12 @@ class CPUReader: Reader {
|
||||
self.read()
|
||||
}
|
||||
|
||||
self.timer = Repeater.every(.seconds(1)) { timer in
|
||||
self.timer = Repeater.init(interval: .seconds(1), observer: { _ in
|
||||
self.read()
|
||||
}
|
||||
self.additionalTimer = Repeater.every(.seconds(1)) { timer in
|
||||
})
|
||||
self.additionalTimer = Repeater.init(interval: .seconds(1), observer: { _ in
|
||||
self.readAdditional()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func start() {
|
||||
@@ -142,7 +142,7 @@ class CPUReader: Reader {
|
||||
var numCPUsU: natural_t = 0
|
||||
let err: kern_return_t = host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numCPUsU, &cpuInfo, &numCpuInfo);
|
||||
let usage = getUsage()
|
||||
//
|
||||
|
||||
if err == KERN_SUCCESS {
|
||||
CPUUsageLock.lock()
|
||||
|
||||
@@ -251,8 +251,8 @@ class CPUReader: Reader {
|
||||
}
|
||||
|
||||
self.updateInterval = value
|
||||
self.timer?.reset(.seconds(Double(value)))
|
||||
self.additionalTimer?.reset(.seconds(Double(value)))
|
||||
self.timer?.reset(.seconds(Double(value)), restart: false)
|
||||
self.additionalTimer?.reset(.seconds(Double(value)), restart: false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,10 @@ class DiskReader: Reader {
|
||||
if self.available {
|
||||
self.read()
|
||||
}
|
||||
|
||||
self.timer = Repeater.init(interval: .seconds(1), observer: { _ in
|
||||
self.read()
|
||||
})
|
||||
}
|
||||
|
||||
func start() {
|
||||
@@ -70,6 +74,6 @@ class DiskReader: Reader {
|
||||
}
|
||||
|
||||
self.updateInterval = value
|
||||
self.timer?.reset(.seconds(Double(value)))
|
||||
self.timer?.reset(.seconds(Double(value)), restart: false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,12 +49,12 @@ class MemoryReader: Reader {
|
||||
self.read()
|
||||
}
|
||||
|
||||
self.timer = Repeater.every(.seconds(1)) { timer in
|
||||
self.timer = Repeater.init(interval: .seconds(1), observer: { _ in
|
||||
self.read()
|
||||
}
|
||||
self.additionalTimer = Repeater.every(.seconds(1)) { timer in
|
||||
})
|
||||
self.additionalTimer = Repeater.init(interval: .seconds(1), observer: { _ in
|
||||
self.readAdditional()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func start() {
|
||||
@@ -165,7 +165,7 @@ class MemoryReader: Reader {
|
||||
}
|
||||
|
||||
self.updateInterval = value
|
||||
self.timer?.reset(.seconds(Double(value)))
|
||||
self.additionalTimer?.reset(.seconds(Double(value)))
|
||||
self.timer?.reset(.seconds(Double(value)), restart: false)
|
||||
self.additionalTimer?.reset(.seconds(Double(value)), restart: false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,59 +14,80 @@ class NetworkReader: Reader {
|
||||
public var availableAdditional: Bool = false
|
||||
public var updateInterval: Int = 0
|
||||
|
||||
private var netProcess: Process = Process()
|
||||
private var pipe: Pipe = Pipe()
|
||||
private var timer: Repeater?
|
||||
private var uploadValue: Int64 = 0
|
||||
private var downloadValue: Int64 = 0
|
||||
|
||||
|
||||
init() {
|
||||
self.value = Observable([])
|
||||
netProcess.launchPath = "/usr/bin/env"
|
||||
netProcess.arguments = ["netstat", "-w1", "-l", "en0"]
|
||||
netProcess.standardOutput = pipe
|
||||
|
||||
if self.available {
|
||||
self.read()
|
||||
}
|
||||
|
||||
self.timer = Repeater.init(interval: .seconds(1), observer: { _ in
|
||||
self.read()
|
||||
})
|
||||
}
|
||||
|
||||
func start() {
|
||||
if netProcess.isRunning {
|
||||
return
|
||||
}
|
||||
self.pipe.fileHandleForReading.waitForDataInBackgroundAndNotify()
|
||||
|
||||
NotificationCenter.default.addObserver(forName: NSNotification.Name.NSFileHandleDataAvailable, object: self.pipe.fileHandleForReading , queue: nil) { _ -> Void in
|
||||
defer {
|
||||
self.pipe.fileHandleForReading.waitForDataInBackgroundAndNotify()
|
||||
}
|
||||
|
||||
let output = self.pipe.fileHandleForReading.availableData
|
||||
if output.isEmpty {
|
||||
return
|
||||
}
|
||||
|
||||
let outputString = String(data: output, encoding: String.Encoding.utf8) ?? ""
|
||||
let arr = outputString.condenseWhitespace().split(separator: " ")
|
||||
|
||||
if !arr.isEmpty && Int64(arr[0]) != nil {
|
||||
guard let download = Int64(arr[2]), let upload = Int64(arr[5]) else {
|
||||
return
|
||||
}
|
||||
self.value << [Double(download), Double(upload)]
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
try netProcess.run()
|
||||
} catch let error {
|
||||
print(error)
|
||||
read()
|
||||
if self.timer != nil && self.timer!.state.isRunning == false {
|
||||
self.timer!.start()
|
||||
}
|
||||
}
|
||||
|
||||
func stop() {
|
||||
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.NSFileHandleDataAvailable, object: nil)
|
||||
self.timer?.pause()
|
||||
}
|
||||
|
||||
func read() {}
|
||||
func read() {
|
||||
var interfaceAddresses: UnsafeMutablePointer<ifaddrs>? = nil
|
||||
|
||||
var upload: Int64 = 0
|
||||
var download: Int64 = 0
|
||||
guard getifaddrs(&interfaceAddresses) == 0 else { return }
|
||||
|
||||
var pointer = interfaceAddresses
|
||||
while pointer != nil {
|
||||
guard let info = getDataUsageInfo(from: pointer!) else {
|
||||
pointer = pointer!.pointee.ifa_next
|
||||
continue
|
||||
}
|
||||
pointer = pointer!.pointee.ifa_next
|
||||
upload = info[0]
|
||||
download = info[1]
|
||||
}
|
||||
freeifaddrs(interfaceAddresses)
|
||||
|
||||
let lastUpload = self.uploadValue
|
||||
let lastDownload = self.downloadValue
|
||||
if lastUpload != 0 && lastDownload != 0 {
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.value << [Double(download - lastDownload), Double(upload - lastUpload)]
|
||||
})
|
||||
}
|
||||
|
||||
self.uploadValue = upload
|
||||
self.downloadValue = download
|
||||
}
|
||||
|
||||
func getDataUsageInfo(from infoPointer: UnsafeMutablePointer<ifaddrs>) -> [Int64]? {
|
||||
let pointer = infoPointer
|
||||
|
||||
let name: String! = String(cString: infoPointer.pointee.ifa_name)
|
||||
let addr = pointer.pointee.ifa_addr.pointee
|
||||
guard addr.sa_family == UInt8(AF_LINK) else { return nil }
|
||||
var networkData: UnsafeMutablePointer<if_data>? = nil
|
||||
|
||||
if name.hasPrefix("en") {
|
||||
networkData = unsafeBitCast(pointer.pointee.ifa_data, to: UnsafeMutablePointer<if_data>.self)
|
||||
return [Int64(networkData?.pointee.ifi_obytes ?? 0), Int64(networkData?.pointee.ifi_ibytes ?? 0)] // upload, download
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setInterval(value: Int) {}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ class MainViewController: NSViewController {
|
||||
|
||||
makeHeader()
|
||||
|
||||
for module in modules.value {
|
||||
for module in modules {
|
||||
module.active.subscribe(observer: self) { (value, _) in
|
||||
for tab in self.tabView.tabViewItems {
|
||||
self.tabView.removeTabViewItem(tab)
|
||||
@@ -53,7 +53,7 @@ class MainViewController: NSViewController {
|
||||
}
|
||||
|
||||
override func viewWillAppear() {
|
||||
for module in modules.value {
|
||||
for module in modules {
|
||||
if module.tabAvailable && module.available.value && module.active.value && module.reader.availableAdditional {
|
||||
module.reader.startAdditional()
|
||||
}
|
||||
@@ -61,7 +61,7 @@ class MainViewController: NSViewController {
|
||||
}
|
||||
|
||||
override func viewWillDisappear() {
|
||||
for module in modules.value {
|
||||
for module in modules {
|
||||
if module.tabAvailable && module.available.value && module.active.value && module.reader.availableAdditional {
|
||||
module.reader.stopAdditional()
|
||||
}
|
||||
@@ -70,7 +70,7 @@ class MainViewController: NSViewController {
|
||||
|
||||
func makeHeader() {
|
||||
var list: [String] = []
|
||||
for module in modules.value {
|
||||
for module in modules {
|
||||
if module.tabAvailable && module.available.value && module.active.value {
|
||||
list.append(module.name)
|
||||
|
||||
@@ -124,7 +124,7 @@ class MainViewController: NSViewController {
|
||||
func buildSettings() -> NSMenu {
|
||||
let menu = NSMenu()
|
||||
|
||||
for module in modules.value {
|
||||
for module in modules {
|
||||
if module.available.value {
|
||||
menu.addItem(module.menu)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// Extensions.swift
|
||||
// Mini Stats
|
||||
// Stats
|
||||
//
|
||||
// Created by Serhiy Mytrovtsiy on 29/05/2019.
|
||||
// Copyright © 2019 Serhiy Mytrovtsiy. All rights reserved.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// Observable.swift
|
||||
// Mini Stats
|
||||
// Stats
|
||||
//
|
||||
// Created by Serhiy Mytrovtsiy on 29/05/2019.
|
||||
// Copyright © 2019 Serhiy Mytrovtsiy. All rights reserved.
|
||||
|
||||
Reference in New Issue
Block a user