【问题标题】:How do those bitmasks actually work?这些位掩码实际上是如何工作的?
【发布时间】:2010-03-31 17:31:57
【问题描述】:

例如,来自 NSCalendar 的这个方法需要一个位掩码:

- (NSDate *)dateByAddingComponents:(NSDateComponents *)comps toDate:(NSDate *)date options:(NSUInteger)opts

所以选项可以是:

NSUInteger options = kCFCalendarUnitYear;

或喜欢:

NSUInteger options = kCFCalendarUnitYear | kCFCalendarUnitMonth | kCFCalendarUnitDay;

我不明白的是,这实际上是如何完成的?我的意思是:他们如何提取那些合并到options 中的值?如果我想编写这样的程序,可以使用位掩码,那会是什么样子?

【问题讨论】:

  • 当这些信息在许多语言中是真实的(甚至语法相同)时,像 iPhone 和 Objective-C 这样的标签真的有帮助吗?

标签: iphone objective-c cocoa cocoa-touch


【解决方案1】:

位掩码真的很基础。你可以这样想(C#,直到有人可以转换):

public enum CalendarUnits
{
    kCFCalendarUnitDay = 1, // 001 in binary
    kCFCalendarUnitMonth = 2, // 010 in binary
    kCFCalendarUnitYear = 4, // 100 in binary
}

然后您可以使用位运算符来组合值:

// The following code will do the following
// 001 or 100 = 101
// So the value of options should be 5
NSUInteger options = kCFCalendarUnitDay | kCFCalendarUnitYear;

这种技术也经常用于安全例程中:

public enum Priveledges
{
    User = 1,
    SuperUser = 2,
    Admin = 4
}

// SuperUsers and Admins can Modify
// So this is set to 6 (110 binary)
public int modifySecurityLevel = SuperUser | Admin;

然后检查安全级别,可以使用按位,看看你是否有足够的权限:

public int userLevel = 1;
public int adminLevel = 4;

// 001 and 110 = 000 so this user doesn't have security
if(modifySecurityLevel & userLevel == userLevel)

// but 100 and 110 = 100 so this user does
if(modifySecurityLevel & adminLevel == adminLevel)
    // Allow the action

【讨论】:

  • 请注意,由于运算符优先级,if(modifySecurityLevel & userLevel == userLevel) 必须是 if((modifySecurityLevel & userLevel) == userLevel) 否则表达式将始终计算为真。
  • 你也可以像这样进行位移:= 1
【解决方案2】:

要做到这一点,您需要对您正在测试的掩码值进行按位与运算,然后查看与运算的结果是否等于掩码本身:

if ((options & kCFCalendarUnitYear) == kCFCalendarUnitYear) {
   // do whatever
}

【讨论】:

  • == kCFCalendarUnitYear 似乎没有必要
  • 事实上if (options & kCFCalendarUnitYear) 应该足够了,因为任何非零值都意味着该位已设置。
  • OTOH,如果您想将此表达式的结果分配给 BOOL (signed char) 变量,您应该包含 == 表达式,因为会将表达式结果的范围缩小到 0 或 1。位掩码本身可能不适合,尤其是在枚举中有超过 8 个掩码常量时。
【解决方案3】:

位掩码之所以有效,是因为在二进制中,2 的每个幂(即 20=1, 21=2, 21= 4) 在位序列中占据一个位置。例如:

decimal | binary 
1       | 0001
2       | 0010
4       | 0100
8       | 1000

当您 or(类似 C 语言中的运算符 |)将两个数字 ab 一起放入 c 时,您是在说“取 a 中的位, b,或两者兼而有之,并将它们放入 c。”由于 2 的幂表示二进制字符串中的单个位置,因此没有重叠,您可以确定设置了哪些位置。例如,如果我们or 2 和 4

0010 | 0100 = 0110

注意它基本上是如何将两者结合起来的。另一方面,如果我们or 5 和 3:

decimal | binary 
5       | 0101
3       | 0011

0101 | 0011 = 0111

请注意,我们无法知道哪些位来自哪里,因为每个位的二进制表示之间存在重叠。

再举一个例子,这一点变得更加明显。让我们取数字 1、2 和 4(所有 2 的幂)

0001 | 0010 | 0100 = 0111

这与5 | 3 的结果相同!但由于原始数字是 2 的幂,我们可以唯一地分辨出每个位的来源。

【讨论】:

  • 很好的例子,但我不明白你的最后一句话。我们如何判断 0111 是来自 0001|0010|0100 还是 0101|0011?
  • 重点是我们知道先验标志只是2的幂。换句话说,我们知道它是 0001|0010|0100| 而不是 0101|0011,因为后者根本不是一个选项,因为它不只包含 2 的幂。
【解决方案4】:

关键是要记住,你合并到“选项”中的每一个值实际上只是一个数字。我不确定您对二进制有多熟悉,但您可以将其视为十进制,只需将数字相加而不是对它们进行 ORing。

假设 A=10、B=100 和 C=1000

如果您想设置 options = A+B,那么 options 将等于 110。然后您调用的方法将查看 A 的“十”位、B 的“百”位和“千”位对于 C。在这个例子中,有一个 1 是百位和十位,因此该方法会知道选项中设置了 A 和 B。

这有点不同,因为计算机使用二进制而不是十进制,但我认为这个想法非常相似,有时在熟悉的编号系统中考虑它会更容易。

【讨论】:

  • 这个想法不仅相似,而且完全相同。只有基数不同:2 与 10。任何熟悉 UNIX/Linux 命令行的人也曾在 base-8(八进制)中完成此操作,因为这就是 chmod 命令的数字权限掩码的工作方式。
【解决方案5】:
typedef NS_OPTIONS(NSUInteger, MyOption)
{
   OptionNone = 0,
   OptionOne = 1 << 0,
   OptionTwo = 1 << 1,
   OptionThree = 1 << 2
};

if (givenValue & OptionOne) {
   // bit one is selected
}

if (givenValue & OptionTwo) {
   // bit two is selected
}

http://en.wikipedia.org/wiki/Mask_(computing)

【讨论】:

    【解决方案6】:

    我发现 Calculator.app 有助于可视化位掩码。 (只需选择 View > Programmer,然后单击按钮显示二进制文件)。 (您可以单击二进制表中的任何 0 或 1 来打开或关闭这些位;或者,输入十进制或十六进制的数字(使用 8 | 10 | 16 NSSegmentedControl 在不同的表示之间切换)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-06-07
      • 2012-08-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多