objective-ccautomatic-ref-countingcore-audioaudioqueueservices

Obj-C objects not being released in a C callback function


I am using the AudioFileOpenWithCallbacks function in my app to provide MP3 data in chunks. I call the function as follows:

AudioFileOpenWithCallbacks((__bridge void *)(self), TTMAudioFile_ReadProc, NULL, TTMAudioFile_GetSizeProc, NULL, 0, &aqData.mAudioFile);

In this case, self is the containing Objective-C class.

My TTMAudioFile_ReadProc callback function is as follows:

OSStatus TTMAudioFile_ReadProc(void *inClientData, SInt64 inPosition, UInt32 requestCount, void *buffer, UInt32 *actualCount) {

    TTMAudioQueuePlayer *this = (__bridge TTMAudioQueuePlayer *)inClientData;
    NSData *data = [this.decryptor dataAtOffset:inPosition length:requestCount error:NULL];
    memcpy(buffer, data.bytes, data.length);

    NSUInteger length = data.length;
    *actualCount = (UInt32)length;

    return noErr;
}

This works, but the NSData isn't being released. Profiling Allocations in Instruments reveal many allocations that look like the following:

enter image description here

Apparently there are two extra retain calls in my callback function, but I don't see where I can be retaining them. Furthermore, I am using ARC, so I don't know why this is happening either.


Solution

  • I needed an autorelease pool.

    The C callback function TTMAudioFile_ReadProc is called on a separate thread from the main thread. In this case, an autorelease pool is not automatically created, unlike when working with dispatch queues.

    OSStatus TTMAudioFile_ReadProc(void *inClientData, SInt64 inPosition, UInt32 requestCount, void *buffer, UInt32 *actualCount) {
        @autoreleasepool {
            TTMAudioQueuePlayer *this = (__bridge TTMAudioQueuePlayer *)inClientData;
            NSData *data = [this.decryptor dataAtOffset:inPosition length:requestCount error:NULL];
    
            memcpy(buffer, data.bytes, data.length);
            NSUInteger length = data.length;
            *actualCount = (UInt32)length;
    
            return noErr;
        }
    }