@ синтезирует массив структур C в Objective-C 2.0 - PullRequest
6 голосов
/ 06 февраля 2009

Я пытаюсь следовать руководству по интерфейсу C ++ в Mac OS X API (Audio Queue Services), но в приложении 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 - синтаксическая ошибка)

Что я могу сделать, чтобы определить ивар как массив структур AudioQueueBufferRef или синтезировать свойство так, чтобы оно ссылалось на массив _buffers?

Ответы [ 6 ]

4 голосов
/ 06 февраля 2009

@synthesize buffers не скомпилируется следующим образом: «ошибка: синтезированное свойство 'buffers” должно быть названо так же, как совместимый ivar, или должно явно назвать ivar "

Попробуйте:

@synthesize buffers = _buffers;
3 голосов
/ 29 июля 2010

Я сейчас делаю аналогичную вещь для приложения OpenGL, и, поскольку это полезный вопрос (и так долго без ответа), я хочу перевести свое решение. Обратите внимание, что, вероятно, меньше усилий использовать NSArray ... но если вам нужно отточить производительность и / или использовать базовые типы C (или GL или аналогичные необъектные), это должно помочь. Кроме того, прости меня, если я делаю что-то, что не работает в OSX, так как мой код на самом деле для iPhone ... Я уверен, что хотя бы концепции одинаковы. Вот схема шагов:

  1. Объявите вашу переменную как указатель
  2. Защитите его от прямого доступа с помощью @ private
  3. Объявите свойство также как указатель, который будет указывать на любой массив, который мы передаем установщику
  4. Напишите или синтезируйте метод установки и получения. Мое использование @synthesize, казалось, запутало вещи и привело к утечке памяти, поэтому я пойду по пути самописи.
  5. (я думаю, отсутствующая ссылка из некоторых предыдущих постов) Поскольку вы возвращаете массив C, выделите память с помощью malloc. Метод init, вероятно, является хорошим местом для этого, и вы можете использовать dealloc или другое место для вызова free (myBuffers), так как ваша переменная имеет достаточно широкий охват.

Я не знаю, что @synthesize будет обрабатывать переменные malloc для вас, но кто-то еще может иметь опыт работы с этим, которого мне не хватает. Поэтому я просто пишу сеттер и геттер. Но обратите внимание, что мы все еще получаем точечный синтаксис и ванильный вид @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;)

2 голосов
/ 06 февраля 2009

Редактировать: Питер Хоси указал, что массив в C не то же самое, что указатель. (подробности см. в этом документе ). Это объяснило бы ошибку, которую вы видите, и сделало бы код, который я отправил, неправильным.

другой SO вопрос , на который gs ссылается его ответ предлагает обходной путь , который я скопировал в контекст этого вопроса:

// 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 объектов.

1 голос
/ 06 февраля 2009

Почему вы переводите структуру в объект Objective-C? Objective-C является строгим надмножеством C, поэтому вы можете просто использовать данную структуру как есть с Objective-C.

[EDIT] В ответ на ваши комментарии компилятор жалуется на объявление mBuffers из-за правил о том, что допустимо в качестве размера статического массива. Правила в C немного строже, чем в C ++. Как легко исправить, просто измените строку

static const int kNumberBuffers = 3;

в

#define kNumberBuffers 3

и тогда структура должна правильно скомпилироваться (при условии, конечно, что вы включили необходимые заголовки, которые определяют все надлежащие типы данных, такие как AudioStreamBasicDescription и т.

1 голос
/ 06 февраля 2009
0 голосов
/ 31 декабря 2009

Вы можете иметь три буферных ивара (buffer0, buffer1, buffer2) или вам нужно сделать его указателем на AudioQueueBufferRefs и отдельно выделить память для хранения трех AudioQueueBufferRefs. Или вы используете NSArray или NSData, если вам нужно переменное количество буферов.

Но, возвращаясь к более широкому контексту, вам, вероятно, не нужно отслеживать AudioQueueBufferRefs. Если вы поставите все три буфера в очередь, вы получите указатели на них в обратных вызовах аудио-очереди.

У меня есть некоторый аудиокод, который записывает и воспроизводит аудио, и он вызывает AudioQueueAllocateBuffer () и AudioQueueEnqueueBuffer () и после этого в значительной степени забывает о буферах.

Если вы пытаетесь написать оболочку Cocoa для AudioQueue, не делайте этого.

Редактировать: Но чтобы ответить на ваш первоначальный вопрос: вы не можете иметь массив в качестве свойства, потому что это не тип данных "Обычная версия". Посмотрите "Объявленные Свойства" в документации Objective C 2.0 в XCode, и это: http://www.fnal.gov/docs/working-groups/fpcltf/Pkg/ISOcxx/doc/POD.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...