BOOL with 64-bit on iOS

@TimBodeit is right, but it doesn't explain why ...

BOOL b1=8960; //b1 == NO

... evaluates to NO on 32-bit iOS and why it evaluates to YES on 64-bit iOS. Let's start from the same beginning.

ObjC BOOL definition

#if (TARGET_OS_IPHONE && __LP64__)  ||  (__ARM_ARCH_7K__ >= 2)
#define OBJC_BOOL_IS_BOOL 1
typedef bool BOOL;
#else
#define OBJC_BOOL_IS_CHAR 1
typedef signed char BOOL; 
// BOOL is explicitly signed so @encode(BOOL) == "c" rather than "C" 
// even if -funsigned-char is used.
#endif

For 64-bit iOS or ARMv7k (watch) it's defined as bool and for the rest as signed char.

ObjC BOOL YES and NO

Read Objective-C Literals, where you can find:

Previously, the BOOL type was simply a typedef for signed char, and YES and NO were macros that expand to (BOOL)1 and (BOOL)0 respectively. To support @YES and @NO expressions, these macros are now defined using new language keywords in <objc/objc.h>:

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

The compiler implicitly converts __objc_yes and __objc_no to (BOOL)1 and (BOOL)0. The keywords are used to disambiguate BOOL and integer literals.

bool definition

bool is a macro defined in stdbool.h and it expands to _Bool, which is a boolean type introduced in C99. It can store two values, 0 or 1. Nothing else. To be more precise, stdbool.h defines four macros to use:

/* Don't define bool, true, and false in C++, except as a GNU extension. */
#ifndef __cplusplus
#define bool _Bool
#define true 1
#define false 0
#elif defined(__GNUC__) && !defined(__STRICT_ANSI__)
/* Define _Bool, bool, false, true as a GNU extension. */
#define _Bool bool
#define bool  bool
#define false false
#define true  true
#endif

#define __bool_true_false_are_defined 1

_Bool

_Bool was introduced in C99 and it can hold the values 0 or 1. What's important is:

When a value is demoted to a _Bool, the result is 0 if the value equals 0, and 1 otherwise.

Now we know where this mess comes from and we can better understand what's going on.

64-bit iOS || ARMv7k

BOOL -> bool -> _Bool (values 0 or 1)

Demoting 8960 to _Bool gives 1, because the value doesn't equal 0. See (_Bool section).

32-bit iOS

BOOL -> signed char (values -128 to 127).

If you're going to store int values (-128 to 127) as signed char, the value is unchanged per C99 6.3.1.3. Otherwise it is implementation defined (C99 quote):

Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.

It means that clang can decide. To make it short, with the default settings, clang wraps it around (int -> signed char):

  • -129 becomes 127,
  • -130 becomes 126,
  • -131 becomes 125,
  • ...

And in the opposite direction:

  • 128 becomes -128,
  • 129 becomes -127,
  • 130 becomes -126,
  • ...

But because signed char can store values in the range -128 to 127, it can store 0 as well. For example 256 (int) becomes 0 (signed char). And when your value 8960 is wrapped around ...

  • 8960 becomes 0,
  • 8961 becomes 1,
  • 8959 becomes -1,
  • ...

... it becomes 0 when stored in signed char (8960 is a multiple of 256, 8960 % 256 == 0), thus it's NO. The same applies to 256, 512, ... multiples of 256.

I strongly recommend using YES, NO with BOOL and not relying on fancy C features like int as a condition in if, etc. That's the reason Swift has Bool, true, and false and you can't use Int values in conditions where Bool is expected. Just to avoid this mess ...


For 32-bit BOOL is a signed char, whereas under 64-bit it is a bool.


Definition of BOOL from objc.h:

/// Type to represent a boolean value.
#if (TARGET_OS_IPHONE && __LP64__)  ||  TARGET_OS_WATCH
#define OBJC_BOOL_IS_BOOL 1
typedef bool BOOL;
#else
#define OBJC_BOOL_IS_CHAR 1
typedef signed char BOOL; 
// BOOL is explicitly signed so @encode(BOOL) == "c" rather than "C" 
// even if -funsigned-char is used.
#endif