Swift 和 AudioFormatGetProperty(_:_:_:_:_:)

Swift and AudioFormatGetProperty(_:_:_:_:_:)

我想知道如何使用 swift.

管理无人管理的 CFType AudioFormatGetProperty()

这个功能是低级的API,还有一些kAudioFormatProperty_XXXreturns 没有注释的 CFType 对象。根据文档,它说

The caller is responsible for releasing the returned string.

The caller must call the CFRelease function for the returned dictionary.

kAudioFormatProperty_FormatName
kAudioFormatProperty_ChannelLayoutName
kAudioFormatProperty_ChannelLayoutSimpleName
kAudioFormatProperty_ChannelName
kAudioFormatProperty_ChannelShortName
kAudioFormatProperty_ID3TagToDictionary

Swift 没有 CFRelease 支持,所以我认为在这种情况下,调用者必须执行类似 CFRelease 的操作。 能给点建议吗?

我的代码片段如下:

var aclSize : Int = 0
let aclPtr : UnsafePointer<AudioChannelLayout>? =
    CMAudioFormatDescriptionGetChannelLayout(desc, &aclSize)

var nameSize : UInt32 = 0
let err1 : OSStatus =
    AudioFormatGetPropertyInfo(kAudioFormatProperty_ChannelLayoutName,
                               UInt32(aclSize), aclPtr, &nameSize)

let count : Int = Int(nameSize) / MemoryLayout<CFString>.size
let ptr : UnsafeMutablePointer<CFString> =
    UnsafeMutablePointer<CFString>.allocate(capacity: count)

let err2 : OSStatus =
    AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutName,
                           UInt32(aclSize), aclPtr, &nameSize, ptr)

// do something here

ptr.deallocate() // Is this same as CFRelease(cfstringref)?

我找到了更好的 AudioFormatGetProperty() 解决方案。

简单的解决方法:

kAudioFormatProperty_* 类似于 FormatName 或 ChannelLayoutName return 单个 CFString 值。

在这种情况下,我可以在 ARC 支持下使用以下功能。无需手动释放

//
var formatSize : UInt32 = UInt32(MemoryLayout<CFString>.size)
var format : CFString!
let err : OSStatus =
    AudioFormatGetProperty(kAudioFormatProperty_FormatName,
                           asbdSize, asbdPtr, &formatSize,
                           &format)
formatString = String(format as NSString)

//
var nameSize : UInt32 = UInt32(MemoryLayout<CFString>.size)
var name : CFString!
let err : OSStatus =
    AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutName,
                           UInt32(aclSize), aclPtr,
                           &nameSize, &name)
layoutString = String(name as NSString)

注意:

如何识别"non-annoted CFType object is really unmanaged or not".

以下是我在这个问题上做的廉价调试程序。 我认为这是不好的诀窍,所以我想知道更多更好的方法。

1) 检查 CFType 的 RetainCount。

如果它像 1152921504606846975 这样巨大的价值,那么它是常量 - 不需要 release()。

let ptr : UnsafeMutablePointer<CFString> = ...
CFShow(ptr.pointee) // to confirm it is live
Swift.print(CFGetRetainCount(ptr.pointee)) // to check retainCount

2) 如果它是像 2 或 3 这样的小值,那么尝试像这样自动释放它:

let unmanaged = Unmanaged<CFString>.passUnretained(ptr.pointee)
_ = unmanaged.autorelease()

3) 如果它立即崩溃(在 runloop 内),它会被管理(可能由也可能不由 ARC 管理)

4) 如果它按原样运行,我假设它没有被管理(因此 autorelase 处理 retainCount)


更新:

这是另一个通过 Playground 进行的测试。

以下显示 RetainCount 从 3 更改为 2。

import UIKit
import AVFoundation

let avacl : AVAudioChannelLayout? =
    AVAudioChannelLayout(layoutTag: kAudioChannelLayoutTag_AAC_5_1)
var unmanaged : Unmanaged<CFString>? = nil
do {
    let aclPtr : UnsafePointer<AudioChannelLayout> = avacl!.layout
    var aclSize : Int = MemoryLayout<AudioChannelLayout>.size +
        (Int(aclPtr.pointee.mNumberChannelDescriptions) - 1) *
        (MemoryLayout<AudioChannelDescription>.size)
    var nameSize : UInt32 = UInt32(MemoryLayout<CFString>.size)

    let ptr = UnsafeMutablePointer<CFString>.allocate(capacity: 1)
    defer { ptr.deallocate() }
    _ = AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutName,
                               UInt32(aclSize), aclPtr, &nameSize, ptr)
    _ = ptr.move() // make CFString managed

    unmanaged = Unmanaged<CFString>.passUnretained(ptr.pointee)
    CFGetRetainCount(unmanaged?.takeUnretainedValue())
}
CFGetRetainCount(unmanaged?.takeUnretainedValue())

以下显示 RetainCount 从 4 更改为 2。(超出范围时减少 2)

import UIKit
import AVFoundation

let avacl : AVAudioChannelLayout? =
    AVAudioChannelLayout(layoutTag: kAudioChannelLayoutTag_AAC_5_1)
var unmanaged : Unmanaged<CFString>? = nil
do {
    let aclPtr : UnsafePointer<AudioChannelLayout> = avacl!.layout
    var aclSize : Int = MemoryLayout<AudioChannelLayout>.size +
        (Int(aclPtr.pointee.mNumberChannelDescriptions) - 1) *
        (MemoryLayout<AudioChannelDescription>.size)
    var nameSize : UInt32 = UInt32(MemoryLayout<CFString>.size)

    var name : CFString!
    _ = AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutName,
                           UInt32(aclSize), aclPtr, &nameSize, &name)

    unmanaged = Unmanaged<CFString>.passUnretained(name!)
    CFGetRetainCount(unmanaged?.takeUnretainedValue())
}
CFGetRetainCount(unmanaged?.takeUnretainedValue())