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 forsigned char
, andYES
andNO
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 is0
if the value equals0
, and1
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
becomes127
, -
-130
becomes126
, -
-131
becomes125
, - ...
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
becomes0
, -
8961
becomes1
, -
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