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

Xcode 15突发警告:监听系统音量的代码触发跨进程通信提示

解决Xcode 15中监听系统音量的运行时警告问题

问题分析

升级Xcode 15后,原有监听系统音量的代码在初始化@Published var volume时触发运行时警告,而Xcode 14中无此问题。核心原因是:在属性定义阶段直接调用AVAudioSession.sharedInstance().outputVolume时,AVAudioSession尚未完成激活流程,Xcode 15修复了Xcode 14的误判bug后,这类不符合规范的初始化逻辑被正确识别并抛出警告。

修复方案

方案1:延迟初始化初始音量值

不在属性定义时直接赋值,而是在激活AudioSession后再设置初始音量,同时确保在主线程更新@Published属性(SwiftUI要求发布属性的修改必须在主线程执行):

修改后的VolumeObserver代码:

final class VolumeObserver: ObservableObject {

    @Published var volume: Float = 0.0 // 先设默认占位值

    private let session = AVAudioSession.sharedInstance()
    private var progressObserver: NSKeyValueObservation!

    func subscribe() {
        do {
            try session.setCategory(.ambient)
            try session.setActive(true, options: .notifyOthersOnDeactivation)
            // 激活Session后再获取初始音量,切换到主线程更新
            DispatchQueue.main.async {
                self.volume = self.session.outputVolume
            }
        } catch {
            print("cannot activate session")
        }
        
        progressObserver = session.observe(\.outputVolume) { [weak self] (session, _) in
            guard let self = self else { return }
            DispatchQueue.main.async {
                self.volume = session.outputVolume
            }
        }
    }

    func unsubscribe() {
        progressObserver.invalidate()
        // 可选:根据业务需求停用Session
        do {
            try session.setActive(false)
        } catch {
            print("cannot deactivate session")
        }
    }

    init() {
        subscribe()
    }
    
    deinit {
        unsubscribe() // 销毁时自动清理监听资源
    }
}

方案2:使用惰性初始化

如果希望保留属性的初始赋值逻辑,可以用lazy修饰,确保首次访问volume时,AudioSession已经完成激活:

final class VolumeObserver: ObservableObject {

    lazy @Published var volume: Float = {
        AVAudioSession.sharedInstance().outputVolume
    }()

    private let session = AVAudioSession.sharedInstance()
    private var progressObserver: NSKeyValueObservation!

    func subscribe() {
        do {
            try session.setCategory(.ambient)
            try session.setActive(true, options: .notifyOthersOnDeactivation)
        } catch {
            print("cannot activate session")
        }
        
        progressObserver = session.observe(\.outputVolume) { [weak self] (session, _) in
            guard let self = self else { return }
            DispatchQueue.main.async {
                self.volume = session.outputVolume
            }
        }
    }

    // 其余unsubscribe、init、deinit方法同方案1
}

额外优化点

  • 监听闭包中使用[weak self]避免循环引用,原代码的[self]可能导致内存泄漏
  • 在deinit中调用unsubscribe,确保对象销毁时自动清理监听和Session资源
  • 视图中的onReceive逻辑无需修改,保持原有写法即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 17:28:29