2008 08 10Managing C memory with Cocoa
malloc
ed 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 :
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).
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 !
NSData already does this. See:
- (id)dataWithBytesNoCopy:(void *)bytes length:(NSUInteger)length freeWhenDone:(BOOL)freeWhenDone;
Do you really need two classes to manage pointers?
> Do you really need two classes to manage pointers ?
No, but I do need a better way to search the docs :)
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;
}
}
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:
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.
@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.
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.
@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!
@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.
Using "goto" is really nice way to do cleanup, don't be afraid of it :) Arguments here.
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 ;)