One approach is to add a second function
NSData *autoreleaseBufferOfLength(void *bytes, NSUInteger length)
{
return [NSData dataWithBytesNoCopy:bytes length:length freeWhenDone:YES];
}
This function will return an autoreleased NSData
object.
Call this function from your buffer-returning function
void *getAutoreleasedBuffer(NSUInteger length)
{
void *buffer = malloc(length);
if (buffer) {
autoreleaseBufferOfLength(buffer, length);
}
return buffer;
}
The NSData
object returned from autoreleaseBufferOfLength
is in the autorelease pool, so the buffer will be freed when the autorelease pool is drained (since the NSData
object created in autoreleaseBufferOfLength
is deallocated then).
I tested for debug this using this main
function
int main(int argc, const char * argv[])
{
@autoreleasepool {
__unused void * buffer = autoreleasingBuffer(32);
NSLog(@"%s:exiting @autoreleasepool block", __PRETTY_FUNCTION__);
}
NSLog(@"%s:exited @autoreleasepool block", __PRETTY_FUNCTION__);
return 0;
}
and by adding a symbolic breakpoint at -[NSConcreteData dealloc]
with the action po @"deallocating NSConcreteData"
and checking "Automatically continue after evaluating". This was the output
[53030:303] int main(int, const char **):exiting @autoreleasepool block
(NSString *) $0 = 0x0000000100300530 deallocating NSConcreteData
[53030:303] int main(int, const char **):exited @autoreleasepool block
I then tested for release, changing the test slightly, adding a __weak
global variable g_data
which is set in autoreleaseBufferOfLength
,
__weak NSData *g_data = nil;
NSData *autoreleaseBufferOfLength(void *bytes, NSUInteger length)
{
return (g_data = [NSData dataWithBytesNoCopy:bytes length:length freeWhenDone:YES]);
}
and altering the logging in main:
int main(int argc, const char * argv[])
{
@autoreleasepool
{
__unused void * buffer = autoreleasingBuffer(32);
NSLog(@"%s:exiting @autoreleasepool block; g_data = %@", __PRETTY_FUNCTION__, g_data);
}
NSLog(@"%s:exited @autoreleasepool block; g_data = %@", __PRETTY_FUNCTION__, g_data);
return 0;
}
When building for release and running this was the output:
[53934:707] int main(int, const char **):exiting @autoreleasepool block; g_data = 00000000 00000070 00000000 00000070 10000000 00000000 00000000 00000000>
[53934:707] int main(int, const char **):exited @autoreleasepool block; g_data = (null)
These tests indicate that wrapping the call to dataWithBytesNoCopy:length:freeWhenDone:
in a second function autoreleaseBufferOfLength
has the effect of creating an NSData object and placing it in the autorelease pool.
Note: This is NOT a good idea. The best way to achieve a malloc
'd which is free
'd by the draining of the autorelease pool is to add a file which is built without ARC (using -fno-objc-arc
), as @ughoavgfhw describes in his answer. Nevertheless, this approach may be of some interest.