BuySellAds.com

Our DNA is written in Objective-C
Jump

Bit Masks

bitmask

Joseph Collins asks:

How do you decipher a bit mask from an argument which logically OR’d multiple values together? Enum uses bit shifting.

This question came to me while looking at UIView’s header file and wondering how Apple handles the animation options bitmask.

If you have several modes of something then usually you get by with an emum. But if you can combine several flags in s single value then you have to do this by means of bit masks. Let’s explore these today.

An enum is basically just a number, an integer. But you are telling the compiler that the individual values are legal values. Internally the first value will be 0, the second will be 1 and so on. If you want the first value to be a different value you can specify this and you can even specify a different number for each value. If you don’t, then they are just numbered sequentially.

Here’s an example from UIView.h:

typedef enum {
    UIViewAnimationCurveEaseInOut,         // slow at beginning and end
    UIViewAnimationCurveEaseIn,            // slow at beginning
    UIViewAnimationCurveEaseOut,           // slow at end
    UIViewAnimationCurveLinear
} UIViewAnimationCurve;

This defines UIViewAnimationCurveEaseInOut as 0 and the other values as 1, 2 and 3 using the automatic numbering. Often a developer chooses the default value to be the first because then a property using this type is also automatically initialized with the first value, aka 0.

From the same header file we can glean another example. Still defined as an enum, but using a bit mask:

enum {
    UIViewAutoresizingNone                 = 0,
    UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
    UIViewAutoresizingFlexibleWidth        = 1 << 1,
    UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
    UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
    UIViewAutoresizingFlexibleHeight       = 1 << 4,
    UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};
typedef NSUInteger UIViewAutoresizing;

This is actually doing what I told you above, setting individual values for the numbers of the enum. The first value is 0, the second is a 1 bit shifted to the left by 0 positions, i.e. 1. The third value is a 1 bit shifted to the left by 1 positions, i.e. 2. The fourth is a 1 bit shifted to the left by 2 positions, i.e. 4. Get it it? They could have written out the numbers like this, but a geeky engineer preferred to write the values as shifts, number << bits_to_shift.

There's another interesting fact to be seen there. Usually you would typedef an enum and call that type something of your own. Then you would use this type for defining your properties. Apple actually does not give the enum (typedef keyword omitted) a name. But actually they define a type UIViewAutoresizing to be an unsigned integer. Isn't that a bit weird because it breaks the compile-time type checking?

To understand working with bits we need to review a couple of operators in the C language.

  • logical OR: a || b
  • logical NOT: !a
  • logical AND: a && b
  • bitwise OR: a | v
  • bitwise NOT: ~a
  • bitwise AND:  a & b
  • bitwise shift left: a << b
  • bitwise shift right:  a >> b

One thing that tends to confuse people is the distinction between “logical” and “bitwise”.  You probably have used the logical operators quite a bit in if statements. Think of the bitwise cousins of essentially the same but not just for one and zero, but for all bits of a number. A logical NOT of 5 is 0, but a bitwise NOT of 5 (bin 101) is 2 (bin 010).

To put it in simpler terms, a NOT flips all ones to zeros and vice versa. The AND and OR are twice the character for logical and once for bitwise. OR means that one of the bits needs to be one for the result to also be one. AND means that both need to be one for the result to be one. Remember that.

To combine several values from a bit mask enum into one, you simply chain them together with the pipe symbol, which is the bitwise OR operator. Mathematically a bitwise OR is the same as an addition of the values, as long as they don’t overlap.

view.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;

Now if you want to use a bit mask in your own code would involve the above mentioned type definitions as well as an if to check if certain bits of this mask are set. The simplest method I know is to basically make use of the logical AND. If the result is not equal to 0 – which it is if the bit was set – then we know that this bit was set.

if (view.autoresizingMask & UIViewAutoresizingFlexibleWidth)
{
   // bit was set
}

You could also compare the result to != 0, greater than 0 or equal to the value you are checking for but personally I think that this version is the most readable. Let me repeat that bit masks only work if you have non-overlapping numbers in the enum. Each flag needs to be a distinct bit in the number, otherwise you end up with unexpected result. Adding values to it that are not single bits would set multiple flags at the same time.

And then there’s the “pro” option. If you need to have more than a single bit on or off, but actually want to have an enum in and enum, there is also a way to do that as you can see in UIViewAnimationOptions. This is really weird to mentally parse if you are seeing that for the first time.

enum {
    UIViewAnimationOptionLayoutSubviews            = 1 <<  0,
    UIViewAnimationOptionAllowUserInteraction      = 1 <<  1, // turn on user interaction while animating
    UIViewAnimationOptionBeginFromCurrentState     = 1 <<  2, // start all views from current value, not initial value
    UIViewAnimationOptionRepeat                    = 1 <<  3, // repeat animation indefinitely
    UIViewAnimationOptionAutoreverse               = 1 <<  4, // if repeat, run animation back and forth
    UIViewAnimationOptionOverrideInheritedDuration = 1 <<  5, // ignore nested duration
    UIViewAnimationOptionOverrideInheritedCurve    = 1 <<  6, // ignore nested curve
    UIViewAnimationOptionAllowAnimatedContent      = 1 <<  7, // animate contents (applies to transitions only)
    UIViewAnimationOptionShowHideTransitionViews   = 1 <<  8, // flip to/from hidden state instead of adding/removing
 
    UIViewAnimationOptionCurveEaseInOut            = 0 << 16, // default
    UIViewAnimationOptionCurveEaseIn               = 1 << 16,
    UIViewAnimationOptionCurveEaseOut              = 2 << 16,
    UIViewAnimationOptionCurveLinear               = 3 << 16,
 
    UIViewAnimationOptionTransitionNone            = 0 << 20, // default
    UIViewAnimationOptionTransitionFlipFromLeft    = 1 << 20,
    UIViewAnimationOptionTransitionFlipFromRight   = 2 << 20,
    UIViewAnimationOptionTransitionCurlUp          = 3 << 20,
    UIViewAnimationOptionTransitionCurlDown        = 4 << 20,
    UIViewAnimationOptionTransitionCrossDissolve   = 5 << 20,
    UIViewAnimationOptionTransitionFlipFromTop     = 6 << 20,
    UIViewAnimationOptionTransitionFlipFromBottom  = 7 << 20,
};
typedef NSUInteger UIViewAnimationOptions;

I’m not dyslexic, but initially I thought “WTF are they doing shifting the number 20 (bin 10100) to the left?”. So you’re forgiven if you have the same reaction.

The first block is as we discussed before. The second and third block are actually more than single bits. You need two bits to represent the values 0 through 3. And three bits for 0 through 7. So here Apple is simply using the values and shifts them into place, 16 or 20 bits respectively. The advantage here is that a single animation options bit mask offers 32 bits that can be used. Packing the transition and the curve values into this still leaves with with a single integer to pass around.

Inside the UIKit code you probably have something like this, which shifts the values back to the right edge and then masks it and then compares.

NSUInteger transition = (animationOption >> 20) & 7;
NSUInteger curve = (animationOption >> 16) & 3;

The bitwise AND serves to mask out other set bits. Though if you are bit shifting the leftmost slot to the right, then the “incoming bits” are all zeroed anyway. But it’s probably a smart idea to make this masking a habit.

When creating your own bit masks I suggest that you name them in a similar way prefixing all enum values the same. This causes them to appear together in autocompletion and groups them logically.

Did you know you can actually also specify binary and hexadecimal values directly in your code? For hex numbers you add prefix 0x for binary prefix 0b. I heard that some compilers don’t know the binary prefix, but ours (LLVM) does. So if you EVER find yourself needing that you can use it. (You probably never will.)

NSUInteger b = 0b101; // binary
NSUInteger h = 0xAA; // hex

If you only take one thing away from this article then it should be the distinction between “logical” and “bitwise”. Because then you would also know what is wrong in the original question. Can you spot it?

Image “bitmask {phantom of the opera}” by S.D. Hilderbrand


Categories: Q&A

%d bloggers like this: