macOS开发:如何监听iOS设备接入或断开Mac的通知
实现方案
你可以通过IOKit框架监听USB设备的插拔事件,过滤Apple Vendor ID即可实现iPhone、iPad的接入/断开检测,具体实现步骤如下:
1. 项目配置
引入IOKit.framework到项目中,若应用开启了沙盒,需要在签名与权限配置中开启USB访问权限。
2. 导入依赖头文件
#import <IOKit/IOKitLib.h> #import <IOKit/usb/IOUSBLib.h>
3. 核心实现代码
先声明全局变量存储监听上下文:
// 全局通知端口 IONotificationPortRef gNotifyPort; // 设备接入迭代器 io_iterator_t gAddedIter; // 设备断开迭代器 io_iterator_t gRemovedIter;
实现设备监听启动方法:
- (void)startMonitoringIOSDevices { // 创建USB设备匹配字典 CFMutableDictionaryRef matchDict = IOServiceMatching(kIOUSBDeviceClassName); if (!matchDict) return; // 过滤Apple设备的固定Vendor ID: 0x05AC int appleVID = 0x05AC; CFNumberRef vidRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &appleVID); CFDictionarySetValue(matchDict, CFSTR(kUSBVendorID), vidRef); CFRelease(vidRef); // 初始化通知端口并加入当前RunLoop gNotifyPort = IONotificationPortCreate(kIOMasterPortDefault); CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(gNotifyPort), kCFRunLoopDefaultMode); // 注册设备接入回调 IOServiceAddMatchingNotification(gNotifyPort, kIOFirstMatchNotification, matchDict, DeviceAddedCallback, (__bridge void *)self, &gAddedIter); // 手动调用一次处理已连接的设备 DeviceAddedCallback((__bridge void *)self, gAddedIter); // 注册设备断开回调,需重新创建匹配字典 CFMutableDictionaryRef removeMatchDict = IOServiceMatching(kIOUSBDeviceClassName); CFNumberRef removeVidRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &appleVID); CFDictionarySetValue(removeMatchDict, CFSTR(kUSBVendorID), removeVidRef); CFRelease(removeVidRef); IOServiceAddMatchingNotification(gNotifyPort, kIOTerminatedNotification, removeMatchDict, DeviceRemovedCallback, (__bridge void *)self, &gRemovedIter); DeviceRemovedCallback((__bridge void *)self, gRemovedIter); }
实现设备插拔回调函数:
// 设备接入回调 void DeviceAddedCallback(void *refCon, io_iterator_t iterator) { io_service_t device; while ((device = IOIteratorNext(iterator))) { // 读取设备产品名判断是否为iPhone/iPad CFStringRef productNameRef = IORegistryEntryCreateCFProperty(device, CFSTR(kUSBProductString), kCFAllocatorDefault, 0); if (productNameRef) { NSString *productName = (__bridge_transfer NSString *)productNameRef; if ([productName containsString:@"iPhone"] || [productName containsString:@"iPad"]) { // 切换到主线程更新UI dispatch_async(dispatch_get_main_queue(), ^{ // 这里调用你自己的NSView提示展示逻辑 NSLog(@"设备接入: %@", productName); }); } } IOObjectRelease(device); } } // 设备断开回调 void DeviceRemovedCallback(void *refCon, io_iterator_t iterator) { io_service_t device; while ((device = IOIteratorNext(iterator))) { CFStringRef productNameRef = IORegistryEntryCreateCFProperty(device, CFSTR(kUSBProductString), kCFAllocatorDefault, 0); if (productNameRef) { NSString *productName = (__bridge_transfer NSString *)productNameRef; if ([productName containsString:@"iPhone"] || [productName containsString:@"iPad"]) { dispatch_async(dispatch_get_main_queue(), ^{ // 这里调用你自己的NSView提示移除逻辑 NSLog(@"设备断开: %@", productName); }); } } IOObjectRelease(device); } }
实现资源释放方法,避免内存泄漏:
- (void)stopMonitoringIOSDevices { if (gAddedIter) { IOObjectRelease(gAddedIter); gAddedIter = 0; } if (gRemovedIter) { IOObjectRelease(gRemovedIter); gRemovedIter = 0; } if (gNotifyPort) { IONotificationPortDestroy(gNotifyPort); gNotifyPort = NULL; } }
注意事项
- 所有UI更新操作必须切换到主线程执行,否则会出现界面异常或崩溃
- 如果需要更精准的过滤,避免把其他Apple USB外设(比如妙控键盘、鼠标)纳入检测,可以在回调中额外匹配设备Product ID,接入对应设备打印PID后自行添加过滤规则即可
- 若应用使用沙盒机制,必须开启USB访问权限,否则无法获取USB设备信息
内容的提问来源于stack exchange,提问作者Edward Chou
相关产品推荐
相关产品推荐

