• Swift 枚举-从汇编角度看枚举内存结构


    一、基本使用

    先看枚举的几种使用(暂不要问,看看是否都能看懂,待会会逐一讲解)

    1、操作一 简单使用

    //第一种方式
    enum Direction {
        case east
        case west
        case south
        case north
        
        func testDir() -> String {
            switch self {
            case .east:
                return "东边"
            case .west:
                return "西边"
            case .south:
                return "南边"
            case .north:
                return "北边"
            }
        }
    }
    
    //第二种方式
    enum Direction1 {
        case east, west, south, north
        
        func testDir() -> String {
            switch self {
            case .east:
                return "东边"
            case .west:
                return "西边"
            case .south:
                return "南边"
            case .north:
                return "北边"
            }
        }
    }
    
    var dir = Direction.east
    dir = .north
    var dir1 = Direction1.east
    dir1 = .north

    第一种和第二种完全一样。

    2、操作二 关联值(Associated Values)

    关联值(Associated Values)将枚举的成员值跟其他类型的值关联存储在一起,非常有用!

    2.1 关联值示例1

    enum Score {
        case points(Int)
        case grade(Character)
    }
    var score = Score.points(98)
    score = .grade("A")
    
    func testScore() {
        switch score {
        case let .points(i):
            print(i,"points")
        case let .grade(i):
            print("grade",i)
        }
    }

    2.2 关联值示例2

    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")
    
    func testDate() {
        switch date {
        case .digit(let year, let month, let day):
            print(year, month, day)
        case let .string(value):
            print(value)
        }
    }

    2.3 关联值示例3

    手机密码方式有以下两种,可以用枚举表示,用关联值表示,图如下:

    上面的图可以用关联值枚举表示如下

    enum Password {
        case number(Int, Int, Int, Int)
        case gesture(String)
    }
    
    var pwd = Password.number(1, 3, 7, 8)
    pwd = .gesture("1378")
    
    func testPwd() {
        switch pwd {
        case let .number(n1, n2, n3, n4):
            print("number is ", n1, n2, n3, n4)
        case let .gesture(str):
            print("gesture is " ,str)
        }
    }

    3、操作三 原始值(Raw Values)

    原始值: 枚举成员可以使用相同类型的默认值预先对应,默认值叫做原始值,原始值不占用枚举变量的内存

    enum PokerSuit: Character {
        case spade = ""
        case heart = ""
        case diamond = ""
        case club = ""
    }
    
    var suit = PokerSuit.spade
    print(suit.rawValue)//
    print(PokerSuit.club.rawValue)//

    4、隐式原始值(Implicitly Assigned Raw Values)

    如果枚举的原始值是Int,String,Swift会自动分配原始值,和成员值一样

    4.1 隐式原始值示例1

    Direction和Direction1意义是一样的

    enum Direction: String {
        case north = "north"
        case south = "south"
        case east  = "east"
        case west  = "west"
    }
    
    enum Direction1: String {
        case north, south, east, west
    }
    
    print(Direction.north) //north
    print(Direction1.north.rawValue)// north

    4.2 隐式原始值示例2

    Int类型,默认为0开始

    enum Season: Int {
        case spring, summer, autumn, winter
    }
    print(Season.spring.rawValue) // 0
    print(Season.summer.rawValue) // 1
    print(Season.autumn.rawValue) // 2
    print(Season.winter.rawValue) //3

    4.3 隐式原始值示例3

    enum Season: Int {
        case spring = 1, summer, autumn = 4, winter
    }
    print(Season.spring.rawValue) // 1
    print(Season.summer.rawValue) // 2
    print(Season.autumn.rawValue) // 4
    print(Season.winter.rawValue) //5

    上面讲述枚举的基本使用,下面我们将进入核心内容-从汇编的角度来看枚举的内存!!

    二、汇编角度看枚举内存

    示例1: 简单实用-通过下面代码查看枚举实例占用多少内存字节等

    enum TestEnum {
        case test1, test2, test3
    }
    
    var t = TestEnum.test1
    t = .test2
    t = .test3

    通过MemoryLayout查看内存大小

    enum TestEnum {
        case test1, test2, test3
    }
    
    var t = TestEnum.test1
    t = .test2
    t = .test3
    print(MemoryLayout<TestEnum>.size) //TestEnum实际占用内存空间
    print(MemoryLayout<TestEnum>.stride)//系统分配给TestEnum的内存空间
    print(MemoryLayout<TestEnum>.alignment)//对齐参数

    运行结果如下

    其实Swift还是很聪明的,仅仅使用一个字节来判断对象的不同,下面窥探test1,test2,test3的内存

    因为Swift不支持枚举看底层的,所以通过一个内存访问小工具查看内存地址,然后通过内存地址查看内存布局

     拿到内存地址后,可以通过view memory查看内容

    将地址0x0000000100006660输入进去

    因为通过上面发现占用一个字节,所以看第一个字节存储的为00,t为test1时

    将断点向后移,看t = test2时,t的内存存储的时

    再次看下t = test2 内存存储的值为

     最后看下t = test3内存存储为

     这种形式的枚举定义形式占用一个字节,可以代表的枚举范围也就是0x00-0xFF共256个case,足以表示所有情况的枚举穷举啦!

    从示例1中,当枚举里面仅仅是case多个对象,枚举内存仅仅会分配1个字节来存储各个case,case对应的为0,1,2……

    示例2 带有原始值

    enum TestEnum: Int {
        case test1 = 1, test2 = 2, test3 = 3
    }
    var t = TestEnum.test1
    t = .test2
    t = .test3

    观察上面带有原始值枚举分配内存和占用内存情况

     从最上面讲述带有原始值的枚举(红色标记)原始值不占用枚举变量的内存

    所以仅仅需要1个字节来区分test1, test2,test3,我们再来看一个test2,看内存存储的是多少 

     看出test2存储的是依然是1,和原始值内容没有任何关系,存储和示例1没有区别,再次印证了,原始值不占用枚举变量的内存,不影响枚举内存结构和存储

    示例3 带有关联值的枚举内存结构

    关联值从上面基本使用得出关联值(Associated Values)将枚举的成员值跟其他类型的值关联存储在一起

    enum TestEnum {
        case test1(Int, Int, Int)
        case test2(Int, Int)
        case test3(Int)
        case test4(Bool)
        case test5
    }
    var t = TestEnum.test1(1, 2, 3)
    t = .test2(4, 5)
    t = .test3(6)
    t = .test4(true)
    t = .test5

    继续使用MemoryLayout来看内存分配

    从上面可看出TestEnum枚举实际占用内存空间大小为25,又因为内存对齐为8,所以系统分配了32个字节的大小给TestEnum

    下面着重讲解为什么实际占用了25个字节,又是怎么存储的?

    通过内存小工具查看枚举地址

     然后View Memory工具查看内存结构如下

     上面得出Int占据8个字节,对于TestEnum.test1(1, 2, 3)用24个字节存储这些关联值,得出关联值(Associated Values)将枚举的成员值跟其他类型的值关联存储在一的结论是正确的!

    (拓展:为什么01,02放在前面,为什么不是放在后面,这牵扯到大小端的问题?下面讲述)

    下面看test2的存储结构t = .test2(4, 5)

     看第25个字节为Test2为0x01 = 1, test1的第25个字节为0x00 = 0, 依次类推,查看test4应该为3,下面揭开谜底

     关联值枚举存储结论

    有一个字节存储成员值,用于区分哪一个成员值

    N个字节存储关联值(N取占用内存量最大的关联值),任何一个case的关联值都会共用这N个字节

    上面代码与查看内存小工具封装代码https://github.com/zxy1829760/SwiftEnum

     拓展-大小端问题

     存储0x11223344,大小端存储如下

     以上就是枚举内存的底层结构,希望对大家有所帮助!!! 下一篇将讲述struct与class的区别!

  • 相关阅读:
    java-数组
    java-条件判断和循环语句
    java-运算符
    python类与对象
    C#全角半角转换函数
    自己学会汉化DevExpress控件[转]
    DevExpress.XtraGrid的使用(部分)
    .Net 代码安全保护产品DNGuard HVM使用
    DataGridView 添加ComboBox
    c# 使用ChartDirector绘图的一些个人体会
  • 原文地址:https://www.cnblogs.com/guohai-stronger/p/12368185.html
Copyright © 2020-2023  润新知