【问题标题】:@synthesize-ing a C array of structs in Objective-C 2.0@synthesize-ing Objective-C 2.0 中的 C 结构数组
【发布时间】:2010-10-05 23:05:57
【问题描述】:

我正在尝试遵循 Mac OS X API(音频队列服务)中的 C++ 接口教程,但在 Cocoa(嗯,实际上只是 Foundation)应用程序中(嗯,实际上只是一个“工具”)。它的结构如下所示:

static const int kNumberBuffers = 3;                              // 1
struct AQPlayerState {
    AudioStreamBasicDescription   mDataFormat;                    // 2
    AudioQueueRef                 mQueue;                         // 3
    AudioQueueBufferRef           mBuffers[kNumberBuffers];       // 4
    AudioFileID                   mAudioFile;                     // 5
    UInt32                        bufferByteSize;                 // 6
    SInt64                        mCurrentPacket;                 // 7
    UInt32                        mNumPacketsToRead;              // 8
    AudioStreamPacketDescription  *mPacketDescs;                  // 9
    bool                          mIsRunning;                     // 10
};

我在将第 4 项翻译成 Objective-C 时遇到了很多麻烦,因为我不知道如何 @synthesize 一个 C 数组。具体来说,这是我目前所拥有的:

PlayerState.h

#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioQueue.h>

@interface PlayerState : NSObject {
  AudioStreamBasicDescription   dataFormat;
  AudioQueueRef                 queue;
  AudioQueueBufferRef           _buffers[3];
  int                           audioFile; // make this an actual type?
  UInt32                        bufferByteSize;
  SInt64                        currentPacket;
  UInt32                        numPacketsToRead;
  AudioStreamPacketDescription* packetDescs;
  bool                          isRunning;
}

@property(assign) AudioStreamBasicDescription   dataFormat;
@property(assign) AudioQueueRef                 queue;
@property(assign) AudioQueueBufferRef           buffers;
@property(assign) int                           audioFile;
@property(assign) UInt32                        bufferByteSize;
@property(assign) SInt64                        currentPacket;
@property(assign) UInt32                        numPacketsToRead;
@property(assign) AudioStreamPacketDescription* packetDescs;
@property(assign) bool                          isRunning;

@end

PlayerState.m

#import "PlayerState.h"

@implementation PlayerState

@synthesize dataFormat;
@synthesize queue;
@synthesize buffers;
@synthesize audioFile;
@synthesize bufferByteSize;
@synthesize currentPacket;
@synthesize numPacketsToRead;
@synthesize packetDescs;
@synthesize isRunning;

@end

@synthesize buffers 编译失败,如下所示:“错误:综合属性 'buffers' 必须与兼容的 ivar 命名相同或必须显式命名 ivar”

这显然是因为对应的ivar被命名为_buffers而不是buffers——但这是必要的,因为我不能将属性定义为数组(可以吗?@property(assign) *AudioQueueBufferRef buffers是语法错误)

如何将 ivar 定义为 AudioQueueBufferRef 结构的数组,或合成属性以使其引用 _buffers 数组?

【问题讨论】:

  • 我不敢相信我在将近一年后遇到了同样的问题……同时尝试做我写这篇文章时想做的同样的事情。我有一个可怕的记忆。此外,没有人回答它 d-;
  • 我想我已经回答了你的问题。总比一年后再次找到它要好,对吧?祝你好运。

标签: c objective-c cocoa macos


【解决方案1】:

@synthesize buffers 编译失败,如下所示:“错误:综合属性 'buffers' 必须与兼容的 ivar 命名相同或必须显式命名 ivar”

试试:

@synthesize buffers = _buffers;

【讨论】:

  • 这使得发布的代码预制件与 eJames 的代码相同 - 我会给你一个 upmod,除非我当天没有选票。不幸的是,我最终陷入了与 eJames 的代码相同的地方(参见上面的 cmets)
【解决方案2】:

我现在正在为 OpenGL 应用程序做类似的事情,因为这是一个有用的问题(而且很久没有答案)我想翻译我的解决方案。请注意,使用 NSArray 可能会更省力……但如果您需要磨练性能和/或使用基本的 C(或 GL 或类似的非对象)类型,这应该会有所帮助。另外,如果我做了一些在 OSX 中不起作用的事情,请原谅我,因为我的代码实际上是用于 iPhone 的……我相信至少概念是相同的。以下是步骤大纲:

  1. 将变量声明为指针
  2. 使用@private 保护其免受外部直接访问
  3. 将该属性声明为一个指针,该指针将被设置为指向我们传递给 setter 的任何数组
  4. 编写或合成一个setter 和getter 方法。我对@synthesize 的使用似乎使事情变得混乱并导致了内存泄漏,因此我将在此处遵循自己编写的路径。
  5. (我认为之前一些帖子中缺少的链接)由于您要返回一个 C 数组,因此请使用 malloc 分配内存。 init 方法可能是一个很好的地方,您可以使用 dealloc 或其他地方调用 free(myBuffers) 因为您的变量具有适当的广泛范围。

我不知道 @synthesize 会为你处理 malloc 的变量,但其他人可能有我缺乏的经验。因此,我只是在编写 setter 和 getter。但请注意,我们仍然会在界面中看到点语法和 @property 的香草外观。

代码如下:

在 PlayerState.h 中:

@interface PlayerState : NSObject {
    AudioStreamBasicDescription   dataFormat;
    AudioQueueRef                 queue;
@private
    AudioQueueBufferRef           *myBuffers;
    // [...]
    @property(assign) AudioStreamBasicDescription   dataFormat;
    @property(assign) AudioQueueRef                 queue;
    @property(assign) AudioQueueBufferRef           *myBuffers;

这给了你一个指针和一个属性,后者相当于指针所指示的内存的访问器方法。我们将编写自己的访问器,并为指针分配内存并为其提供指向的东西。

现在在 PlayerState.m 中:

-(id)init
{
    if (self = [super init]) {
        myBuffers = (AudioQueueBufferRef*)malloc(sizeof(AudioQueueBufferRef) * 3);
        /* memory is allocated. Free it elsewhere. Note that "Leaks" will
        probably falsely flag this, but you can test it in Object Allocations
        and see that it is clearly released. */
    }
    return self;
}

// return an array of structs
-(AudioQueueBufferRef*)myBuffers
{
    // you can check for values, etc here, but basically it all comes down to:
    return myBuffers;
}

    // the setter; there are several ways to write it, depending on what your objective
    -(void)setMyBuffers:(AudioQueueBufferRef*)abc
    {
    int i = 3;
    while (i > 0) {
        i--;
        myBuffers[i] = abc[i];
    }
    //another possible implementation, not tested
    //free(myBuffers);
    //myBuffers = NULL;
    //myBuffers = xyz;
    }

// if you want to set individual attributes, you can have another method like:
-(void)setMyBuffersA:(AudioQueueBufferRef)a B:(AudioQueueBufferRef)b C:(AudioQueueBufferRef)c
{
    myBuffers[0] = a;
    myBuffers[1] = b;
    myBuffers[2] = c;
}

现在你可以像这样从另一个类中调用它们:

-(void)methodOfOtherClass
{
    PlayerState * playerState = [[PlayerState alloc] init];
    AudioQueueBufferRef abc[3] = //something

    [playerState setMyBuffers:(AudioQueueBufferRef*)abc];
    DLog(@"I'm in OtherClass and playerState.myBuffers returns %@, %@, %@", playerState.myBuffers[0],playerState.myBuffers[1],playerState.myBuffers[2])

    abc[1] = 6.22; // reassigning one of the values in our local variable
    playerState.myBuffers = (GLfloat*)abc; //call the setter again, with dot syntax this time if you like
    DLog(@"I'm in OtherClass and playerState.myBuffers returns %@, %@, %@", playerState.myBuffers[0],playerState.myBuffers[1],playerState.myBuffers[2])

    [playerState setMyBuffersA:yourStructA B:yourStructB C:yourStructC];
    DLog(@"I'm in OtherClass and playerState.myBuffers returns %@, %@, %@", playerState.myBuffers[0],playerState.myBuffers[1],playerState.myBuffers[2])
}

这对我使用 iPhone OS 和我的(诚然略有不同)openGL 类型的测试没问题。但是,我认为它也对你有用。有可能你必须调整一些东西,或者我在复制和粘贴我的原始代码时引入了一个小错字。此外,如果您喜欢像我一样在编码时记录一些内容,那么记录缓冲区结构的某些成员以查看应该存在的内容可能会更有用。

此外,如果您习惯于在使用 NSLog 包装器进行编码时打印对象,即:DLog(@"array is %@", myNSarray),您将得到一个 EXC_BAD_ACCESS 尝试对 C 数组执行相同操作。 C 数组不是 Obj-C 对象,因此请记住这一点,并且比 Objective-C 对象多握一点;)

【讨论】:

  • 当/如果我返回引发此问题的项目时,我将不得不看看这个......我什至没有原始代码,所以我什至无法测试它是否有效对于原始问题:x
【解决方案3】:

编辑:Peter Hosey 指出 C 中的数组与指针相同。 (详见this document)。这将解释您看到的错误,并使我发布的代码出错。

gshis answer 中链接到的other SO question 暗示了一个work-around,我已经在这个问题的上下文中复制了它:

// PlayerState.h:
@interface PlayerState : NSObject 
{
    AudioQueueBufferRef           _buffers[3];
}

@property(readonly) AudioQueueBufferRef * buffers;


// PlayerState.m:
@implementation PlayerState

@dynamic buffers;
- (AudioQueueBufferRef *)buffers { return _buffers; }

@end

这将允许您访问 buffers,就好像它是指向 AuidoQueueBufferRef 对象数组的指针一样。

【讨论】:

  • 如果我这样做(接口中的AudioQueueBufferRef* buffers[3],标题中的@property(assign) AudioQueueBufferRef* buffers,实现中的@synthesize buffers),问题是它告诉我:“错误:属性类型” buffers' 与 ivar 'buffers' 的类型不匹配”
  • 数组自动是指针类型。在界面中的 AudioQueueBufferRef 之后,您不需要额外的 *。
  • 那么……我应该把它改成什么?我尝试将括号、带有数字的括号和星号放在一起。我只是无法让它与这三者的任何组合一起编译。
  • 正如我在上面的代码中展示的那样。声明数组时不要在 AudioQueueBufferRef 后使用 *,但声明属性时一定要使用 *。
  • eJames:不完全是。数组不同于指针。 (这是我的指针教程中的两个已知错误之一,我没有时间修复。)我曾经有一个指向解释所有差异的页面的链接,但找不到它。这是一个替代品:lysator.liu.se/c/c-faq/c-2.html
【解决方案4】:

这里看起来像这个问题:

Create an array of integers property in objective-c

【讨论】:

    【解决方案5】:

    为什么要将结构转换为 Objective-C 对象? Objective-C 是 C 的 strict 超集,因此您可以直接将给定的结构体与 Objective-C 一起使用。

    [编辑] 作为对您的 cmets 的回应,编译器抱怨 mBuffers 声明,因为关于允许的静态数组大小的规则。 C 中的规则比 C++ 中的规则更严格一些。作为一个简单的修复,只需更改行

    static const int kNumberBuffers = 3;
    

    进入

    #define kNumberBuffers 3
    

    然后该结构应正确编译(当然,前提是您已包含定义所有正确数据类型的必要标头,例如AudioStreamBasicDescription 等)。

    【讨论】:

    • 不幸的是,这不起作用。该结构必须使用一些我不知道的 C++ 魔法,因为将其逐字复制粘贴到 Foundation 工具中会导致编译错误(无论出于何种原因,编译器都不喜欢 AudioQueueBufferRef mBuffers[kNumberBuffers] 行)。
    • 您是否将您的源文件重命名为 .mm 以便 xcode 知道期待 Objective-c++(两者的混合)?
    • 不,我没有。我想避免进入 Objective-C++ 或任何涉及 C++ 的东西。这里的目标是编写一个 Objective-C 类来包装 AQS 文档中描述的工具。不过我会试试看,只是为了好玩!
    【解决方案6】:

    您可以拥有三个缓冲区变量(buffer0、buffer1、buffer2),或者您需要将其设为指向 AudioQueueBufferRefs 的指针并分别分配内存来保存三个 AudioQueueBufferRefs。或者,如果您需要可变数量的缓冲区,则使用 NSArray 或 NSData。

    但是回到更大的上下文,您可能不需要跟踪 AudioQueueBufferRefs。如果您将所有三个缓冲区都排入队列,您将在音频队列回调中获得指向它们的指针。

    我有一些音频代码可以记录和播放音频,它调用 AudioQueueAllocateBuffer() 和 AudioQueueEnqueueBuffer() 之后几乎忘记了缓冲区。

    如果您尝试为 AudioQueue 编写 Cocoa 包装器,请不要这样做。

    编辑:但要回答您最初的问题:您不能将数组作为属性,因为它不是“Plain Ol' Data”类型。请参阅 Xcode 中的 Objective-C 2.0 文档中的“声明的属性”,这是:http://www.fnal.gov/docs/working-groups/fpcltf/Pkg/ISOcxx/doc/POD.html

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-08-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多