这些位掩码实际上如何工作?

don*_*ile 26 iphone cocoa cocoa-touch objective-c

例如,NSCalendar中的此方法采用位掩码:

- (NSDate *)dateByAddingComponents:(NSDateComponents *)comps toDate:(NSDate *)date options:(NSUInteger)opts
Run Code Online (Sandbox Code Playgroud)

所以选项可以是:

NSUInteger options = kCFCalendarUnitYear;
Run Code Online (Sandbox Code Playgroud)

或者喜欢:

NSUInteger options = kCFCalendarUnitYear | kCFCalendarUnitMonth | kCFCalendarUnitDay;
Run Code Online (Sandbox Code Playgroud)

我没有得到的是,这实际上是怎么做到的?我的意思是:他们怎么能拿出那些合并成的价值options?如果我想编写这样的东西,那可能需要一个位掩码,那看起来怎么样?

Jus*_*ner 36

Bitmasks非常基本.你可以这样想(C#直到有人可以转换):

public enum CalendarUnits
{
    kCFCalendarUnitDay = 1, // 001 in binary
    kCFCalendarUnitMonth = 2, // 010 in binary
    kCFCalendarUnitYear = 4, // 100 in binary
}
Run Code Online (Sandbox Code Playgroud)

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

// The following code will do the following
// 001 or 100 = 101
// So the value of options should be 5
NSUInteger options = kCFCalendarUnitDay | kCFCalendarUnitYear;
Run Code Online (Sandbox Code Playgroud)

此技术也经常用于安全例程:

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;
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

  • 请注意,由于运算符优先级,`if(modifySecurityLevel&userLevel == userLevel)`必须是`if((modifySecurityLevel&userLevel)== userLevel)`否则表达式将始终求值为true. (2认同)

Joh*_*sch 32

要执行此操作,您希望按顺序对要测试的掩码值,然后查看ANDing的结果是否等于掩码本身:

if ((options & kCFCalendarUnitYear) == kCFCalendarUnitYear) {
   // do whatever
}
Run Code Online (Sandbox Code Playgroud)

  • 实际上`if(options&kCFCalendarUnitYear)`应该足够了,因为任何非零值都意味着该位已被设置. (12认同)
  • OTOH,如果你想将这个表达式的结果赋值给`BOOL`(`signed char`)变量,你*应该*包含`==`表达式,因为这会将表达式结果的范围缩小到0或1位掩码本身可能不适合,特别是如果枚举中有超过8个掩码常量. (7认同)
  • == kCFCalendarUnitYear似乎没必要 (5认同)

dav*_*nal 16

位掩码起作用,因为在二进制中,每个2的幂(即,2 0 = 1,2 1 = 2,2 1 = 4)占据位序列中的单个点.例如:

decimal | binary 
1       | 0001
2       | 0010
4       | 0100
8       | 1000
Run Code Online (Sandbox Code Playgroud)

当你or(运营商|在类似C语言),两个号码a,并b汇集成c,你说"采取在比特a,b或两者并把它们放在c".由于2的幂表示二进制字符串中的单个位置,因此没有重叠,您可以确定设置了哪些.例如,如果我们or2和4

0010 | 0100 = 0110
Run Code Online (Sandbox Code Playgroud)

请注意它基本上是如何组合两者的.另一方面,如果我们or5和3:

decimal | binary 
5       | 0101
3       | 0011

0101 | 0011 = 0111
Run Code Online (Sandbox Code Playgroud)

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

通过另一个例子,这变得更加明显.我们取数字1,2和4(所有2的幂)

0001 | 0010 | 0100 = 0111
Run Code Online (Sandbox Code Playgroud)

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


Mic*_*son 6

关键是要记住,你合并到"选项"中的每个值都只是一个数字.我不确定你对二进制文件有多熟悉,但你可以用十进制来想一想,只是添加数字而不是ORing它们.

假设A = 10,B = 100,C = 1000

如果你想设置options = A + B,那么选项将等于110.你调用的方法将查看A的"十"位置,B的"数百"位置和C的"数千"位置.在这个例子中,有一个是数百位和十位,因此该方法将知道在选项中设置了A和B.

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

  • 这个想法不仅相似,而且完全相同.只有基数不同:2对10.任何熟悉UNIX/Linux命令行的人都在base-8(八进制)中完成了这一操作,因为这是chmod命令的数字权限掩码的工作方式. (2认同)