Drux
Drux

Reputation: 12680

Looping over BOOLs in Objective-C

Is it safe to loop over Objective-C BOOLs like so:

for (BOOL flagA = NO; flagA <= YES; flagA++)
    for (BOOL flagB = NO; flagB <= flagA; flagB++)
        // ...

I'd like to use this to cycle through all relevant permutations of flags in a XCTestCase.

But it appears as if YES++ is still YES at least on some platforms (hence leading to an infinite loop e.g. on iPhone 6 Plus simulator) whereas I would have expected that BOOL would just be treated as int (and hence YES++ becoming 2).

Do I have to loop over ints (my best guess) instead, or can the use of BOOL be salvaged in some convenient way?

Upvotes: 2

Views: 284

Answers (4)

michal.ciurus
michal.ciurus

Reputation: 3664

You are all missing the point here. Drux is asking why can't he increment over BOOL, while it should be a char (8 bit value), which is perfectly incrementable.

The Answer is very easy. BOOL is sometimes a char and sometimes a bool depending on the target. From objc.h file:

#if !defined(OBJC_HIDE_64) && TARGET_OS_IPHONE && __LP64__
typedef bool BOOL;
#else
typedef signed char BOOL; 

If you iterate over a bool you will get value of 1 maximum.

EDIT:

Can you please add a reference to where the semantics of ++ for bool are specified? - Drux

Even though that bool has to be 8 bits minimum, it can't have any other value than 0 or 1. Why ? Because bool a = 3 (bool equal operator) converts 3 into a bool value, which is true which is 1.

So bool a = true; a++ is the same as bool a = 2; which makes a have a value of 1

Upvotes: 4

Ephemera
Ephemera

Reputation: 9010

If you're set on operating over BOOLs, then instead of:

for (BOOL flagA = NO; flagA <= YES; flagA++)
    for (BOOL flagB = NO; flagB <= flagA; flagB++)
        // ...

You should really be doing something this (though it is not what you want):

for (BOOL flagA = NO; flagA != YES; flagA = !flagA)
    for (BOOL flagB = NO; flagB != flagA; flagB = !flagB)
        // This is the only safe way to 'iterate' BOOLs

The behaviour, (BOOL)++ is not well-defined* as a BOOL can only be YES or NO. What you really should be doing is casting your BOOL to an int, and iterating over that, or refactoring your loop entirely to use int types.

The problem with casting your BOOL values to ints is, as you have pointed out, BOOL is typedef'd to something with only 8 bits of information*, therefore it only makes sense to have 255 iterations. In fact in more recent times, BOOL is not cast-able at all because it is defined as a compiler intrinsic (objc_bool, which can have values __objc_yes and __objc_no). __objc_no++ has no meaning.

TL;DR My (strong) suggestion would be to refactor your code so you are iterating over integers, and inspecting BOOLs within each iteration. Whether you cast your BOOL values, or refactor your loop is up to you, but iterating over BOOL values in the way you have indicated is both unsafe and (now, because of that) unsupported.


* In past years, the implementation details of BOOL were obvious (namely a cast to an unsigned char). With the advent of compiler intrinsics, the details are hidden (though they are likely the same). The reason they are now hidden is because you're really not supposed to rely on them, and the easiest way to stop people relying on them is to hide them from the compiler altogether.

Upvotes: 0

Nicolas Miari
Nicolas Miari

Reputation: 16256

I think @Sulthan means something like this (made overly explicit on purpose):

for(int indexA = 0; indexA <= 1; indexA++){
    for(int indexB = 0; indexB <= indexA; indexB++){

        BOOL flagA = (indexA == 1) ? YES : NO;
        BOOL flagB = (indexB == 1) ? YES : NO;

        // Use your flags (booleans) here...

    }
}

(Of course, you can use just the ints in place of booleans in Objective-C, if you want to avoid using too many redundant variables).

ADDENDUM: I actually performed a "Jump to definition" in Xcode (OSX project), and the part looks like this:

#if __has_feature(objc_bool)
#define YES __objc_yes
#define NO  __objc_no
#else
#define YES ((BOOL)1)
#define NO  ((BOOL)0)
#endif

(usr/include/objc/objc.h)

Can't "Jump to Definition" on __objc_yes (gives "Symbol Not Found")

Upvotes: 0

KIDdAe
KIDdAe

Reputation: 2722

The only way I see would be adding a break in your loop to escape the infinite loop.

Another possibilities is to use simple integer and stop the for loop when counter == 2

for (BOOL flagA = NO; YES; flagA++) {
    for (BOOL flagB = NO; YES; flagB++) {
        // Do something
        if (flagB)
          break;
    }
    if (flagA)
       break;
}

Upvotes: 0

Related Questions