在Objective-C中声明和检查/比较(位掩码)枚举


78

您知道可可里有这个东西,例如您可以创建一个 UIView并执行:

view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

我有一个UIView具有多个状态的自定义,enum这样的:

enum DownloadViewStatus {
  FileNotDownloaded,
  FileDownloading,
  FileDownloaded
};

对于每个创建的子视图,我设置其tagsubview1.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),除非您只想检查完全匹配。在这种情况下,您首先不需要位掩码,而只是一个普通的旧枚举。请参阅我的答案的下半部分。
Regexident

Answers:


278

声明位掩码:

或者到指定的绝对值(124,...)你可以声明位掩码(这些都是怎么叫)是这样的:

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的答案有关后者的更多信息,)

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

因此,OR输入两个值将执行以下操作:

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:谢谢!认为这个问题应该比以前提供更多的解释。
Regexident

是的,但是您正在将二进制值格式化为十六进制值(以0x开头)。位掩码在位级别上工作。一个简单的错误,我敢肯定您甚至没有注意到它。但是有人可能会看这个,并且错误地认为每个枚举最多可以有8个选项,而实际上您最多可以有32个不同的选项。更正: FileNotDownloaded = (0x1 << 0), // => %...00000001等等
Michael Zimmerman

1
Apple为枚举和位掩码声明提供了一对很棒的宏NS_ENUM和NS_OPTION。使用它们。有关详细说明,请参见NSHipster网站。
uchuugaka

2
充分意识到他们。;)(请参阅Abizern的回答)NS_OPTIONS还是为了完整起见添加了一个变体。
2013年

1
对。我明白您对溢出的意思。也许应该只是((status&FileNotDownloaded)== FileNotDownloaded),所以标记只有两种可能。
uchuugaka 2014年

20

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

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

进一步参考:


是的,NS_ENUM和NS_OPTION宏很棒。
uchuugaka 2013年

1
enum DownloadViewStatus {
  FileNotDownloaded = 1,
  FileDownloading = 2,
  FileDowloaded = 4
};

这将使您有效地执行“或”与“与”运算。


4
定义值的标准方法是1 << 01 << 11 << 2等这清楚你用位和口罩工作。
Mike Weller

1
@AhmedAlHafoudh:但是,本文并未解决OP的第二个问题:使用位掩码(相对于简单地声明它们)。看我的答案。
Regexident

1

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

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

更严格(bitmask & contains) == contains-零时也可以工作contains
DJm00n
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.