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

如何确保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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 21:20:05