10

I want to compare 2 BOOL values in objective-c.

I found out that (3)-(6) of the following code works.
(1)-(2) doesn't work because BOOL is just signed char.

(3) works and is very readable but I think bool isn't objective-c.
Using bool in objective-c code is good?

Which is the safe and good way to compare 2 BOOL values in objective-c?
Are there other better ways to compare?

BOOL b = YES;
BOOL c = 2;

NSLog(@"(1) %d", b == c); // not work
NSLog(@"(2) %d", (BOOL)b == (BOOL)c); // not work
NSLog(@"(3) %d", (bool)b == (bool)c);
NSLog(@"(4) %d", !b == !c);
NSLog(@"(5) %d", !!b == !!c);
NSLog(@"(6) %d", (b != 0) == (c != 0));

results:

(1) 0
(2) 0
(3) 1
(4) 1
(5) 1
(6) 1
js_
  • 4,671
  • 6
  • 44
  • 61
  • 7
    `2` is not a valid `BOOL` value; only `YES` and `NO` are. What you are doing is invalid. – trojanfoe Jun 21 '12 at 08:35
  • The 4th method looks good to me. It is short, and it safely convert value to either 0 or 1. – nhahtdh Jun 21 '12 at 08:37
  • 1
    @trojanfoe: The thing is, Obj-C will happily accept assignment of number to BOOL, and you can even do bitwise operation with it. – nhahtdh Jun 21 '12 at 08:37
  • 1
    I'm agree with trojanfoe, and also I think XCode should generate warning in case of such assignments. Wrong BOOL assignments is a source of terrible bugs. – Oleg Trakhman Jun 21 '12 at 08:43
  • For XCode this is really difficult to find, because internally you are assigning integers even for YES and NO. – Sulthan Jun 21 '12 at 08:54
  • @trojanfoe From the compiler/language point of view `2` is a perfectly valid value for a `BOOL`. – Nikolai Ruhe Jun 21 '12 at 08:59
  • 2
    @NikolaiRuhe Of course, however asking a question about safe usage of `BOOL` and then assigning `2`, isn't valid. – trojanfoe Jun 21 '12 at 09:19
  • @trojanfoe thanks. I added a comment in Omar's answer. I explained the reason that assigning 2 to BOOL variable can happen. http://stackoverflow.com/questions/11134352/in-objective-c-safe-and-good-way-to-compare-2-bool-values/11134435#comment14594266_11134435 – js_ Jun 21 '12 at 09:32
  • Assigning it to 2 is valid, however comparing the actual values of the BOOLs is not. Instead when you put the BOOLs into another boolean expression, then Objective-c will handle all values greater than 0 as true. – jake_hetfield Jun 21 '12 at 10:25
  • @jake_hetfield Assigning 2 is no problem. But assigning 256 is dangerous. `BOOL b = 256; NSLog(@"b is %d", b);` results `b is 0`. So, `if (b)` evaluated as false. But `if (256)` evaluated as true. – js_ Jun 21 '12 at 11:08
  • I agree, I wouldn't recommend assign a BOOL to anything else than true or false =) – jake_hetfield Jun 21 '12 at 11:43

6 Answers6

11

Comparing two boolean values should be handled with an XOR operation.

Trying to compare the two booleans directly is a misuse of the fundamentals of Boolean Algebra: http://en.wikipedia.org/wiki/Boolean_algebra_(logic)

When you are doing

BOOL a = (b == c);

then this value may return false even if both b and c are true. However the expression b && c will always return YES if both b and c are true, i.e. greater than 0, and NO otherwise.

Instead this is what you are actually trying to do:

BOOL xor = b && !c || !b && c;
BOOL equal = !xor;

equivalent with

BOOL equal = !(b && !c || !b && c);

or

BOOL equal = (b && c) || (!b && !c)

If you have to spend time making sure that your BOOL values are normalized (i.e. set to either 1 or 0) then your doing something wrong.

jake_hetfield
  • 3,388
  • 1
  • 23
  • 30
  • thanks jake! i think you and @Sulthan are right. it's beautiful but it's longer and less readable/writable to me than !! or (bool). why c doesn't have logical xor operator? – js_ Jun 21 '12 at 11:22
  • 2
    readability can be improved from the variable names. If you for example wants to check if a person is either hungry but not tired or tired but not hungry, and you have two bools that are named the same, then the expression (hungry && !tired) || (!hungry && tired) makes more sense than the expression (hungry != tired). (How can hungry be equal to tired, and what would that mean?) – jake_hetfield Jun 21 '12 at 11:40
  • 1
    The 4th expression in the OP's question actually solves the problem fast and clean. It will just normalize the number to 0 or 1, then `==` will behave like XNOR (NOT XOR). Other expressions (3 and 5, 6) are also correct. – nhahtdh Jun 22 '12 at 00:57
  • Programatically it is safe, and may be considered "clean" by some, but I stand with the opinion that a boolean expression should be a stand-alone expression that broken down to the use of && only and has the return value true or false. To compare two Boolean expressions with each other, normalized or not, is not regular boolean algebra. YES: if(true) if(false) if(bool1&&bool2) if(bool1 && !bool2 || int1==int2) if(int1==int2) and so on. NO: if(bool1==bool2) if(bool1==true) if(!bool1==bool2) and so on. They are all programatically correct, but the latter are not good looking to me. – jake_hetfield Jun 25 '12 at 09:12
5

It's perfectly valid to use bool in Objective-C as it's part of the C99 standard (§7.16). In my opinion it's also the best way to handle safe comparisons of boolean types.

The only reason not to use bool everywhere is that BOOL is omnipresent in Objective-C and the frameworks.

Nikolai Ruhe
  • 81,520
  • 17
  • 180
  • 200
  • I use `bool` everywhere except where the Cocoa API calls for `BOOL`. A safe conversion from one to the other would be (foo is bool and bar is BOOL): `foo = bar ? true : false;` or `bar = foo ? YES : NO;` – JeremyP Jun 21 '12 at 10:31
  • thanks. i thought bool was c++. @JeremyP, why `foo = bar;` `bar = foo;` are not safe? – js_ Jun 21 '12 at 12:32
  • Assigning `true` or `false` to a `BOOL` is both valid and safe. The standard says they expand to the integer constants `0` or `1` (also in §7.16). – Nikolai Ruhe Jun 21 '12 at 13:45
  • @js_ It always works. Direct assignment should be safe too with modern non buggy compilers. – JeremyP Jun 21 '12 at 20:43
  • 1
    @JeremyP so why do you do `foo = bar ? true : false;` or `bar = foo ? YES : NO;`? XCode's compiler is buggy? – js_ Jun 22 '12 at 05:51
4

apart from the other answers, I would like to note that comparing bools for equality is not a very common operation. BOOL is not a type, it's just a macro which hides the fact that booleans are only integers. For every boolean operation, you should rather use programming structures and operators that can handle integers as booleans correctly:

e.g: if (condition1 == NO) {} should be if (!condition1) {}

if (condition1 == condition2) {}
can be
if ((condition1 && condition2) || (!condition1 && !condition2)) {}
or better
BOOL newCondition = (condition1 && condition2) || (!condition1 && !condition2); if (newCondition) {}

The shortest way to write a condition doesn't have to be the best way.

Sulthan
  • 128,090
  • 22
  • 218
  • 270
  • 2
    I don't think comparing boolean values is seldom. One common case is a setter for a BOOL property with a shortcut early return: - (void)setAnimate:(BOOL)animate { if (_animate == animate) return; // else do whatever it takes to set up or tear down animation } – Nikolai Ruhe Jun 21 '12 at 15:22
  • 5
    Also, for the sake of correctness: `BOOL` is not a macro but a typedef. – Nikolai Ruhe Jun 21 '12 at 15:24
  • Thanks for `typedef` note. The _old value check_ is something really special. Usually, you don't care about the property type at all and you don't even need to know it's a BOOL. – Sulthan Jun 21 '12 at 16:03
  • Also, see http://stackoverflow.com/questions/12131879/shorthand-for-all-bools-yes-or-all-bools-no – Nikolai Ruhe Aug 26 '12 at 17:28
1

Convert your number to a valid BOOL, by answering what do you mean by "2"

in your context, is 2 = YES

Int number = 2;
BOOL c = (number == 2); //2 is YES

is > 0 = YES

Int number = 2;
BOOL c = (number > 0); //2 is YES

It depends manly on what is TRUE and what is False in your application

Omar Abdelhafith
  • 21,163
  • 5
  • 52
  • 56
  • 1
    Thanks. I always try to convert to valid BOOL when assigning after I learned that multiples of 256 are implicitly converted to 0(NO). But it is possible that a method which other person made returns BOOL value other than 0/1. And I don't convert returned BOOL value to valid BOOL like `b = !![obj methodWhchReturnsBOOL];` So, `c = 2` can happen. – js_ Jun 21 '12 at 09:19
  • This kind of conversion is not necessary if you are using BOOLs correctly. If you need to think about that, then you are doing something fundamentally wrong. The expression (B && C) will return YES even if c is 2 and b is 1. It is possible to base all your code on these type of expressions, and that is the correct way in boolean algebra. (that is why it is called BOOL/bool/Boolean) See my answer for references. – jake_hetfield Jun 21 '12 at 10:12
1

From objc.h:

typedef signed char     BOOL; 
....

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

Apparently, BOOL is signed char, so it is true that you can assign number to variable of BOOL type, which will mess up the comparison (since the comparison is integer comparison).

I think your (4) method using negation to convert arbitrary integral value to 0 or 1 is a good short way to safely compare the logical value of 2 BOOL.

nhahtdh
  • 55,989
  • 15
  • 126
  • 162
-1

If you assign the integer 2 to a variable of type BOOL, your code is broken. Whatever you get after that, it's what you deserve.

Therefore, the only sensible way to compare two BOOLs a and b is

if (a == b) ...
if (a != b) ...
gnasher729
  • 51,477
  • 5
  • 75
  • 98
  • I would say, the only sensible way would be `if(a && b)` and `if (!a && b)`, as this operators will in dead be real boolean operations, while `==` and `!=` are numeric operators. – vikingosegundo Mar 24 '14 at 17:38