6 Stimmen

@synthesize-ing a C array of structs in Objective-C 2.0

Ich versuche, ein Tutorial für eine C++-Schnittstelle in der Mac OS X-API (Audio Queue Services) zu folgen, aber in einem Cocoa (gut, eigentlich nur Foundation) Anwendung (gut, eigentlich nur ein "Werkzeug"). Es hat eine Struktur, die wie folgt aussieht:

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
};

Ich habe eine Menge Probleme mit der Übersetzung von Artikel 4 in Objective-C, weil ich nicht herausfinden kann, wie man @synthesize ein C-Array. Genauer gesagt, das ist, was ich bis jetzt habe:

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 lässt sich wie folgt nicht kompilieren: "Fehler: Die synthetisierte Eigenschaft 'buffers' muss entweder den gleichen Namen wie ein kompatibles Ivar haben oder explizit ein Ivar benennen"

Dies liegt offensichtlich daran, dass das entsprechende Ivar den Namen _buffers und nicht buffers - aber das ist notwendig, weil ich eine Eigenschaft nicht als Array definieren kann (oder doch? @property(assign) *AudioQueueBufferRef buffers ist ein Syntaxfehler)

Was kann ich tun, um entweder die ivar als ein Array von AudioQueueBufferRef structs, oder synthetisieren Sie die Eigenschaft so, dass sie sich auf die _buffers Array?

4voto

Peter Hosey Punkte 94684

@synthesize buffers lässt sich wie folgt nicht kompilieren: "Fehler: Die synthetisierte Eigenschaft 'buffers' muss entweder den gleichen Namen wie ein kompatibles Ivar haben oder explizit ein Ivar benennen"

Versuchen Sie es:

@synthesize buffers = _buffers;

3voto

Rab Punkte 2746

Ich bin gerade dabei, eine ähnliche Sache für eine OpenGL-Anwendung, und da dies eine nützliche Frage ist (und so lange ohne eine Antwort) Ich möchte meine Lösung zu übersetzen. Beachten Sie, dass es wahrscheinlich weniger Aufwand zu verwenden NSArray... aber wenn Sie brauchen, um die Leistung zu verbessern und / oder grundlegende C (oder GL oder ähnliche nicht-Objekt) Typen verwenden sollte dies helfen. Verzeihen Sie mir auch, wenn ich etwas tue, das unter OSX nicht funktioniert, da mein Code eigentlich für das iPhone gedacht ist... Ich bin sicher, dass zumindest die Konzepte sind die gleichen though. Hier ist ein Überblick über die Schritte:

  1. Deklarieren Sie Ihre Variable als Zeiger
  2. Schützen Sie sie vor direktem Zugriff von außen mit @private
  3. Deklarieren Sie die Eigenschaft auch als Zeiger, der auf ein beliebiges Array verweist, das wir an den Setter übergeben
  4. Schreiben oder synthetisieren Sie eine Setter- und Getter-Methode. Meine Verwendung von @synthesize schien die Dinge zu verwirren und führte zu einem Speicherleck, so dass ich hier den Weg des eigenen Schreibens gehe.
  5. (Das fehlende Glied aus einigen früheren Beiträgen, denke ich) Da Sie ein C-Array zurückgeben, weisen Sie Speicher mit malloc zu. Die init-Methode ist wahrscheinlich ein guter Ort dafür, und Sie können dealloc oder anderswo verwenden, um free(myBuffers) aufzurufen, da Ihre Variable einen angemessenen Umfang hat.

Ich weiß nicht, ob @synthesize malloc'd-Variablen für dich handhaben kann, aber vielleicht hat jemand anderes Erfahrung damit, die mir fehlt. Daher schreibe ich nur den Setter und Getter. Aber beachten Sie, dass wir immer noch die Punktsyntax und das Vanilla-Aussehen von @property in der Schnittstelle erhalten.

Hier ist der Code:

In PlayerState.h:

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

Damit erhalten Sie einen Zeiger und eine Eigenschaft, wobei letztere den Zugriffsmethoden auf den vom Zeiger angegebenen Speicher entspricht. Wir werden unsere eigenen Zugriffsmethoden schreiben, dem Zeiger Speicher zuweisen und ihm etwas geben, auf das er zeigen kann.

Jetzt in 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;
}

Nun können Sie diese von einer anderen Klasse aus wie folgt aufrufen:

-(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])
}

Bei mir funktioniert das mit iPhone OS und meinen (zugegebenermaßen etwas anderen) OpenGL-Typen. Ich denke jedoch, es wird auch bei Ihnen funktionieren. Es ist möglich, dass Sie etwas anpassen müssen, oder dass ich einen kleinen Tippfehler beim Kopieren und Einfügen über meinen ursprünglichen Code eingeführt habe. Auch, wenn Sie gerne Dinge protokollieren, während Sie wie ich programmieren, könnte es nützlicher sein, einige Mitglieder der Puffer-Strukturen zu protokollieren, um zu sehen, dass das, was da ist, das ist, was da sein soll.

Auch wenn Sie gewohnt sind, Objekte zu drucken, während Sie mit Ihrem NSLog-Wrapper programmieren, d.h.: DLog(@"array is %@", myNSarray) erhalten Sie eine EXC_BAD_ACCESS dasselbe mit einem C-Array zu tun. Das C-Array ist kein Obj-C-Objekt, also behalten Sie das im Hinterkopf und halten Sie die Hand ein bisschen mehr auf, als Sie es bei einem Objective-C-Objekt tun würden ;)

2voto

e.James Punkte 112528

Edita: Peter Hosey hat darauf hingewiesen, dass ein Array in C pas dasselbe wie ein Zeiger. (siehe dieses Dokument für Details). Das würde den Fehler erklären, den Sie sehen, und würde den Code, den ich gepostet habe, falsch machen.

En andere SO Frage que gs Links zu in seine Antwort schlägt eine Abhilfe die ich kopiert habe, im Zusammenhang mit dieser Frage:

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

@property(readonly) AudioQueueBufferRef * buffers;

// PlayerState.m:
@implementation PlayerState

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

@end

Dies würde Ihnen den Zugang zu folgenden Informationen ermöglichen buffers als wäre es ein Zeiger auf ein Array von AuidoQueueBufferRef Objekte.

1voto

Georg Schölly Punkte 120083

1voto

Adam Rosenfield Punkte 373807

Warum übersetzen Sie die Struktur in ein Objective-C-Objekt? Objective-C ist ein Streng Obermenge von C, so können Sie einfach die gegebene Struktur als-is mit Objective-C verwenden.

[EDIT] Als Antwort auf Ihre Kommentare beschwert sich der Compiler über die mBuffers Deklaration wegen der Regeln darüber, was als Größe eines statischen Arrays zulässig ist. Die Regeln in C sind ein wenig strenger als die Regeln in C++. Als einfache Lösung ändern Sie einfach die Zeile

static const int kNumberBuffers = 3;

in

#define kNumberBuffers 3

und dann sollte die Struktur korrekt kompiliert werden (vorausgesetzt natürlich, Sie haben die notwendigen Header eingefügt, die alle richtigen Datentypen definieren, wie AudioStreamBasicDescription , usw.).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X