1、Enum
1.1、Enum特性
- Swift 中的枚举比 OC 中更加灵活,不需给枚举中的每一个成员都提供值;并且枚举成员可以是字符串、字符、任意的整数值,或者是浮点类型。
-
.rawValue: 取 case的原始值,与枚举值打印出的内容可能相同,但本质不同 -
在硬编码符号表里获得
-
- 查看SIL
- 使用了可失败初始化器
alloc_box开辟堆区内存,得到rawValue,_allocateUninitializeArray<A>(_:)方法开辟连续内存空间,并将枚举值存进去
- 使用了可失败初始化器
- 枚举还可以 定义方法(方法需要变异: mutating)、属性、Extension 和 遵循协议
1.2、关联值
- 通过
()中写入 参数及其类型,使枚举值携带更多信息,并且不能再赋予初始值- 网络请求就可以多使用这种枚举
1.3、Enum的大小
- 在 Swift 中进行枚举布局的时候一直是尝试使用最少的空间来存储 enum,也就是说,如果在系统能明确一个 case值 的空间有浪费时,就会尝试用其他case填满这个空间
1.3.1、No-payload enums
- 没有关联值的,默认用
Int8类型存储,每个case占1位,UInt8 能够表示 256 cases,也就意味着如果一个默认枚举类型且没有关联值的 case 少于 256,当前枚举类型的大小都是 1 字节。
1.3.2、Single-payload enums
- 1个负载时,会使用负载类型中的额外空间来记录没有负载的 case值。Bool类型是 1 字节,也就是 UInt8,1字节有8位,表达Bool值的内容只需要1位低位的 0, 1 这两种情况,剩余7位就可以给其他case用)
enum LZEnum{ case test_one(Bool) case test_two case test_three case test_four }
1.3.3、Mutil-payload enums
-
多负载时,一般来说枚举类型的大小取决于
当前最大关联值的大小。enum LZEnum{ case test_one(Bool) case test_two(Int) case test_three case test_four } 当前 LZEnum 的大小就等于 sizeof(Int) + sizeof(rawVlaue) = 9enum LZEnum{ case test_one(Bool) case test_two(Int, Int, Int) case test_three case test_four } 当前 LZEnum 的大小就是 sizeof(Int) * 3 + sizeof(rawVlaue) = 25 -
源码查看
getEnumTagCounts():
1.4、indirect
-
Enum 属于值类型,在编译时大小就应该确定,但要做到像下边的 二叉树 类型,关联值还是一个枚举时无法在编译时确定大小,所以就需要使用到 indirect 关键字 把这个枚举分配到堆空间上
-
开发中也可以使用这种方式来编写 链表
2、Optional
- 允许被修饰的数据为空,可以写为:
var name: Int?或者var name: Optional<Int>
2.1、可选值的作用
- 源码中查看其本质其实是一个 Enum,我们可以利用当前编译器的类型检查来达到语法书写层面的安全性
@frozen public enum Optional<Wrapped>: ExpressibleByNilLiteral { case none case some(Wrapped) }
2.2、if let
- 如果每一个可选值都用模式匹配的方式来获取值,在代码书写上就比较繁琐,我们还可以使用
if let的方式来进行 可选值绑定if let name = nameTextField.text { print("\(name)") }
2.3、guard let
- 和 if let 刚好相反,
guard let守护一定有值,如果没有,直接返回guard let name = nameTextField.text, name != "" else { print("name为空值") return } }
2.4、可选链
- 我们都知道在 OC 中我们给一个 nil 对象发送消息什么也不会发生,Swift 中我们是没有办法向一个 nil 对象直接发送消息,但是借助 可选链 可以达到类似的效果
//语法调用 let str: String? = "Lz" let upperStr = str?.uppercased().lowercased() //函数调用 var closure: ((Int) -> ())? closure?(1) // closure 为 nil 不执行 //下标 let dict = ["one": 1, "two": 2] dict?["one"] // Optional(1) dict?["three"] // nil- 如果可选内容为空,则直接不执行后边方法,有内容时才执行,对于下标和函数调用也适用
2.5、?? 运算符 (空合并运算符)
- ( a ?? b ) 将对 可选类型a 进行空判断,如果a有值就返回a,没值就返回b
- 表达式 a 必须是 Optional 类型
- 默认值 b 的类型必须要和 a 存储值的类型一致
2.6、隐式解析可选类型
- 隐式解析可选类型是可选类型的一种,使用的过程中和非可选类型无异。它们之间唯一的区别是,隐式解析可选类型会向 Swift编译器 打包票在访问值的时候绝对不会为 nil,如果确实为 nil 了那么就会崩溃
- 可选类型使用时需要!进行解包
- 隐式解析可选类型使用时不需解包,像一般参数一样使用
var age1: Int? //可选类型 var age2: Int! //隐式解析可选类型 let x = age1! + 10 //需要解包 let y = age2 + 10 //无需解包
3、运算符重载
- 对于 运算符操作,必须使用
static关键字 - 定义运算符功能,规则参考 Swift指导 和 苹果官方Swift文档
3.1、已有运算符重定义
- 需要注意
prefix、infix、postfix修饰的运算符可能名称一样,但效果不一样,比如减法和负数符号,都是“-”,但意义不同
3.2、定义新运算符
-
可以创建一个优先级组,规定优先级与结合性;也可使用系统自带的优先级组
-
定义 运算符名称 ,运算位置 : 前置、后置还是中置运算符,与遵循的 优先级组
- 优先级组表明一组符号运算时先算哪个,有 加法级组
AdditionPrecedence、乘法级组MultiplicationPrecedence、位运算级组 BitwiseShiftPrecedence 等等,可在 苹果官方Swift文档 中查看
- 优先级组表明一组符号运算时先算哪个,有 加法级组
-
实现自定义运算符方法
可能的报错
Adjacent operators are in unordered precedence groups 'MultiplicationPrecedence' and 'LZprecedence'
- 中间出现过一个错误,原因是定义的 +++ 运算符虽然指定了 LZprecedence 优先级高于加法优先级,但未规定和乘法优先级的优先顺序,就出现了该报错