As we continue our series of articles on Effective Objective-C, I’ll focus on enumerations. Enumerations are used a lot in OC, so do we really know what they are?
Because OC is based on C language, OC also has the enum function: enum.
Enumerations are used frequently in system frameworks, but they are easily overlooked by developers, and I often overlook them too.
Where are enumerations used?
When an error status code is represented as a series of constants or an optional combination, enumeration is a good way to name it.
Enumerations are just a way of naming constants. The various states that an object experiences can be defined as a simple enumeration.
Simple enumeration
For example, we use the following enumeration to represent network-state connections:
enum PPSConnectionState{
PPSConnectionStateDisconnected,
PPSConnectionStateConnecting,
PPSConnectionStateConnected,
};Copy the code
The code is easier to read because each enumeration is named so that we can easily understand what it represents. The compiler assigns each enumeration a unique number, starting at 0 and incrementing each enumeration by 1. The data type used to implement enumerations depends on the compiler, but the number of bits must be sufficient to fully represent the following enumeration numbers.
But if we define enumerations as above, then we define an enumeration variable as follows:
enum PPSConnectionState state = PPSConnectionStateConnected;Copy the code
We’re going to have to type enum every time and that’s a little bit cumbersome we’re going to type PPSConnectionState just like a normal object and if we’re going to do that we’re going to define enumerations using the typedef keyword
enum PPSConnectionState {
PPSConnectionStateDisconnected,
PPSConnectionStateConnecting,
PPSConnectionStateConnected,
};
typedef enum PPSConnectionState PPSConnectionState;Copy the code
You can abbreviate it now:
PPSConnectionState state = PPSConnectionStateConnected;Copy the code
The C++11 standard revised several features of enumerations. One useful feature is that when we define an enumeration, we can define the underlying data type of the enumeration (used by the compiler to indicate the number of the enumeration) so that we can use forward-declared enumeration variables.
enum PPSConnectionState : NSInteger{ PPSConnectionStateDisconnected, PPSConnectionStateConnecting, PPSConnectionStateConnected, }; / / can also directly use enum PPSConnectionState {PPSConnectionStateDisconnected = 0, PPSConnectionStateConnecting, PPSConnectionStateConnected, };Copy the code
The underlying data type of the enumeration is the same in both ways. The first way the compiler assigns the enumeration to start at 0 by default. If we want our enumeration to start at 1, we can use the second way, which changes 0 to 1, and our enumeration will start at 1.
Combinatorial enumeration
When working with the framework of a system, we often come across combinatorial enumerations, especially when using UIKit, such as:
enum UIViewAutoresizing{
UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
...
};Copy the code
Like the enumeration above, which shows how a view should be resized horizontally and vertically, we see that their underlying data uses binary values in order to combine enumerations. For example, we can define an enumeration:
enum UIViewAutoresizing resizing = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth;Copy the code
The two enums are then combined
In the Foundation framework, we define auxiliary macros that are often used to specify the underlying data of an enumeration when defining an enumeration
typedef NS_ENUM(NSUInteger, PPSConnectionState){
PPSConnectionStateDisconnected,
PPSConnectionStateConnecting,
PPSConnectionStateConnected,
};Copy the code
typedef NS_OPTION(NSUInteger, UIViewAutoresizing){
UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
...
};Copy the code
We see two macros above, so how do we choose
Anything that needs to combine enumerations as bits or operations should be defined using NS_OPTION
Any enumeration that does not require composition should be defined using NS_ENUM
Pay attention to
In switch statements, we often use enumeration types, but we always like to use the default branch in switch statements. If we use enumeration, we don’t want to use the default branch, because if we add another state to the enumeration, Then we need to find every switch statement to process, otherwise we will go straight to the default branch and the compiler will run without any warning. But without the default branch, if we don’t change switch when we add enumeration types, the compiler will warn us at compile time that the enumeration is not completely judged.
conclusion
- We should always use enumerations to represent the state of a state machine (an object), and we should pay attention to the naming of enumerations.
- When you define an enumeration, if multiple enumerations can be combined, then the underlying data of the enumeration should be expressed as binary values, powers of two.
- When defining enumeration types using NS_ENUM NS_OPTION, we can specify the underlying data directly, which ensures that the enumeration is the underlying data that we developers want.
- Do not implement the default statement when the switch statement judges enumerations, so that the compiler will be alerted when an enumeration type is added