Parmanoir

Managing C memory with Cocoa

Using malloced temporary data can get quite spaghetti :
  • allocate and fill temporary data
  • check for a condition, bail and free the data if condition fails
  • check for another condition, bail and free the data if condition fails
  • do some actual work !
  • free the data with free. At last !

That's 3 times the same free code. Short of using goto to actually jump to cleanup code, there's no easy C solution. But in Cocoa you can leverage autorelease to automatically free your pointers !

Meet PointerFreeer, a class that will "autorelease" a pointer :

@implementation PointerFreeer
+ (id)withPointer:(void*)p
{
	id o = [[PointerFreeer alloc] init];
	[o setPointer:p];
	[o autorelease];
	return	o;
}
- (void)setPointer:(void*)p
{
	ptr = p;
}
- (void)dealloc
{
	free(ptr);
	[super dealloc];
}
@end


And here's an example of how it shortens and simplifies code :

Without PointerFreeer

BOOL myFunction(...)
{
	myType* data = malloc(sizeof(myType)*elementCount);

	// Check for something
	BOOL condition = ...
	if (!condition)
	{
		free(data);
		return NO;
	}

	// Check for something else
	BOOL anotherCondition = ...
	if (!anotherCondition)
	{
		free(data);
		return NO;
	}

	// Actual work !
	doSomeStuffWithData(data);

	// And free temp data
	free(data);

	return YES;
}

With PointerFreeer !

BOOL myFunction(...)
{
	myType* data = malloc(sizeof(myType)*elementCount);

	// Autorelease our pointer
	[PointerFreeer withPointer:data];

	// Check for something
	BOOL condition = ...
	if (!condition)		return NO;

	// Check for something else
	BOOL anotherCondition = ...
	if (!anotherCondition)	return NO;

	// Actual work !
	doSomeStuffWithData(data);

	return YES;
}

Dmitry Chestnykh
2008 08 10

Using "goto" is really nice way to do cleanup, don't be afraid of it :) Arguments here.

BOOL myFunction(...)
{
    myType* data = malloc(sizeof(myType)*elementCount);

    // Check for something
    BOOL success = ...
    if (!success)
        goto cleaup;

    // Check for something else
    success = ...
    if (!success)
        goto cleanup;

    // Actual work !
    doSomeStuffWithData(data);

    // And free temp data
cleanup:
    free(data);
    return success;
}

Also, don't forget that with GC -dealloc is never called, so it's safer to have -finalize as well it you think you'll ever use your code in GC-required app ;)

Dmitry Chestnykh
2008 08 10

BTW, by Cocoa conventions, you it's better to call your class method pointerFreererWithPointer or something like this, because initWith... are assumed to be instance methods, which return non-autoreleased objects. (Not that it's a restriction of some kind, just a convention).

Patrick Geiller
2008 08 10

Nice to see the Linux guys embrace goto ! But if you want to return YES or NO depending on your result, you still have to set a BOOL so that the goto can know what to return — or am I missing something ?

Yes I did mess up by calling it initWithPointer — I'll fix that. Thank you !

Jonathan Wight
2008 08 11

NSData already does this. See:

  • (id)dataWithBytesNoCopy:(void *)bytes length:(NSUInteger)length freeWhenDone:(BOOL)freeWhenDone;

Do you really need two classes to manage pointers?

Patrick Geiller
2008 08 11

> Do you really need two classes to manage pointers ?

No, but I do need a better way to search the docs :)

Ankur
2008 08 11

Why not just defer the malloc until after checking the conditions?

BOOL myFunction(...) {
    if(condition << anotherCondition) {
        myType* data = malloc(sizeof(myType)*elementCount);
        doSomeStuffWithData(data);
        free(data);
        return YES;
    } else {
        return NO;
    }
}
Bill
2008 08 11

In -setPointer:, you don't sanity check the argument for a NULL value. If you allow ptr to be assigned a NULL value, things will ultimately go boom when free(ptr) eventually gets invoked by -dealloc:

Dmitry Chestnykh
2008 08 11

But if you want to return YES or NO depending on your result, you still have to set a BOOL so that the goto can know what to return — or am I missing something?

See return success; in my example of goto.

Nice to see the Linux guys embrace goto !

I've just made a search and found 6695 goto's in the sources of XNU kernel. So Mac OS X guys also embrace goto ;) If you want an "approval" of a Cocoa programmer, here it is.

Patrick Geiller
2008 08 11

@Ankur sometimes it's just not an option to delay malloc. That's doable, but that usually implies one call to count the elements and another to actually fill data — not delaying malloc makes that just one pass.

@Bill good point ! But as I didn't check for NULL after malloc(), I think it will blow up before reaching the free ;)

@Dmitry I'd missed the return success. Ok, I'll stop spurning goto now.

Ankur
2008 08 12

Autorelease is expensive. Eliminate the BOOLs and do if(... || ...) rather than having two separate if statements. "Without PointerFreer" will be only one line longer than "With PointerFreer" and a heck of a lot faster.

Qwerty Denzel
2008 08 12

@Bill I'm pretty sure doing free(ptr) if ptr = NULL is safe.

But you're right that you ideally should be checking if malloc returned NULL. Although it would likely mean you'd run out of memory, and the whole program would 'blow up' anyway!

Patrick Geiller
2008 08 12

@ankur I'm doing that for convenience. I could shuffle around the code and free the data right in the function, but that's not high reward work as I'm not looking for speed in that particular case.

After considering goto, I think the best solution is to define a function named malloc_autorelease that will do just what its name implies - hiding the autoreleasing of the pointer and allowing to write code without thinking about memory freeing.


Follow me on Twitter
Planet Cocoa
Cocoa.fr

2011 02 22Distance field
2010 07 202Binding through NSApp
2010 05 122Forwarding invocations
2010 02 272Core Image black fringes
2010 02 21Quickest Way to Shell
2010 02 08Who's calling ?
2009 09 2138 ways to use Blocks in Snow Leopard
2009 08 182Bracket Mess
2009 08 124Taming JavascriptCore within and without WebView
2009 04 15Debugging with Activity Monitor
2009 03 25How Core Image Color Tracking works
2009 03 1510Custom NSThemeFrame
2009 03 10Which framework is running ?
2009 03 074CoreUI can paint pretty big
2009 02 18Localization with functions
2009 01 30Did you forget to nest alloc and init?
2009 01 16JSCocoa on the iPhone
2009 01 11Mixing WebView and JavascriptCore
2009 01 09Badge overflow
2009 01 09Find your Garbage Collection leaks with Instruments

Powered by MediaWiki