在 Objective-C 中循环 BOOL
Posted
技术标签:
【中文标题】在 Objective-C 中循环 BOOL【英文标题】:Looping over BOOLs in Objective-C 【发布时间】:2015-04-24 13:52:35 【问题描述】:像这样循环遍历 Objective-C BOOL 是否安全:
for (BOOL flagA = NO; flagA <= YES; flagA++)
for (BOOL flagB = NO; flagB <= flagA; flagB++)
// ...
我想用它来循环遍历XCTestCase
中所有相关的标志排列。
但至少在某些平台上,YES++
似乎仍然是 YES
(因此会导致无限循环,例如在 iPhone 6 Plus 模拟器上),而我原本预计 BOOL
会被视为 @ 987654326@(因此YES++
变为2
)。
我是否必须改为循环使用int
s(我的最佳猜测),还是可以以某种方便的方式挽救BOOL
的使用?
【问题讨论】:
为什么不直接使用整数,然后使用整数作为布尔值? @Sulthan 请参阅 Q 中的最后一行。那么这是要走的路吗?你知道为什么BOOL
(显然)在不同平台上的工作方式不同(就++
而言)?
我目前在您的代码中看不到错误,因为YES
应该是 1,NO
应该是 0,BOOL
应该是有符号字符,但您永远不应该将布尔值与 @987654336 进行比较@ 或递增的布尔值。这些操作是未定义的。
有问题的代码实际上可以在我的机器上运行。
@NicolasMiari YES++
只是作为flag = YES; flag++
的快捷方式。
【参考方案1】:
你们都错过了这里的重点。 Drux 在问为什么他不能在 BOOL 上递增,而它应该是一个完全可递增的 char(8 位值)。
答案很简单。 BOOL 有时是字符,有时是 bool
,具体取决于目标。来自objc.h
文件:
#if !defined(OBJC_HIDE_64) && TARGET_OS_IPHONE && __LP64__
typedef bool BOOL;
#else
typedef signed char BOOL;
如果您遍历bool
,您将获得1
的最大值。
编辑:
您能否添加一个参考,说明 ++ for bool 的语义是在哪里指定的? - 德鲁克斯
尽管bool
必须至少为 8 位,但它不能有除0
或1
之外的任何其他值。为什么 ?因为bool a = 3
(布尔等于运算符)将3
转换为bool
值,即true
即1
。
所以bool a = true; a++
与bool a = 2;
相同,这使得a
的值为1
【讨论】:
我们没有错过任何一点。我们告诉他,他所做的事情没有定义。 但你没有告诉他原因。 原因不是实际的宏实现,原因是它背后的逻辑。这是一个布尔值,因此没有定义增量。这是一个布尔值,因此没有定义比较。 那么,bool
(not BOOL
) 被定义为特殊的 1 位类型?这使它“环绕”模2?不知道!
我认为你成功了。您能否添加对 ++
的语义在哪里指定 bool
的引用?【参考方案2】:
如果您打算在 BOOLs
上运行,则不要:
for (BOOL flagA = NO; flagA <= YES; flagA++)
for (BOOL flagB = NO; flagB <= flagA; flagB++)
// ...
你真的应该这样做(虽然这不是你想要的):
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
(BOOL)++
的行为没有明确定义*,因为 BOOL
只能是 YES
或 NO
。您真正应该做的是将您的 BOOL
转换为 int
,然后对其进行迭代,或者完全重构您的循环以使用 int
类型。
将BOOL
值转换为ints
的问题是,正如您所指出的,BOOL
是类型定义为只有 8 位信息的东西*,因此只有 255 次迭代才有意义.事实上,最近,BOOL
根本无法转换,因为它被定义为编译器内在函数(objc_bool
,它可以有值__objc_yes
和__objc_no
)。 __objc_no++
没有意义。
TL;DR 我的(强烈)建议是重构您的代码,以便您迭代整数,并在每次迭代中检查 BOOLs
。是否强制转换 BOOL
值,或重构循环取决于您,但以您指示的方式迭代 BOOL
值既不安全,也不受支持(因此现在)。
* 在过去的几年里,BOOL
的实现细节是显而易见的(即转换为无符号字符)。随着编译器内在函数的出现,细节被隐藏了(尽管它们可能相同)。它们现在被隐藏的原因是因为你真的不应该依赖它们,而阻止人们依赖它们的最简单方法是完全对编译器隐藏它们。
【讨论】:
为什么你认为第二版不是我想要的?我觉得很好。 (虽然我同意帽子循环int
s 是更好的选择)。
似乎有了这个循环,你甚至不会输入一次。 flagA == flagB 在第二个循环的第一次迭代中,然后 flagA == YES 并且您不会再次进入第一个循环。
@KIDdAe 我现在看到了。谢谢。【参考方案3】:
我认为@Sulthan 的意思是这样的(故意过于明确):
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...
(当然,如果您想避免使用过多的冗余变量,您可以只使用 int
s 来代替 Objective-C 中的布尔值)。
ADDENDUM:我实际上在 Xcode(OSX 项目)中执行了“跳转到定义”,部分看起来像这样:
#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)
无法在 __objc_yes
上“跳转到定义”(给出“未找到符号”)
【讨论】:
但是为什么是 sg.这样甚至有必要吗?BOOL
不只是映射到具有超过 1 位表示的某种类型(可能是unsigned char
)吗?
至少,我会避免使用布尔值作为循环变量,因为它会让任何需要维护你的代码的人感到困惑(这不是人们习惯看到的东西)。
我知道你的意思,但我认为这取决于实现并且在某种程度上不能保证。如果我没记错的话,true
只需要“非零”,不一定是 1(我可能是错的)。
__objc_yes
是编译器文字,请参阅 LLVM 文档。它也被隐式翻译为((BOOL)1)
。
好的,所以它应该工作。尽管如此,我仍然认为这是不好的做法,他最好使用显式 int
s 作为循环变量。【参考方案4】:
我看到的唯一方法是在循环中添加一个中断以逃避无限循环。
另一种可能性是使用简单整数并在counter == 2
时停止 for 循环
for (BOOL flagA = NO; YES; flagA++)
for (BOOL flagB = NO; YES; flagB++)
// Do something
if (flagB)
break;
if (flagA)
break;
【讨论】:
是的,即使使用BOOL
s 也可以。 (虽然在这种情况下我更喜欢循环 int
s ——在这种情况下,中断是 IMO 多余的。)
@Drux 是的,我可能也会。但由于问题是找到使用BOOL
的方法,所以我只回答了我看到的唯一可能性。
谢谢,不胜感激。以上是关于在 Objective-C 中循环 BOOL的主要内容,如果未能解决你的问题,请参考以下文章
您如何使用 Objective-C 让声音文件自动播放到 iOS 应用程序并循环播放?
Objective-C 高性能的循环遍历 forin - NSEnumerator - 枚举 优化