views:

480

answers:

5

For example, this method from NSCalendar takes a bitmask:

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

So options can be like:

NSUInteger options = kCFCalendarUnitYear;

or like:

NSUInteger options = kCFCalendarUnitYear | kCFCalendarUnitMonth | kCFCalendarUnitDay;

What I don't get is, how is this actually done? I mean: How can they pull out those values which are merged into options? If I wanted to program something like this, that can take a bitmask, how would that look?

+2  A: 

To do this, you want to bitwise AND the value you're testing against the mask, then see if the result of the ANDing equals the mask itself:

if ((options & kCFCalendarUnitYear) == kCFCalendarUnitYear) {
   // do whatever
}
John Rasch
== kCFCalendarUnitYear seems unnecessary
sigjuice
FRotthowe
COOL man, that's COOL!
dontWatchMyProfile
OTOH, if you wanted to assign the result of this expression to a `BOOL` (`signed char`) variable, you *should* include the `==` expression, because that will reduce the range of expression results to 0 or 1. The bit mask itself probably won't fit, especially if there are more than 8 mask constants in the enumeration.
Peter Hosey
+6  A: 

Bitmasks are pretty basic really. You can think of it like this (C# until somebody can convert):

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

You can then use the bitwise operators to combine the values:

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

This technique is also often used in security routines:

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

// SuperUsers and Admins can Modify
// So this is really
// modifySecurityLevel = SuperUser | Admin;
public int modifySecurityLevel = 6;

Then to check the security level, you can use the bitwise and to see if you have sufficient permission:

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
Justin Niessner
+3  A: 

The key is remembering that each one of those values you merge into "options" is really just a number. I'm not sure how familiar you are with binary, but you can think of it in decimal and just add numbers rather than ORing them.

Let's say A=10, B=100, and C=1000

If you wanted to set options = A+B, then options would equal 110. The method you called would then look at the "tens" place for A, the "hundreds" place for B, and the "thousands" place for C. In this example, there is a 1 is the hundreds place and the tens place, so the method would know that A and B were set in the options.

It's a little different since computers use binary not decimal, but I think the idea is very similar, and sometimes it's easier to think about it in a familiar numbering system.

Michael Patterson
good explanation! thanks!
dontWatchMyProfile
The idea is not just similar, but exactly the same. Only the base is different: 2 vs. 10. Anyone who's familiar with the UNIX/Linux command line has also done this in base-8 (octal), as that's how the chmod command's numeric permission masks work.
Peter Hosey
+1  A: 

Bitmasks work because in binary, each power of 2 (i.e., 20=1, 21=2, 21=4) occupies a single spot in the sequence of bits. For example:

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

When you or (the operator | in C-like languages) two numbers a and b together into c, you're saying "take the bits that are in a, b, or both and put them in c." Since a power of two represents a single position in a binary string, there's no overlap, and you can determine which ones were set. For example, if we or 2 and 4

0010 | 0100 = 0110

Notice how it basically combined the two. On the other hand, if we or 5 and 3:

decimal | binary 
5       | 0101
3       | 0011

0101 | 0011 = 0111

notice that we have no way of telling which bits came from where, because there was overlap between the binary representation of each.

This becomes more apparent with one more example. Let's take the numbers 1, 2, and 4 (all powers of two)

0001 | 0010 | 0100 = 0111

This is the same result as 5 | 3! But since the original numbers are powers of two, we can tell uniquely where each bit came from.

notJim
A: 

I've found Calculator.app to be helpful in visualizing bit masks. (Just choose View > Programmer, and then click the button to Show Binary). (You can click on any of the 0s or 1s in the binary table to switch those bits on or off; or, enter numbers in decimal or hex (use the 8 | 10 | 16 NSSegmentedControl to switch between different representations)).

NSGod