May 7 2012

BOOL’s sharp corners

Update October 2013 – On 64-bit iOS (device and simulator) BOOL is now actually bool, so the sharp corners have thankfully gone away for that platform. For everything else, though…

Objective-C is actually a pretty old language, dating back from from the mid eighties. As such, it’s got some sharp corners here and there due to limitations of early C. Today’s sharp corner is BOOL.

BOOL seems innocuous enough. It holds a boolean value. We All Know that in C a zero is a false value, and non-zero is a true value. There’s even the handy YES and NO macros to represent truth and untruth. It turns out that BOOL is actually just a typedef for a signed char – an eight-bit value. It’s not a first-class boolean type, which is one with compiler support (like bool) to make sure it acts sanely. There’s a couple of places where BOOL‘s non-first-classitude can cause subtle problems.

Don’t Compare to YES

YES is Objective-C’s truth value, equal to the value 1. BOOL can actually hold any non-zero value from -128 through 127 (in addition to zero), so there are 255 different flavors of truth. It’s a nice convention that functions and methods always return YES or NO, but you can’t depend on a truth value from one of these to be equal to YES (the value 1). Comparing to YES is generally a bad idea:

if ([kitteh haz: cheezburger] == YES) {
    // LOL

You have to trust that -haz: returns YES or NO, and no other value. Without looking at the source or disassembly I can’t trust its truth value is always YES. There is no compiler enforcement that a BOOL return value, or variable, always holds NO or YES.

How could a non-YES value find its way coming out of a function? A common C idiom (not necessarily ideal, but common) is to do some kind of math and then use that value to determine a Truth. If the math results in zero, then the Truth is false. If the math results in a non-zero value, then the truth is True.

Here’s a contrived piece of logic – given two integers, indicate if they’re different. An experienced C programmer might do this:

static BOOL different (int thing1, int thing2) {
    return thing1 - thing2;
} // difference

(You can find this code, and other code referenced here at this gist)

If you used a bool return type this function would actually work correctly.

But with Objective-C, the BOOL return value is actually cast to a char and returned, so it will be the result of the subtraction modulo 256. Comparing to YES will end up causing false negatives.

Here are some uses of this function, comparing to YES:

if (different(11, 10) == YES) printf ("11 != 10\n");
else printf ("11 == 10\n");

if (different(10, 11) == YES) printf ("10 != 11\n");
else printf ("10 == 11\n");

if (different(10, 15) == YES) printf ("10 != 15\n");
else printf ("10 == 15\n");

if (different(512, 256) == YES) printf ("512 != 256\n");
else printf ("512 == 256\n");

You’d hope that all of these would print out “thing1 != thing2” in all cases. But that’s not the case:

11 != 10
10 == 11
10 == 15
512 == 256

Only the first case correctly says that the two numbers are different. If you only had the first case in your unit test, you’d think that things were working correctly. (ship it!)

The actual return values from different are 1, -1, -5, and 0. Only the first one happens to equal YES. And notice the last expression actually evaluated to NO. More on this weirdity in a bit.

Because I don’t trust the general programmer population to be aware of this subtlety when using BOOL, I use this idiom for checking BOOL values:

if ([kitteh haz: cheezburger]) {
    // LOL

Of course, it’s always safe to compare against NO, because there is only one false value in C expressions – zero.

But you can rely on logical expressions

When I write code that returns a BOOL, I return a YES or NO value explicitly, versus doing some kind of arithmetic. You can also rely on logical expressions, though, to return values of zero and one, which happen to map to NO and YES.

I was actually caught unawares by this behavior, and was a good Learning Experience for me. I was doing a code review at Google, and saw this code:

BOOL something () {
    // stuff stuff stuff
    return a == b;

And I dinged it, saying the return should be return (a == b) ? YES : NO;, because I thought the value of the expression was undefined, and would just evaluate to a C truth or false value. (I blame some buggy compilers in my distant past.)

One of my gurus there, David Philip Oster, countered that it was actually legal, and the value of logical expressions is well-defined. When DPO makes a statement like that, he’s usually correct. So after a couple of minutes of writing some test code and finding chapter and verse in the C99 and C++ standards, I was convinced: logical expressions in C and C++ will always evaluate to a bool true or false value, which are one and zero, and happily equal to YES and NO.

I still won’t ever compare directly to YES because I don’t have time to review everyone’s code I call.

Slice of Life

Another BOOL sharp corner is a variation of the above. Not only can a non-NO BOOL have a non-YES value, sometimes it can be NO.

That sounds scary. How can that happen?

BOOL is a char, which is eight bits. If you try to squeeze a value larger than a char through BOOL, the compiler will happily truncate the upper bits, slicing them off.

So, what does this code print?

    BOOL truefalse = (BOOL)256;
    printf ("256 -> %d\n", truefalse);

Zero. NO. This “true” value is actually zero. Why? Here it is bitwise:


The compiler happily stripped off the upper byte, leaving zeros. Granted, a casting of a large constant into a BOOL might raise a red flag. But then again, the different function returned the value of a subtraction, truncating the value silently and didn’t require a cast.

The same code, but using a standard bool type works fine:

    bool stdTruefalse = (bool)256;
    printf ("256 -> %d (bool)\n", stdTruefalse);

So if your entire C boolean experience has been with with first-class types like bool (lower-case), then the BOOL (upper-case) behavior probably comes as a surprise.

The same thing can happen with pointers. (Thanks to Mike Ash for introducing me to this class of errors.) You might think this is safe code:

static NSString *g_name;

static BOOL haveName () {
    return (BOOL)g_name;
} // haveName

If g_name is non-nil, it’s a true value, so this function will be returning a true value. It might not be YES, but truth is truth in C, isn’t it? If this function returned bool, it would return the correct value.

Unfortunately, with BOOL, it doesn’t. If the address happens to have a zero lower byte, this function will return zero due to the same slicing behavior.

Here’s two cases. The first is the string living at address 0x010CA880.

Slice 1

That returns a true value, 0×80. It’s not YES, but it’s true. So code in general still works. If the address happens to be aligned in memory such that the lower byte is zero, say 0x010CA800, the compiler would slice off the top three bytes leaving a zero:

Slice 3

So you can see the function works in most cases, except for those times when the string happens to lie at particular addresses. It only fails every now and then. This is the kind of bug that can change from run to run.

Luckily clang and current versions of gcc complain if you try to pass a larger integer or pointer through a BOOL, requiring a cast. Hopefully adding the cast will raise some red flags in the programmer writing the code.

The take-away from this? For this kind of test I would either return an explicit YES or NO value:

if (g_name) return YES;
else return NO;

or use a logical expression

return g_name != nil;

And not depend on automatic pointer->BOOL (or any integer->BOOL) behavior.

Going BOOLing

So, what’s the point of all of this? Mainly that us as users of Objective-C, we can’t forget the C portion of the language. There are the occasional sharp corners that come from Objective-C’s C heritage that we need to be aware of, such as BOOL being a signed char. Many situations that work correctly with bool, the first-class boolean type, can fail in weird and wonderful ways with BOOL.


  1. John Brewer

    In paragraph 3 you state “BOOL can actually hold any non-zero value from 1 through 255″. If BOOL is a typedef for a signed character, as you stated in paragraph 2 (and objc.h agrees), valid values are -128 to 127. The signedness of BOOL probably leads to other sharp edges beyond its 8-bitness for type conversion.

  2. Steve Weller

    Another sharp corner is BOOL and bit testing:

    enum {
    kMonday = 0×1000,
    kTuesday = 0×0100,
    kWednesday = 0×0010,
    kThursday = 0×0001,

    int today;

    today = kTuesday;
    BOOL isMotherVisit = kTuesday & today;
    if(isMotherVisit) NSLog(@”Mother”);

    today = kWednesday;
    BOOL isBusinessTime = kWednesday & today;
    if(isBusinessTime) NSLog(@”Business”);

    Prints “Business”, but not “Mother”.

    Confusingly, this works as expected:

    today = kTuesday;
    if(kTuesday & today) NSLog(@”Mother”);

    today = kWednesday;
    if(kWednesday & today) NSLog(@”Business”);

    printing “Mother” and “Business”.

  3. Dad

    Why wouldn’t you just consider putting anything except YES or NO into a BOOL variable to be a bug? This just looks like a lot or work to work around sloppy programming to me. Wouldn’t it make more sense to agree that BOOL should only contain YES or NO and anyone who assigns it anything else has written a bug into their code?

    Since logical comparisons Do The Right Thingâ„¢ it really seems like there is no excuse for misusing a BOOL variable with random values.

  4. Mark Dalrymple

    Me? I would never put anything other than YES or NO into a BOOL. But I don’t control, and can’t code review everybody else’s code, especially those folks who like being clever, or who are used to or expecting bool’s (lower case) semantics. So best to be defensive, and understand what’s going on under the hood.

  5. Ivo

    I am not so sure this is the right type of defensive programming. If you don’t use It because the programmer of a method you call might be a louder coder, you might just as well write ‘don’t ever call anyone else’s method, bugs could be hiding in there!’

  6. Nicolas Bouilleaud

    Also, it’s easy to get things wrong when using performSelector with objective-c methods returning BOOL.

    Let’s say I have a method :

    – (BOOL) foo;

    This code would be correct, obviously :

    if( [someObject foo] ) …

    However, this would lead to random errors :

    if( [someObject performSelector:@selector(foo)] ) …

    because performSelector returns an id. All the bytes of the pointer are used as the “if” condition, but “foo” only wrote the last byte, meaning you’re basically testing garbage.

    You’ll need to explicitly cast to BOOL for this to work. Of course, Clang will complain that you’re casting “from pointer to integer of different size”, so :

    if( (BOOL) (intptr_t) [someObject performSelector:@selector(foo)] ) …

    should do.

    (I wrote this from memory, I didn’t run tests again. However, I *did* make this error in the past.)

  7. Mark Dalrymple

    @Ivo – seems like a stretch to go “here is a known sharp corner that can catch people unawares” to “be frightened of your own shadow”. If we had a first-class BOOL of course it’d be a non-issue. I and others have seen (and got the joy of debugging) these kinds of BOOL problems.

    But in general, the amount of scrutiny a call gets depends on its origins. If it’s from Apple, typically I assume it does the right thing. If it’s from cow-orkers, I usually trust stuff unless I know the programmer in question has had issues in the past. If it’s a well-known open source provider, it gets a free ride. If I get the source from someone I don’t know, I usually do a quick code review of the parts I’m using (BOOL usage, memory management pre-ARC, use of NSError, etc)

  8. Mike Fountain

    At the end of your “Don’t Compare to Yes” examples, you summarize with “Of course, it’s always safe to compare against NO, because there is only one false value in C expressions – zero.” However, I believe your fourth example contradicts this.
    Consider the adjusted conditional statement:
    if (different(512, 256) != NO) printf ("512 != 256\n");
    else printf ("512 == 256\n");

    The function call would return (512-256) mod 256, which is 0. This causes the conditional statement to evaluate as:
    if(0 != NO) // This is false (since NO is 0), so it will execute the else
    Thus, “512 == 256″ would be printed.

    Just another sharp corner!

  9. Jason

    Is this ok?

    return !g_name;


    return !!g_name;

  10. T Chan

    A related gotcha is that (BOOL)2 can be interpreted as NO — some UIKit setters appear to simply copy the BOOL into a bitfield, which causes everything except the bottom bit to be dropped. Stick to 0 and 1!

  11. allspa

    Is it ok to always use ‘bool’ instead of ‘BOOL’ in Objective-C, if ‘BOOL’ has this strange behavior?
    Is there anything wrong with using ‘bool’ in Objective-C?

    • Using regular bool should be ok. (So long as the compiler doesn’t gripe or warn going either directions – I haven’t tested it) _true_ is 1 which is YES, so it’s safe to pass in to something that (horrifically) might be comparing == YES. false is NO is 0, so that’s good.

  12. Russell

    I just fixed a bug in my program that was caused by doing exactly one of the things you said not to do, namely casting an object to a BOOL. Solved the intermittent NO problem! I’m not sure I would have found the bug as quickly as I did if I hadn’t had this article to think back to. Thank you!

Leave a Comment

Join the discussion. Do not worry, your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>