Brendt
Brendt

Reputation: 397

Dereference pointer with ARC in C function in iOS

I'm using The Amazing Audio Engine to handle playback in syncing in an iOS app.

The framework requires you to use C functions as the call back (playbackTimingReceiver) which is called on the audio thread. You then need to message the main thread again using a C function (AEAudioControllerSendAsynchronousMessageToMainThread) to which you pass a handler (pageTurnHandler).

I'm not overly experienced working with C but as I understand it I'm passing a pointer in the message which needs to be dereferenced.

Which I can achieve successfully with the line:

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

But only if I turn ARC off in the project for that file using the -fno-objc-arc flag in compiled sources on the projects target.

To my question, is it possible to achieve this with ARC turned on? If so what is the correct syntax?

Relevant code segment:

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

Upvotes: 4

Views: 807

Answers (3)

atnmachine
atnmachine

Reputation: 35

You can just tell ARC the type of storage you would like:

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

Just be sure to do necessary nil checks before accessing any properties or calling any methods.

Upvotes: 0

CRD
CRD

Reputation: 53000

Without running the code there appear to be two errors:

1) You are passing the contents of audioController when it looks like you meant to pass the contents of receiver - so last two args to AEAudioControllerSendAsynchronousMessageToMainThread should be &receiver & sizeof(PlaykbackManager *)

2) You need a bridge cast to get the object reference back out

Something like:

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

Note: when passing object references from the ARC controlled world to the C world you often transfer ownership on the way in - so ARC doesn't release the referenced object - and transfer ownership back on the way back out - so ARC resumes ownership management. However due to the nature of AEAudioControllerSendAsynchronousMessageToMainThread, where userInfo is passed by address and copied internally - hence the size argument, it is tricker to transfer ownership. Therefore the above code does not. This means you must make sure that whatever object receiver references stays alive by having another owner.

Upvotes: 1

jscs
jscs

Reputation: 64002

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

should do the trick. This first casts the userInfo to a pointer-to-pointer, because it contains the address of the original object pointer. Dereference that to get the original pointer, and use __bridge_transfer with a type -- id or PlaybackManager will work -- to tell ARC that the dereferenced value is actually an object that it needs to take care of.

Upvotes: 2

Related Questions