在 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)。

我是否必须改为循环使用ints(我的最佳猜测),还是可以以某种方便的方式挽救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 位,但它不能有除01 之外的任何其他值。为什么 ?因为bool a = 3(布尔等于运算符)将3转换为bool值,即true1

所以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 只能是 YESNO。您真正应该做的是将您的 BOOL 转换为 int,然后对其进行迭代,或者完全重构您的循环以使用 int 类型。

BOOL 值转换为ints 的问题是,正如您所指出的,BOOL 是类型定义为只有 8 位信息的东西*,因此只有 255 次迭代才有意义.事实上,最近,BOOL 根本无法转换,因为它被定义为编译器内在函数(objc_bool,它可以有值__objc_yes__objc_no)。 __objc_no++ 没有意义。

TL;DR 我的(强烈)建议是重构您的代码,以便您迭代整数,并在每次迭代中检查 BOOLs。是否强制转换 BOOL 值,或重构循环取决于您,但以您指示的方式迭代 BOOL 值既不安全,也不受支持(因此现在)。


* 在过去的几年里,BOOL 的实现细节是显而易见的(即转换为无符号字符)。随着编译器内在函数的出现,细节被隐藏了(尽管它们可能相同)。它们现在被隐藏的原因是因为你真的不应该依赖它们,而阻止人们依赖它们的最简单方法是完全对编译器隐藏它们。

【讨论】:

为什么你认为第二版不是我想要的?我觉得很好。 (虽然我同意帽子循环 ints 是更好的选择)。 似乎有了这个循环,你甚至不会输入一次。 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...

    

(当然,如果您想避免使用过多的冗余变量,您可以只使用 ints 来代替 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) 好的,所以它应该工作。尽管如此,我仍然认为这是不好的做法,他最好使用显式 ints 作为循环变量。【参考方案4】:

我看到的唯一方法是在循环中添加一个中断以逃避无限循环。

另一种可能性是使用简单整数并在counter == 2 时停止 for 循环

for (BOOL flagA = NO; YES; flagA++) 
    for (BOOL flagB = NO; YES; flagB++) 
        // Do something
        if (flagB)
          break;
    
    if (flagA)
       break;

【讨论】:

是的,即使使用 BOOLs 也可以。 (虽然在这种情况下我更喜欢循环 ints ——在这种情况下,中断是 IMO 多余的。) @Drux 是的,我可能也会。但由于问题是找到使用BOOL 的方法,所以我只回答了我看到的唯一可能性。 谢谢,不胜感激。

以上是关于在 Objective-C 中循环 BOOL的主要内容,如果未能解决你的问题,请参考以下文章

您如何使用 Objective-C 让声音文件自动播放到 iOS 应用程序并循环播放?

objective-c启用ARC时的内存管理 (循环引用)

Objective-C 高性能的循环遍历 forin - NSEnumerator - 枚举 优化

Objective-C学习笔记——循环语句for和do-while的使用

Block循环引用问题(Objective-c)

使用Objective-C中的单例保留循环