@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 ...