views:

169

answers:

3

I found today a comment in a source file:

//  - no longer compare BOOL against YES (dangerous!)

So my question is:

Is comparing BOOL against YES in Objective-C really that dangerous? And why is that? (Can the value of YES change during runtime? Like NO is always 0 but YES can be 1, 2 or 3 - depending on runtime, compiler, your linked frameworks?)

+6  A: 

The problem is that BOOL is not a native type, but a typedef:

typedef signed char      BOOL;

#define YES             (BOOL)1
#define NO              (BOOL)0

As a char, its values aren't constrained to TRUE and FALSE. What happens with another value?

BOOL b = 42;
if (b)
{
    // true
}
if (b != YES)
{
    // also true
}
Michael Petrotta
yeah ok, but that's the coder's fault if he sets his BOOL vars to some arbitrary values because he's a lazy hacker.
Jaroslaw Szpilewski
@jrk: or the coder's coworker did, or the codebase's previous maintainer, or a developer for a library used in the project... It never hurts to be paranoid.
Michael Petrotta
A: 

When the code uses a BOOL variable, it is supposed to use such variable as a boolean. The compiler doesn't check if a BOOL variable gets a different value, in the same way the compiler doesn't check if you initialize a variable passed to a method with a value taken between a set of constants.

kiamlaluno
+4  A: 

You should never compare booleans against anything in any of the C based languages. The right way to do it is to use either:

if (b)

or:

if (!b)

This makes your code much more readable (especially if you're using intelligently named variables and functions like isPrime(n) or childThreadHasFinished) and safe. The reason something like:

if (b == TRUE)

is not so safe is that there are actually a large number of values of b which will evaluate to true, and TRUE is only one of them.

Consider the following:

#define FALSE 0
#define TRUE  1

int flag = 7;
if (flag)         printf ("number 1\n");
if (flag == TRUE) printf ("number 2\n");

You should get both those lines printed out if it were working as expected but you only get the first. That's because 7 is actually true if treated correctly (0 is false, everything else is true) but the explicit test for equality evaluates to false.

Update:

In response to your comment that you thought there'd be more to it than coder stupidity: yes, there is (but I still wouldn't discount coder stupidity as a good enough reason - defensive programming is always a good idea).

I also mentioned readability, which is rather high on my list of desirable features in code.

A condition should either be a comparison between objects or a flag (including boolean return values):

if (a == b) ...
if (c > d) ...
if (strcmp (e, "Urk") == 0) ...
if (isFinished) ...
if (userPressedEsc (ch)) ...

If you use (what I consider) an abomination like:

if (isFinished == TRUE) ...

where do you stop:

if (isFinished == TRUE) ...
if ((isFinished == TRUE) == TRUE) ...
if (((isFinished == TRUE) == TRUE) == TRUE) ...

and so on.

The right way to do it for readability is to just use appropriately named flag variables.

paxdiablo
yeah ok, but if I am using a BOOL variable then I won't set it to anything different than YES or NO. it's like comparing unsigned with signed and crying because weird stuff happens.I thought that there might be another reason than the programmer's stupidity not comparing BOOL.
Jaroslaw Szpilewski
Yes, there's readability as well. Booleans should be of the form "something == something", "something < something" and so on if you're actually comparing things. A *flag* should be named appropriately (finishedLoop, gotUserData, ...) so that you can just use "if (flag)". The "if (flag == TRUE)" construct is an abomination in terms of readability since that's just another check. Is that where you stop or do you keep going: "if ((flag == TRUE) == TRUE)", "if (((flag == TRUE) == TRUE) == TRUE)", ad infinitum (reduction ad absurdum). :-)
paxdiablo