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

MacOS AudioHardwareSystem内存泄漏:新CoreAudio API如何避免泄漏?

CoreAudio新API内存泄漏问题的临时解决方法

我喜爱新CoreAudio API的简洁语法,但它存在严重的内存泄漏问题。请问有没有办法不用退回旧API就能避免这个泄漏?

问题重现代码(存在内存泄漏)

func coreAudioProcessLeak() async throws {
    for process in try AudioHardwareSystem.shared.processes {
        print(try process.bundleID as Any)
    }
}

已通过Instruments确认,泄漏点指向for process in try AudioHardwareSystem.shared.processes这一行。

无泄漏但写法繁琐的旧API实现

func coreAudioProcessNoLeak() async throws {

    var listAddr = AudioObjectPropertyAddress(
        mSelector: kAudioHardwarePropertyProcessObjectList,
        mScope:    kAudioObjectPropertyScopeGlobal,
        mElement:  kAudioObjectPropertyElementMain
    )
    
    var listBytes: UInt32 = 0
    var status = AudioObjectGetPropertyDataSize(
        AudioObjectID(kAudioObjectSystemObject),
        &listAddr,
        0,
        nil,
        &listBytes
    )
    guard status == noErr else { throw NSError(
        domain: NSOSStatusErrorDomain, code: Int(status), userInfo: nil) }
    guard listBytes > 0 else { return }
    
    let count = Int(listBytes) / MemoryLayout<AudioObjectID>.stride
    var procIDs = Array<AudioObjectID>(repeating: 0, count: count)
    
    var tmpBytes = listBytes
    status = AudioObjectGetPropertyData(
        AudioObjectID(kAudioObjectSystemObject),
        &listAddr,
        0,
        nil,
        &tmpBytes,
        &procIDs
    )
    guard status == noErr else { throw NSError(
        domain: NSOSStatusErrorDomain, code: Int(status), userInfo: nil) }
    

    for procID in procIDs {
        var bidAddr = AudioObjectPropertyAddress(
            mSelector: kAudioProcessPropertyBundleID,
            mScope:    kAudioObjectPropertyScopeGlobal,
            mElement:  kAudioObjectPropertyElementMain
        )
        
        // 该属性的载荷为CFStringRef
        var bidBytes: UInt32 = UInt32(MemoryLayout<CFString?>.size)
        var cfStr: CFString? = nil
        let err = withUnsafeMutablePointer(to: &cfStr) { cfStr in
            AudioObjectGetPropertyData(
                procID,
                &bidAddr,
                0,
                nil,
                &bidBytes,
                cfStr
            )
        }

        if err == noErr, let cfStr {
            print(cfStr as String)
        }
    }
}

无需退回旧API的临时修复方案

这个泄漏属于Apple新CoreAudio API的底层实现问题,目前可以通过以下两种方式临时规避:

  • 将processes序列转为数组后遍历
    通过Array(try AudioHardwareSystem.shared.processes)先将序列转为数组,明确管理所有Process实例的生命周期,避免迭代过程中内存无法及时释放:

    func coreAudioProcessFixed1() async throws {
        let processes = Array(try AudioHardwareSystem.shared.processes)
        for process in processes {
            print(try process.bundleID as Any)
        }
    }
    
  • 在循环内添加自动释放池
    利用autoreleasepool包裹每次迭代的代码,强制释放当前循环中创建的临时对象,解决内存累积问题:

    func coreAudioProcessFixed2() async throws {
        for process in try AudioHardwareSystem.shared.processes {
            autoreleasepool {
                print(try process.bundleID as Any)
            }
        }
    }
    

如果以上方法仍无效,建议同时向Apple提交Bug Report,督促官方修复API的内存泄漏问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 23:04:51