macos 在 Objective-C 2.0 中 @synthesize-ing 一个 C 结构数组
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/520696/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
@synthesize-ing a C array of structs in Objective-C 2.0
提问by ELLIOTTCABLE
I'm trying to follow a tutorial for a C++ interface in the Mac OS X API (Audio Queue Services), but in a Cocoa (well, actually just Foundation) application (well, actually just a 'tool'). It has a struct that looks like this:
我正在尝试在 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
};
I'm having a lot of trouble with translating item 4 into Objective-C, because I can't figure out how to @synthesize
a C array. Specifically, this is what I have so far:
我在将第 4 项转换为 Objective-C 时遇到了很多麻烦,因为我不知道如何转换@synthesize
为 C 数组。具体来说,这是我迄今为止所拥有的:
PlayerState.h
玩家状态.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
玩家状态.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
fails to compile as follows: "error: synthesized property 'buffers' must either be named the same as a compatible ivar or must explicitly name an ivar"
@synthesize buffers
无法编译如下:“错误:合成属性‘缓冲区’必须与兼容的 ivar 命名相同,或者必须明确命名为 ivar”
This is obviously because the corresponding ivar is named _buffers
and not buffers
- but this is necessary, because I can't define a property as an array (can I? @property(assign) *AudioQueueBufferRef buffers
is a syntax error)
这显然是因为相应的 ivar 被命名_buffers
而不是buffers
- 但这是必要的,因为我无法将属性定义为数组(可以吗?@property(assign) *AudioQueueBufferRef buffers
是语法错误)
What can I do to either define the ivar as an array of AudioQueueBufferRef
structs, or synthesize the property such that it refers to the _buffers
array?
我该怎么做才能将 ivar 定义为AudioQueueBufferRef
结构数组,或者合成属性以使其引用该_buffers
数组?
回答by Peter Hosey
@synthesize buffers
fails to compile as follows: "error: synthesized property 'buffers' must either be named the same as a compatible ivar or must explicitly name an ivar"
@synthesize buffers
无法编译如下:“错误:合成属性‘缓冲区’必须与兼容的 ivar 命名相同,或者必须明确命名为 ivar”
Try:
尝试:
@synthesize buffers = _buffers;
回答by Rab
I'm just now doing a similar thing for an OpenGL app, and since this is a useful question (and so long without an answer) I want to translate my solution. Note that it's probably less effort to use use NSArray… but if you need to hone performance and/or use basic C (or GL or similar non-object) types this should help. Also, forgive me if I do something that doesn't work in OSX, as my code is actually for iPhone... I'm sure that at least the concepts are the same though. Here is an outline of steps:
我现在正在为 OpenGL 应用程序做类似的事情,因为这是一个有用的问题(而且很长时间没有答案),我想翻译我的解决方案。请注意,使用 NSArray 可能更省力……但是如果您需要磨练性能和/或使用基本的 C(或 GL 或类似的非对象)类型,这应该会有所帮助。另外,如果我做了一些在 OSX 中不起作用的事情,请原谅我,因为我的代码实际上是针对 iPhone 的...我确信至少概念是相同的。以下是步骤概要:
- Declare your variable as a pointer
- Protect it from direct outside access with @private
- Declare the property as also a pointer, which will be set to point at any array we pass to the setter
- Write or synthesize a setter and getter method. My use of @synthesize seemed to confuse things and led to a memory leak, so I'll follow the write-your-own path here.
- (The missing link from some previous posts, I think) Since you are returning a C array, allocate memory using malloc. The init method is probably a good place for this, and you can use dealloc or elsewhere to call free(myBuffers) since your variable is of suitably broad scope.
- 将变量声明为指针
- 使用@private 保护它免受外部直接访问
- 将该属性声明为一个指针,它将被设置为指向我们传递给 setter 的任何数组
- 编写或合成一个 setter 和 getter 方法。我对@synthesize 的使用似乎混淆了事情并导致了内存泄漏,所以我将在这里按照自己编写的路径进行操作。
- (我认为之前一些帖子中缺少的链接)由于您要返回一个 C 数组,因此请使用 malloc 分配内存。init 方法可能是一个很好的地方,您可以使用 dealloc 或其他地方来调用 free(myBuffers) 因为您的变量具有适当的广泛范围。
I don't know that @synthesize it will handle malloc'd variables for you, but someone else may have experience with that that I lack. Therefore, I am just writing the setter and getter. But note we still get dot syntax and the vanilla look of @property in the interface.
我不知道 @synthesize 会为你处理 malloc 的变量,但其他人可能有我缺乏的经验。因此,我只是在编写 setter 和 getter。但是请注意,我们仍然可以在界面中获得点语法和@property 的原版外观。
Here's code:
这是代码:
In PlayerState.h:
在 PlayerState.h 中:
@interface PlayerState : NSObject {
AudioStreamBasicDescription dataFormat;
AudioQueueRef queue;
@private
AudioQueueBufferRef *myBuffers;
// [...]
@property(assign) AudioStreamBasicDescription dataFormat;
@property(assign) AudioQueueRef queue;
@property(assign) AudioQueueBufferRef *myBuffers;
That gives you a pointer and a property, the latter of which is equivalent to the accessor methods to the memory indicated by the pointer. We'll write our own accessors, and we will allocate memory for the pointer and give it something to point at.
这为您提供了一个指针和一个属性,后者等效于指针所指示的内存的访问器方法。我们将编写自己的访问器,并为指针分配内存并为其提供指向的对象。
Now in PlayerState.m:
现在在 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;
}
Now you can call these from another class like this:
现在你可以像这样从另一个类调用这些:
-(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])
}
This tests OK for me using iPhone OS and my (admittedly slightly different) openGL types. However, I think it will work for you too. It's possible you'll have to adjust something, or that I've introduced a minor typo while copying and pasting over my original code. Also, if you like to log things while you're coding like I do, it might be more useful to log some member of the buffer structs to see that what's there is what's supposed to be there.
这对我使用 iPhone 操作系统和我的(诚然略有不同的)openGL 类型测试没问题。但是,我认为它也适用于您。您可能需要调整某些内容,或者我在复制和粘贴原始代码时引入了一个小错误。此外,如果您喜欢像我一样在编码时记录内容,那么记录缓冲区结构的某些成员以查看应该存在的内容可能会更有用。
Also if you're used to printing objects while you code with your NSLog wrapper, ie: DLog(@"array is %@", myNSarray)
you'll get an EXC_BAD_ACCESS
trying to do the same with a C array. The C array is not an Obj-C object, so keep that in mind and hold its hand a bit more than you would an Objective-C object ;)
此外,如果您习惯于在使用 NSLog 包装器编码时打印对象,即:DLog(@"array is %@", myNSarray)
您将EXC_BAD_ACCESS
尝试使用 C 数组执行相同操作。C 数组不是 Obj-C 对象,因此请记住这一点,并且比使用 Objective-C 对象时握住它的手多一点;)
回答by e.James
Edit:Peter Hoseyhas pointed out that an array in C is notthe same thing as a pointer. (see this documentfor details). That would explain the error you are seeing, and would make the code that I posted wrong.
编辑:彼得Hosey指出,在C数组是不一样的东西作为一个指针。(有关详细信息,请参阅此文档)。这将解释您所看到的错误,并使我发布的代码出错。
The other SO questionthat gslinks to in his answersuggests a work-around, which I have copied, in the context of this question:
在这个问题的上下文中,gs在他的回答中链接到的另一个 SO 问题建议了一个解决方法,我已经复制了它:
// PlayerState.h:
@interface PlayerState : NSObject
{
AudioQueueBufferRef _buffers[3];
}
@property(readonly) AudioQueueBufferRef * buffers;
// PlayerState.m:
@implementation PlayerState
@dynamic buffers;
- (AudioQueueBufferRef *)buffers { return _buffers; }
@end
This would allow you to access buffers
as if it were a pointer to an array of AuidoQueueBufferRef
objects.
这将允许您buffers
像访问AuidoQueueBufferRef
对象数组一样访问它。
回答by Georg Sch?lly
回答by Adam Rosenfield
Why are you translating the structure into an Objective-C object? Objective-C is a strictsuperset of C, so you can just use the given struct as-is with Objective-C.
为什么要将结构转换为 Objective-C 对象?Objective-C 是 C 的严格超集,因此您可以在 Objective-C 中按原样使用给定的结构体。
[EDIT]In response to your comments, the compiler is complaining about the mBuffers
declaration because of the rules about what's allowable as the size of a static array. The rules in C are a little stricter than the rules in C++. As an easy fix, just change the line
[编辑]为了回应您的评论,编译器抱怨mBuffers
声明,因为关于静态数组大小的规则。C 中的规则比 C++ 中的规则严格一些。作为一个简单的修复,只需更改行
static const int kNumberBuffers = 3;
into
进入
#define kNumberBuffers 3
and then the struct should compile correctly (provided, of course, that you've included the necessary headers that define all of the proper data types such as AudioStreamBasicDescription
, etc.).
然后该结构应该可以正确编译(当然,前提是您已经包含了定义所有正确数据类型(例如AudioStreamBasicDescription
等)的必要头文件。
回答by lucius
You can either have three buffer ivars (buffer0, buffer1, buffer2) or you need to make it a pointer to the AudioQueueBufferRefs and separately allocate memory to hold the three AudioQueueBufferRefs. Or you use an NSArray or NSData if you need a variable number of buffers.
您可以拥有三个缓冲区变量(buffer0、buffer1、buffer2),或者您需要使其成为指向 AudioQueueBufferRefs 的指针并单独分配内存来保存三个 AudioQueueBufferRefs。或者,如果您需要可变数量的缓冲区,则使用 NSArray 或 NSData。
But stepping back to a larger context, you probably don't need to keep track of the AudioQueueBufferRefs. If you enqueue all three buffers, you'll get pointers to them in the audio queue callbacks.
但是回到更大的上下文,您可能不需要跟踪 AudioQueueBufferRefs。如果您将所有三个缓冲区加入队列,您将在音频队列回调中获得指向它们的指针。
I have some audio code which records and plays back audio, and it calls AudioQueueAllocateBuffer() and AudioQueueEnqueueBuffer() and pretty much forgets about the buffers after that.
我有一些音频代码可以记录和播放音频,它调用 AudioQueueAllocateBuffer() 和 AudioQueueEnqueueBuffer() 并且在此之后几乎忘记了缓冲区。
If you're trying to write a Cocoa wrapper for AudioQueue, don't do that.
如果您正在尝试为 AudioQueue 编写 Cocoa 包装器,请不要这样做。
Edit: But to answer your original question: You can't have an array as a property, because it's not a "Plain Ol' Data" type. See "Declared Properties" in the Objective-C 2.0 documentation in Xcode, and this: http://www.fnal.gov/docs/working-groups/fpcltf/Pkg/ISOcxx/doc/POD.html
编辑:但是要回答您原来的问题:您不能将数组作为属性,因为它不是“Plain Ol' Data”类型。请参阅 Xcode 中 Objective-C 2.0 文档中的“声明的属性”,以及:http: //www.fnal.gov/docs/working-groups/fpcltf/Pkg/ISOcxx/doc/POD.html