[swift-users] Initialization Catch-22?
Greg Parker
gparker at apple.com
Tue Sep 26 02:40:58 CDT 2017
> On Sep 25, 2017, at 6:12 PM, Kenny Leung via swift-users <swift-users at swift.org> wrote:
>
> I’m trying to implement the AudioQueue example from Apple in Swift, and keep it as Swifty as I can. I’ve run into a problem where I have a let ivar for the AudioQueue, but the only way to initialize that let ivar is to pass a block to the function that creates the AudioQueue. I get the error, "Variable 'self.mQueue' used before being initialized”. Is there any way to get around this catch?
The compiler cannot guarantee that the code running inside your block does not try to use mQueue before it is initialized. Swift strictly enforces its initialization rules, so the compiler rejects your code.
One common solution is to change mQueue into an implicitly-unwrapped optional var. The compiler still can't tell if your block object uses mQueue before it is initialized, but if it does it will get a well-defined runtime error due to the nil IUO. That well-defined error is sufficient to pacify the initialization rules.
Another solution might be to divide your data into two different types. It looks like the block object only needs to access self.audioBuffers and self.source. You might be able to box those two fields into a separate class type and have the block object capture a reference to that object only. Then the initialization order is simple enough for the compiler to understand:
1. PDAudioPlayer.init() initializes a source+audioBuffers object.
2. PDAudioPlayer.init() calls AudioQueueNewOutputWithDispatchQueue(), passing it a block that captures a reference to the fully-initialized source+audioBuffers object.
3. PDAudioPlayer.init() uses the results of #1 and #2 to complete its own initialization or return nil.
This adds an extra allocation and extra indirection to each PDAudioPlayer object, but as long as you don't have millions of these things the performance difference should be negligible.
> ———8<————8<————
> class PDAudioPlayer {
> static let kNumberBuffers :Int = 3 // 1
> //var mDataFormat:AudioStreamBasicDescription // 2
> let mQueue :AudioQueueRef // 3
> //var mBuffers :[AudioQueueBufferRef] // 4
> //var mAudioFile :AudioFileID? // 5
> var bufferByteSize :UInt32? // 6
> var mCurrentPacket :Int64? // 7
> var mNumPacketsToRead :UInt32? // 8
> var mPacketDescs :UnsafePointer<AudioStreamPacketDescription>? // 9
> var mIsRunning: Bool = false // 10
>
> let dispatchQueue :DispatchQueue
> let source :PDFileAudioSource
> var audioBuffers :[PDAudioBuffer]
>
> init?(source:PDFileAudioSource) {
> self.source = source
> self.dispatchQueue = DispatchQueue(label:"AudioPlayer", qos:.default, attributes:[], autoreleaseFrequency:.workItem, target:nil)
> self.audioBuffers = [PDAudioBuffer]()
>
> var tempAudioQueue :AudioQueueRef?
> var dataFormat = source.mDataFormat
> let status = AudioQueueNewOutputWithDispatchQueue(&tempAudioQueue, &dataFormat, 0, self.dispatchQueue) { [weak self] (queue, buffer) in // ERROR on this line
> guard let this = self else {
> return // block
> }
> guard let audioBuffer = this.audioBufferForAudioQueueBuffer(aqBuffer:buffer) else {
> return // block
> }
> this.source.fillBuffer(audioBuffer)
> }
> if status != 0 {
> return nil
> }
> guard let audioQueue = tempAudioQueue else {
> return nil
> }
> self.mQueue = audioQueue
> }
>
> private func audioBufferForAudioQueueBuffer(aqBuffer:AudioQueueBufferRef) -> PDAudioBuffer? {
> for buffer in self.audioBuffers {
> if buffer.audioQueueBuffer == aqBuffer {
> return buffer
> }
> }
> return nil
> }
> }
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20170926/5ad9f910/attachment.html>
More information about the swift-users
mailing list