从C++代码调用Objective C函数

9

我在谷歌上搜索了这个主题,找到了数百万个结果。但是这些页面都没有帮助我。我认为我的问题很普遍。我正在尝试音频编程,特别是使用音频队列。我的程序的目的并不重要,用来解释问题。但简而言之:当我尝试从C ++代码调用Objective-C函数时,我会收到一个错误。下面是包含错误的代码:

AudioRecorder.h:
#import <Foundation/Foundation.h>


@interface AudioRecorder : NSObject {

}

-(void)setup;
-(void)startRecording;
-(void)endRecording;
-(void)playAlarmSound;

@end

以下为实现代码:AudioRecorder.mm:

#import "AudioRecorder.h"
#include <AudioToolbox/AudioToolbox.h>
#include <iostream>

using namespace std;

@implementation AudioRecorder

static const int kNumberBuffers = 3;
...
static void HandleInputBuffer (void                                 *aqData,
                           AudioQueueRef                        inAQ,
                           AudioQueueBufferRef                  inBuffer,
                           const AudioTimeStamp                 *inStartTime,
                           UInt32                               inNumPackets,
                           const AudioStreamPacketDescription   *inPacketDesc ) {

    AQRecorderState *pAqData = (AQRecorderState *) aqData;              

    if (inNumPackets == 0 &&                                            
        pAqData->mDataFormat.mBytesPerPacket != 0)
        inNumPackets =
        inBuffer->mAudioDataByteSize / pAqData->mDataFormat.mBytesPerPacket;

    UInt32 size;
    AudioQueueGetPropertySize ( inAQ, kAudioQueueProperty_CurrentLevelMeter, &size );
    char* levelMeterData = new char[size];
    AudioQueueGetProperty ( inAQ, kAudioQueueProperty_CurrentLevelMeter, levelMeterData, &size );
    AudioQueueLevelMeterState* meterState =  reinterpret_cast<AudioQueueLevelMeterState*>(levelMeterData);
    cout << "mAveragePower = " << meterState->mAveragePower << endl;
    cout << "mPeakPower = " << meterState->mPeakPower << endl;
    delete levelMeterData;
    [self playAlarmSound]; //<--- here I get the error: Use of undeclared identifier 'self'   

    if (pAqData->mIsRunning == 0)                                   
        return;

    AudioQueueEnqueueBuffer ( pAqData->mQueue, inBuffer, 0, NULL );
}
...
-(void)playAlarmSound {
    NSLog(@"Alarmsound....");
}

当我省略 "[self playAlarmSound];" 后,一切都正常工作。那么如何从我的 C++ 代码中调用这个 Objective-C 函数呢?
2个回答

6

self 只存在于 Objective-C 方法中,而这是 C 风格的函数。你需要在设置回调时从 Objective-C 方法中传递 self 到 inUserData 中,然后将其强制转换回正确的类型。

//This is an example for using AudioQueueNewInput
//Call this in an Objective-C method passing self to inUserData
AudioQueueNewInput (
   const AudioStreamBasicDescription  *inFormat,
   AudioQueueInputCallback            inCallbackProc,

   // this is where you will pass (void*)self
   void                               *inUserData, 
   CFRunLoopRef                       inCallbackRunLoop,
   CFStringRef                        inCallbackRunLoopMode,
   UInt32                             inFlags,
   AudioQueueRef                      *outAQ
);

以及您原来的实现

static void HandleInputBuffer (void                                 *aqData,
                           AudioQueueRef                        inAQ,
                           AudioQueueBufferRef                  inBuffer,
                           const AudioTimeStamp                 *inStartTime,
                           UInt32                               inNumPackets,
                           const AudioStreamPacketDescription   *inPacketDesc ) 
{
    AudioRecorder *ar_instance = (AudioRecorder*)aqData;
    ...
    [ar_instance playAlarmSound];
    ...
}

1
是的,但我认为缺少“self”的根本原因是因为这不是类的方法,而不是因为它是C风格的函数。任何Objective-C对象都可以在此函数中使用 - 只是“self”仅在类的方法中可用。 - Luke
我相信我所说的是“self仅存在于Objective-C方法中,而那是一个C风格函数”,最初可能有一个错别字,但它应该传达了同样的信息。 - Joe

4
这确实是一个常见的问题。这里self无法使用,不是因为它是Objective-C代码,而是因为它不是AudioRecorder类的方法。你在一个Objective-C++文件中,所以所有有效的Objective-C代码都可以使用。如果你有anAudioRecorder的良好引用,[anAudioRecorder playAlarmSound]将很好地工作。
那么如果我们没有self怎么办?通常的方法是使用此函数的void* aqData参数作为指向您的AudioRecorder对象的指针。当您注册此回调时,您告诉它void*参数将是什么,在本例中是指向您的AQRecorderState对象或结构体的指针,但似乎您并没有使用它。相反,您可以在注册时使用指向self的指针,以便您可以在此处使用该对象。
另一种选择是使用共享的AudioRecorder对象,在这种情况下,您可以调用类似[AudioRecorder sharedInstance](一个类方法,而不是实例)来获取您想要的AudioRecorder对象。因为其他答案详细解释了第一种方法,所以这里介绍如何使用共享实例选项:向您的AudioRecorder对象添加一个静态实例和一个类方法sharedInstance,如下所示:
static AudioRecorder* sharedMyInstance = nil;

+ (id) sharedInstance {
    @synchronized(self) {
        if( sharedMyInstance == nil )
            sharedMyInstance = [[super allocWithZone:NULL] init];
    }
    return sharedMyInstance;
} // end sharedInstance()

然后,当您想从回调中使用AudioRecorder时,可以使用[AudioRecorder sharedInstance]获取共享实例。如果只有一个AudioRecorder,这是一种非常有用的范例 - 它消除了很多引用传递。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接