如何无递归实现URLSessionWebSocketTask消息监听?递归是否会栈溢出?
URLSessionWebSocketTask 递归接收消息的栈溢出问题与非递归实现
1. 递归实现不会导致栈溢出
你写的递归逻辑本质是异步伪递归,完全不会造成栈溢出。核心原因是:URLSessionWebSocketTask.receive(completionHandler:)是异步方法,它的回调闭包会在任务完成后由系统调度到指定队列执行。当你在回调里调用listen时,上一次的listen函数早已执行完毕并出栈,新的listen调用会创建一个全新的栈帧,不会在原有栈上累积。这种调用方式和同步递归完全不同,不存在栈溢出风险。
2. 非递归实现方案
如果不想用递归,可以通过任务调度或队列管理来实现持续接收,这里提供两种常用方案:
方式一:用 DispatchWorkItem 重复调度
private func startListening(completion: @escaping ((Error) -> Void)) { guard let task, !task.isCancelled else { return } let receiveWork = DispatchWorkItem { [weak self] in guard let self = self, let task = self.task, !task.isCancelled else { return } task.receive { result in switch result { case .success(let message): self.handle(message: message) // 完成当前接收后,重新调度下一次接收 DispatchQueue.global().async(execute: receiveWork) case .failure(let error): completion(error) } } } // 启动首次接收 DispatchQueue.global().async(execute: receiveWork) }
方式二:用 OperationQueue 管理接收任务
通过串行队列保证消息接收顺序,每次接收完成后自动添加下一个接收任务:
private let receiveQueue = OperationQueue() private func startListening(completion: @escaping ((Error) -> Void)) { guard let task, !task.isCancelled else { return } receiveQueue.maxConcurrentOperationCount = 1 // 强制串行,保证消息顺序 func enqueueNextReceive() { let operation = BlockOperation { [weak self] in guard let self = self, let task = self.task, !task.isCancelled else { return } let semaphore = DispatchSemaphore(value: 0) task.receive { result in switch result { case .success(let message): self.handle(message: message) // 完成当前接收后,添加下一个接收任务 self.enqueueNextReceive() case .failure(let error): // 根据业务需求调整completion的执行队列 DispatchQueue.main.async { completion(error) } } semaphore.signal() } semaphore.wait() } receiveQueue.addOperation(operation) } enqueueNextReceive() }
注意:两种方案都需要处理
task的生命周期和取消状态,避免无效调用。
内容的提问来源于stack exchange,提问作者zzheads
相关产品推荐
相关产品推荐

