继续学习Swift文档,从上一章节:闭包,咱们学习了Swift闭包相关的内容,如闭包的定义和使用、闭包的简写、速记参数名称、尾随闭包、捕获值、闭包的类型、转义闭包(@escaping)和自动闭包(@autoclosure)等这些内容。如今,咱们学习Swift的枚举相关的内容。因为篇幅较长,这里分篇来记录,接下来,Fighting!html
若是你已经熟练掌握了枚举的使用,那请移步下一章节:结构体和类express
枚举为一组相关值定义了通用类型,并使您可以在代码中以类型安全的方式使用这些值。编程
若是熟悉C,就知道C中的枚举将相关名称分配给一组整数值。 Swift中的枚举更加灵活,无需为每种枚举提供值。 若是为每种枚举状况提供了一个值(称为原始值),则该值能够是字符串,字符或任何整数或浮点类型的值。swift
或者,枚举案例能够指定要存储的任何类型的关联值以及每一个不一样的案例值,这与其余语言中的并集或变体很类似。 您能够将一组常见的相关案例定义为一个枚举的一部分,每一个案例都有一组与之关联的不一样类型的适当类型的值。api
Swift中的枚举自己就是一流的类型。 它们采用了许多传统上仅由类支持的功能(意思是能够和类同样提供相关功能),例如提供枚举当前值的附加信息的计算属性,以及提供与枚举所表示的值相关的功能的实例方法。 枚举还能够定义初始值设定项以提供初始case值。 能够扩展以扩展其功能,使其超出其最初的实现; 并能够遵循协议以提供标准功能。安全
想知道更多的功能,请参阅Properties, Methods, Initialization, Extensions, 和 Protocols.bash
您可使用enum关键字引入枚举,并将其整个定义放在一对大括号内:闭包
enum SomeEnumeration {
// enumeration definition goes here
}
复制代码
这是指南针的四个要点的示例:less
enum CompassPoint {
case north
case south
case east
case west
}
复制代码
枚举里定义的这些值(例如north, south, east, 和west) 是枚举里的cases。可使用case关键字新增cases。编程语言
注意
与C和Objective-C等语言不一样,Swift枚举案例默认状况下未设置整数值。 在上面的CompassPoint示例中,北,南,东和西并不隐式等于0、一、2和3。相反,不一样的枚举状况自己就是具备明肯定义的CompassPoint类型的值。
多个cases能够写在一行上,用逗号隔开:
enum Planet {
case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}
复制代码
每一个枚举定义都定义一个新类型。 与Swift中的其余类型同样,它们的名称(例如CompassPoint和Planet)以大写字母开头。 给枚举类型使用单数而不是复数的名称,以方便阅读:
var directionToHead = CompassPoint.west
复制代码
当使用CompassPoint的可能值之一进行初始化时,会推断directionToHead的类型。 将directionToHead声明为CompassPoint后,您可使用较短的点语法将其设置为其余CompassPoint值:
directionToHead = .east
复制代码
directionToHead的类型是已知的,所以能够在设置其值时将类型删除。 当使用显式类型的枚举值时,这使得代码易于阅读。
可使用switch匹配枚举中不一样的值:
directionToHead = .south
switch directionToHead {
case .north:
print("Lots of planets have a north")
case .south:
print("Watch out for penguins")
case .east:
print("Where the sun rises")
case .west:
print("Where the skies are blue")
}
// Prints "Watch out for penguins"
复制代码
上面代码能够解读为:
判断directionToHead的值,若是directionToHead等于.north,则打印"Lots of planets have a north"...以此类推。
如控制流中所述,考虑枚举的状况时,switch语句必须是详尽的。 若是省略.west的case,则此代码不会编译,由于它没有考虑CompassPoint case的完整列表。 要求详尽无遗,以确保不会意外省略枚举case。
若是不适合为每一个枚举案例提供case,则能够提供默认case以涵盖全部未明确解决的案例:
let somePlanet = Planet.earth
switch somePlanet {
case .earth:
print("Mostly harmless")
default:
print("Not a safe place for humans")
}
// Prints "Mostly harmless"
复制代码
对于某些枚举,收集全部该枚举的cases颇有用。 您能够经过在枚举名称后输入:CaseIterable来启用此功能。 Swift将全部案例的集合做为枚举类型的allCases属性公开。 这是一个例子:
enum Beverage: CaseIterable {
case coffee, tea, juice
}
let numberOfChoices = Beverage.allCases.count
print("\(numberOfChoices) beverages available")
// Prints "3 beverages available"
复制代码
在上面的示例中,您编写Beverage.allCases来访问包含全部Beverage枚举案例的集合。 您能够像使用其余集合同样使用allCases-该集合的元素是枚举类型的实例,所以在这种状况下,它们是“Beverage”值。 上面的示例计算了有多少个case,下面的示例使用for循环遍历全部案例。
for beverage in Beverage.allCases {
print(beverage)
}
// coffee
// tea
// juice
复制代码
上面示例中使用的语法将枚举标记为遵循CaseIterable协议。 有关协议的信息,请参见Protocols。
上一节中的示例说明枚举的cases自己就是已定义(和键入)的值。 您能够将常量或变量设置为Planet.earth,而后再检查此值。 可是,有时能够将其余类型的值与这些案例值一块儿存储。 此附加信息称为关联值,每次将这种状况用做代码中的值时,它都会有所不一样。
您能够定义Swift枚举来存储任何给定类型的关联值,而且若是须要,每种枚举的值类型能够不一样。 相似于这些的枚举在其余编程语言中被称为区分联合,标记联合或变体。
例如,假设库存跟踪系统须要经过两种不一样类型的条形码来跟踪产品。 某些产品用UPC格式的一维条形码标记,其使用数字0到9。每一个条形码都有一个数字系统数字,后跟五个制造商代码数字和五个产品代码数字。 这些后面跟一个校验位,以验证代码是否已正确扫描:
其余产品带有QR码格式的2D条形码标签,可使用任何ISO 8859-1字符,而且能够编码长达2,953个字符的字符串:
库存跟踪系统能够方便地将UPC条形码存储为四个整数的元组,并将QR码条形码存储为任意长度的字符串。
在Swift中,定义两种类型产品条形码的枚举可能看起来像这样:
enum Barcode {
case upc(Int, Int, Int, Int)
case qrCode(String)
}
复制代码
能够被解读为:
定义了一个叫Barcode的枚举,带有一个(Int, Int, Int, Int)类型的upc值和一个String类型的qrCode值。
此定义不提供任何实际的Int或String值,只是定义Barcode常数和变量等于Barcode.upc或Barcode.qrCode时能够存储的关联值的类型。
而后,您可使用如下两种类型之一建立新的条形码:
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
复制代码
本示例建立一个名为productBarcode的新变量,并为其分配Barcode.upc值,并具备关联的元组值(8,85909,51226,3)。
您能够为同一产品分配不一样类型的条形码:
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")
复制代码
此时,原始的Barcode.upc及其整数值将替换为新的Barcode.qrCode及其字符串值。 Barcode类型的常量和变量能够存储.upc或.qrCode(及其关联值),但在任何给定时间只能存储其中之一。
您可使用switch语句检查不一样的条形码类型,相似于将枚举值与Switch语句匹配中的示例。 可是,此次,关联值被提取为switch语句的一部分。 您能够将每一个关联的值提取为常量(带有let前缀)或变量(带有var前缀),以供在switch case的主体内使用:
switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case .qrCode(let productCode):
print("QR code: \(productCode).")
}
// Prints "QR code: ABCDEFGHIJKLMNOP."
复制代码
为了简化起见,若是枚举案例的全部关联值都提取为常量,或者全部提取的变量都为变量,则能够在案例名称前放置一个var或let批注:
switch productBarcode {
case let .upc(numberSystem, manufacturer, product, check):
print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).")
case let .qrCode(productCode):
print("QR code: \(productCode).")
}
// Prints "QR code: ABCDEFGHIJKLMNOP."
复制代码
关联值中的条形码示例显示了如何声明枚举的case存储了不一样类型的关联值。 做为关联值的替代方法,枚举案例能够预先填充默认值(称为原始值),这些默认值都是相同的类型。
这是一个将原始ASCII值与命名枚举cases一块儿存储的示例:
enum ASCIIControlCharacter: Character {
case tab = "\t"
case lineFeed = "\n"
case carriageReturn = "\r"
}
复制代码
在这里,被称为ASCIIControlCharacter的枚举的原始值被定义为Character类型,并被设置为一些更常见的ASCII控制字符。 字符串和字符中描述了字符值。
原始值能够是字符串,字符或任何整数或浮点数类型。 每一个原始值在其枚举声明中必须惟一。
注意
原始值与关联值不一样。 首次在代码中定义枚举时,原始值将设置为预填充的值,例如上面的三个ASCII代码。 特定枚举状况的原始值始终相同。 关联值是在您根据枚举的一种状况建立新的常数或变量时设置的,而且每次均可以不一样。
当您使用存储整数或字符串原始值的枚举时,没必要为每种状况显式分配原始值。 不须要时,Swift会自动为您分配值。
例如,当整数用于原始值时,每种状况的隐式值都比前一种状况大一。 若是第一种状况未设置值,则其值为0。
下面的枚举是对先前Planet枚举的改进,其中整数原始值表明从太阳到每一个行星的阶数:
enum Planet: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}
复制代码
在上面的示例中,Planet.mercury的显式原始值为1,Planet.venus的隐式原始值为2,依此类推。
若是将字符串用于原始值,则每种状况的隐式值就是该case名称的文本。
下面的枚举是对早期CompassPoint枚举的改进,使用字符串原始值来表示每一个方向的名称:
enum CompassPoint: String {
case north, south, east, west
}
复制代码
在上面的示例中,CompassPoint.south的隐式原始值为“ south”,依此类推。
您可使用其rawValue属性访问枚举用例的原始值:
let earthsOrder = Planet.earth.rawValue
// earthsOrder is 3
let sunsetDirection = CompassPoint.west.rawValue
// sunsetDirection is "west"
复制代码
若是您使用原始值类型定义枚举,则枚举会自动接收一个采用原始值类型值的初始化程序(做为称为rawValue的参数),并返回枚举case或nil。 您可使用此初始化程序尝试建立枚举的新实例。
本示例从其原始值7识别天王星:
let possiblePlanet = Planet(rawValue: 7)
// possiblePlanet is of type Planet? and equals Planet.uranus
复制代码
可是,并不是全部可能的Int值都能找到匹配的行星。 所以,原始值初始化程序始终返回可选的枚举状况。 在上面的示例中,possiblePlanet的类型为Planet?或“可选的Planet”。
注意:
原始值初始化程序是一个失败的初始化程序,由于并不是每一个原始值都将返回枚举状况。 有关更多信息,请参见失败的初始化程序。
若是您尝试查找位置为11的行星,则原始值初始化程序返回的可选Planet值将为nil:
let positionToFind = 11
if let somePlanet = Planet(rawValue: positionToFind) {
switch somePlanet {
case .earth:
print("Mostly harmless")
default:
print("Not a safe place for humans")
}
} else {
print("There isn't a planet at position \(positionToFind)")
}
// Prints "There isn't a planet at position 11"
复制代码
本示例使用可选绑定尝试访问原始值为11的行星。若是让somePlanet = Planet(rawValue:11)建立,则该语句建立一个可选Planet,并将somePlanet设置为该可选Planet的值(若是能够检索)。 。 在这种状况下,没法检索位置为11的行星,所以执行else分支。
递归枚举是一种枚举,该枚举具备该枚举的另外一个实例做为一个或多个枚举案例的关联值。 您能够经过在其前面写一个indirect来指示枚举用例是递归的,这告诉编译器插入必要的间接层。
例如,这是一个存储简单算术表达式的枚举:
enum ArithmeticExpression {
case number(Int)
indirect case addition(ArithmeticExpression, ArithmeticExpression)
indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}
复制代码
您也能够在枚举开始以前编写递归的代码,以让具备关联值的全部枚举案例启用递归功能:
indirect enum ArithmeticExpression {
case number(Int)
case addition(ArithmeticExpression, ArithmeticExpression)
case multiplication(ArithmeticExpression, ArithmeticExpression)
}
复制代码
该枚举能够存储三种算术表达式:素数,两个表达式的加法以及两个表达式的乘法。 加法和乘法案例的关联值也是算术表达式,这些关联值使嵌套表达式成为可能。 例如,表达式(5 + 4)* 2在乘法的右侧有一个数字,在乘法的左侧有另外一个表达式。 因为数据是嵌套的,所以用于存储数据的枚举也须要支持嵌套-这意味着该枚举须要递归。 下面的代码显示了为(5 + 4)* 2建立的ArithmeticExpression递归枚举:
let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))
复制代码
递归函数是使用具备递归结构的数据的直接方法。 例如,这是一个计算算术表达式的函数:
func evaluate(_ expression: ArithmeticExpression) -> Int {
switch expression {
case let .number(value):
return value
case let .addition(left, right):
return evaluate(left) + evaluate(right)
case let .multiplication(left, right):
return evaluate(left) * evaluate(right)
}
}
print(evaluate(product))
// Prints "18"
复制代码
该函数经过简单地返回相关值来表示纯数字。 它经过声明left和right表达式表示addition或multiplication,而后将它们相加或相乘来表示加法或乘法。
这一章节内容主要的知识点有:
enum Barcode {
case upc(Int, Int, Int, Int)
case qrCode(String)
}
复制代码
enum ASCIIControlCharacter: Character {
case tab = "\t"
case lineFeed = "\n"
case carriageReturn = "\r"
}
复制代码
能够隐式设置原始值:
enum Planet: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}
复制代码
则Planet.mercury的显式原始值为1,Planet.venus的隐式原始值为2,以此类推。
若是将字符串用于原始值,则每种状况的隐式值就是该case名称的文本。
enum CompassPoint: String {
case north, south, east, west
}
复制代码
可使用其rawValue属性访问枚举用例的原始值:
let earthsOrder = Planet.earth.rawValue
// earthsOrder is 3
let sunsetDirection = CompassPoint.west.rawValue
// sunsetDirection is "west"
复制代码
也能够用原始值进行初始化:
let possiblePlanet = Planet(rawValue: 7)
// possiblePlanet is of type Planet? and equals Planet.uranus
复制代码
enum ArithmeticExpression {
case number(Int)
indirect case addition(ArithmeticExpression, ArithmeticExpression)
indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}
复制代码
或
indirect enum ArithmeticExpression {
case number(Int)
case addition(ArithmeticExpression, ArithmeticExpression)
case multiplication(ArithmeticExpression, ArithmeticExpression)
}
复制代码
好了,关于枚举的内容已经所有总结完毕,与OC里的枚举差异仍是很大的,用法更灵活,只要掌握了枚举,就能够在项目开发中灵活运用,相信你对这个功能也很感兴趣。
最后,喜欢的朋友能够点个👍哦,你的鼓励才是个人动力,嘿嘿嘿~
参考文档:Swift - Enumerations