【问题标题】:Declaring and checking/comparing (bitmask-)enums in Objective-C在 Objective-C 中声明和检查/比较(位掩码)枚举
【发布时间】:2013-04-16 12:32:52
【问题描述】:

你知道在 Cocoa 中有这个东西,例如你可以创建一个UIView 然后做:

view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

我有一个具有多种状态的自定义 UIView,我在 enum 中定义如下:

enum DownloadViewStatus {
  FileNotDownloaded,
  FileDownloading,
  FileDownloaded
};

对于每个创建的子视图,我设置其tag: subview1.tag = FileNotDownloaded;

然后,我有一个用于视图状态的自定义设置器,它执行以下操作:

for (UIView *subview in self.subviews) {
  if (subview.tag == viewStatus)
    subview.hidden = NO;
  else
    subview.hidden = YES;
}

但是我想要做的是允许这样做:

subview1.tag = FileNotDownloaded | FileDownloaded;

所以我的subview1 以两种状态显示。目前,由于| 运算符似乎添加了两个枚举值,因此它不会出现在这两种状态中的任何一种中。

有没有办法做到这一点?

【问题讨论】:

  • 您的 (subview.tag == viewStatus) 对我来说看起来不对。应该是((subview.tag & viewStatus) != 0x0),除非您只想检查精确匹配。在这种情况下,您首先不需要位掩码,而只需要一个普通的旧枚举。请参阅我的答案的后半部分。

标签: objective-c c cocoa enums bitmask


【解决方案1】:
enum DownloadViewStatus {
  FileNotDownloaded = 1,
  FileDownloading = 2,
  FileDowloaded = 4
};

这将让您有效地执行按位 OR 和 AND。

【讨论】:

  • 定义值的标准方法是1 << 01 << 11 << 2 等。这表明您正在使用位和掩码。
  • @AhmedAlHafoudh:这篇文章并没有解决 OP 的第二个问题:使用位掩码(而不是简单地声明它们)。看我的回答。
【解决方案2】:

声明位掩码:

除了分配绝对值(124、...),您还可以像这样声明 bitmasks(如何调用它们):

typedef enum : NSUInteger {
  FileNotDownloaded = (1 << 0), // => 00000001
  FileDownloading   = (1 << 1), // => 00000010
  FileDownloaded     = (1 << 2)  // => 00000100
} DownloadViewStatus;

或使用现代 ObjC 的 NS_OPTIONS/NS_ENUM 宏:

typedef NS_OPTIONS(NSUInteger, DownloadViewStatus) {
  FileNotDownloaded = (1 << 0), // => 00000001
  FileDownloading   = (1 << 1), // => 00000010
  FileDownloaded    = (1 << 2)  // => 00000100
};

(有关后者的更多信息,请参阅Abizern's answer

位掩码的概念是(通常)用单个位集定义每个枚举值。

因此ORing 两个值执行以下操作:

DownloadViewStatus status = FileNotDownloaded | FileDownloaded; // => 00000101

相当于:

  00000001 // FileNotDownloaded
| 00000100 // FileDownloaded
----------
= 00000101 // (FileNotDownloaded | FileDownloaded)

比较位掩码:

检查位掩码时要记住的一点:

检查完全相等:

假设状态是这样初始化的:

DownloadViewStatus status = FileNotDownloaded | FileDownloaded; // => 00000101

如果你想检查status 等于 FileNotDownloaded,你可以使用:

BOOL equals = (status == FileNotDownloaded); // => false

相当于:

   00000101 // (FileNotDownloaded | FileDownloaded)
== 00000100 // FileDownloaded
-----------
=  00000000 // false

检查“会员”:

如果你想检查status是否仅仅包含 FileNotDownloaded,你需要使用:

BOOL contains = (status & FileNotDownloaded) != 0; // => true

   00000101 // (FileNotDownloaded | FileDownloaded)
&  00000100 // FileDownloaded
-----------
=  00000100 // FileDownloaded
!= 00000000 // 0
-----------
=  00000001 // 1 => true

看到细微差别(以及为什么您当前的“if”表达式可能是错误的)?

【讨论】:

  • @Abizern:谢谢!认为这个问题值得比以前提供的更多解释。
  • 是的,但您正在将二进制值格式化为十六进制值(以 0x 开头)。位掩码在位级别上工作。简单的错误,我敢肯定你甚至没有注意到它。但是有人可能会看到并错误地假设每个枚举最多可以有 8 个选项,而实际上您最多可以有 32 个不同的选项。更正:FileNotDownloaded = (0x1 &lt;&lt; 0), // =&gt; %...00000001
  • Apple 为枚举和位掩码声明提供了一对美妙的宏 NS_ENUM 和 NS_OPTION。使用它们。请参阅 NSHipster 网站了解一些很好的描述。
  • 完全了解它们。 ;)(请参阅 Abizern 的回答)无论如何,为了完整起见,添加了 NS_OPTIONS 的变体。
  • 对。我明白你所说的溢出是什么意思。也许它应该只是 ((status & FileNotDownloaded) == FileNotDownloaded) 所以标签只有两个可能的结果。
【解决方案3】:

虽然@Regexident 提供了一个很好的答案 - 我必须提到用NS_OPTIONS 声明枚举选项的现代Objective-C 方式:

typedef NS_OPTIONS(NSUInteger, DownloadViewStatus) {
  FileNotDownloaded = 0,
  FileDownloading   = 1 << 0,
  FileDownloaded    = 1 << 1
};

进一步参考:

【讨论】:

  • 是的,NS_ENUM 和 NS_OPTION 宏很棒。
【解决方案4】:

可用于位掩码检查以提高可读性的有用功能。

BOOL bitmaskContains(NSUInteger bitmask, NSUInteger contains) {
    return (bitmask & contains) != 0;
}

【讨论】:

  • 更严格的(bitmask &amp; contains) == contains - 即使为零contains也可以工作
猜你喜欢
  • 1970-01-01
  • 2010-10-31
  • 1970-01-01
  • 1970-01-01
  • 2018-08-05
  • 2012-02-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多