Swift:如何等待Delegate回调完成?第三方SDK异步转同步问询
解决方案:用信号量实现同步等待回调结果
这是个典型的「回调地狱转同步流程」场景,咱们可以借助DispatchSemaphore在单线程环境下阻塞等待第三方SDK的回调,同时改造TheirServiceDelegate的实现来捕获每个步骤的成功/失败状态,把异步回调转换成清爽的同步函数调用。下面一步步拆解实现:
1. 改造MyClient核心结构
首先在MyClient中添加几个关键成员:用于阻塞等待的信号量、存储步骤错误的变量、存储最终结果的变量,同时给第三方SDK绑定Delegate:
class MyClient: TheirServiceDelegate { static let instance = MyClient() let myService = TheirService() // 信号量:初始值为0,用来阻塞当前线程直到回调触发 private let semaphore = DispatchSemaphore(value: 0) // 存储当前步骤的错误信息(用于抛出异常) private var currentStepError: Error? // 存储最终业务结果 private var finalUsefulResult: SomeObject? private init() { // 给第三方SDK绑定当前类作为Delegate myService.delegate = self } // MARK: - 同步流程方法 func connect(user: String, password: String) throws { // 每个步骤开始前重置状态,避免上一次的结果/错误干扰 resetFlowState() // 执行初始化 myService.initialize() // 等待回调触发,设置10秒超时防止无限阻塞 if semaphore.wait(timeout: .now() + 10) == .timedOut { throw CustomFlowError.timeout } // 如果有错误,直接抛出 if let error = currentStepError { throw error } // 执行bootstrap resetFlowState() myService.bootstrap() semaphore.wait(timeout: .now() + 10) if let error = currentStepError { throw error } // 执行登录 resetFlowState() myService.login(user, password) semaphore.wait(timeout: .now() + 10) if let error = currentStepError { throw error } } func doSomethingUseful() throws -> SomeObject { resetFlowState() // 执行步骤1 myService.doSomethingUsefulStep1() semaphore.wait(timeout: .now() + 10) if let error = currentStepError { throw error } // 执行步骤2 resetFlowState() myService.doSomethingUsefulStep2() semaphore.wait(timeout: .now() + 10) if let error = currentStepError { throw error } // 返回最终结果,无结果则抛出异常 guard let result = finalUsefulResult else { throw CustomFlowError.noResultReturned } return result } // MARK: - 辅助方法 private func resetFlowState() { currentStepError = nil finalUsefulResult = nil // 信号量的计数会被wait消耗,这里无需额外重置 } // MARK: - TheirServiceDelegate 回调实现 func onInitializeSuccess() { // 成功则直接唤醒等待的线程 semaphore.signal() } func onInitializeFailure(error: Error) { currentStepError = error semaphore.signal() } func onBootstrapSuccess() { semaphore.signal() } func onBootstrapFailure(error: Error) { currentStepError = error semaphore.signal() } func onLoginSuccess() { semaphore.signal() } func onLoginFailure(error: Error) { currentStepError = error semaphore.signal() } func onDoSomethingUsefulStep1Success() { semaphore.signal() } func onDoSomethingUsefulStep1Failure(error: Error) { currentStepError = error semaphore.signal() } func onDoSomethingUsefulStep2Success(result: SomeObject) { finalUsefulResult = result semaphore.signal() } func onDoSomethingUsefulStep2Failure(error: Error) { currentStepError = error semaphore.signal() } } // 自定义错误类型,方便统一抛出流程异常 enum CustomFlowError: Error { case timeout case noResultReturned }
2. 关键逻辑说明
- 信号量的核心作用:每次调用第三方SDK方法后,
semaphore.wait()会阻塞当前线程,直到Delegate回调触发semaphore.signal(),线程才会继续执行下一个步骤,完美适配第三方SDK的单线程串行要求。 - 状态重置:每个步骤开始前调用
resetFlowState()清空错误和结果,避免上一次流程的状态污染当前流程。 - 超时保护:给
wait设置超时时间,防止第三方SDK回调异常导致线程无限阻塞。 - 错误传递:在Delegate的失败回调中存储错误,唤醒线程后在同步方法中检查并抛出异常,实现错误的向上传递。
3. 调用方式
现在你可以完全按照期望的同步方式调用:
do { try MyClient.instance.connect(user: "your_username", password: "your_password") let usefulResult = try MyClient.instance.doSomethingUseful() // 处理业务结果 print("获取到结果:\(usefulResult)") } catch { // 统一处理所有流程错误 print("流程执行失败:\(error)") }
注意事项
- 务必在非主线程调用
connect()和doSomethingUseful(),如果在主线程调用semaphore.wait()会导致UI卡死。 - 如果第三方SDK的回调是在主线程触发的,信号量的
signal()可以正常唤醒等待的后台线程,不会有冲突。 - 可以根据业务场景调整超时时间,或者在极端情况下去掉超时(不推荐,避免线程卡死)。
内容的提问来源于stack exchange,提问作者timbre timbre
相关产品推荐
相关产品推荐

