How to Accept Multiple Enum Values in a Property or Method

enum

Every iOS developer has probably come across a property or method in an API that accepts multiple enum values at once, separated by a vertical pipe. How does that work, and how can we do the same?

First, let’s go over what an enum is and how to create one. An enum, short for enumeration, is a user-defined type consisting of a set of named constants called enumerators. It is used to group related values in a strongly-typed way. Behind the scenes, each enumerator maps to an integer. If we don’t specify explicit integer values, the compiler will automatically assign values sequentially starting from 0. In this example, we’ll be setting explicit integer values that are increasing powers of 2 (you’ll see why later). We’ll also be using the new NS_ENUM syntax introduced in iOS 6:

typedef NS_ENUM(NSInteger, MRColor) {
    MRColorNone = 0,
    MRColorRed = 1,
    MRColorBlue = 2,
    MRColorWhite = 4,
    MRColorGreen = 8
};

Now let’s imagine invoking a method with several of these color values at once, like this:

[self paint:MRColorRed|MRColorBlue];

In C, we combine multiple enum values by separating them with a vertical pipe. This symbol represents a bitwise OR operation. It compares each bit in two values, and for each bit returns 1 if either value is 1, and returns 0 if not. Here’s what happens if perform an OR operation on the included enum values, MRColorRed (1) and MRColorBlue (2):

0001 ← MRColorRed (1)
0010 ← MRColorBlue (2)
----
0011 ← equals 3

So our method receives a color value of 3 as its input. Now, we can use something called the bitwise AND operator (&). For each bit, it returns 1 if both values are 1, and returns 0 if not. Let’s perform an AND operation on the input (3) and both of the included enum values, MRColorRed (1) and MRColorBlue (2):

0011 ← input (3)
0001 ← MRColorRed (1)
----
0001 ← equals 1
0011 ← input (3)
0010 ← MRColorBlue (2)
----
0010 ← equals 2

Now let’s try an AND operation on the input (3) and the non-included enum values, MRColorWhite (4) and MRColorGreen (8):

0011 ← input (3)
0100 ← MRColorWhite (4)
----
0000 ← equals 0
0011 ← input (3)
1000 ← MRColorGreen (8)
----
0000 ← equals 0

Interesting. So for an included enumerator, we get its own value back. For non-included enumerators, we get 0. So that means that inside our method, we simply need to perform a binary AND on the input parameter and every possible enum value to see if it was included. In C, since 0 resolves to false and any other number resolves to true, we can just put the AND operation inside an if statement:

- (void) paint:(MRColor)color
{
    if (color & MRColorRed) {
        //red was included
    }
    if (color & MRColorBlue) {
        //blue was included
    }
    if (color & MRColorWhite) {
        //white was included
    }
    if (color & MRColorGreen) {
        //green was included
    }
    …
}

This only works because we chose powers of 2 as our enum values. This allows each enum value to map to a single bit in our input integer value. Think of it as a row of on-off switches, where each switch is a bit. This is also called a bit mask.

Bitshift Syntax

Curious developers will notice that the enum definitions in Apple’s header files look very strange. Why is that?

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

The << symbol is also known as the bitshift operator. It takes every bit passed to it and shifts it “left” by a given number of spaces. So:

1 << 0: 0001 which is 1
1 << 1: 0010 which is 2
1 << 2: 0100 which is 4
1 << 3: 1000 which is 8

The resulting number values are the same as the powers of two we used! So that means we can alternatively define our enum like this:

typedef NS_OPTIONS(NSUInteger, MRColor) {
    MRColorNone  = 0,
    MRColorRed   = 1 << 0,
    MRColorBlue  = 1 << 1,
    MRColorWhite = 1 << 2,
    MRColorGreen = 1 << 3
};

It’s just a different way of expressing the same values in code using sequential numbers instead of increasing powers of 2, which doesn’t look as clean. Finally, with iOS 6 the recommended syntax for defining an enum that contains a bitmask is NS_OPTIONS, not NS_ENUM.

So there you have it! Now you can write properties or methods that accept multiple enum parameters! Enjoy!

About these ads

About Martin Rybak

I am a New York area software developer and MBA with 10+ years of experience on the Microsoft stack. Over the past few years I have also expanded into iOS development using native Objective-C. I architect and develop full-stack web applications, iOS apps, database systems, and backend services.

7 responses to “How to Accept Multiple Enum Values in a Property or Method

  1. Hello,
    Thanks for the info :)

    if I am not wrong the line :”Here’s what happens if perform an OR operation on the numbers 3 and 1:”
    should be ‘Here’s what happens if perform an AND operation on the numbers 3 and 1:’ i Think it is a small typo mistake.

  2. Nick

    if (color | MRColorRed == MRColorRed) <– This is a binary or and incorrect. You can (or should) simply do this: if(color & MRColorRed) <– This returns true when the MRColorRed bit is a 1 and false when it's a 0.

  3. Great suggestion Nick. Much simpler, thanks!

  4. Great article Objective-C enum values. When I started reading and you described using the bitmasking capability of an NS_ENUM, I was thinking, “I’m pretty sure that’s what NS_OPTIONS is for”… and then you covered that right afterwards.

    Though I am curious, other than just convention, exactly what is the functional or implementation difference is between the two? NSHipster seems to talk about being able to verify that each NS_OPTION listed is the same type, and another site I found mentioned something about C++ compiler compatibility.

    Thanks.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 25 other followers

%d bloggers like this: