Objective-C 枚举的使用

228 阅读3分钟

概述

 在 C、C++、Java 这些语言中就有它们对应的枚举,定义的方式也多种多样,但使用目的只有一个:让代码可读性更强。这里我们只简单说说 Objective-C 中枚举的使用。

在 Apple 提供给我们的 API 中,经常会看到使用枚举的例子:

有这样的:

typedef NS_ENUM(NSInteger, UIViewAnimationTransition) {
    UIViewAnimationTransitionNone,
    UIViewAnimationTransitionFlipFromLeft,
    UIViewAnimationTransitionFlipFromRight,
    UIViewAnimationTransitionCurlUp,
    UIViewAnimationTransitionCurlDown,
};

还有这样的:

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
};

可以看到在 Objective-C 中为我们提供了两种定义宏的枚举:NS_ENUM 和 NS_OPTIONS,这两者其实本质上是一样的,都是用来声明枚举的,不过细小的区别还是有的:

NS_ENUM:常用来声明一般的 NSInteger 类型的枚举。

NS_OPTIONS:常用来声明 NSUinteger 类型的明位掩码(bitmasked)。

说明:用 enum 就可以声明一般类型和位掩码类型,但是苹果官方建议我们使用 NS_ENUM 和 NS_OPTIONS,它们可以推断出不同类型的枚举,还可以适配到不同的开发平台。

NS_OPTIONS

我们重点看看 NS_OPTIONS 类型的枚举:

在 NS_OPTIONS 类型的枚举中,我们看到每个枚举值后有 <<,其实这就是左移位运算符,这样我们就可以通过 |(或位运算符)进行组合使用,a << b 就表示把 a 转化为二进制后左移 b 位。

使用场景:如果一个枚举变量可能代表多个枚举值的时候,我们就需要声明成 NS_OPTIONS 类型的枚举。其原理只是把各个枚举值加起来(转化成十进制再想加),获得一个新值(十进制)。我们知道枚举值相对唯一,位运算就很好的解决了这个问题,它可以确保枚举值组合在一起的唯一性。

那么苹果官方是怎么知道我们多个条件组合使用了呢?答案是通过 &(与位运算符)进行判断的:

    UIControlEvent controlEvents = UIControlEventEditingDidBegin | UIControlEventValueChanged | UIControlEventEditingDidEnd;
    
    if (controlEvents & UIControlEventEditingDidBegin) {
        NSLog(@"UIControlEventEditingDidBegin");
    } else if (controlEvents & UIControlEventValueChanged) {
        NSLog(@"UIControlEventValueChanged");
    } else if (controlEvents & UIControlEventEditingDidEnd) {
        NSLog(@"UIControlEventEditingDidEnd");
    }

在这里我举例说明 NS_OPTIONS 类型枚举的用法:

typedef NS_OPTIONS(NSUInteger, MyType) {
    MyTypeA = 1 << 0, // 值为1,2的0次方
    MyTypeB = 1 << 1, // 值为2,2的1次方
    MyTypeC = 1 << 2, // 值为4,2的2次方
    MyTypeD = 1 << 3, // 值为8,2的3次方
};

// 使用
NSUInteger value = MyTypeA | MyTypeB;
    
NSLog(
    @"%zd, %zd, %zd, %zd", 
    value & MyTypeA, 
    value & MyTypeB, 
    value & MyTypeC, 
    value & MyTypeD
);

// 输出结果:1, 2, 0, 0

这里我们再通过二进制解释一下:

     转化成二进制:
     
     MyTypeA: 0 0 0 1
        |
     MyTypeB: 0 0 1 0
        |
     MyTypeC: 0 1 0 0
        |
     MyTypeD: 1 0 0 0
     ----------------
     value  : 1 1 1 1
     
     ps:以上的结果是通过|得到 value 的值(|的意思是只要有一个为1,结果就为1)
     
     value  : 1 1 1 1
        &
     MyTypeA: 0 0 0 1
     ----------------
     结果值:   0 0 0 1
     
     ps:0001就是 MyTypeA 的值

从结果可以看出:1 是 MyTypeA 的值,2 是 MyTypeB 的值,其它枚举值没有参与组合使用,值都为 0。这也说明,如果 value & MyTypeC 的值为 0,说明 value 不包含 MyTypeC,反之亦然。