在UIKit中对UIButton的理解中, 其中有一个设置如下:
[button setTitle:@"点击" forState:UIControlStateNormal];
其中UIControlStateNormal是一个枚举值,代码如下:
typedef NS_OPTIONS(NSUInteger, UIControlState) {
UIControlStateNormal = 0,
UIControlStateHighlighted = 1 << 0, // used when UIControl isHighlighted is set
UIControlStateDisabled = 1 << 1,
UIControlStateSelected = 1 << 2, // flag usable by app (see below)
UIControlStateFocused API_AVAILABLE(ios(9.0)) = 1 << 3, // Applicable only when the screen supports focus
UIControlStateApplication = 0x00FF0000, // additional flags available for application use
UIControlStateReserved = 0xFF000000 // flags reserved for internal framework use
};
这个就很费解了,为啥定义一个宏,还来一个宏定义。有啥猫腻。看看宏是怎么定义的。
//第一个宏:
#define NS_OPTIONS(_type, _name) CF_OPTIONS(_type, _name)
//CF_OPTIONS跳不进去了,那就只能用强大的GhatGPT了
#define CF_OPTIONS(_type, _name) enum _name : _type _name; enum _name : _type
反正这些语法对于现在的我,是完全不能理解的。好歹我之前也是写过一些C/C++应用的,居然这个语法都不能很好的去理解。那接下来咋们就好好全方面的去从底层语法理解吧。
CF_OPTIONS
是一个宏定义,用于定义 Core Foundation 框架中的位掩码类型。
_type
表示底层数据类型,咋们这里就是NSInteger整数类型_name
表示类型名称,可以是任何合法的标识符。当前分析的名字就是UIControlState
其中关键宏CF_OPTIONS中,有两个语句块,分别是:
- enum _name : _type _name;
- enum _name : _type
现在把焦点放到这两个语句块的分析上面来。
从《Objective-C基础教程》和《Objective-C编程全解》中去看枚举的介绍,基本上没有什么详细的介绍,只是简单的带过。
那么我们就先从C语言语法中去看看枚举类型是什么情况,然后在回头来分析咋们现在的语法。
在C语言中定义枚举类型的方式如下:
enum 枚举名称
{
标识符 = 整型常量,
标识符 = 整型常量,
标识符 = 整型常量,
标识符 = 整型常量
};
我们做一个示例,定义一个枚举名称为Test,拥有TestA、TestB等枚举成员的枚举类型。代码如下:
enum Test
{
TestA = 0,
TestB = 1,
TestC = 2,
TestD = 3
};
- 注1: 当枚举成员都未设置"=整型常量"时,默认从第一个标识符开始,从0依次递增加1
- 注2: 当为某个枚举成员设置"=整型常量"时,其后面的标识符会在此基础上依次递增加1
在使用该枚举类型时,我们需要创建一个枚举变量。创建枚举变量的方式如下:
enum 枚举名称 枚举变量 = 枚举变量值;
以"创建一个枚举名称为Test的枚举变量test,并为其赋值TestB"为例
enum Test test = TestB;
- 注: 枚举变量值的取值只能从对应枚举类型的枚举成员中选择
我们也可以在定义枚举类型的同时定义枚举变量,该枚举变量不用初始化,可直接对其进行赋值并使用
enum Test
{
TestA = 0,
TestB,
TestC,
TestD
} test;
- 注: 该枚举名称可以省略,直接定义枚举变量即可(称作"匿名枚举")
如上我们针对于枚举进行了一些常识的复习,对于匿名枚举也有了认识。看来,人的记忆曲线持续的事件真的很短,我也就7/8年没有写C,就忘得差不多了。
接下来我们结合typedef一起分析,这样更加接近我们的这次的目的。
通过typedef为枚举类型设置一个"别名",这样便可以像使用int一样使用枚举类型:
typedef enum _Test
{
TestA = 0,
TestB,
TestC,
TestD
} Test;
定义"别名"后,便可采用如下方式定义枚举变量
Test test = TestB;
这样是不是就方便很多了。针对匿名枚举,在通过typedef设置"别名"的形式如下:
typedef enum
{
TestA = 0,
TestB,
TestC,
TestD
} Test;
定义"别名"后,可以和上面非匿名的方式一样:
Test test = TestB;
到这里,基础的主要部分都补充完成了。到了核心揭秘的时刻了。
我们把宏部分的代码展开如下:
//第一个语句块
typedef enum UIControlState : NSUInteger UIControlState;
//第二个语句块
enum UIControlState : NSUInteger {
UIControlStateNormal = 0,
UIControlStateHighlighted = 1 << 0, // used when UIControl isHighlighted is set
UIControlStateDisabled = 1 << 1,
UIControlStateSelected = 1 << 2, // flag usable by app (see below)
UIControlStateFocused API_AVAILABLE(ios(9.0)) = 1 << 3, // Applicable only when the screen supports focus
UIControlStateApplication = 0x00FF0000, // additional flags available for application use
UIControlStateReserved = 0xFF000000 // flags reserved for internal framework use
};
这么已展开的话, 那么我们这里又缺失了一个语法的理解。enum UIControlState : NSUInteger。这种语法。通过ChatGPT理解如下:
在 Objective-C 中,可以使用 enum
关键字定义枚举类型。语法格式为:
enum EnumName {
EnumValue1,
EnumValue2,
// ...
};
其中 EnumName
表示枚举类型的名称,EnumValue1
、EnumValue2
等表示枚举值。枚举值默认从 0 开始自增,也可以手动指定枚举值的数值。
在定义枚举类型时,可以使用 :
指定枚举值的底层数据类型。例如,下面的代码定义了一个名为 MyEnum
的枚举类型,底层数据类型为 NSUInteger
:
enum MyEnum : NSUInteger {
MyEnumValue1,
MyEnumValue2
};
这里的 : NSUInteger
表示枚举值的底层数据类型为 NSUInteger
。MyEnumValue1
和 MyEnumValue2
是枚举值,它们的数值分别为 0 和 1。
所以这个是Objective-C特有的语法。到目前为止,语法知识补充完毕。
---------------------------------- 核心部分 ---------------------------------------------------------
我们不厌其烦的在一次把宏展开的代码贴到下面:
//第一个语句块
typedef enum UIControlState : NSUInteger UIControlState;
//第二个语句块
enum UIControlState : NSUInteger {
UIControlStateNormal = 0,
UIControlStateHighlighted = 1 << 0, // used when UIControl isHighlighted is set
UIControlStateDisabled = 1 << 1,
UIControlStateSelected = 1 << 2, // flag usable by app (see below)
UIControlStateFocused API_AVAILABLE(ios(9.0)) = 1 << 3, // Applicable only when the screen supports focus
UIControlStateApplication = 0x00FF0000, // additional flags available for application use
UIControlStateReserved = 0xFF000000 // flags reserved for internal framework use
};
上面的代码解释如下:
定义了一个UIControlState : NSUInteger的枚举类型的枚举。如下:
枚举中间用了位掩码进行每个具体枚举值的定义。本质和是否用位掩码,还是用给定的数值,其实没有多大的关系的。这个enum语法是做不了限制的。所以很多文章说typedef NS_OPTIONS(NSUInteger, UIControlState)多用于带有移位运算的枚举是不成立的,这个只是君子协议,没有从语法上进行限制的。从目前代码层面的分析
enum UIControlState : NSUInteger {
UIControlStateNormal = 0,
UIControlStateHighlighted = 1 << 0, // used when UIControl isHighlighted is set
UIControlStateDisabled = 1 << 1,
UIControlStateSelected = 1 << 2, // flag usable by app (see below)
UIControlStateFocused API_AVAILABLE(ios(9.0)) = 1 << 3, // Applicable only when the screen supports focus
UIControlStateApplication = 0x00FF0000, // additional flags available for application use
UIControlStateReserved = 0xFF000000 // flags reserved for internal framework use
};
然后通过typedef为枚举类型UIControlState : NSUInteger设置一个"别名"叫UIControlState。这样后续枚举类型UIControlState : NSUInteger就可以用"别名"UIControlState来定义变量。
typedef enum UIControlState : NSUInteger UIControlState;
这段代码最后的分析就是,为啥typedef对枚举类型UIControlState : NSUInteger的"别名"定义前置了,这个从语法上面没有问题么?
目前ChatGPT也没有很好的解答,原本CF_OPTIONS(_type, _name)这个宏定义就是ChatGPT给的。其实也就没有必要进行深究了,除非真的拔开Core Foundation 框架进行真实代码分析了。
今天代码就分析到这里了。
附件一:
本文引用如下信息:
1,《iOS之枚举用法 - 简书》
2,GhatGPT的解答。
3,《Objective-C基础教程》
4,《Objective-C编程全解》