Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why would CFRelease(NULL) crash?

Is there a reason why CFRelease does not check for NULL? Isn't it unacceptable when [nil release]; free(NULL); delete NULL; all work perfectly fine?

like image 443
David Lin Avatar asked Jul 16 '09 03:07

David Lin


4 Answers

The source code to CoreFoundation is publicly available. Specifically, for Snow Leopard the code to CFRelease is in http://www.opensource.apple.com/source/CF/CF-550/CFRuntime.c

Here is what the relevant portion looks like:

void CFRelease(CFTypeRef cf) {
    if (NULL == cf) HALT;
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
    if (CF_IS_COLLECTABLE(cf)) {
        if (CFTYPE_IS_OBJC(cf)) {
            // release the GC-visible reference.
            auto_zone_release(auto_zone(), (void*)cf);
        } else {
            // special-case CF objects for better performance.
            _CFRelease(cf);
        }
        return;
    }
#endif
}

This doesn't answer your question about design motivations, but you also asked why CFRelease does not check for NULL. It does check, and fails on purpose when NULL is passed as the parameter.

My personal belief is similar to Quinn's- that the CF designers felt it is a programming error to pass NULL.

like image 64
sbooth Avatar answered Jan 04 '23 17:01

sbooth


Good point, it doesn't seem to make much sense at first glance. Of course, the behavior is properly documented, but it would be nice if it could handle NULL gracefully. Notice that CFRetain and CFMakeCollectable (new in 10.4, GC enabled in 10.5) exhibit the same behavior. I'm not privy to all the motivations for designing it that way, but the emphasis was probably more on internal consistency with the rest of the CoreFoundation framework.

It's difficult/impossibly to know why CF was designed that way unless you can ask one of the designers. My best guess is that the designers decided that passing NULL for memory management functions is (should be?) a programming error. One could argue that causing a crash on NULL is a desirable "fail-fast" behavior, since bugs that crash almost immediately are easier to track down than bugs which silently do nothing instead of what you expect. Personally, I prefer the do-nothing-on-null approach, but I guess that's life...

Given that the API can't/won't change, you can either test for NULL or work around the problem case. One option might be to define an inline function or macro that only calls CFRelease for non-NULL references. In any case, it's probably best to be explicit in your code to avoid confusion down the road.

like image 32
Quinn Taylor Avatar answered Jan 04 '23 18:01

Quinn Taylor


All of these functions are part of different APIs that follow different conventions with regards to handling NULL:

  1. CFRelease is part of the CoreFoundation C SDK, which does not accept NULL reference as arguments by default.
  2. [nil release] uses Objective-C (which allows dereferences of nil)
  3. free(NULL) is part of C library (libc) which permits NULL arguments
  4. delete NULL is part of the C++ library (libc++) which permits NULL arguments

I guess the CoreFoundation SDK writers decided to be more consistent with the rest of SDK rather than with similar function in other SDKs.

like image 35
notnoop Avatar answered Jan 04 '23 17:01

notnoop


You may have a look at the source code of CFReleaseProtector to get a handle on (or better understanding of) this issue.

CFRelease no more crash on NULL,

http://unsanity.org/archives/haxies/cfrelease_no_mo.php

You may unpack CFReleaseProtector.sit with the command line tool unar (part of The Unarchiver; see its Google code downloads list).

like image 45
tim Avatar answered Jan 04 '23 18:01

tim