我目前正在学习在 iOS 中使用音频队列服务。为此,我遵循了音频队列服务编程指南 - 录制音频。这行得通。
现在我想在回调函数接收到来自麦克风的输入数据时更新一个 UI 元素(功率计)。这是我遇到问题的地方。
我不确定从回调函数调用视图/视图控制器以更新功率计的好方法是什么。我试过这个:在指南中描述的管理 state 的自定义结构中,我添加了一个指向我的视图控制器的指针:
struct AQRecorderState {
AudioStreamBasicDescription mDataFormat; // 2
AudioQueueRef mQueue; // 3
AudioQueueBufferRef mBuffers[kNumberBuffers]; // 4
AudioFileID mAudioFile; // 5
UInt32 bufferByteSize; // 6
SInt64 mCurrentPacket; // 7
bool mIsRunning; // 8
void* mViewController; // <- I've added this line
};
当我开始录制时,我初始化了这个指针。这是在视图控制器的方法中完成的,当开始按钮接收到 TouchUp 事件时会调用该方法:
- (IBAction)startAudioRecording:(id)sender {
struct AQRecorderState* aqData = createAQRecorderState();
void* pViewControllerAsVoidPointer = (__bridge void*) self;
aqData->mViewController = pViewControllerAsVoidPointer;
//...
分配结构的方法 createAQRecorderState() 如下所示:
struct AQRecorderState* createAQRecorderState() {
struct AQRecorderState* pAqData = (struct AQRecorderState*) malloc(sizeof(AQRecorderState));
initializeAQRecorderState(*pAqData); // sets members of the struct; does not set pointer to view controller
return pAqData;
}
AFAIK,我在开始录制时创建的结构的实例被传递给以后对回调函数的调用。出于这个原因,我在回调函数中添加了以下代码,以便随后调用视图控制器:
void HandleInputBuffer(
void *aqData,
AudioQueueRef inAQ,
AudioQueueBufferRef inBuffer,
const AudioTimeStamp *inStartTime,
UInt32 inNumPackets,
const AudioStreamPacketDescription *inPacketDesc
) {
//...
NSLog(@"interesting part start");
void* pViewControllerAsVoidPtr = pAqData->mViewController;
NSLog(@"localized pointer to void");
ViewController* pViewController = (__bridge ViewController*) pViewControllerAsVoidPtr; //EXC_BAD_ACCESS
NSLog(@"localized pointer to ViewController");
//...
}
但是,当我进行桥接指针转换时,我在上面标记行的回调函数中得到了 EXC_BAD_ACCESS (code=1, address=0xffffffff)。然后我用调试器检查了这个,实际上pViewControllerAsVoidPtr
并没有保存我在pAqData->mViewController
方法执行期间存储的地址startAudioRecording:
。
为什么那个指针会改变?或者:如何正确地从我的回调函数调用视图控制器来更新 UI?