如何确保AVSpeechSynthesizer存活至AsyncStream完成?求优雅实现
解决AVSpeechSynthesizer转AsyncStream时的生命周期问题
你的判断没错,原代码失效的核心原因是局部的AVSpeechSynthesizer实例在AsyncStream闭包执行完毕后就被销毁,导致音频合成的回调无法持续触发,流自然没有输出。
下面是符合Swift风格的优化实现,既能保持toSpeechStream的简洁签名,又能确保合成器在流生命周期内存活:
func toSpeechStream(utterance: AVSpeechUtterance) -> AsyncStream<AVAudioPCMBuffer> { AsyncStream { continuation in let synthesizer = AVSpeechSynthesizer() // 通过continuation的终止回调持有合成器,避免提前释放 continuation.onTermination = { _ in synthesizer.stopSpeaking(at: .immediate) } synthesizer.write(utterance, toBufferCallback: { buffer in guard let pcmBuffer = buffer as? AVAudioPCMBuffer else { return } continuation.yield(pcmBuffer) // 用frameLength判断合成结束:系统会返回空缓冲区作为结束标记 if pcmBuffer.frameLength == 0 { continuation.finish() } }) } }
关键优化点:
- 生命周期绑定:利用
continuation.onTermination回调,让合成器被continuation间接持有,直到流正常完成或被主动取消。流终止时,回调会触发并停止合成,随后合成器会被自动释放,无内存泄漏风险。 - 正确的结束判定:原代码用
frameCapacity <= 1判断结束不准确,应该检查frameLength == 0——AVSpeechSynthesizer在合成完成时,会返回一个实际帧数为0的缓冲区作为结束信号。
这样实现后,外部调用只需简单消费流即可,无需额外管理合成器的生命周期:
for await buffer in toSpeechStream(utterance: myUtterance) { // 处理音频缓冲区 }
内容的提问来源于stack exchange,提问作者Matt Bierner
相关产品推荐
相关产品推荐

