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

如何让iOS NFC交互函数同步执行?无需usleep等待结果

解决iOS NFC ISO15693命令同步执行问题

你的核心问题是sendI2CCommand依赖异步的NFC请求,但当前实现无法等待异步回调完成就返回结果,用usleep是不可靠的临时方案,信号量崩溃则是因为线程死锁——NFC回调默认在主线程执行,若你在主线程调用semaphore.wait(),主线程会被阻塞,回调永远无法触发signal(),导致死锁。

以下是两种可靠的解决方案:

方案一:修复信号量实现(兼容iOS 14及以下)

确保信号量的等待操作在后台线程执行,同时所有分支都必须调用signal(),避免永久阻塞。修改后的代码如下:

public static func sendI2CCommand(tag: NFCISO15693Tag, command: UInt8) -> String {
    var res = ""
    let semaphore = DispatchSemaphore(value: 0)
    
    tag.sendRequest(requestFlags: 0x12, commandCode: 0xD4, data: Data([0x04, 0x02, 0x00, command])) { result in
        switch result {
        case .success:
            semaphore.signal()
        case .failure:
            tag.sendRequest(requestFlags: 0x12, commandCode: 0xD5, data: Data([0x04, 0x02, 0x00])) { secondResult in
                switch secondResult {
                case .success:
                    semaphore.signal()
                case .failure:
                    tag.sendRequest(requestFlags: 0x12, commandCode: 0xD2, data: Data([0x04, 0x00, 0x06])) { thirdResult in
                        switch thirdResult {
                        case .success((let responseFlag, let response2)):
                            var foo = response2
                            foo.insert(responseFlag.rawValue, at: 0)
                            let responseHex = foo.hexadecimalCompressed.substring(with: 2..<4)
                            res = responseHex
                            semaphore.signal()
                        case .failure, default:
                            semaphore.signal()
                        }
                    }
                default:
                    semaphore.signal()
                }
            }
        default:
            semaphore.signal()
        }
    }
    
    // 在后台线程等待,避免主线程死锁
    DispatchQueue.global().sync {
        semaphore.wait()
    }
    
    return res
}

方案二:使用Async/Await(iOS 15+,推荐)

通过withCheckedThrowingContinuation将异步的sendRequest包装为Async函数,彻底消除回调嵌套和线程死锁问题,代码更易维护。

1. 封装NFC请求为Async方法

extension NFCISO15693Tag {
    func sendRequestAsync(requestFlags: UInt8, commandCode: UInt8, data: Data) async throws -> (NFCISO15693ResponseFlag, Data) {
        return try await withCheckedThrowingContinuation { continuation in
            self.sendRequest(requestFlags: requestFlags, commandCode: commandCode, data: data) { result in
                switch result {
                case .success(let response):
                    continuation.resume(returning: response)
                case .failure(let error):
                    continuation.resume(throwing: error)
                }
            }
        }
    }
}

2. 修改sendI2CCommand为Async函数

public static func sendI2CCommand(tag: NFCISO15693Tag, command: UInt8) async -> String {
    var res = ""
    do {
        // 执行第一条命令
        try await tag.sendRequestAsync(requestFlags: 0x12, commandCode: 0xD4, data: Data([0x04, 0x02, 0x00, command]))
    } catch {
        do {
            // 第一条失败,执行第二条
            try await tag.sendRequestAsync(requestFlags: 0x12, commandCode: 0xD5, data: Data([0x04, 0x02, 0x00]))
        } catch {
            do {
                // 第二条失败,执行第三条并处理结果
                let (responseFlag, response2) = try await tag.sendRequestAsync(requestFlags: 0x12, commandCode: 0xD2, data: Data([0x04, 0x00, 0x06]))
                var foo = response2
                foo.insert(responseFlag.rawValue, at: 0)
                let responseHex = foo.hexadecimalCompressed.substring(with: 2..<4)
                res = responseHex
            } catch {
                // 处理所有错误,返回空字符串或自定义错误标记
                res = ""
            }
        }
    }
    return res
}

3. 调用方式

用Task启动异步任务,自动按顺序执行命令:

Task {
    guard let tag = self.tag else { return }
    let fw_id = await NFCUtils.sendI2CCommand(tag: tag, command: NFCConstants.CONFIG_FW_ID)
    print("Read FirmwareID \(fw_id)")
    
    let sw_id = await NFCUtils.sendI2CCommand(tag: tag, command: NFCConstants.CONFIG_SW_ID)
    print("Read SotfwareID \(sw_id)")
    
    let ex_id = await NFCUtils.sendI2CCommand(tag: tag, command: NFCConstants.CONFIG_EX_ID)
    print("Read ExternalID \(ex_id)")
}

内容的提问来源于stack exchange,提问作者tiamat

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 00:32:41