关于Objective-C中的BOOL你所要知道的

问题

曾经同事遇到一个关于 BOOL 的坑,这里分享下

1
2
3
4
- (BOOL)isPreloadingViewShowing
{
return self.mainCtl.preLoadingView.superview;
}

当 preLoadingView 明明有 superview 的时候,该函数居然有可能返回 NO

通过进一步的测试,发现在64位机器上没有这个问题,而在32位机器上有可能出现

Why?!先写个 Demo 测试下


Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static BOOL dif(int a, int b)
{
return a-b;
}

if (dif(11, 10))
{
NSLog(@"11 != 10");
}

if (dif(10, 11))
{
NSLog(@"10 != 11");
}

if (dif(512, 256))
{
NSLog(@"512 != 256");
}

Demo 运行结果

  • 64位机器上
1
2
3
11 != 10
10 != 11
512 != 256
  • 32位机器上
1
2
11 != 10
10 != 11

唯一的问题是为什么32位机器上最后一个的结果不对?!

Objective-C 中的 BOOL 到底是什么

第一个要搞清楚的问题是,Obj-C 中的 BOOL 到底是什么?打开 objc.h,查看 BOOL 的定义

1
2
3
4
5
6
7
// objc.h

#if (TARGET_OS_IPHONE && __LP64__) || TARGET_OS_WATCH
typedef bool BOOL;
#else
typedef signed char BOOL;
#endif

意思是,在64位机器上,BOOL 是 bool,bool 的取值只有 true or false;在32位机器上,BOOL 是 signed char,长度8 bits

所以,没有多余的机器可以测试的时候,我们可以把static BOOL dif(int a, int b)改为static bool dif(int a, int b)static signed char dif(int a, int b)来模拟64位机器或32位机器的情况

Demo 结果分析

Demo 中,a-b的结果是 int 类型,

int 作为返回值的时候会被强转为 BOOL,32位机器上 int 强转为 BOOL 的时候,其实是转为 signed char,只取了最低的8位,而64位机器上,BOOL 就是 bool,什么问题都没有,0即 false,非0即 true,可以像 C/C++ 语言那样放心使用

  • 64位机器上
1
2
3
11-10=1 -> 0x0000000000000001 -> true
10-11=-1 -> 0xffffffffffffffff -> true
512-256=256 -> 0x0000000000000100 -> true
  • 32位机器上
1
2
3
11-10=1 -> 0x00000001 -> 00000001 -> true
10-11=-1 -> 0xffffffff -> 11111111 -> true
512-256=256 -> 0x00000100 -> 00000000 -> false

关于32位机器与64位机器数据类型长度类型,可以参见32位机器与64位机器数据类型长度


解决方法

理清了 Demo,最上面的问题就迎刃而解了,superview 是一个指针(指针在 32/64 位机器上的位数是不同的),32位机器上,如果指针地址刚好最后8位都是0,那么即使指针不是空的,也会导致返回结果为空

正确的做法应该为

1
2
3
4
5
6
7
- (BOOL)isPreloadingViewShowing
{
if (self.mainCtl.preLoadingView.superview)
return YES;
else
return NO;
}

结论

取 int 或指针等类型作为 BOOL 的返回值时要注意,强转的时候可能会只取最低8位作为结果,尽量避免这种情况

参考

一杯冰可乐