Указатель разыменования с функцией ARC в C в iOS

Я использую The Amazing Audio Engine для обработки воспроизведения при синхронизации в приложении iOS.

Платформа требует, чтобы вы использовали функции C в качестве обратного вызова (playbackTimingReceiver), который вызывается в аудиопотоке. Затем вам нужно снова отправить сообщение основному потоку, используя функцию C (AEAudioControllerSendAsynchronousMessageToMainThread), которой вы передаете обработчик (pageTurnHandler).

У меня нет большого опыта работы с C, но, насколько я понимаю, я передаю указатель в сообщении, которое нужно разыменовать.

Чего я могу успешно достичь с помощью строки:

PlaybackManager* receiver = *((PlaybackManager**)userInfo);

Но только если я отключу ARC в проекте для этого файла, используя флаг -fno-objc-arc в скомпилированных источниках для цели проекта.

На мой вопрос, можно ли добиться этого при включенном ARC? Если да, то каков правильный синтаксис?

Соответствующий сегмент кода:

#pragma mark - Audio Timing Callback
-(AEAudioControllerTimingCallback)timingReceiverCallback
{
    return playbackTimingReceiver;
}

static void playbackTimingReceiver(PlaybackManager* receiver,
                                   AEAudioController *audioController,
                                   const AudioTimeStamp *time,
                                   UInt32 const frames,
                                   AEAudioTimingContext context)
{
    receiver->_hostTime = getUptimeInMilliseconds(time->mHostTime);
    AEAudioControllerSendAsynchronousMessageToMainThread(audioController,
                                                         pageTurnHandler,
                                                         &audioController,
                                                         sizeof(id));
}

static void pageTurnHandler(AEAudioController *audioController, void *userInfo, int userInfoLength)
{
    PlaybackManager* receiver = *((PlaybackManager**)userInfo);
    NSLog(@"Receiver:%@", receiver);
}

person Brendt    schedule 18.07.2013    source источник


Ответы (3)


PlaybackManager * receiver = (__bridge_transfer id)*(void **)userInfo;

должен сделать трюк. Это сначала приводит userInfo к указателю на указатель, потому что он содержит адрес исходного указателя объекта. Разыменуйте это, чтобы получить исходный указатель, и используйте __bridge_transfer с типом — id или PlaybackManager будет работать — чтобы сообщить ARC, что разыменованное значение на самом деле является объектом, о котором ему нужно позаботиться.

person jscs    schedule 19.07.2013
comment
Это не разыменование пустого указателя, это разыменование указателя на указатель. void ** - person jscs; 19.07.2013
comment
Спасибо за это... перестал жаловаться компилятор, но во время выполнения я получаю плохой доступ: Тема 1: EXE_BAD_ACCESS (код = 1, адрес = 0x30000008) - person Brendt; 19.07.2013
comment
Говорит ли register read rdi -> производит адрес po (id)<the address> в отладчике что-нибудь, @Brendt? Также register read rsi p <that address>? Предположим, вы разбились в objc_msg_send(). - person jscs; 19.07.2013

Без запуска кода появляются две ошибки:

1) Вы передаете содержимое audioController, когда кажется, что вы хотели передать содержимое receiver, поэтому последние два аргумента для AEAudioControllerSendAsynchronousMessageToMainThread должны быть &receiver и sizeof(PlaykbackManager *)

2) Вам нужно приведение моста, чтобы вернуть ссылку на объект

Что-то типа:

static void playbackTimingReceiver(PlaybackManager* receiver,
                                   AEAudioController *audioController,
                                   const AudioTimeStamp *time,
                                   UInt32 const frames,
                                   AEAudioTimingContext context)
{
    receiver->_hostTime = getUptimeInMilliseconds(time->mHostTime);
    AEAudioControllerSendAsynchronousMessageToMainThread(audioController,
                                                         pageTurnHandler,
                                                         &receiver,
                                                         sizeof(PlaybackManager*));
}

static void pageTurnHandler(AEAudioController *audioController, void *userInfo, int userInfoLength)
{
    PlaybackManager* receiver = (__bridge Playback *)*((PlaybackManager**)userInfo);
    NSLog(@"Receiver:%@", receiver);
}

Примечание: при передаче ссылок на объекты из мира, контролируемого ARC, в мир C вы часто передаете право собственности на входе — поэтому ARC не освобождает указанный объект — и передаете право собственности обратно на обратном пути — поэтому ARC возобновляет управление владением. Однако из-за природы AEAudioControllerSendAsynchronousMessageToMainThread, где userInfo передается по адресу и копируется внутри - отсюда и аргумент размера, передать право собственности сложнее. Поэтому приведенный выше код не работает. Это означает, что вы должны убедиться, что любой объект, на который ссылается receiver, остается живым, имея другого владельца.

person CRD    schedule 19.07.2013

Вы можете просто указать ARC тип хранилища, который вам нужен:

PlaybackManager *audioBufferPlayer = *(__weak PlaybackManager **)userInfo;

Просто не забудьте выполнить необходимые нулевые проверки перед доступом к каким-либо свойствам или вызовом каких-либо методов.

person atnmachine    schedule 13.12.2016