views:

625

answers:

3

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?

+3  A: 

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.

notnoop
To me, this answer pretty much says: CFRelease crashes because CF works that way. free(NULL) doesn't crash because C standard says so. delete NULL doesn't crash because C++ standard says so. It doesn't answer my question. I'm asking WHY the CF SDK designer deliberately decided to just let their public APIs crash the program.
David Lin
Without asking a CF designer directly (assuming they can/will tell you why) we're just speculating. I've put forth my best guess in my answer.
Quinn Taylor
If you are asking a question that can only be answered by the person who wrote this API, why ask on SO? The point of SO is to ask questions that are answerable by the community.
smorgan
Your "community" doesn't seem to include developers at Apple who may know the reason.
David Lin
+1  A: 

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.

Quinn Taylor
Consider that Quartz CGImageRelease and similars do accept NULL: "This function is equivalent to CFRelease, except that it does not cause an error if the image parameter is NULL."
IlDan
Right, it would be nice, but that is in CoreGraphics, not Core Foundation. If CFRelease and friends were changed to **not** crash when handed NULL, it could cause unexpected results in existing code (although hopefully nobody is intentionally causing crashes this way) and confuse CoreFoundation programmers who are used to the behavior. Even so, I tend to agree with you.
Quinn Taylor
+3  A: 

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.

sbooth