枚举 可选项

2019-08-12  本文已影响0人  纳兰沫

枚举

枚举的基本用法

关联值

如果取值只有几个固定的类型 可以考虑使用枚举

有时会将`枚举的成员值`跟`其他类型的值`关联存储在一起
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.png
2.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 查看区别

列子.png
num1 和 num3 不相同 类型不同
上一篇下一篇

猜你喜欢

热点阅读