Since as long as I have been a programmer, bitwise operators have been an important but sometimes daunting part of my work. These are the programming tools with which you take a numerical value, let’s say seven, and manipulate it at the level of the bits that make up its binary representation. You can display the binary representation of any value in lldb, the standard Apple debugger, using “p/t”:
(lldb) p/t 7
(int) $0 = 0b00000000000000000000000000000111
One of the most common use cases for bitbwise manipulation is to squeeze lots of information into a small amount of computer memory. For example, a 32-bit integer can hold 32 distinct boolean values:
(lldb) p/t -1
(int) $1 = 0b11111111111111111111111111111111
Yep! The signed, 32-bit value for -1 is often represented as “all the bits are on!” But we could also consider this bit field to be a representation of the enabled/disabled states for 32 distinct preferences in our app. In which case, it’s common to declare numerical constants in code that make it easy to know which bit stands for what.
In C, this is often done by declaring an enumeration where each element is a different power of two, which is by definition the numeric value of each of the bits in any binary number. Apple’s own Objective-C headers use this all over the place. For example, CALayer declares a mask type that allows the four corners of a rectangle to be identified as 1, 2, 4, or 8:
typedef NS_OPTIONS (NSUInteger, CACornerMask)
kCALayerMinXMinYCorner = 1U << 0,
kCALayerMaxXMinYCorner = 1U << 1,
kCALayerMinXMaxYCorner = 1U << 2,
kCALayerMaxXMaxYCorner = 1U << 3,
To work with these types of values, you often need to use bitwise operators to set, test, unset, or toggle particular elements in a value. Working with bitwise values can be pretty brain-bending, but I recommend that everybody learn about and understand why and how each of the most common bitwise operators work. I won’t delve too deep here, but for example if you wanted to “add” the value 8, for kCALayerMaxXMaxYCorner, to an existing mask of value 7, representing all three other corners, this is what it would look like at a bitwise level:
(lldb) p/t 7
(int) $2 = 0b00000000000000000000000000000111
(lldb) p/t 8
(int) $3 = 0b00000000000000000000000000001000
(lldb) p/t 7 | 8
(int) $4 = 0b00000000000000000000000000001111
A bitwise operator acts on each bit of a value independently, potentially transforming it into a new value. The bitwise OR operator, which is “|” in C, did this above by looking at every bit in the value for 7, and every bit in the value for 8, and if either one had a value of 1, the bit in the resulting value is also set to 1. You can see this visually by playing with the operators in lldb as I’ve demonstrated above.
Even once you fully understand bitwise operators, they’re pretty annoying to work with on a day-to-day basis. That’s why in C, I’ve used these macros for years that simplify the task of performing these common tasks. I’ll leave it as an exercise to figure out why these all work the way they do:
// Basic bitwise operators for our convenience
#define RS_SET_BIT(mask, addedBit) (mask |= addedBit)
#define RS_TEST_BIT(mask, testBit) ((mask & testBit) != 0)
#define RS_CLEAR_BIT(mask, removedBit) (mask &= ~(removedBit))
#define RS_TOGGLE_BIT(mask, toggledBit) (mask ^= toggledBit)
For example, if you were maintaining a list of corner masks, and wanted to remove the kCALayerMinXMaxYCorner value, you just:
CACornerMask existingCornerMask = ... whatever ...
It’s more readable, easier to remember, and most importantly easier to get right than always coming up with the exact combination of bitwise operations to remove a value from a binary bitmask.
What about Swift? These macros are based on C’s macro preprocessor, and Swift doesn’t support such a thing. The good news is Swift handles bitmasks in a fundamentally better way, by promoting them to a first-class type called OptionMask. If you’re lucky enough to be working in Swift, you can achieve the same thing as above with:
var existingCornerMask: CACornerMask = ... whatever ...
Bitwise manipulation is important in most every field of computer programming, and as I said, you should really understand it. But as I also said, once you’ve understood it, you should probably strive to never use bitwise operators again. At least not directly.