枚举是为一组相关值定义的一个通用的类型,使我们在代码中能够安全地操作这些值。
如果对C语言比较熟悉,就会知道在C语言里枚举是给一组相关名称赋予一系列的整型值。但是Swift语言中的枚举更加灵活,不需要为每个枚举成员指定初始值。如果枚举成员被赋予了初始值,这个值可以是字符、字符串、任何整型或浮点类型。
此外,就像其它编程语言中的联合类型或变体类型,Swift语言中的枚举变量可以赋予每个成员任何一种类型的值。我们可以把一系列相关的值定义为一个枚举变量,每个枚举成员都有相应的值与其关联。
枚举作为Swift语言的核心类型拥有强大的能力,它继承了很多一直以来只有类支持的特性,比如提供当前值之外的信息的运算属性,和提供枚举所代表的相关值的函数的实例方法。枚举类型可以通过定义初始化器为每个成员提供初始值,也可以在原始实现上扩展更多的功能,并通过遵循特定协议对外提供标准功能。
关于这些特性的更多信息,请参考属性、方法、初始化、扩展、以及协议。
10.1枚举语法
我们可以用enum关键字定义枚举变量,并把完整的定义用一对大括号包裹起来:
<此处添加代码10.1-P131>
以指南针的四个方位为例:
<此处添加代码10.1-P132>
枚举中定义的变量的值(如 North、South、East、West)被称作该枚举变量的成员值(或成员)。关键字case表示这一行定义了新的成员值。
注意:与C或者Objective-C语言不同,Swift语言中的枚举类型在创建时不会被赋予一组默认的整型值。上述CompassPoint指南针例子中,North、South、East、West 的值并不是默认为0,1,2,3。相反地,通过 CompassPoint 类型的声明,枚举成员拥有各自独立的类型。
多个成员值可以写在同一行,使用逗号分隔:
<此处添加代码10.1-P132>
每个枚举变量都定义了一个全新的类型。与Swift语言中其它类型一样,枚举变量的命名(比如CompassPoint 和 Planet)必须以大写字母开头,并采用单数而不是复数形式,这样阅读时就一目了然了:
<此处添加代码10.1-P132>
当变量directionToHead 以 CompassPoint 中的一个值初始化时,编译器将推断directionToHead的类型。而一旦把directionToHead 以 CompassPoint 类型声明后,就可以用很简单的方法设置它为 CompassPoint 的其它值:
<此处添加代码10.1-P132>
directionToHead 的类型已知,我们就可以在赋值时省略它的类型。当遇到显式声明的枚举变量时,这种语法可以让代码更具可读性。
10.2用Swich语句匹配枚举值
我们可以用一个Swich语句匹配一个枚举值:
<此处添加代码10.2-P133>
这段代码可以这样理解:
“考虑directionToHead的值:当它等于.North时,输出 “Lots of planets have a north” ;当它等于.South时,输出 “Watch out for penguins” ”……
就像在流程控制里描述的一样,一个switch 语句在确定枚举成员时必须全面考虑。在上面的例子中,如果case 语句忽略了.West 成员,代码将无法编译,原因是没有考虑到所有 CompassPoint 的成员。这种严格的要求确保了每个枚举成员不会被意外地忽略。
当不能为每一个枚举成员都提供 case 语句时,可以给那些没有显式提到的成员设置一个 default 关键字:
<此处添加代码10.2-P133>
10.3相关值
上一节的例子表明了枚举成员是具有独立定义和类型的值。我们可以为Planet.Earth 赋值为常量或变量,并且之后验证这个值。然而,有的时候存储为除了成员值之外的其他类型的相关值也很有用,因为这可以让我们在声明枚举成员时存储额外的自定义信息,并且允许我们在每次使用这些成员时改变这些信息。
Swift枚举类型可以存储任何指定类型的相关值,并且每个成员的类型可以互不相同。枚举类型与其他编程语言中被我们熟知的鉴别联合类型、标签联合类型或者变异类型相似。
例如,假设在一个存货清单跟踪系统中需要跟踪两种带有不同条形码的产品。一些产品被标记UPC-A的格式的一维条形码,这一格式使用的是0到9的数字。每一个条形码都有一个“号码系统”数字,然后是一个十位的标识符,最后有一个“检验位”数字来验证条形码是否被扫描:
其他产品被标记QR代码格式的二维条形码,这一格式使用ISO 8859-1字符集的任意字符,并且可以表示长达2953个字符的字符串:
如果我们的存货清单跟踪系统能同时以三个整型数组的形式储存UPC-A格式,并以任意长度的字符串格式保存QR格式的话,那就很方便了。
在Swift语言中,可以这样定义这两种产品的条形码的枚举类型:
<此处添加代码10.3-P135>
这段代码可以这样理解:
“声明一个叫Barcode 的枚举类型,它包含了三个整型数组(Int,Int,Int)的相关值UPC,以及字符串(String)类型的相关值QRCode 。”
这个声明不包含任何确切的整型或字符串类型的值,它只是在 Barcode.UPCA 和Barcode.QRCode 的描述中定义了 Barcode 的枚举变量或常量可以存储的相关值类型。
现在条形码可以通过任意一个类型创建:
<此处添加代码10.3-P135>
这个例子创建了一个名为 productBarcode 的新变量,并赋予了它由三个数值数组关联的 Barcode.UPCA 成员值。在上面的 10 位标识符中出现了一个下划线85909_51226,这是为了让它作为条形码更具可读性。
我们也可以赋予这个产品另外一种类型的条纹码:
<此处添加代码10.3-P136>
这时原有的 Barcode.UPCA 和它关联的数值会被替换成新的 Barcode.QRCode字符串。已经被定义为 Barcode 类型的变量或常量允许使用 .UPCA 或者.QRCode 类型存储数据,但同一时刻只能使用其中一种类型。
与上述相同,使用 switch 语句可以用来检查不同类型的条形码,然而我们可以把关联值提取出来作为 switch 语句的一部分。我们把每个关联值作为常量(使用前缀 let)或者变量(使用前缀 var)提取出来,放在 case 内容里面:
<此处添加代码10.3-P136>
如果一个枚举成员所有的关联类型都能被提取为常量或变量,则可以在枚举成员名称前放一个 let 或者var 声明,简单来说就是这样:
<此处添加代码10.3-P136>
10.4 原始值
前面一节关联值中条形码的例子,讲述了如何为枚举成员声明不同类型的关联值。此外,枚举成员可以预先设定默认值(称为原始值),它们的类型一致。
下面的例子展示了枚举成员的原始ASCII码:
<此处添加代码10.4-P137>
这个例子中,一个名为ASCIIControlCharacter的枚举类型的原始值被定义为Character类型,并为其设置了一些常见的ASCII控制字符。关于字符可以参考字符串与字符一章。
注意原始值和关联值并不一样,就像上述例子中的三个ASCII码,原始值是在代码中第一次定义枚举类型时设置的,并且一个特定枚举成员的原始值始终保持不变。而关联值是根据某一枚举成员创建一个新的常量或变量时设置的,并且每次使用时都可以不同。
原始值可以是字符串、字符、任何整型或浮点数类型。枚举类型声明每一个原始值都必须是唯一的。当原始值为整型并且一些枚举成员没有设置特定值时它们会自增。
下面的枚举类型是上述例子中Planet 枚举类型的一个改进,它使用了整型的原始值代表每个星球距离太阳远近的顺序:
<此处添加代码10.4-P137>
自增就是意味着Planet.Venus的原始值为2,依此类推。
我们可以通过枚举成员的toRaw方法访问其原始值:
<此处添加代码10.4-P137>
通过枚举成员的fromRaw 方法尝试去发现带有特定原始值的枚举成员。下面的例子通过原始值为7找到了Uranus:
<此处添加代码10.4-P138>
然而并不是所有的Int值都会有一个匹配的星球,因此,fromRaw 方法返回的是一个可选的枚举成员。上述的例子中,possiblePlanet 的类型是Planet?或者是optional Planet。
如果试图去找到一个位置为9的星球,fromRaw方法返回的可选的Planet 值为nil:
<此处添加代码10.4-P138>
这个例子使用可选的绑定去试图访问一个原始值为9的星球。如果可以访问,则if let
somePlanet = Planet.fromRaw(9) 这一语句将一个可选 Planet 赋值给 somePlanet。但目前没有办法找到一个位置为 9 的星球,所以 else 分支就被执行了。
网友评论