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
相关产品推荐
相关产品推荐

