I want to know how to manage unmannaged CFType AudioFormatGetProperty() with swift.
This function is low level API, and some kAudioFormatProperty_XXX returns CFType object without annotation. According to Documentation, it says
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 has NO CFRelease support, so I think in this case caller have to do something like CFRelease. Would you please give some advice?
My code snippet follows:
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)?
I found better solution for AudioFormatGetProperty().
Simple solution:
kAudioFormatProperty_* like FormatName or ChannelLayoutName return single CFString value.
In this case I can use following with ARC support. No need to release manually.
//
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)
NOTE:
How to identify if "non-annoted CFType object is really unmanaged or not".
Followings are cheap debugging procedure I did in this question. I believe this is bad know-how, so I would like to know more better way.
1) Put some check for CFType's RetainCount.
If it is huge value like 1152921504606846975, then it is constant - no need to release().
let ptr : UnsafeMutablePointer<CFString> = ...
CFShow(ptr.pointee) // to confirm it is live
Swift.print(CFGetRetainCount(ptr.pointee)) // to check retainCount
2) If it is small value like 2 or 3, then try to autorelease it like:
let unmanaged = Unmanaged<CFString>.passUnretained(ptr.pointee)
_ = unmanaged.autorelease()
3) If it immediately crash (within runloop), it is managed (may or may not be by ARC)
4) If it runs as is, I assume it is not managed (so autorelase handles retainCount)
Update:
This is another test via Playground.
Following shows RetainCount changed from 3 to 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())
Following shows RetainCount changed from 4 to 2. (2 drop when out-of-scope)
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())