Swift枚举熟练运用必备知识
2022-05-25 本文已影响0人
Sweet丶
Swift中的枚举有原始值和关联值,其使用范围相比OC来说多了很多,因此也更复杂,需要我们花更多的时间来学习它,本文是对学习后和在实际中的运用做的一个总结。
目前来看需要掌握的内容如下:
一、Swift枚举声明
>>> A. 枚举的类型及原始值、关联值
- 声明枚举时,类型继承列表(即冒号后面)写原始值类型,不写时默认是
Int
,如果是其它类型则需要写明。
- 声明枚举时,类型继承列表(即冒号后面)写原始值类型,不写时默认是
- 原始值代表的是一个枚举变量的
rawValue
,rawValue
的本质是枚举的计算属性。
- 原始值代表的是一个枚举变量的
- 在写枚举的case时:
如果原始值类型是Int
、String
类型,不写明原始值会自动生成原始值, 对于Int
类型默认从0开始,下一个是上一个case的原始值+1;对于String
类型,默认原始值是case名;
如果是其它原始值类型的枚举,可以用"="写明对应的原始值,见下面的Direction
.
- 在写枚举的case时:
- 每个case后面可以追加枚举关联值,比如下面的
Score
.
- 每个case后面可以追加枚举关联值,比如下面的
-
递归枚举:如果枚举关联值中有枚举本身类型,则这个时候在枚举定义前或者
case
前需要加indirect
。
-
递归枚举:如果枚举关联值中有枚举本身类型,则这个时候在枚举定义前或者
- 枚举变量初始化时使用一般使用
enumName.caseName
。
- 枚举变量初始化时使用一般使用
- 枚举默认遵守
RawRepresentable
协议,协议里面有可失败初始化器init?(rawValue: Self.RawValue)
和计算属性rawValue
. 所以我们可以通过这个协议的方法来完成初始化和获取原始值rawValue
。
- 枚举默认遵守
- 枚举的原始值类型必须是可以用字面量表示的,比如
Int
、Float
、String
等, 不然的话会报错Raw value for enum case must be a literal
.
- 枚举的原始值类型必须是可以用字面量表示的,比如
- 枚举的case不能即写明原始值又写关联值,写了报错
Enum with raw type cannot have cases with arguments
- 枚举的case不能即写明原始值又写关联值,写了报错
// 1.这里的原始值类型是Character,如果不写明= "d"则会报错.
// 2.如果是不写原始值类型Character,默认是Int。
// 3.对于原始值类型是Int或者String,不写明“=”后面的也不会报错。
enum Direction: Character {
case north = "d"
case south = "s"
case east = "e"
case west = "w"
}
// 带枚举关联值的枚举
enum Score {
case point(Int, Int, Int)
case grade(Character)
}
// case中关联值有自身枚举类型的递归枚举
indirect enum ArithExpr {
case number(Int)
case sum(ArithExpr, ArithExpr)
case difference(ArithExpr, ArithExpr)
}
>>> B. 枚举的其它性质
Swift中枚举可以遵守协议,可以添加成员方法、静态方法、静态变量、计算属性。
enum BasketballNum4: Character, CaseIterable {
case AAAA16 = "2"
// 可添加计算属性
var name: String { "dandy" }
// 可添加静态变量
static var name2: String { "dandy.static" }
// 可添加成员方法
func testSelf() {
print(self.name)
print(Self.name2)
}
// 可添加静态方法
static func testSelf2() {
print(self.name2)
}
}
二、枚举变量的内存
弄清楚枚举的内存使用情况能让我们在使用时对性能消耗有所了解。
使用MemoryLayout
可以查看分配内存和使用内存情况,比如下面的方式:
print("MemoryLayout<String>.size", MemoryLayout<String>.size)
print("MemoryLayout<String>.stride=", MemoryLayout<String>.stride)
print("MemoryLayout<String>.alignment=", MemoryLayout<String>.alignment)
2.1 关联值的存储
- 如果没有枚举关联值时,枚举变量只占一个字节,这个字节里面的内容就是
case
的序号。 - 如果有枚举关联值,那么枚举内存分配 = 各个关联值类型所占的字节总和 + 1(这1个字节是存放枚举case序号的)+ 根据内存对齐
alignment
补齐字节。 - 内存对齐
alignment
是关联值类型中最大的那个。
证明过程:
我这里是学习李明杰大师的方法做的,使用他的demo就可以实现:https://github.com/CoderMJLee/Mems
打开demo --》在main文件顶部写showEnum() --》运行程序 --》打印枚举变量地址 --》如图打开内存面板,输入这个变量的地址值 --》 即可查看该内存里面的值。
枚举关联值的存储.jpg
2.2 原始值的存储
rawValue
是一个计算属性,在编译时就已经确定了每个枚举值对应的rawValue
值,不需要存储。证明方法: 可以分析swift
文件编译过程生成的中间文件sil
。
可以参考文章:Swift进阶(六)枚举和可选类型
2.3 枚举中方法、计算属性在内存中什么位置?
- 计算属性也可以认为是方法,方法的本质就是函数, 方法、函数都存放在代码段,所以计算属性、成员方法都存放在代码段。
- 静态变量就是全局变量,存放在内存的数据段。
- 静态方法存放在代码段。
证明方法见博客Swift 方法及方法在内存中的位置
- 静态方法存放在代码段。
三、Swift枚举应用举例
- 跟OC语言枚举一样的情况,比如订单状态枚举。
- 应用于将多个相似的方法整合成一个,比如:RxSwift中的信号发送。
// 在OC中是分别有onNext、onComplete、onError三个方法,RxSwift的内部中转方法只使用一个on
func on(_ event: Event<Element>)
public enum Event<Element> {
/// Next element is produced.
case next(Element)
/// Sequence terminated with an error.
case error(Swift.Error)
/// Sequence completed successfully.
case completed
}
- 系统的可选类型也是一个枚举。可选类型可以赋值为nil,相当于赋值了
Optional.none
, 原因是遵守了ExpressibleByNilLiteral
协议。
- 系统的可选类型也是一个枚举。可选类型可以赋值为nil,相当于赋值了
- 将APP所有的通知名集中到一个枚举中,枚举原始值类型声明为
String
,这样在使用直接用点语法找case
,而且APP中包含哪些通知也变得一目了然。
- 将APP所有的通知名集中到一个枚举中,枚举原始值类型声明为
- 埋点神策事件时使用枚举关联值来做,代码设计感会很不错。
- 网络请求使用
Moya
来实现的话,一个请求对应相关联参数,这个也可以用枚举关联值来做。
- 网络请求使用
四、OC中使用Swift枚举
OC文件中导入#import "项目名-Swift.h"
文件后去使用。
'@objc' enum must declare an integer raw type
.
枚举原始值类型为Int的Swift
枚举才允许在OC中使用,并且Swift枚举前需要加@objc
。注意:OC中Integer就是Swift中的Int。
五、Swift中使用OC枚举
将OC枚举所在的文件导入到桥接文件中项目名-Bridging-Header.h
。
Swift
中用枚举类型名 + 点语法就可以了。
OC中定义字符串枚举:
Apple官方的做法
.h 文件中
typedef NSString *AddressRecType NS_STRING_ENUM;
FOUNDATION_EXPORT AddressRecType const AddressRecTypeHistory;
FOUNDATION_EXPORT AddressRecType const AddressRecTypeCurrentLocation;
FOUNDATION_EXPORT AddressRecType const AddressRecTypeRGeo;
.m 文件中
AddressRecType const AddressRecTypeHistory = @"history";
AddressRecType const AddressRecTypeCurrentLocation = @"curLocation";
AddressRecType const AddressRecTypeRGeo = @"RGeo";
.swift 文件中 --------------
let enum5 = AddressRecType.currentLocation
有些情况使用字符串枚举更合适,比如:你写一个Pod私有库需要提供给Swift新项目
中业务方使用,他们使用的方式是需要取到这个枚举对应的一个字符串类型作为参数去发送请求,这个参数如果由业务方根据枚举做一层映射的话,这样业务方麻烦,也怕他们会写错。
这个时候可以在OC中定义字符串枚举后,在Swift中使用该枚举的rawValue
来取得这个字符串就能避免这个问题。