Swift 5.1 (8) - 枚举类型

级别: ★☆☆☆☆
标签:「iOS」「Swift 5.1」「枚举」「迭代枚举」「枚举关联值」「递归枚举」
作者: 沐灵洛
审校: QiShare团队


Enumeration:枚举类型

一个枚举类型是为一组相关联的值定义的一个公共类型,使得这些关联值能够在代码中以类型安全的方式进行处理。
C语言中的枚举类型将相关的枚举项使用整数值表示。而Swift中的枚举更灵活,并且没有为枚举项提供值。如果为为枚举项提供值(则该值称为原始值raw value),该值可以是字符串,字符,任何整数或浮点类型的值。
另外,枚举项可以指定任何类型的关联值与枚举项的值一起存储。我们可以定义一组通用的相关项作为枚举的一部分,每个枚举都有一组比较合适的类型的不同值与之关联。同时Swift中的枚举类型采用了许多只有类才支持的特征,比如枚举:

  • 能够提供计算属性,用于支持枚举当前值之外的额外的信息;
  • 能够提供实例方法,用于支持与枚举所代表的值相关的功能;
  • 能够定义初始化方法,提供初始枚举项的值;
  • 能够支持扩展, 可以在原始实现的基础上扩展其功能;
  • 能够遵守协议,用于提供一切标准的功能;

枚举语法

使用enum关键字引入枚举,并将其整个定义放在{}

enum <#name#> {
    case <#case#>
}

枚举的定义与使用

enum CompassPoint {
    case north
    case west
    case east
    case south
}
//多个枚举写在一行中。
enum CompassPoint {
    case north,west,east,south
}

每个枚举定义都定义了一个新类型。

var direction : CompassPoint = .north
var direction = CompassPoint.north

使用Switch语句匹配枚举值

let direction : CompassPoint = .north
switch direction {
case .east:
    print("ease")
case .north:
    print("north")
case .west:
    print("west")
case .south:
    print("south")
}

迭代枚举项

通过在枚举名称后使用: CaseIterable,可以使枚举,拥有一个关于所有枚举项的集合。调用枚举类型的allCases属性,可以获取这个集合。

enum CompassPoint : CaseIterable{
    case north,west,east,south
}
//输出allCases
/*
 log:[swift_basic.EnumerationPractice.CompassPoint.north, swift_basic.EnumerationPractice.CompassPoint.west, swift_basic.EnumerationPractice.CompassPoint.east, swift_basic.EnumerationPractice.CompassPoint.south]
 */
print(CompassPoint.allCases)
switch CompassPoint.allCases.first! {
case .north:
    print("输出正确")
default:
    print("输出失败")
}
//eachCase每一项都是枚举类型的示例
for eachCase in CompassPoint.allCases {
    /*north
     west
     east
     south*/
    print(eachCase)
}

枚举关联值

枚举类型中在枚举项的旁边存贮额外的其他类型的值,这些值被称为关联值。并且每次在代码中使用有关联值的枚举项时,该关联值都会有所不同。
Swift中可以定义枚举以存储任何给定类型的关联值,并且每个枚举项的值类型也可以不同。
例如:商品码有二维码和条形码两种形式。二维码的信息可以提取为String类型,条形码的信息是一串数字,按照:一位系统数字+五位制造商标识+五位产品标识+一位校验数字的格式,我们可以联想到(Int,Int,Int,Int)元组类型。所以我们使用关联值定义枚举类型如下: 通用商品条码

enum ProductCode {
    case upc(Int,Int,Int,Int)//通用商品条码
    case qrCode(String)//二维码
}

上述代码解读:定义了一个枚举类型ProductCode,有两个枚举项upcqrCode。这两个枚举项分别可以存储(Int, Int, Int, Int)类型和String类型的关联值。
ProductCode枚举类型的定义不提供任何实际意义的IntString值。 它只定义ProductCode常量和变量在等于ProductCode.upcProductCode.qrCode时可以存储的关联值的类型。

var productCode = ProductCode.upc(1, 22, 333, 4444)
productCode = ProductCode.qrCode("二维码哈")

使用Switch语句匹配枚举项并且提取枚举项的关联值。可以使用letvar将每个关联值提取为常量或变量,以在case的正文中使用。

var productCode = ProductCode.upc(1, 22, 333, 4444)
switch productCode {
case .upc(let x, let y, let z, let zz):
    print("从switch语句中提取的相匹配的枚举项的关联值为x:\(x) y:\(y) z:\(z) zz:\(zz)")
case ProductCode.qrCode(let str):
    print("从switch语句中提取的相匹配的枚举项的关联值为:\(str)")
}
productCode = ProductCode.qrCode("二维码哈")
switch productCode {
case let .upc(x, y, z, zz):
    print("从switch语句中提取的相匹配的枚举项的关联值为x:\(x) y:\(y) z:\(z) zz:\(zz)")
case let .qrCode(str):
    print("从switch语句中提取的相匹配的枚举项的关联值为:\(str)")
}

枚举原始值

关联值的示例中展示了枚举如何声明它们存储不同类型的关联值。作为关联值的替代,枚举也可以预先填充默认值(称为原始值),它们都是相同的类型。

enum NameType : String{
    case ZhangFei = "张飞"
    case GuanYu = "关羽"
    case LiuBei = "刘备"
}
let name = NameType.ZhangFei
print(name.rawValue)//!< log:张飞

枚举NameType的原始值被定义为String类型,并被设置原始值。原始值可以是字符串,字符或任何整数或浮点数类型。每个原始值在其枚举声明中必须是唯一的。
原始值与关联值不同。原始值:预先填充的默认值,对于特定的枚举项,其原始值总是相同的。关联值:每次基于枚举情况之一创建一个常量或变量时,其关联值可以是不同的。
隐式分配原始值
使用原始值为整数或字符串类型的枚举时,可以不用为每个case显式分配原始值。因为Swift会自动为我们分配值。例如,当整数用于原始值时,每种情况的隐含值比前一种情况多一个。如果第一种情况没有设置值,则其值为0。可以使用rawValue属性访问枚举项的原始值。

enum Planet: Int,CaseIterable {
    case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}
//查看各项的`rawvalue`
var planetOutStr = "枚举类型Planet各项的原始值"
for item in Planet.allCases {
    planetOutStr += "\(item) : \(item.rawValue) "
}
print(planetOutStr)//!< 枚举类型Planet各项的原始值mercury : 1 venus : 2 earth : 3 mars : 4 jupiter : 5 saturn : 6 uranus : 7 neptune : 8

原始值为字符串类型的枚举,每个枚举项隐式分配的原始值是该枚举项的名称。

enum CompassPoint :String,CaseIterable{
    case north,west,east,south
}
var directionOutStr = "枚举类型CompassPoint各项的原始值"
for item in CompassPoint.allCases {
    directionOutStr += "\(item) : '\(item.rawValue)' "
}
print(directionOutStr)//!< 枚举类型CompassPoint各项的原始值north : 'north' west : 'west' east : 'east' south : 'south' 

从原始值初始化
如果使用原始值类型定义枚举,则枚举会自动接收一个参数名:rawValue参数类型:原始值类型的初始话方法,并返回枚举项或nil。我们可以使用此初始化方法尝试创建该枚举类型的新实例。

let possiblePalnet = Planet.init(rawValue: 7)
//!< Planet类型的可选项。Optional(swift_basic.EnumerationPractice.Planet.uranus)
//无法根据原始值匹配到枚举项的情况
let possiblePalnet = Planet.init(rawValue: 9)//!< Planet类型的可选项。9已经超出了枚举的所有项的范围。log:nil
//需要解包,可选绑定
if let possiblePlanet = possiblePalnet {
    print(possiblePlanet)
    switch possiblePlanet {
    case .earth:
        print("地球")
    default:
        print("其他行星")
    }
} else {
    print("没有找到合适的枚举项")
}

枚举的递归

定义一个枚举时,若该枚举类型的某个case关联了一个或多个该枚举类型的值时,系统会自动提示,需要添加indirect关键字,因为此时的枚举类型已经被定义为了拥有递归结构的递归枚举。通过在枚举项的前面使用indirect关键字,来表示此枚举项是可递归的。
以下示例为存储了三个数学表达式递归枚举的定义。

//方式一
//递归枚举,存储了三个数学表达式 使用`indirect`来表示枚举项时可递归调用的。
enum ArithmeticExpression {
    case number(Int)
    indirect case addition(ArithmeticExpression, ArithmeticExpression)
    indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}

在枚举开始之前使用indirect关键字,为具有关联值的所有枚举项启用递归:

//方式二
indirect enum ArithmeticExpression {
    case number(Int)
    case addition(ArithmeticExpression, ArithmeticExpression)
    case multiplication(ArithmeticExpression, ArithmeticExpression)
}

ArithmeticExpression枚举类型的每一项都设置有相应的关联值,并且additionmultiplication都关联了可以存储(ArithmeticExpression, ArithmeticExpression)类型的值。即:分别关联了两个ArithmeticExpression枚举类型。

递归枚举的嵌套:

//存储了一个为5的关联值
let five = ArithmeticExpression.number(5)
//存储了一个为6的关联值
let six = ArithmeticExpression.number(6)
//存储了一个为3的关联值
let three = ArithmeticExpression.number(3)
//addition枚举项,关联了两个当前枚举类型的值。
let sum = ArithmeticExpression.addition(five, six)
//multiplication枚举项,关联了两个当前枚举类型的值。
let multiplication = ArithmeticExpression.multiplication(sum, three)//数学表达式的呈现形式。

创建递归函数验证multiplication是否可以计算正确结果

//创建一个递归函数验证可递归性
func arithmeticOperation(_ expression : ArithmeticExpression) -> Int {
    
    switch expression {
    case let .number(x):
        return x
    case let .addition(x, y):
        //! 此处x和y仍旧是表达式,所以需要先进行递归运算得出`Int`类型的关联值
        return arithmeticOperation(x) + arithmeticOperation(y)
    case .multiplication(let x, let y):
        //! 此处x和y仍旧是`ArithmeticExpression`,所以需要先进行递归运算得出`Int`类型的关联值
        return arithmeticOperation(x) * arithmeticOperation(y)
    }
}
print("嵌套的数学表达式进行递归调用后的结果是:\(arithmeticOperation(multiplication))")
//!< log:嵌套的数学表达式进行递归调用后的结果是:33

参考资料:
swift 5.1官方编程指南


推荐文章:
iOS 给UILabel添加点击事件
用SwiftUI给视图添加动画
用SwiftUI写一个简单页面
Swift 5.1 (7) - 闭包
iOS App启动优化(三)—— 自己做一个工具监控App的启动耗时
iOS App启动优化(二)—— 使用“Time Profiler”工具监控App的启动耗时
iOS App启动优化(一)—— 了解App的启动流程

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 207,113评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,644评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,340评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,449评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,445评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,166评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,442评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,105评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,601评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,066评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,161评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,792评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,351评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,352评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,584评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,618评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,916评论 2 344

推荐阅读更多精彩内容