枚举
枚举的基本用法
关联值
如果取值只有几个固定的类型 可以考虑使用枚举
有时会将`枚举的成员值`跟`其他类型的值`关联存储在一起
enum Score {
case points(Int)
case grade(Character)
}
var score = Score.points(96)
score = .grade("A")
switch score {
case let .points(i):
print(i,"poin")
case let .grade(i):
print(i,"point")
}
enum Date {
case digit(year: Int, month: Int, day: Int)
case string(String)
}
var date = Date. digit(year: 2011, month: 9, day: 10)
date = .string("2011-09-10")
switch date {
case .digit(let year, let month, let day):
print(year, month, day)
case let .string(value):
print (value)
}
原始值
枚举成员可以使用"相同类型"的默认值预先关联 这个就是原始值
enum PokerSuit: Character {
case spade = "1"
case heart = "2"
case diamond = "3"
case club = "4"
}
Character 是原始值的类型
隐式原始值
如果枚举的原始值类型是Int String Swift会自动分配原始值
原始值会跟成员的名称一样
递归枚举
递归枚举必须加 indirect 关键字 否则会报错
indirect enum ArithExpr {
case number(Int)
case sum(ArithExpr,ArithExpr)
case difference(ArithExpr,ArithExpr,ArithExpr)
}
递归枚举的具体实现.png
MemoryLayout
可以使用MemoryLayout获取数据类型占有的内存大小
MemoryLayout<Int>.size 变量实际用到的空间大小
MemoryLayout<Int>.stride 变量分配占有的内存大小
MemoryLayout<Int>.alignment 对齐参数
enum Password {
case number(Int, Int, Int, Int)
case other
}
"是32 + 1的字节总数 那个1 是为了判断到底是number 还是 other"
var pwd = Password.number(5, 6, 4, 7)
pwd = .other
MemoryLayout.size(ofValue: pwd)
MemoryLayout.size(ofValue: pwd)
MemoryLayout.stride(ofValue: pwd)
MemoryLayout.alignment(ofValue: pwd)
关联值和原始值的区别
1.原始值是不会存储到枚举变量里面的 不会占有他们的内存 只占有1个字节
2.关联值是会存储到枚举变量里面的 是占用枚举变量的内存的,然后,会根据不同的值 占有不同的内存
原始值不考虑存储在什么位置 因为可以直接在枚举里面写rawValue这个方法根据if判断来实现
窥探内存
1.png2.png
枚举类型有关联值的话 首先必定有一个字节来存储成员值 N个字节存储关联值(N取占用内存最大的关联值),然后 任何一个case的关联值都共用这 N个字节
enum TestEnum {
case test1(Int, Int, Int)
case test2(Int, Int)
case test3(Int)
case test4 (Bool)
case test5
}
print(MemoryLayout<TestEnum>.size)
print(MemoryLayout<TestEnum>.stride)
print(MemoryLayout<TestEnum>.alignment)
25 32 8
25个字节 24个是用来存放原始值的内容的 第25个字节是存放是枚举成员的类型
enum TestEnum{
case test
}
var t = TestEnum.test
print(MemoryLayout<TestEnum>.size)
print(MemoryLayout<TestEnum>.stride)
print(MemoryLayout<TestEnum>.alignment)
0 1 1
枚举里面只有一个case 不需要内存区分是哪一个 也就不需要那一个字节
enum TestEnum{
case test(Int)
}
var t = TestEnum.test(10)
print(MemoryLayout<TestEnum>.size)
print(MemoryLayout<TestEnum>.stride)
print(MemoryLayout<TestEnum>.alignment)
8 8 8
可选项
可选项 一般也可叫可选类型 它允许将值设置为nil
在类型名称后面加一个问号? 来定义一个可选项
var array = [1,15,40,29]
func get(_ index: Int) -> Int? {
if index < 0 || index >= array.count{
return nil
}
return array[index]
}
get(3)
强制解包
1.可选项是对其他类型一层包装 可以理解为一个盒子
- 如果为nil 那么它是一个空盒子
- 如果不为nil 那么盒子里面装的是 被包装类型的数据
2.如果要从可选项中取出被包装的数据(将盒子里面装的东西取出来),需要使用感叹号! 进行强制解包
3.如果对值为nil的可选项(空盒子)进行强制解包 将会产生运行时错误
强制解包 只是把值拿出来用一下,盒子里面还是有内容的,不是空的
判断可选项里面是否包含值
var num = Int("kkk1124")
if num != nil {
print("字符串转换成功")
}else{
print("字符串转换失败")
}
可选项绑定
1.可以使用可选项绑定来判断可选项是否包含值
- 如果包含就自动解包 把值赋值给一个临时的常量或者变量 并返回true 否则返回false
if let number = Int("123"){
print("字符串转换成功");
//number作用域仅限于这个大括号
//number 是强制解包之后的Int值
}else{
print("字符串转换失败");
}
等价写法
if let first = Int("4") {
if let second = Int("42") {
if first < second && second < 100 {
print("\(first) < \(second) < 100")
}
}
}
if let first = Int("4"),
let second = Int("42"),
first < second && second < 100{
print("\(first) < \(second) < 100")
}
可选项绑定没办法使用&& 只能使用,代表同时成立
while 循环中使用可选项
var strs = ["10","20","abc","-20","30"]
var index = 0
var sum = 0
while let num = Int(strs[index]),num > 0 {
sum += num
index += 1
}
print(sum)
空合并运算符??(Nil - Coalescing)
public func ?? <T>(optional: T? defaultValue: @autoclosure() throws -> T?) rethrows -> T?
public func ?? <T>(optional: T? defaultValue: @autoclosure() throws -> T) rethrows -> T
a ?? b
- a 是可选项
- b 可以是可选项 或者不是可选项
- b 跟 a 的存储类型必须相同
- 如果a 不为nil 就返回a
- 如果a 为nil 就返回b
- 如果b不是可选项 返回a的时候会自动解包
let a: Int? = 1
let b: Int? = 2
let c = a ?? b optinon(1)
let a: Int? = nil
let b: Int? = 2
let c = a ?? b optinon(2)
let a: Int? = 1
let b: Int = 2
let c = a ?? b 1
返回类型取决于b
//如果不使用?? 运算符
let a: Int? = nil
let b: Int = 2
let c: Int
if let tmp = a {
c = tmp
}else{
c = b
}
多个 ?? 一起使用
let a: Int? = 1
let b: Int? = 2
let c = a ?? b ?? 3
print("\(c)") 1
多个 ?? 一起使用 看最右边的类型
?? 跟 if let 配合使用
let a: Int? = nil
let b: Int? = 2
if let c = a ?? b {
print(c)
}
let a: Int? = nil
let b: Int? = 2
if let c = a, let d = b {
print(c)
print(d)
}
if语句实现登录
func login(_ info: [String : String]) {
let username: String
if let tmp = info["username"]{
username = tmp
}else{
print("请输入用户名")
return
}
let password: String
if let tmp = info["password"] {
password = tmp
}else{
print("请输入密码")
return
}
print("用户名 \(username)","密码: \(password)")
}
login(["username" : "jack", "password" : "123456"])
login(["password" : "123456"])
login(["username" : "jack"])
用户名 jack 密码: 123456
请输入用户名
请输入密码
数组下标越界 会直接崩溃 而字典没有相关key的话,会返回nil 可以使用if let判断
guard语句
guard 条件 lese{
//do something
//退出当前作用域 return break continue throw error
}
当guard 条件为false的时候 会指向大括号里面的代码
当guard 条件为true的时候 就会跳出guard语句
guard语句特别适合用于"提前退出"
- 当guard语句进行可选项绑定的时候 绑定的常量 变量也能在外层作用域中使用
func login(_ info: [String : String]) {
guard let username = info["username"] else{
print("请输入用户名")
return
}
guard let password = info["password"] else{
print("请输入密码")
return
}
print("用户名 \(username)","密码: \(password)")
}
login(["username" : "jack", "password" : "123456"])
login(["password" : "123456"])
login(["username" : "jack"])
用户名 jack 密码: 123456
请输入用户名
请输入密码
隐式解包
1.在某些情况下 可选项一旦被设置值之后,就会一直拥有值
2.在这种情况下 可以去掉检查 也不必每次访问的时候都进行解包 因为他能确保每次访问的时候都有值
3.可以在类型后面加个感叹号! 定义一个隐式解包的可选项
let num1: Int! = 10 //无论是感叹号还是问号 都是可选项
let num2: Int = num1//隐式解包的可选项 可以省略感叹号
if num1 != nil {
print(num1 + 6)
}
if let num3 = num1 {
print(num3)
}
隐式解包的可选项 如果赋值为nil 会报错
如果希望别人给你具体的值 但是,有可能还是返回nil 就可以使用隐式解包 如果返回为空 那么就直接报错 提醒他不合理
无论是感叹号还是问号 都是可选项
字符串插值
- 可选项在字符串插值或者直接打印时 编译器直接发生警告
var age: Int? = 10
print("my age is \(age)")
具体警告.png
至少3种方法解决警告
1.强制解包 !
2.String(describing: age)
3.age ?? 0
多重可选项
var num: Int? = 10 //可选项 盒子里面装了10
var num2: Int?? = num1 //大盒子里面 装了一个可选项 这个小盒子里面装了一个10
var num3: Int?? = 10 //大盒子里面 装了一个可选项 这个小盒子里面装了一个10
可以使用ldb指令 frame variable -R 或者fr v -R 查看区别
num1 和 num3 不相同 类型不同
网友评论