写在文章之前,首先先要声明的是,笔者是第一次编写macOS APP,如有欠缺,还请斧正。
macOS系统什么都好,但是笔者是个强迫症患者,还是希望可以看到实时网速的,基于这个目的,我在app store中搜索了相关的软件,结果都不是免费的,幸好笔者自身从事IT行业,技术马马虎虎,就想着写个小工具给自己使用,期间一共花费两天时间,记录下来,分享给同行,以上就是此篇文章的由来。
要想在状态栏显示实时网速,笔者将其分解为两个步骤:1.数据源的获取,即如何获取实时网速。2.显示到状态栏。
数据源的获取
如何获取实时网速?第一反应就是官方的API,结果我在官方文档并没有找到相关的API(如有同行知道,还请告知于笔者),所以,我尝试在百度上搜索,结果还是一无所获,在此情况下,我使用Google搜索有关资料,在一个GitHub作者的作品中,我发现了该作者是使用了nettop的命令行工具,大致原理即定时启动shell进程执行此命令,将返回的数据进行分析,获取到实时网速。这种方法也不是不行,但是定时器不停地启动进程,相应的性能就会差很多,权衡了下,最终放弃了这个方案。这时候,笔者突然记起Linux系统下面有个ifaddrs.h的头文件,在其中有个if_data的指针,这个指针的声明里就包含总进出流量,也就是说,只要定时检查进出流量,再除以间隔时间,就可以大致得到实时流量。
想到这里,直接在macOS系统下查看了下头文件,并写了个demo测试了下,发现没问题,可以使用(不知道为什么,在笔者系统上,系统自带的活动监视器里面的流量统计和此函数获得的数据不太一致,但计算实时网速的时候相差不大,有厉害的大佬还望告知)。于是我就编写了一个C库给swift调用。以下就是大致代码。
#include "netlib.h"#include<ifaddrs.h>#include<net/if.h>#include<stdlib.h>int request_net_speed(p_callback func,void* obj){ struct ifaddrs *ifa_list=NULL; struct ifaddrs *ifa=NULL; struct if_data *ifd=NULL; int ret=0; ret=getifaddrs(&ifa_list); if(ret<0) return -1; for(ifa=ifa_list;ifa;ifa=ifa->ifa_next) { if(!(ifa->ifa_flags&IFF_UP)&&!(ifa->ifa_flags&IFF_RUNNING)) continue; if(ifa->ifa_data==0) continue; ifd=(struct if_data*)ifa->ifa_data; func(ifa->ifa_name,ifd->ifi_ibytes,ifd->ifi_obytes,obj); } ifa=NULL; ifd=NULL; free(ifa_list); return 0;}
这个函数传入一个函数指针,执行外部方法。以上数据源的获取就完成了。
显示到状态栏
笔者是第一次编写macOS APP,所有很多东西都是通过百度或者谷歌查询到的,首选的语言是swift,至于为什么不用OC,毕竟swift是官方主推的语言嘛。
其实到这里就很简单了,特别是对一些苹果开发者来说,不过笔者是新手,所以这边就把笔者认为的重要的代码贴下。
private let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)func applicationDidFinishLaunching(_ aNotification: Notification) { // Create the SwiftUI view that provides the window contents. let barHeight = NSApplication.shared.mainMenu?.menuBarHeight testCalcFontSize(barHeight:barHeight!) let menu = NSMenu() // menu.addItem(withTitle: "开始", action: #selector(onWake), keyEquivalent: "") // menu.addItem(withTitle: "暂停", action: #selector(onSleep), keyEquivalent: "") menu.addItem(withTitle: "退出", action: #selector(quitApp), keyEquivalent: "") statusItem.menu = menu start(); // 休眠或唤醒时重新运行程序 NSWorkspace.shared.notificationCenter.addObserver( self, selector: #selector(onWake(notification:)), name: NSWorkspace.didWakeNotification, object: nil) NSWorkspace.shared.notificationCenter.addObserver( self, selector: #selector(onSleep(notification:)), name: NSWorkspace.willSleepNotification, object: nil) }private func start(){ let observer = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()) // obj为捕获的实例 即传入的observer timer = Timer.scheduledTimer(withTimeInterval: TimeInterval(interval), repeats: true){ (ktimer) in request_net_speed({ (name,ibytes,obytes,obj) in guard obj != nil else { return } let myself = Unmanaged<AppDelegate>.fromOpaque(UnsafeMutableRawPointer(obj!)).takeUnretainedValue() myself.showText(name: name!, ibytes: ibytes, obytes: obytes) }, observer) } }
笔者个人认为只要找到NSStatusBar这个类,那么你就成功了一大半,至于剩下的工作就是如何调用这个类将需要显示的内容显示上去。其中,感觉有点技术难度的就是用swift调用C了,原本这个小工具计划一天搞定的,之所以两天就是卡在了这个地方,这边的答案是从stack overflow上找到的,简单说下。request_net_speed这个是个C方法,将swift中的回调方法传入到C中,因为笔者是第一次使用swift一时间没理解过来,在C# java swift等语言中,有个重要的概念叫实例,笔者就卡在这里,这边需要将类自身的实例传到C中,然后C调用swift方法的时候,由swift捕获此实例,再使用此实例调用实例自身的方法(即myself.showText)。
以上就是自己动手编写macOS实时网速显示,挂上GitHub「链接」,有需要的小伙伴可以拉取源代码。
TIPS:“nettool”这是前文提过的作者的作品,没得到授权就不挂链接了,可以自己搜索下。
版权声明:内容来源于互联网和用户投稿 如有侵权请联系删除