Swift开发macOS USB检测工具 插入iPhone输出异常如何修复
问题根因
你遇到的插入时重复回调的核心问题是IOKit的IOServiceAddMatchingNotification函数会消耗传入的匹配字典(自动释放字典的引用),你将同一份匹配字典同时传给了匹配通知和终止通知,第一个通知调用完成后字典就被释放,第二个通知拿到的是已释放的野指针对象,匹配规则失效,导致触发了异常的Terminated回调。
解决方案
第一步:创建两份独立的匹配字典
在startDetection方法中,分别为匹配通知和终止通知创建独立的匹配字典,不要共用同一个实例:
func startDetection ( ) -> Bool { guard matchedIterator == 0 else { return true } // 为匹配通知创建独立的匹配字典 let matchMatchingDict = IOServiceMatching(kIOUSBDeviceClassName) as! NSMutableDictionary matchMatchingDict[kUSBVendorID] = NSNumber(value: vendorID) matchMatchingDict[kUSBProductID] = NSNumber(value: productID) // 为终止通知创建独立的匹配字典 let termMatchingDict = IOServiceMatching(kIOUSBDeviceClassName) as! NSMutableDictionary termMatchingDict[kUSBVendorID] = NSNumber(value: vendorID) termMatchingDict[kUSBProductID] = NSNumber(value: productID) let matchCallback: IOServiceMatchingCallback = { (userData, iterator) in let detector = Unmanaged<IOUSBDetector> .fromOpaque(userData!).takeUnretainedValue() detector.dispatchEvent( event: .Matched, iterator: iterator ) }; let termCallback: IOServiceMatchingCallback = { (userData, iterator) in let detector = Unmanaged<IOUSBDetector> .fromOpaque(userData!).takeUnretainedValue() detector.dispatchEvent( event: .Terminated, iterator: iterator ) }; let selfPtr = Unmanaged.passUnretained(self).toOpaque() // 匹配通知传入独立的matchMatchingDict let addMatchError = IOServiceAddMatchingNotification( self.notifyPort, kIOFirstMatchNotification, matchMatchingDict, matchCallback, selfPtr, &self.matchedIterator ) // 终止通知传入独立的termMatchingDict let addTermError = IOServiceAddMatchingNotification( self.notifyPort, kIOTerminatedNotification, termMatchingDict, termCallback, selfPtr, &self.terminatedIterator ) guard addMatchError == 0 && addTermError == 0 else { if self.matchedIterator != 0 { IOObjectRelease(self.matchedIterator) self.matchedIterator = 0 } if self.terminatedIterator != 0 { IOObjectRelease(self.terminatedIterator) self.terminatedIterator = 0 } return false } // This is required even if nothing was found to "arm" the callback self.dispatchEvent(event: .Matched, iterator: self.matchedIterator) self.dispatchEvent(event: .Terminated, iterator: self.terminatedIterator) return true }
第二步(可选,兼容特殊设备)
如果部分特殊型号的iPhone因为模式切换仍会出现短时间重复匹配的情况,可以增加设备唯一标识跟踪逻辑,彻底避免重复回调:
- 给
IOUSBDetector类增加私有属性,存储已激活的设备ID:
private var activeDeviceIDs: Set<UInt64> = [] private let queueLock = NSLock()
- 修改
dispatchEvent方法,增加设备ID校验逻辑:
private func dispatchEvent ( event: Event, iterator: io_iterator_t ) { repeat { let nextService = IOIteratorNext(iterator) guard nextService != 0 else { break } // 获取设备唯一注册表ID var deviceID: UInt64 = 0 let status = IORegistryEntryGetRegistryEntryID(nextService, &deviceID) guard status == KERN_SUCCESS else { IOObjectRelease(nextService) continue } queueLock.lock() defer { queueLock.unlock() } if event == .Matched { // 已存在的设备不重复触发回调 guard !activeDeviceIDs.contains(deviceID) else { IOObjectRelease(nextService) continue } activeDeviceIDs.insert(deviceID) } else if event == .Terminated { // 未记录的设备终止不触发回调 guard activeDeviceIDs.contains(deviceID) else { IOObjectRelease(nextService) continue } activeDeviceIDs.remove(deviceID) } if let cb = self.callback, let q = self.callbackQueue { q.async { cb(self, event, nextService) IOObjectRelease(nextService) } } else { IOObjectRelease(nextService) } } while (true) }
修改完成后,插入iPhone时就只会触发一次Matched输出,拔出时触发一次Terminated输出。
内容的提问来源于stack exchange,提问作者Kênh Vui Để Học
相关产品推荐
相关产品推荐

