-6

我正在阅读有关 iphone 音频的教程,它使用 C/C++。我不熟悉 THIS-> 的使用。它似乎指的是指向全局变量的指针。这是教程 - iPhone Core Audio 第 3 部分 - 音频回调

我试图理解的语句是语句的 THIS-> 部分:

// Pass in a reference to the phase value, you have to keep track of this
// so that the sin resumes right where the last call left off
float phase = THIS->sinPhase;

该教程表明 THIS-> 用于获取访问 AudioController 变量的方法。sinPhase 似乎是全局变量。

请解释为什么创建“相位”引用而不是直接引用全局变量“sinPhase”。请记住,我是一个客观的 C 编程人员,试图理解这个 C/C++ 代码。

4

2 回答 2

6

在这个例子中,THIS不是对全局变量的引用;它在上面的函数中定义为 void 指针的强制转换inRefCon

static OSStatus renderInput(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
{
    // Get a reference to the object that was passed with the callback
    // In this case, the AudioController passed itself so
    // that you can access its data.
    AudioController *THIS = (AudioController*)inRefCon;

这是 C 中相当常见的模式。为了将回调传递给某些 API,以便它稍后可以调用您的代码,您需要同时传递一个函数指针和一个 void 指针。void 指针包含函数指针需要操作的任何数据。在您的回调中,您需要将其转换回指向实际类型的指针,以便您可以访问其中的数据。在这种情况下,示例的作者命名了强制转换指针THIS,可能是为了使它看起来更面向对象,尽管这只是 C 语言并且THIS没有特殊含义。

您问他们为什么将其分配给局部变量而不是THIS->sinPhase到处使用。没有理由不能THIS->sinPhase在任何地方使用;他们可能只是将其分配给局部变量phase以节省打字时间。优化器在局部变量上比在通过指针传入的变量上做得更好的可能性很小,因为它可以对局部变量做出更多假设(特别是,它可以假设没有其他人在同时)。因此,使用局部变量循环可能会运行得稍微快一些,但如果不进行测试我不能确定;最可能的原因只是为了节省打字并使代码更具可读性。

下面是一个简单的例子,说明这样的回调 API 是如何工作的;希望这能让我们更容易理解回调 API 的工作原理,而无需同时尝试理解 Core Audio 中发生的其他事情。假设我想编写一个函数,将回调应用于整数 10 次。我可能会写:

int do_ten_times(int input, int (*callback)(int)) {
    int value = input;
    for (int i = 0; i < 10; ++i) {
        value = callback(value);
    }
    return value;
}

现在我可以用不同的函数来调用它,如下所示add_one()times_two()

int add_one(int x) {
    return x + 1;
}

int times_two(int x) {
    return x * 2;
}

result = do_ten_times(1, add_one);
result = do_ten_times(1, times_two);

但是假设我希望能够添加或乘以不同的数字;我可以尝试为每个要相加或相乘的数字编写一个函数,但是如果代码中没有固定数字,而是基于输入,那么您就会遇到问题。您不能为每个可能的数字编写一个函数;您将需要传入一个值。所以让我们为回调添加一个值,并将do_ten_times()该值传入:

int do_ten_times(int input, int (*callback)(int, int), int data) {
    int value = input;
    for (int i = 0; i < 10; ++i) {
        value = callback(value, data);
    }
    return value;
}

int add(int x, int increment) {
    return x + increment;
}

int times(int x, int multiplier) {
    return x * multiplier;
}

result = do_ten_times(1, add, 3);
result = do_ten_times(1, times, 4);

但是,如果有人想编写一个随整数以外的变量变化的函数怎么办?例如,如果您想编写一个根据输入是负数还是正数添加不同数字的函数怎么办?现在我们需要传入两个值。同样,我们可以扩展我们的接口以传入两个值;但是我们最终需要传入更多的值,不同类型的值等等。我们注意到它do_ten_times并不关心我们传入的值的类型;它只需要将它传递给回调,回调可以根据需要对其进行解释。我们可以使用 void 指针来实现这一点;然后回调将该 void 指针转换为适当的类型以获取值:

int do_ten_times(int input, int (*callback)(int, void *), void *data) {
    int value = input;
    for (int i = 0; i < 10; ++i) {
        value = callback(value, data);
    }
    return value;
}

int add(int x, void *data) {
    int increment = *(int *)data;
    return x + increment;
}

int times(int x, void *data) {
    int multiplier = *(int *)data;
    return x * multiplier;
}

struct pos_neg {
    int pos;
    int neg;
};
int add_pos_neg(int x, void *data) {
    struct pos_neg *increments = (struct pos_neg *)data;
    if (x >= 0)
        return x + increments->pos;
    else
        return x + increments->neg;
}

int i = 3;
result = do_ten_times(1, add, &i);
int m = 4;
result = do_ten_times(1, times, &m);
struct pos_neg pn = { 2, -2 };
result = do_ten_times(-1, add_pos_neg, &pn);

当然,这些都是玩具示例。在 Core Audio 案例中,回调用于生成音频数据的缓冲区;每次音频系统需要生成更多数据以保持流畅播放时都会调用它。通过 传递的信息void *inRefCon用于跟踪您在当前缓冲区中到达正弦波的确切位置,因此下一个缓冲区可以从最后一个缓冲区停止的地方开始。

于 2012-11-23T18:08:17.803 回答
2

如果它没有在范围内声明(即在该上下文中声明的局部变量),我看到两个选项:

  • 要么它是一个定义,它实际上是指实例:#define THIS this
  • 正如你所建议的,要么是一个全局变量。

THIS在 C++ 中没有任何内在含义,所以它也可以。不管是哪一种,都太可怕了

它被复制到不同变量中的原因,如果它是一个全局变量,而不是直接使用,可能是为了清楚起见,也可能是为了不意外地修改它。

于 2012-11-23T18:07:39.423 回答