You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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因为模式切换仍会出现短时间重复匹配的情况,可以增加设备唯一标识跟踪逻辑,彻底避免重复回调:

  1. 给IOUSBDetector类增加私有属性,存储已激活的设备ID:
private var activeDeviceIDs: Set<UInt64> = []
private let queueLock = NSLock()
  1. 修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 19:54:05