OHID 是 macOS(和 iOS)底层输入系统的一部分,属于 I/O Kit(驱动层框架),全称是:IOHID = I/O Human Interface Device。
它是一套用于处理 人类输入设备(Human Interface Devices) 的驱动接口,比如:键盘、鼠标、触控板、游戏手柄、触摸屏或自定义输入设备(如扫描枪)。
基本作用
1、底层:比 NSEvent / CGEvent 更低级,直接处理物理输入设备数据;
2、实时:IOHID 能在输入发生的第一时间收到事件(适用于游戏、低延迟设备);
3、权限要求高:很多功能需要辅助功能权限;
4、可绕过 App 层限制:比如可拦截被系统或其他 App 吞掉的组合键;
5、使用复杂:是 C 接口,必须通过复杂的匹配字典、RunLoop 注册等;
6、风险高:不当使用可能影响系统稳定性;macOS 安全策略越来越限制它的使用。
使用示例
1、监听快捷键:
let manager = IOHIDManagerCreate(kCFAllocatorDefault, IOOptionBits(kIOHIDOptionsTypeNone))
let matchingDict: [String: Any] = [
kIOHIDDeviceUsagePageKey as String: kHIDPage_GenericDesktop,
kIOHIDDeviceUsageKey as String: kHIDUsage_GD_Keyboard
]
IOHIDManagerSetDeviceMatching(manager!, matchingDict as CFDictionary)
let callback: IOHIDValueCallback = { context, result, sender, value in
let element = IOHIDValueGetElement(value)
let usagePage = IOHIDElementGetUsagePage(element)
let usage = IOHIDElementGetUsage(element)
let pressed = IOHIDValueGetIntegerValue(value) != 0
let this = Unmanaged<HIDKeyboardMonitor>.fromOpaque(context!).takeUnretainedValue()
let isOptionPressed = false
// Option 键的 usage 是 0x3A(左 Option)
if usage == 0x3A {
isOptionPressed = pressed
}
// A 键的 usage 是 0x04
if usage == 0x04 && pressed && isOptionPressed {
print("Option + A detected!")
}
}
IOHIDManagerRegisterInputValueCallback(manager!, callback, UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()))
IOHIDManagerScheduleWithRunLoop(manager!, CFRunLoopGetCurrent(), CFRunLoopMode.defaultMode.rawValue)
IOHIDManagerOpen(manager!, IOOptionBits(kIOHIDOptionsTypeNone))
这相当于直接监听设备底层输入事件,比 RegisterEventHotKey 优先级更高,也能捕获注册不到的组合键。
权限问题
macOS 对 IOHID 使用非常严格:
1、macOS 10.15+:需要辅助功能权限;
2、部分功能需关闭 App Sandboxing(否则无法监听全局事件);
3、沙盒 App 无法使用 IOHIDManagerCreate 监听系统设备。
总结
IOHID 是 macOS 输入事件的“驱动级入口”,权限高、性能强,但使用复杂且受限制。
实际上IOHID在当前的Swift版本中,比较困难,以下是难点:
1、需要用户授权“输入监控”权限:

否则会提示:
0x1003b4301: TCC deny IOHIDDeviceOpen
这个报错表示应用尝试访问 HID 设备(键盘、鼠标等),但系统拒绝了,因为没有获得用户授权的“输入监控”权限(Input Monitoring)。
2、需要关闭App Sandbox权限,如果不关闭,Xcode会提示:
IOServiceOpen failed: 0xe00002e2
问题原因为,程序没有权限访问某个 IOKit 服务(比如 IOHIDSystem、IOHIDDevice),在“App Sandbox 没有关闭”的前提下,即使赋予输入监控权限,Sandbox 依然会阻止访问 IOHID 低层接口。

3、实际测试发现,TCC权限系统拒绝IOHIDDeviceOpen的访问,即使添加了辅助功能权限、输入监控权限、关闭了App Sandbox。
仍然无法访问物理设备级HID权限,这个权限受 macOS 更深层的保护机制限制(TCC + SIP),尤其是在沙盒外运行的普通 app,macOS 会视其访问硬件键盘为高危操作。
可能的原因为:macOS 的 IOHIDManager 属于低层次的 I/O 框架,设计用于驱动层或系统扩展开发者(如键盘驱动、键盘拦截器),而不是用户空间应用。如果不是系统扩展、内核扩展(KEXT) 或 System Extension(DriverKit) 类型的 App,就会被限制访问。
如果是一个全局热键工具、辅助设备处理器、或模拟键盘输入的底层工具,IOHID 是必学接口。但如果只是做普通 App,优先选 RegisterEventHotKey 或 CGEventTap。
如果想要监听快捷键,可以使用以下三种方式进行替代:
1、NSEvent.addGlobalMonitorForEvents(需要辅助权限)
NSEvent.addGlobalMonitorForEvents(matching: [.keyDown]) { event in
if event.modifierFlags.contains(.option) && event.charactersIgnoringModifiers == "a" {
print("Option + A pressed")
}
}
优点:适合监听键盘事件,行为符合用户预期;
权限要求:必须加入辅助功能权限;
无需关闭 Sandbox。
2、使用 Carbon RegisterEventHotKey(系统层级快捷键注册)
优点:可注册系统级快捷键,抢占式;
权限要求:无辅助权限也可以使用;
优先级比其他监听方式要低。
3、macOS监听/拦截输入事件CGEventTap
优点:低层级机制,监听或拦截系统中的输入事件。
权限要求:需要启用“按键接收”权限。
因为属于Core Graphics框架,API从C和Objective-C演变过来,代码涉及C语言,使用比较复杂。
相关文章
1、macOS注册全局快捷键RegisterEventHotKey :https://fangjunyu.com/2025/07/26/macos%e6%b3%a8%e5%86%8c%e5%85%a8%e5%b1%80%e5%bf%ab%e6%8d%b7%e9%94%aeregistereventhotkey/
2、macOS监听/拦截输入事件CGEventTap:https://fangjunyu.com/2025/07/23/macos%e7%9b%91%e5%90%ac-%e6%8b%a6%e6%88%aa%e8%be%93%e5%85%a5%e4%ba%8b%e4%bb%b6cgeventtap/
3、macOS模拟事件CGEvent:https://fangjunyu.com/2025/07/22/macos%e6%a8%a1%e6%8b%9f%e4%ba%8b%e4%bb%b6cgevent/
4、Apple安全机制TCC:https://fangjunyu.com/2025/07/26/apple%e5%ae%89%e5%85%a8%e6%9c%ba%e5%88%b6tcc/